VOID IpxRefBinding( IN PBINDING Binding ) /*++ Routine Description: This routine increments the reference count on a device context. Arguments: Binding - Pointer to a transport device context object. Return Value: none. --*/ { CTEAssert (Binding->ReferenceCount > 0); // not perfect, but... (VOID)InterlockedIncrement (&Binding->ReferenceCount); } /* IpxRefBinding */
NTSTATUS CloseRtAddress( IN PDEVICE pDevice, IN PIRP pIrp) { NTSTATUS status; PRT_INFO pRt; CTELockHandle OldIrq; PLIST_ENTRY pHead; ULONG Index; IpxPrint0("CloseRtAddress - entered\n"); // pRt = REQUEST_OPEN_CONTEXT(pIrp); pRt = pRtInfo; Index = RT_ADDRESS_INDEX(pIrp); IpxPrint1("CloseRtAdd: Index = (%d)\n", Index); IpxVerifyRt(pRt); CTEAssert(pRt && (pRt == pRtInfo)); CTEAssert(Index < IPX_RT_MAX_ADDRESSES); CTEAssert(pRt->AddFl[Index].State == RT_CLOSING); // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)(pRt->AddFl[Index].AddressFile); //REQUEST_OPEN_TYPE(pIrp) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; CTEGetLock(&pRt->Lock, &OldIrq); pRt->AddFl[Index].State = RT_EMPTY; pRt->NoOfAdds--; CTEFreeLock(&pRt->Lock, OldIrq); // // THis is a counter to the RT_CREATE // IpxDereferenceRt(pRt, RT_CLOSE); return(STATUS_SUCCESS); }
NTSTATUS SpxTimerInit( VOID ) /*++ Routine Description: Initialize the timer component for the appletalk stack. Arguments: Return Value: --*/ { #if !defined(_PNP_POWER) BOOLEAN TimerStarted; #endif !_PNP_POWER // Initialize the timer and its associated Dpc. timer will be kicked // off when we get the first card arrival notification from ipx KeInitializeTimer(&spxTimer); CTEInitLock(&spxTimerLock); KeInitializeDpc(&spxTimerDpc, spxTimerDpcRoutine, NULL); spxTimerTick = RtlConvertLongToLargeInteger(SPX_TIMER_TICK); #if !defined(_PNP_POWER) TimerStarted = KeSetTimer(&spxTimer, spxTimerTick, &spxTimerDpc); CTEAssert(!TimerStarted); #endif !_PNP_POWER return STATUS_SUCCESS; }
VOID IpxDerefBinding( IN PBINDING Binding ) /*++ Routine Description: This routine dereferences a device context by decrementing the reference count contained in the structure. Currently, we don't do anything special when the reference count drops to zero, but we could dynamically unload stuff then. Arguments: Binding - Pointer to a transport device context object. Return Value: none. --*/ { LONG result; result = InterlockedDecrement (&Binding->ReferenceCount); CTEAssert (result >= 0); if (result == 0) { IpxDestroyBinding (Binding); } } /* IpxDerefBinding */
//* TdiQueryInfoEx - Extended TDI query information. // // This is the new TDI query information handler. We take in a TDIObjectID // structure, a buffer and length, and some context information, and return // the requested information if possible. // // Input: Request - The request structure for this command. // ID - The object ID // Buffer - Pointer to buffer to be filled in. // Size - Pointer to size in bytes of Buffer. On exit, // filled in with bytes written. // Context - Pointer to context buffer. // // Returns: Status of attempt to get information. // TDI_STATUS TdiQueryInformationEx(PTDI_REQUEST Request, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, void *Context) { uint BufferSize = *Size; uint InfoSize; void *InfoPtr; uint Fixed; CTELockHandle Handle; #ifndef VXD CTELock *LockPtr; #else #ifdef DEBUG CTELock *LockPtr; #endif #endif uint Offset = 0; uchar InfoBuffer[sizeof(TCPConnTableEntry)]; uint BytesRead; uint Valid; uint Entity; uint BytesCopied; // First check to see if he's querying for list of entities. Entity = ID->toi_entity.tei_entity; if (Entity == GENERIC_ENTITY) { *Size = 0; if (ID->toi_class != INFO_CLASS_GENERIC || ID->toi_type != INFO_TYPE_PROVIDER || ID->toi_id != ENTITY_LIST_ID) { return TDI_INVALID_PARAMETER; } // Make sure we have room for it the list in the buffer. InfoSize = EntityCount * sizeof(TDIEntityID); if (BufferSize < InfoSize) { // Not enough room. return TDI_BUFFER_TOO_SMALL; } *Size = InfoSize; // Copy it in, free our temp. buffer, and return success. (void)CopyFlatToNdis(Buffer, (uchar *)EntityList, InfoSize, &Offset, &BytesCopied); return TDI_SUCCESS; } //* Check the level. If it can't be for us, pass it down. #ifndef UDP_ONLY if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) { #else if (Entity != CL_TL_ENTITY) { #endif // When we support multiple lower entities at this layer we'll have // to figure out which one to dispatch to. For now, just pass it // straight down. return (*LocalNetInfo.ipi_qinfo)(ID, Buffer, Size, Context); } if (ID->toi_entity.tei_instance != TL_INSTANCE) { // We only support a single instance. return TDI_INVALID_REQUEST; } // Zero returned parameters in case of an error below. *Size = 0; if (ID->toi_class == INFO_CLASS_GENERIC) { // This is a generic request. if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) { if (BufferSize >= sizeof(uint)) { *(uint *)&InfoBuffer[0] = (Entity == CO_TL_ENTITY) ? CO_TL_TCP : CL_TL_UDP; (void)CopyFlatToNdis(Buffer, InfoBuffer, sizeof(uint), &Offset, &BytesCopied); return TDI_SUCCESS; } else return TDI_BUFFER_TOO_SMALL; } return TDI_INVALID_PARAMETER; } if (ID->toi_class == INFO_CLASS_PROTOCOL) { // Handle protocol specific class of information. For us, this is // the MIB-2 stuff or the minimal stuff we do for oob_inline support. #ifndef UDP_ONLY if (ID->toi_type == INFO_TYPE_CONNECTION) { TCPConn *Conn; TCB *QueryTCB; TCPSocketAMInfo *AMInfo; CTELockHandle TCBHandle; if (BufferSize < sizeof(TCPSocketAMInfo) || ID->toi_id != TCP_SOCKET_ATMARK) return TDI_INVALID_PARAMETER; AMInfo = (TCPSocketAMInfo *)InfoBuffer; CTEGetLock(&ConnTableLock, &Handle); Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); if (Conn != NULL) { CTEStructAssert(Conn, tc); QueryTCB = Conn->tc_tcb; if (QueryTCB != NULL) { CTEStructAssert(QueryTCB, tcb); CTEGetLock(&QueryTCB->tcb_lock, &TCBHandle); if ((QueryTCB->tcb_flags & (URG_INLINE | URG_VALID)) == (URG_INLINE | URG_VALID)) { // We're in inline mode, and the urgent data fields are // valid. AMInfo->tsa_size = QueryTCB->tcb_urgend - QueryTCB->tcb_urgstart + 1; // Rcvnext - pendingcnt is the sequence number of the // next byte of data that will be delivered to the // client. Urgend - that value is the offset in the // data stream of the end of urgent data. AMInfo->tsa_offset = QueryTCB->tcb_urgend - (QueryTCB->tcb_rcvnext - QueryTCB->tcb_pendingcnt); } else { AMInfo->tsa_size = 0; AMInfo->tsa_offset = 0; } CTEFreeLock(&QueryTCB->tcb_lock, TCBHandle); *Size = sizeof(TCPSocketAMInfo); CopyFlatToNdis(Buffer, InfoBuffer, sizeof(TCPSocketAMInfo), &Offset, &BytesCopied); return TDI_SUCCESS; } } return TDI_INVALID_PARAMETER; } #endif if (ID->toi_type != INFO_TYPE_PROVIDER) return TDI_INVALID_PARAMETER; switch (ID->toi_id) { case UDP_MIB_STAT_ID: #if UDP_MIB_STAT_ID != TCP_MIB_STAT_ID case TCP_MIB_STAT_ID: #endif Fixed = TRUE; if (Entity == CL_TL_ENTITY) { InfoSize = sizeof(UDPStats); InfoPtr = &UStats; } else { #ifndef UDP_ONLY InfoSize = sizeof(TCPStats); InfoPtr = &TStats; #else return TDI_INVALID_PARAMETER; #endif } break; case UDP_MIB_TABLE_ID: #if UDP_MIB_TABLE_ID != TCP_MIB_TABLE_ID case TCP_MIB_TABLE_ID: #endif Fixed = FALSE; if (Entity == CL_TL_ENTITY) { InfoSize = sizeof(UDPEntry); InfoPtr = &ReadAOTable; CTEGetLock(&AddrObjTableLock, &Handle); #ifndef VXD LockPtr = &AddrObjTableLock; #else #ifdef DEBUG LockPtr = &AddrObjTableLock; #endif #endif } else { #ifndef UDP_ONLY InfoSize = sizeof(TCPConnTableEntry); InfoPtr = &ReadTCBTable; CTEGetLock(&TCBTableLock, &Handle); #ifndef VXD LockPtr = &TCBTableLock; #else #ifdef DEBUG LockPtr = &TCBTableLock; #endif #endif #else return TDI_INVALID_PARAMETER; #endif } break; default: return TDI_INVALID_PARAMETER; break; } if (Fixed) { if (BufferSize < InfoSize) return TDI_BUFFER_TOO_SMALL; *Size = InfoSize; (void)CopyFlatToNdis(Buffer, InfoPtr, InfoSize, &Offset, &BytesCopied); return TDI_SUCCESS; } else { struct ReadTableStruct *RTSPtr; uint ReadStatus; // Have a variable length (or mult-instance) structure to copy. // InfoPtr points to the structure describing the routines to // call to read the table. // Loop through up to CountWanted times, calling the routine // each time. BytesRead = 0; RTSPtr = InfoPtr; ReadStatus = (*(RTSPtr->rts_validate))(Context, &Valid); // If we successfully read something we'll continue. Otherwise // we'll bail out. if (!Valid) { CTEFreeLock(LockPtr, Handle); return TDI_INVALID_PARAMETER; } while (ReadStatus) { // The invariant here is that there is data in the table to // read. We may or may not have room for it. So ReadStatus // is TRUE, and BufferSize - BytesRead is the room left // in the buffer. if ((int)(BufferSize - BytesRead) >= (int)InfoSize) { ReadStatus = (*(RTSPtr->rts_readnext))(Context, InfoBuffer); BytesRead += InfoSize; Buffer = CopyFlatToNdis(Buffer, InfoBuffer, InfoSize, &Offset, &BytesCopied); } else break; } *Size = BytesRead; CTEFreeLock(LockPtr, Handle); return (!ReadStatus ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); } } if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { // We want to return implementation specific info. For now, error out. return TDI_INVALID_PARAMETER; } return TDI_INVALID_PARAMETER; } //* TdiSetInfoEx - Extended TDI set information. // // This is the new TDI set information handler. We take in a TDIObjectID // structure, a buffer and length. We set the object specifed by the ID // (and possibly by the Request) to the value specified in the buffer. // // Input: Request - The request structure for this command. // ID - The object ID // Buffer - Pointer to buffer containing value to set. // Size - Size in bytes of Buffer. // // Returns: Status of attempt to get information. // TDI_STATUS TdiSetInformationEx(PTDI_REQUEST Request, TDIObjectID *ID, void *Buffer, uint Size) { TCPConnTableEntry *TCPEntry; CTELockHandle TableHandle, TCBHandle; TCB *SetTCB; uint Entity; TCPConn *Conn; TDI_STATUS Status; //* Check the level. If it can't be for us, pass it down. Entity = ID->toi_entity.tei_entity; if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) { // Someday we'll have to figure out how to dispatch. For now, just pass // it down. return (*LocalNetInfo.ipi_setinfo)(ID, Buffer, Size); } if (ID->toi_entity.tei_instance != TL_INSTANCE) return TDI_INVALID_REQUEST; if (ID->toi_class == INFO_CLASS_GENERIC) { // Fill this in when we have generic class defines. return TDI_INVALID_PARAMETER; } //* Now look at the rest of it. if (ID->toi_class == INFO_CLASS_PROTOCOL) { // Handle protocol specific class of information. For us, this is // the MIB-2 stuff, as well as common sockets options, // and in particular the setting of the state of a TCP connection. if (ID->toi_type == INFO_TYPE_CONNECTION) { TCPSocketOption *Option; uint Flag; uint Value; #ifndef UDP_ONLY // A connection type. Get the connection, and then figure out // what to do with it. Status = TDI_INVALID_PARAMETER; if (Size < sizeof(TCPSocketOption)) return Status; CTEGetLock(&ConnTableLock, &TableHandle); Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); if (Conn != NULL) { CTEStructAssert(Conn, tc); Status = TDI_SUCCESS; if (ID->toi_id == TCP_SOCKET_WINDOW) { // This is a funny option, because it doesn't involve // flags. Handle this specially. Option = (TCPSocketOption *)Buffer; // We don't allow anyone to shrink the window, as this // gets too weird from a protocol point of view. Also, // make sure they don't try and set anything too big. if (Option->tso_value > 0xffff) Status = TDI_INVALID_PARAMETER; else if (Option->tso_value > Conn->tc_window || Conn->tc_tcb == NULL) { Conn->tc_flags |= CONN_WINSET; Conn->tc_window = Option->tso_value; SetTCB = Conn->tc_tcb; if (SetTCB != NULL) { CTEStructAssert(SetTCB, tcb); CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); CTEAssert(Option->tso_value > SetTCB->tcb_defaultwin); if (DATA_RCV_STATE(SetTCB->tcb_state) && !CLOSING(SetTCB)) { SetTCB->tcb_flags |= WINDOW_SET; SetTCB->tcb_defaultwin = Option->tso_value; SetTCB->tcb_refcnt++; CTEFreeLock(&SetTCB->tcb_lock, TCBHandle); SendACK(SetTCB); CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); DerefTCB(SetTCB, TCBHandle); } else { CTEFreeLock(&SetTCB->tcb_lock, TCBHandle); } } } CTEFreeLock(&ConnTableLock, TableHandle); return Status; } Flag = 0; Option = (TCPSocketOption *)Buffer; Value = Option->tso_value; // We have the connection, so figure out which flag to set. switch (ID->toi_id) { case TCP_SOCKET_NODELAY: Value = !Value; Flag = NAGLING; break; case TCP_SOCKET_KEEPALIVE: Flag = KEEPALIVE; break; case TCP_SOCKET_BSDURGENT: Flag = BSD_URGENT; break; case TCP_SOCKET_OOBINLINE: Flag = URG_INLINE; break; default: Status = TDI_INVALID_PARAMETER; break; } if (Status == TDI_SUCCESS) { if (Value) Conn->tc_tcbflags |= Flag; else Conn->tc_tcbflags &= ~Flag; SetTCB = Conn->tc_tcb; if (SetTCB != NULL) { CTEStructAssert(SetTCB, tcb); CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); if (Value) SetTCB->tcb_flags |= Flag; else SetTCB->tcb_flags &= ~Flag; if (ID->toi_id == TCP_SOCKET_KEEPALIVE) { SetTCB->tcb_alive = TCPTime; SetTCB->tcb_kacount = 0; } CTEFreeLock(&SetTCB->tcb_lock, TCBHandle); } } } CTEFreeLock(&ConnTableLock, TableHandle); return Status; #else return TDI_INVALID_PARAMETER; #endif } if (ID->toi_type == INFO_TYPE_ADDRESS_OBJECT) { // We're setting information on an address object. This is // pretty simple. return SetAddrOptions(Request, ID->toi_id, Size, Buffer); } if (ID->toi_type != INFO_TYPE_PROVIDER) return TDI_INVALID_PARAMETER; #ifndef UDP_ONLY if (ID->toi_id == TCP_MIB_TABLE_ID) { if (Size != sizeof(TCPConnTableEntry)) return TDI_INVALID_PARAMETER; TCPEntry = (TCPConnTableEntry *)Buffer; if (TCPEntry->tct_state != TCP_DELETE_TCB) return TDI_INVALID_PARAMETER; // We have an apparently valid request. Look up the TCB. CTEGetLock(&TCBTableLock, &TableHandle); SetTCB = FindTCB(TCPEntry->tct_localaddr, TCPEntry->tct_remoteaddr, (ushort)TCPEntry->tct_remoteport, (ushort)TCPEntry->tct_localport); // We found him. If he's not closing or closed, close him. if (SetTCB != NULL) { CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); CTEFreeLock(&TCBTableLock, TCBHandle); // We've got him. Bump his ref. count, and call TryToCloseTCB // to mark him as closing. Then notify the upper layer client // of the disconnect. SetTCB->tcb_refcnt++; if (SetTCB->tcb_state != TCB_CLOSED && !CLOSING(SetTCB)) { SetTCB->tcb_flags |= NEED_RST; TryToCloseTCB(SetTCB, TCB_CLOSE_ABORTED, TableHandle); CTEGetLock(&SetTCB->tcb_lock, &TableHandle); if (SetTCB->tcb_state != TCB_TIME_WAIT) { // Remove him from the TCB, and notify the client. CTEFreeLock(&SetTCB->tcb_lock, TableHandle); RemoveTCBFromConn(SetTCB); NotifyOfDisc(SetTCB, NULL, TDI_CONNECTION_RESET); CTEGetLock(&SetTCB->tcb_lock, &TableHandle); } } DerefTCB(SetTCB, TableHandle); return TDI_SUCCESS; } else { CTEFreeLock(&TCBTableLock, TableHandle); return TDI_INVALID_PARAMETER; } } else return TDI_INVALID_PARAMETER; #else return TDI_INVALID_PARAMETER; #endif } if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { // We want to return implementation specific info. For now, error out. return TDI_INVALID_REQUEST; } return TDI_INVALID_REQUEST; }
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 */
//* TdiQueryInformation - Query Information handler. // // The TDI QueryInformation routine. Called when the client wants to // query information on a connection, the provider as a whole, or to // get statistics. // // Input: Request - The request structure for this command. // QueryType - The type of query to be performed. // Buffer - Buffer to place data into. // BufferSize - Pointer to size in bytes of buffer. On return, // filled in with bytes copied. // IsConn - Valid only for TDI_QUERY_ADDRESS_INFO. TRUE // if we are querying the address info on // a connection. // // Returns: Status of attempt to query information. // TDI_STATUS TdiQueryInformation(PTDI_REQUEST Request, uint QueryType, PNDIS_BUFFER Buffer, uint *BufferSize, uint IsConn) { union { TDI_CONNECTION_INFO ConnInfo; TDI_ADDRESS_INFO AddrInfo; TDI_PROVIDER_INFO ProviderInfo; TDI_PROVIDER_STATISTICS ProviderStats; } InfoBuf; uint InfoSize; CTELockHandle ConnTableHandle, TCBHandle, AddrHandle, AOHandle; #ifndef UDP_ONLY TCPConn *Conn; TCB *InfoTCB; #endif AddrObj *InfoAO; void *InfoPtr = NULL; uint Offset; uint Size; uint BytesCopied; switch (QueryType) { case TDI_QUERY_BROADCAST_ADDRESS: return TDI_INVALID_QUERY; break; case TDI_QUERY_PROVIDER_INFO: InfoBuf.ProviderInfo.Version = 0x100; #ifndef UDP_ONLY InfoBuf.ProviderInfo.MaxSendSize = 0xffffffff; #else InfoBuf.ProviderInfo.MaxSendSize = 0; #endif InfoBuf.ProviderInfo.MaxConnectionUserData = 0; InfoBuf.ProviderInfo.MaxDatagramSize = 0xffff - sizeof(UDPHeader); InfoBuf.ProviderInfo.ServiceFlags = MY_SERVICE_FLAGS; InfoBuf.ProviderInfo.MinimumLookaheadData = 1; InfoBuf.ProviderInfo.MaximumLookaheadData = 0xffff; InfoBuf.ProviderInfo.NumberOfResources = 0; InfoBuf.ProviderInfo.StartTime.LowPart = StartTime; InfoBuf.ProviderInfo.StartTime.HighPart = 0; InfoSize = sizeof(TDI_PROVIDER_INFO); InfoPtr = &InfoBuf.ProviderInfo; break; case TDI_QUERY_ADDRESS_INFO: InfoSize = sizeof(TDI_ADDRESS_INFO) - sizeof(TRANSPORT_ADDRESS) + TCP_TA_SIZE; CTEMemSet(&InfoBuf.AddrInfo, 0, TCP_TA_SIZE); InfoBuf.AddrInfo.ActivityCount = 1; // Since noone knows what // this means, we'll set // it to one. if (IsConn) { #ifdef UDP_ONLY return TDI_INVALID_QUERY; #else CTEGetLock(&AddrObjTableLock, &AddrHandle); CTEGetLock(&ConnTableLock, &ConnTableHandle); Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); if (Conn != NULL) { CTEStructAssert(Conn, tc); InfoTCB = Conn->tc_tcb; // If we have a TCB we'll // return information about that TCB. Otherwise we'll return // info about the address object. if (InfoTCB != NULL) { CTEStructAssert(InfoTCB, tcb); CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle); CTEFreeLock(&ConnTableLock, TCBHandle); CTEFreeLock(&AddrObjTableLock, ConnTableHandle); BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address, InfoTCB->tcb_saddr, InfoTCB->tcb_sport); CTEFreeLock(&InfoTCB->tcb_lock, AddrHandle); InfoPtr = &InfoBuf.AddrInfo; break; } else { // No TCB, return info on the AddrObj. InfoAO = Conn->tc_ao; if (InfoAO != NULL) { // We have an AddrObj. CTEStructAssert(InfoAO, ao); CTEGetLock(&InfoAO->ao_lock, &AOHandle); BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address, InfoAO->ao_addr, InfoAO->ao_port); CTEFreeLock(&InfoAO->ao_lock, AOHandle); CTEFreeLock(&ConnTableLock, ConnTableHandle); CTEFreeLock(&AddrObjTableLock, AddrHandle); InfoPtr = &InfoBuf.AddrInfo; break; } } } // Fall through to here when we can't find the connection, or // the connection isn't associated. CTEFreeLock(&ConnTableLock, ConnTableHandle); CTEFreeLock(&AddrObjTableLock, AddrHandle); return TDI_INVALID_CONNECTION; break; #endif } else { // Asking for information on an addr. object. #ifdef VXD InfoAO = GetIndexedAO((uint)Request->Handle.AddressHandle); if (InfoAO == NULL) return TDI_ADDR_INVALID; #else InfoAO = Request->Handle.AddressHandle; #endif CTEStructAssert(InfoAO, ao); CTEGetLock(&InfoAO->ao_lock, &AOHandle); if (!AO_VALID(InfoAO)) { CTEFreeLock(&InfoAO->ao_lock, AOHandle); return TDI_ADDR_INVALID; } BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address, InfoAO->ao_addr, InfoAO->ao_port); CTEFreeLock(&InfoAO->ao_lock, AOHandle); InfoPtr = &InfoBuf.AddrInfo; break; } break; case TDI_QUERY_CONNECTION_INFO: #ifndef UDP_ONLY InfoSize = sizeof(TDI_CONNECTION_INFO); CTEGetLock(&ConnTableLock, &ConnTableHandle); Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); if (Conn != NULL) { CTEStructAssert(Conn, tc); InfoTCB = Conn->tc_tcb; // If we have a TCB we'll return the information. Otherwise // we'll error out. if (InfoTCB != NULL) { ulong TotalTime; ulong BPS, PathBPS; IP_STATUS IPStatus; CTEULargeInt TempULargeInt; CTEStructAssert(InfoTCB, tcb); CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle); CTEFreeLock(&ConnTableLock, TCBHandle); CTEMemSet(&InfoBuf.ConnInfo, 0, sizeof(TDI_CONNECTION_INFO)); InfoBuf.ConnInfo.State = (ulong)InfoTCB->tcb_state; IPStatus = (*LocalNetInfo.ipi_getpinfo)(InfoTCB->tcb_daddr, InfoTCB->tcb_saddr, NULL, &PathBPS); if (IPStatus != IP_SUCCESS) { InfoBuf.ConnInfo.Throughput.LowPart = 0xFFFFFFFF; InfoBuf.ConnInfo.Throughput.HighPart = 0xFFFFFFFF; } else { InfoBuf.ConnInfo.Throughput.HighPart = 0; TotalTime = InfoTCB->tcb_totaltime / (1000 / MS_PER_TICK); if (TotalTime != 0) { TempULargeInt.LowPart = InfoTCB->tcb_bcountlow; TempULargeInt.HighPart = InfoTCB->tcb_bcounthi; BPS = CTEEnlargedUnsignedDivide(TempULargeInt, TotalTime, NULL); InfoBuf.ConnInfo.Throughput.LowPart = MIN(BPS, PathBPS); } else InfoBuf.ConnInfo.Throughput.LowPart = PathBPS; } // To figure the delay we use the rexmit timeout. Our // rexmit timeout is roughly the round trip time plus // some slop, so we use half of that as the one way delay. #ifdef VXD InfoBuf.ConnInfo.Delay.LowPart = (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2; InfoBuf.ConnInfo.Throughput.HighPart = 0; #else // VXD InfoBuf.ConnInfo.Delay.LowPart = (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2; InfoBuf.ConnInfo.Delay.HighPart = 0; // // Convert milliseconds to 100ns and negate for relative // time. // InfoBuf.ConnInfo.Delay = RtlExtendedIntegerMultiply( InfoBuf.ConnInfo.Delay, 10000 ); CTEAssert(InfoBuf.ConnInfo.Delay.HighPart == 0); InfoBuf.ConnInfo.Delay.QuadPart = -InfoBuf.ConnInfo.Delay.QuadPart; #endif // VXD CTEFreeLock(&InfoTCB->tcb_lock, ConnTableHandle); InfoPtr = &InfoBuf.ConnInfo; break; } } // Come through here if we can't find the connection or it has // no TCB. CTEFreeLock(&ConnTableLock, ConnTableHandle); return TDI_INVALID_CONNECTION; break; #else // UDP_ONLY return TDI_INVALID_QUERY; break; #endif // UDP_ONLY case TDI_QUERY_PROVIDER_STATISTICS: CTEMemSet(&InfoBuf.ProviderStats, 0, sizeof(TDI_PROVIDER_STATISTICS)); InfoBuf.ProviderStats.Version = 0x100; InfoSize = sizeof(TDI_PROVIDER_STATISTICS); InfoPtr = &InfoBuf.ProviderStats; break; default: return TDI_INVALID_QUERY; break; } // When we get here, we've got the pointers set up and the information // filled in. CTEAssert(InfoPtr != NULL); Offset = 0; Size = *BufferSize; (void)CopyFlatToNdis(Buffer, InfoPtr, MIN(InfoSize, Size), &Offset, &BytesCopied); if (Size < InfoSize) return TDI_BUFFER_OVERFLOW; else { *BufferSize = InfoSize; return TDI_SUCCESS; } }
NTSTATUS NbiBind( IN PDEVICE Device, IN PCONFIG Config ) /*++ Routine Description: This routine binds the Netbios module of ISN to the IPX module, which provides the NDIS binding services. Arguments: Device - Pointer to the Netbios device. Config - Pointer to the configuration information. Return Value: The function value is the final status from the initialization operation. --*/ { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; /* union { IPX_INTERNAL_BIND_INPUT Input; IPX_INTERNAL_BIND_OUTPUT Output; } Bind; */ InitializeObjectAttributes( &ObjectAttributes, &Config->BindName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwCreateFile( &Device->BindHandle, SYNCHRONIZE | GENERIC_READ, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0L); if (!NT_SUCCESS(Status)) { NB_DEBUG (BIND, ("Could not open IPX (%ws) %lx\n", Config->BindName.Buffer, Status)); NbiWriteGeneralErrorLog( Device, EVENT_TRANSPORT_ADAPTER_NOT_FOUND, 1, Status, Config->BindName.Buffer, 0, NULL); return Status; } // // Fill in our bind data. // #if defined(_PNP_POWER) Device->BindInput.Version = ISN_VERSION; #else Device->BindInput.Version = 1; #endif _PNP_POWER Device->BindInput.Identifier = IDENTIFIER_NB; Device->BindInput.BroadcastEnable = TRUE; Device->BindInput.LookaheadRequired = 192; Device->BindInput.ProtocolOptions = 0; Device->BindInput.ReceiveHandler = NbiReceive; Device->BindInput.ReceiveCompleteHandler = NbiReceiveComplete; Device->BindInput.StatusHandler = NbiStatus; Device->BindInput.SendCompleteHandler = NbiSendComplete; Device->BindInput.TransferDataCompleteHandler = NbiTransferDataComplete; Device->BindInput.FindRouteCompleteHandler = NbiFindRouteComplete; Device->BindInput.LineUpHandler = NbiLineUp; Device->BindInput.LineDownHandler = NbiLineDown; Device->BindInput.ScheduleRouteHandler = NULL; #if defined(_PNP_POWER) Device->BindInput.PnPHandler = NbiPnPNotification; #endif _PNP_POWER Status = ZwDeviceIoControlFile( Device->BindHandle, // HANDLE to File NULL, // HANDLE to Event NULL, // ApcRoutine NULL, // ApcContext &IoStatusBlock, // IO_STATUS_BLOCK IOCTL_IPX_INTERNAL_BIND, // IoControlCode &Device->BindInput, // Input Buffer sizeof(Device->BindInput), // Input Buffer Length &Device->Bind, // OutputBuffer sizeof(Device->Bind)); // OutputBufferLength // // We open synchronous, so this shouldn't happen. // CTEAssert (Status != STATUS_PENDING); // // Save the bind data. // if (Status == STATUS_SUCCESS) { NB_DEBUG2 (BIND, ("Successfully bound to IPX (%ws)\n", Config->BindName.Buffer)); // RtlCopyMemory (&Device->Bind, &Bind.Output, sizeof(IPX_INTERNAL_BIND_OUTPUT)); #if !defined(_PNP_POWER) RtlZeroMemory (Device->ReservedNetbiosName, 16); RtlCopyMemory (&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_MAXIMUM_NIC_ID, (USHORT)0, &Device->MaximumNicId, sizeof(Device->MaximumNicId), NULL); CTEAssert (Status == STATUS_SUCCESS); #endif !_PNP_POWER } else { NB_DEBUG (BIND, ("Could not bind to IPX (%ws) %lx\n", Config->BindName.Buffer, Status)); NbiWriteGeneralErrorLog( Device, EVENT_TRANSPORT_BINDING_FAILED, 1, Status, Config->BindName.Buffer, 0, NULL); ZwClose(Device->BindHandle); } return Status; } /* NbiBind */
VOID NbiProcessDatagram( IN NDIS_HANDLE MacBindingHandle, IN NDIS_HANDLE MacReceiveContext, IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR LookaheadBuffer, IN UINT LookaheadBufferSize, IN UINT LookaheadBufferOffset, IN UINT PacketSize, IN BOOLEAN Broadcast ) /*++ Routine Description: This routine handles datagram indications. Arguments: MacBindingHandle - A handle to use when calling NdisTransferData. MacReceiveContext - A context to use when calling NdisTransferData. RemoteAddress - The local target this packet was received from. MacOptions - The MAC options for the underlying NDIS binding. LookaheadBuffer - The lookahead buffer, starting at the IPX header. LookaheadBufferSize - The length of the lookahead data. LookaheadBufferOffset - The offset to add when calling NdisTransferData. PacketSize - The total length of the packet, starting at the IPX header. Broadcast - TRUE if the frame was a broadcast datagram. Return Value: None. --*/ { PADDRESS Address; NDIS_STATUS NdisStatus; PUCHAR NetbiosName; NB_CONNECTIONLESS UNALIGNED * Connectionless = (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; PDEVICE Device = NbiDevice; PSINGLE_LIST_ENTRY s; PNB_RECEIVE_RESERVED ReceiveReserved; PNB_RECEIVE_BUFFER ReceiveBuffer; ULONG DataOffset; UINT BytesTransferred; PNDIS_PACKET Packet; CTELockHandle LockHandle; // // See if there is an address that might want this. // if (Broadcast) { NetbiosName = (PVOID)-1; } else { NetbiosName = (PUCHAR)Connectionless->Datagram.DestinationName; if (Device->AddressCounts[NetbiosName[0]] == 0) { return; } } DataOffset = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); #if defined(_PNP_POWER) if ((PacketSize < DataOffset) || (PacketSize > DataOffset + Device->CurMaxReceiveBufferSize)) { #else if ((PacketSize < DataOffset) || (PacketSize > DataOffset + Device->Bind.LineInfo.MaximumPacketSize)) { #endif _PNP_POWER NB_DEBUG (DATAGRAM, ("Datagram length %d discarded\n", PacketSize)); return; } Address = NbiFindAddress (Device, NetbiosName); if (Address == NULL) { return; } // // We need to cache the remote name if the packet came across the router. // This allows this machine to get back to the RAS client which might // have sent this datagram. We currently dont allow broadcasts to go out // on the dial-in line. // Dont cache some of the widely used group names, that would be too much // to store in cache. // #if 0 if ( Connectionless->IpxHeader.TransportControl && !( (Address->NetbiosAddress.NetbiosName[15] == 0x0 ) && (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && !( (Address->NetbiosAddress.NetbiosName[15] == 0x01 ) && (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && !( (Address->NetbiosAddress.NetbiosName[15] == 0x1E ) && (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { #endif if ( Connectionless->IpxHeader.TransportControl && ( (Address->NetbiosAddress.NetbiosName[15] == 0x1c ) && (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { PNETBIOS_CACHE CacheName; NB_GET_LOCK (&Device->Lock, &LockHandle); if ( FindInNetbiosCacheTable ( Device->NameCache, Connectionless->Datagram.SourceName, &CacheName ) != STATUS_SUCCESS ) { CacheName = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); if (CacheName ) { RtlCopyMemory (CacheName->NetbiosName, Connectionless->Datagram.SourceName, 16); CacheName->Unique = TRUE; CacheName->ReferenceCount = 1; RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); CacheName->NetworksAllocated = 1; CacheName->NetworksUsed = 1; CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); CacheName->Networks[0].LocalTarget = *RemoteAddress; NB_DEBUG2 (CACHE, ("Alloc new cache from Datagram %lx for <%.16s>\n", CacheName, CacheName->NetbiosName)); CacheName->TimeStamp = Device->CacheTimeStamp; InsertInNetbiosCacheTable( Device->NameCache, CacheName); } } else if ( CacheName->Unique ) { // // We already have an entry for this remote. We should update // the address. This is so that if the ras client dials-out // then dials-in again and gets a new address, we dont end up // caching the old address. // if ( !RtlEqualMemory( &CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12) ) { RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); CacheName->Networks[0].LocalTarget = *RemoteAddress; } } NB_FREE_LOCK (&Device->Lock, LockHandle); } // // We need to allocate a packet and buffer for the transfer. // s = NbiPopReceivePacket (Device); if (s == NULL) { NbiDereferenceAddress (Address, AREF_FIND); return; } ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); s = NbiPopReceiveBuffer (Device); if (s == NULL) { ExInterlockedPushEntrySList( &Device->ReceivePacketList, &ReceiveReserved->PoolLinkage, &NbiGlobalPoolInterlock); NbiDereferenceAddress (Address, AREF_FIND); return; } ReceiveBuffer = CONTAINING_RECORD (s, NB_RECEIVE_BUFFER, PoolLinkage); Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); ReceiveReserved->u.RR_DG.ReceiveBuffer = ReceiveBuffer; // // Now that we have a packet and a buffer, set up the transfer. // The indication to the TDI clients will happen at receive // complete time. // NdisChainBufferAtFront (Packet, ReceiveBuffer->NdisBuffer); ReceiveBuffer->Address = Address; ReceiveReserved->Type = RECEIVE_TYPE_DATAGRAM; CTEAssert (!ReceiveReserved->TransferInProgress); ReceiveReserved->TransferInProgress = TRUE; TdiCopyLookaheadData( &ReceiveBuffer->RemoteName, Connectionless->Datagram.SourceName, 16, (MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); (*Device->Bind.TransferDataHandler) ( &NdisStatus, MacBindingHandle, MacReceiveContext, LookaheadBufferOffset + DataOffset, PacketSize - DataOffset, Packet, &BytesTransferred); if (NdisStatus != NDIS_STATUS_PENDING) { #if DBG if (NdisStatus == STATUS_SUCCESS) { CTEAssert (BytesTransferred == PacketSize - DataOffset); } #endif NbiTransferDataComplete( Packet, NdisStatus, BytesTransferred); } } /* NbiProcessDatagram */ VOID NbiIndicateDatagram( IN PADDRESS Address, IN PUCHAR RemoteName, IN PUCHAR Data, IN ULONG DataLength ) /*++ Routine Description: This routine indicates a datagram to clients on the specified address. It is called from NbiReceiveComplete. Arguments: Address - The address the datagram was sent to. RemoteName - The source netbios address of the datagram. Data - The data. DataLength - The length of the data. Return Value: None. --*/ { PLIST_ENTRY p, q; PIRP Irp; ULONG IndicateBytesCopied; PREQUEST Request; TA_NETBIOS_ADDRESS SourceName; PTDI_CONNECTION_INFORMATION RemoteInformation; PADDRESS_FILE AddressFile, ReferencedAddressFile; PTDI_CONNECTION_INFORMATION DatagramInformation; TDI_ADDRESS_NETBIOS UNALIGNED * DatagramAddress; PDEVICE Device = NbiDevice; NB_DEFINE_LOCK_HANDLE (LockHandle) CTELockHandle CancelLH; // // Update our statistics. // ++Device->Statistics.DatagramsReceived; ADD_TO_LARGE_INTEGER( &Device->Statistics.DatagramBytesReceived, DataLength); // // Call the client's ReceiveDatagram indication handler. He may // want to accept the datagram that way. // TdiBuildNetbiosAddress (RemoteName, FALSE, &SourceName); ReferencedAddressFile = NULL; NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); for (p = Address->AddressFileDatabase.Flink; p != &Address->AddressFileDatabase; p = p->Flink) { // // Find the next open address file in the list. // AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { continue; } NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); // // do we have a datagram receive request outstanding? If so, we will // satisfy it first. We run through the receive datagram queue // until we find a datagram with no remote address or with // this sender's address as its remote address. // for (q = AddressFile->ReceiveDatagramQueue.Flink; q != &AddressFile->ReceiveDatagramQueue; q = q->Flink) { Request = LIST_ENTRY_TO_REQUEST (q); DatagramInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) REQUEST_PARAMETERS(Request))->ReceiveDatagramInformation; if (DatagramInformation && (DatagramInformation->RemoteAddress) && (DatagramAddress = NbiParseTdiAddress(DatagramInformation->RemoteAddress, FALSE)) && (!RtlEqualMemory( RemoteName, DatagramAddress->NetbiosName, 16))) { continue; } break; } if (q != &AddressFile->ReceiveDatagramQueue) { RemoveEntryList (q); NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); if (ReferencedAddressFile != NULL) { NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); } ReferencedAddressFile = AddressFile; // // Do this deref now, we hold another one so it // will stick around. // NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); IndicateBytesCopied = 0; // // Fall past the else to copy the data. // } else { NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); if (ReferencedAddressFile != NULL) { NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); } ReferencedAddressFile = AddressFile; // // No receive datagram requests; is there a kernel client? // if (AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE_DATAGRAM]) { IndicateBytesCopied = 0; if ((*AddressFile->ReceiveDatagramHandler)( AddressFile->HandlerContexts[TDI_EVENT_RECEIVE_DATAGRAM], sizeof (TA_NETBIOS_ADDRESS), &SourceName, 0, NULL, TDI_RECEIVE_COPY_LOOKAHEAD, DataLength, // indicated DataLength, // available &IndicateBytesCopied, Data, &Irp) != STATUS_MORE_PROCESSING_REQUIRED) { // // The client did not return a request, go to the // next address file. // NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); continue; } Request = NbiAllocateRequest (Device, Irp); IF_NOT_ALLOCATED(Request) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); continue; } } else { // // The client has nothing posted and no handler, // go on to the next address file. // NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); continue; } } // // We have a request; copy the actual user data. // if ( REQUEST_NDIS_BUFFER (Request) ) { REQUEST_STATUS(Request) = TdiCopyBufferToMdl ( Data, IndicateBytesCopied, DataLength - IndicateBytesCopied, REQUEST_NDIS_BUFFER (Request), 0, &REQUEST_INFORMATION (Request)); } else { // // No buffer specified in the request // REQUEST_INFORMATION (Request) = 0; // // If there was any data to be copied, return error o/w success // REQUEST_STATUS(Request) = ( (DataLength - IndicateBytesCopied) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ); } // // Copy the addressing information. // RemoteInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) REQUEST_PARAMETERS(Request))->ReturnDatagramInformation; if (RemoteInformation != NULL) { RtlCopyMemory( (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, &SourceName, (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); } NB_GET_CANCEL_LOCK( &CancelLH ); IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); NB_FREE_CANCEL_LOCK( CancelLH ); NbiCompleteRequest (Request); NbiFreeRequest (Device, Request); NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); } // end of for loop through the address files
ULONG SpxTimerScheduleEvent( IN TIMER_ROUTINE Worker, // Routine to invoke when time expires IN ULONG MsTime, // Schedule after this much time IN PVOID pContext // Context(s) to pass to the routine ) /*++ Routine Description: Insert an event in the timer event list. If the list is empty, then fire off a timer. The time is specified in ms. We convert to ticks. Each tick is currently 100ms. It may not be zero or negative. The internal timer fires at 100ms granularity. Arguments: Return Value: --*/ { PTIMERLIST pList; CTELockHandle lockHandle; ULONG DeltaTime; ULONG Id = 0; // Convert to ticks. DeltaTime = MsTime/SPX_MS_TO_TICKS; if (DeltaTime == 0) { DBGPRINT(SYSTEM, INFO, ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", MsTime, DeltaTime)); DeltaTime = 1; } DBGPRINT(SYSTEM, INFO, ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", MsTime, DeltaTime)); // Negative or Zero DeltaTime is invalid. CTEAssert (DeltaTime > 0); DBGPRINT(SYSTEM, INFO, ("SpxTimerScheduleEvent: Routine %lx, Time %d, Context %lx\n", Worker, DeltaTime, pContext)); CTEGetLock(&spxTimerLock, &lockHandle); if (spxTimerStopped) { DBGPRINT(SYSTEM, FATAL, ("SpxTimerScheduleEvent: Called after Flush !!\n")); } else do { pList = SpxBPAllocBlock(BLKID_TIMERLIST); if (pList == NULL) { break; } #if DBG pList->tmr_Signature = TMR_SIGNATURE; #endif pList->tmr_Cancelled = FALSE; pList->tmr_Worker = Worker; pList->tmr_AbsTime = DeltaTime; pList->tmr_Context = pContext; Id = pList->tmr_Id = spxTimerId++; // Take care of wrap around if (spxTimerId == 0) spxTimerId = 1; // Enqueue this handler spxTimerEnqueue(pList); } while (FALSE); CTEFreeLock(&spxTimerLock, lockHandle); return Id; }
VOID NbiPnPNotification( 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. --*/ { PDEVICE Device = NbiDevice; USHORT MaximumNicId = 0; CTELockHandle LockHandle; UCHAR PrevReservedName[NB_NETBIOS_NAME_SIZE]; UNICODE_STRING UnicodeDeviceName; NB_DEBUG2( DEVICE, ("Received a pnp notification, opcode %d\n",OpCode )); switch( OpCode ) { case IPX_PNP_ADD_DEVICE : { IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; BOOLEAN ReallocReceiveBuffers = FALSE; NB_GET_LOCK( &Device->Lock, &LockHandle ); if ( PnPInfo->NewReservedAddress ) { *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); // RtlZeroMemory(Device->ReservedNetbiosName, NB_NETBIOS_NAME_SIZE); // RtlCopyMemory(&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); } if ( PnPInfo->FirstORLastDevice ) { CTEAssert( PnPInfo->NewReservedAddress ); CTEAssert( Device->State != DEVICE_STATE_OPEN ); // // we must do this while we still have the device lock. // if ( !Device->LongTimerRunning ) { Device->LongTimerRunning = TRUE; NbiReferenceDevice (Device, DREF_LONG_TIMER); CTEStartTimer( &Device->LongTimer, LONG_TIMER_DELTA, NbiLongTimeout, (PVOID)Device); } Device->State = DEVICE_STATE_OPEN; CTEAssert( !Device->MaximumNicId ); Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; Device->Bind.LineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumSendSize; ReallocReceiveBuffers = TRUE; } else { if ( PnPInfo->LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { ReallocReceiveBuffers = TRUE; } // // MaxSendSize could become smaller. // Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; } Device->MaximumNicId++; // // NbiCreateAdapterAddress( PnPInfo->NodeAddress ); // // And finally remove all the failed cache entries since we might // find those routes using this new adapter // FlushFailedNetbiosCacheEntries(Device->NameCache); NB_FREE_LOCK( &Device->Lock, LockHandle ); if ( ReallocReceiveBuffers ) { PWORK_QUEUE_ITEM WorkItem; WorkItem = NbiAllocateMemory( sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buffer work item"); if ( WorkItem ) { ExInitializeWorkItem( WorkItem, NbiReAllocateReceiveBufferPool, (PVOID) WorkItem ); ExQueueWorkItem( WorkItem, DelayedWorkQueue ); } else { NB_DEBUG( DEVICE, ("Cannt schdule work item to realloc receive buffer pool\n")); } } // // Notify the TDI clients about the device creation // if ( PnPInfo->FirstORLastDevice ) { UnicodeDeviceName.Buffer = Device->DeviceName; UnicodeDeviceName.MaximumLength = Device->DeviceNameLength; UnicodeDeviceName.Length = Device->DeviceNameLength - sizeof(WCHAR); if ( !NT_SUCCESS( TdiRegisterDeviceObject( &UnicodeDeviceName, &Device->TdiRegistrationHandle ) )) { NB_DEBUG( DEVICE, ("Failed to register nwlnknb with TDI\n")); } } break; } case IPX_PNP_DELETE_DEVICE : { IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; PLIST_ENTRY p; PNETBIOS_CACHE CacheName; USHORT i,j,NetworksRemoved; NB_GET_LOCK( &Device->Lock, &LockHandle ); CTEAssert( Device->MaximumNicId ); Device->MaximumNicId--; if ( PnPInfo->FirstORLastDevice ) { Device->State = DEVICE_STATE_LOADED; Device->MaximumNicId = 0; } // // MaximumSendSize could change if the card with the smallest send size just // got removed. MaximumPacketSize could only become smaller and we ignore that // since we dont need to(want to) realloc ReceiveBuffers. // Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; // // Flush all the cache entries that are using this NicId in the local // target. // RemoveInvalidRoutesFromNetbiosCacheTable( Device->NameCache, &PnPInfo->NicHandle ); NbiDestroyAdapterAddress( NULL, PnPInfo->NodeAddress ); NB_FREE_LOCK( &Device->Lock, LockHandle ); /* // // Now mark the previous reserved name in conflict if it has // been registered by any of our client // if ( Address = NbiFindAddress( Device, PrevReservedName ) ) { NB_GET_LOCK( &Address->Lock, &LockHandle ); Address->Flags |= ADDRESS_FLAGS_CONFLICT; NB_FREE_LOCK( &Address->Lock, LockHandle ); NB_DEBUG( ADDRESS, ("Reserved Address %lx<%.16s> is marked CONFLICT\n",Address,Address->NetbiosAddress.NetbiosName)); // // nbifindaddress added a reference, so deref // NbiDereferenceAddress( Address, AREF_FIND ); } */ // // inform tdi clients about the device deletion // if ( PnPInfo->FirstORLastDevice ) { if ( !NT_SUCCESS( TdiDeregisterDeviceObject( Device->TdiRegistrationHandle ) )) { NB_DEBUG( DEVICE, ("Failed to Deregister nwlnknb with TDI\n")); } } break; } case IPX_PNP_ADDRESS_CHANGE: { IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; PADDRESS Address; BOOLEAN ReservedNameClosing = FALSE; CTEAssert( PnPInfo->NewReservedAddress ); NB_GET_LOCK( &Device->Lock, &LockHandle ); *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); NB_FREE_LOCK( &Device->Lock, LockHandle ); break; } case IPX_PNP_TRANSLATE_DEVICE: break; case IPX_PNP_TRANSLATE_ADDRESS: break; default: CTEAssert( FALSE ); } } /* NbiPnPNotification */
//---------------------------------------------------------------------------- NTSTATUS SendIrpFromRt ( IN PDEVICE pDevice, IN PIRP pIrp ) { CTELockHandle OldIrq; NTSTATUS Status; ULONG Index; PRT_INFO pRt; IpxPrint0("SendIrpfromRt - entered\n"); // pRt = REQUEST_OPEN_CONTEXT(pIrp); pRt = pRtInfo; Index = RT_ADDRESS_INDEX(pIrp); IpxVerifyRt(pRt); CTEAssert(pRt && (pRt == pRtInfo)); do { // // Check if the add. file slot indicates that it is OPEN. If it is // not open, then we should return STATUS_INVALID_HANDLE. The // reason why it may not be open is if we got a cleanup/close before // this irp. // CTEGetLock(&pRt->Lock, &OldIrq); if (pRt->AddFl[Index].State != RT_OPEN) { // // free the lock, set the status and break out // CTEFreeLock (&pRt->Lock, OldIrq); Status = STATUS_INVALID_HANDLE; break; } // // Let us reference the RtInfo structure so that it does not dissapear // and also for some accounting // IpxReferenceRt(pRt, RT_SEND); IpxPrint1("SendIrpFromRt: Index = (%d)\n", Index); // // Store the AF pointer since IpxTdiSendDatagram will use it. Free // the device lock since we have nothing more to do with our structures // here. // // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)(pRtInfo->AddFl[Index].AddressFile); CTEFreeLock (&pRt->Lock, OldIrq); Status = IpxTdiSendDatagram(pDevice->DeviceObject, pIrp); // // All done with this send. Derefernce the RtInfo structure. // IpxDereferenceRt(pRtInfo, RT_SEND); } while(FALSE); IpxPrint0("SendIrpfromRt - leaving\n"); return(Status); }
NTSTATUS CleanupRtAddress( IN PDEVICE pDevice, IN PIRP pIrp) /*++ Routine Description; This Routine handles closing the Rt Object that is used by by RT to send and receive name service datagrams on port 137. Arguments; pIrp - a ptr to an IRP Return Value; NTSTATUS - status of the request --*/ { NTSTATUS status; PRT_INFO pRt; CTELockHandle OldIrq; PLIST_ENTRY pHead; ULONG Index; PLIST_ENTRY pLE; PIRP pTmpIrp; IpxPrint0("CleanupRtAddress - entered\n"); // // if the endpoint structure is allocated, then deallocate it // // pRt = REQUEST_OPEN_CONTEXT(pIrp); pRt = pRtInfo; Index = RT_ADDRESS_INDEX(pIrp); IpxPrint1("CleanupRtAdd: Index = (%d)\n", Index); IpxVerifyRt(pRt); CTEAssert(pRt && (pRt == pRtInfo)); CTEAssert(Index < IPX_RT_MAX_ADDRESSES); do { PLIST_ENTRY pRcvEntry; PRTRCV_BUFFER pRcv; PRT_IRP pRtAddFl = &pRt->AddFl[Index]; CTEAssert(pRtAddFl->State == RT_OPEN); IpxPrint1("CleanupRtAddress: Got AF handle = (%lx)\n", pRtAddFl); IpxReferenceRt(pRt, RT_CLEANUP); status = STATUS_SUCCESS; CTEGetLock (&pRt->Lock, &OldIrq); // // prevent any more dgram getting queued up // pRtAddFl->State = RT_CLOSING; CTEFreeLock (&pRt->Lock, OldIrq); // // free any rcv buffers that may be queued up // pHead = &pRtAddFl->RcvList; while (pRcvEntry = ExInterlockedRemoveHeadList(pHead, &pRt->Lock)) { pRcv = CONTAINING_RECORD(pRcvEntry,RTRCV_BUFFER,Linkage); CTEAssert(pRcv); IpxPrint1("CleanupRtAddress:Freeing buffer = (%lx)\n", pRcv); RtFreeMem(pRcv,pRcv->TotalAllocSize); } // // Complete all irps that are queued // while (pLE = ExInterlockedRemoveHeadList(&pRtAddFl->RcvIrpList, &pRt->Lock)) { // // The recv irp is here so copy the data to its buffer and // pass it up to RT // pTmpIrp = CONTAINING_RECORD(pLE, IRP, Tail.Overlay.ListEntry); IpxPrint1("CleanupRtAddress: Completing Rt rcv Irp from AdFl queue pIrp=%X\n" ,pTmpIrp); pTmpIrp->IoStatus.Information = 0; pTmpIrp->IoStatus.Status = STATUS_CANCELLED; NTIoComplete(pTmpIrp, (NTSTATUS)-1, (ULONG)-1); } //end of while // // dequeue and complete any irps on the complete queue. // while (pLE = ExInterlockedRemoveHeadList(&pRt->CompletedIrps, &pRt->Lock)) { pTmpIrp = CONTAINING_RECORD(pLE, IRP, Tail.Overlay.ListEntry); if (RT_ADDRESS_INDEX(pTmpIrp) == Index) { IpxPrint1("CleanupRtAddress:Completing Rt rcv Irp from CompleteIrps queue pIrp=%X\n" ,pTmpIrp); pTmpIrp->IoStatus.Information = 0; pTmpIrp->IoStatus.Status = STATUS_CANCELLED; NTIoComplete(pTmpIrp, (NTSTATUS)-1, (ULONG)-1); } else { ExInterlockedInsertHeadList(&pRt->HolderIrpsList, pLE, &pRt->Lock); } } CTEGetLock(&pRt->Lock, &OldIrq); while(!IsListEmpty(&pRt->HolderIrpsList)) { pLE = RemoveHeadList(&pRt->HolderIrpsList); InsertHeadList(&pRt->CompletedIrps, pLE); } CTEFreeLock(&pRt->Lock, OldIrq); // // Store AF pointer in Irp since we will now be freeing the address file // (in driver.c). // // // We always have addressfile in the Irp // // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)(pRtAddFl->AddressFile); IpxDereferenceRt(pRt, RT_CLEANUP); } while (FALSE); IpxPrint0("CleanupRtAddress: Return\n"); return(status); }
VOID SpxTimerFlushAndStop( VOID ) /*++ Routine Description: Force all entries in the timer queue to be dispatched immediately. No more queue'ing of timer routines is permitted after this. The timer essentially shuts down. Arguments: Return Value: --*/ { PTIMERLIST pList; CTELockHandle lockHandle; CTEAssert (KeGetCurrentIrql() == LOW_LEVEL); DBGPRINT(SYSTEM, ERR, ("SpxTimerFlushAndStop: Entered\n")); CTEGetLock(&spxTimerLock, &lockHandle); spxTimerStopped = TRUE; KeCancelTimer(&spxTimer); if (spxTimerList != NULL) { // Dispatch all entries right away while (spxTimerList != NULL) { pList = spxTimerList; CTEAssert(VALID_TMR(pList)); spxTimerList = pList->tmr_Next; DBGPRINT(SYSTEM, INFO, ("spxTimerFlushAndStop: Dispatching %lx\n", pList->tmr_Worker)); // The timer routines assume they are being called at DISPATCH // level. This is OK since we are calling with SpinLock held. (*pList->tmr_Worker)(pList->tmr_Context, TRUE); spxTimerCount --; SpxBPFreeBlock(pList, BLKID_TIMERLIST); } RtlZeroMemory(spxTimerTable, sizeof(spxTimerTable)); } CTEFreeLock(&spxTimerLock, lockHandle); // Wait for all timer routines to complete while (spxTimerDispatchCount != 0) { SpxSleep(SPX_TIMER_WAIT); } }
VOID spxTimerEnqueue( IN PTIMERLIST pListNew ) /*++ Routine Description: Here is a thesis on the code that follows. The timer events are maintained as a list which the timer dpc routine looks at every timer tick. The list is maintained in such a way that only the head of the list needs to be updated every tick i.e. the entire list is never scanned. The way this is achieved is by keeping delta times relative to the previous entry. Every timer tick, the relative time at the head of the list is decremented. When that goes to ZERO, the head of the list is unlinked and dispatched. To give an example, we have the following events queued at time slots X Schedule A after 10 ticks. X+3 Schedule B after 5 ticks. X+5 Schedule C after 4 ticks. X+8 Schedule D after 6 ticks. So A will schedule at X+10, B at X+8 (X+3+5), C at X+9 (X+5+4) and D at X+14 (X+8+6). The above example covers all the situations. - NULL List. - Inserting at head of list. - Inserting in the middle of the list. - Appending to the list tail. The list will look as follows. BEFORE AFTER ------ ----- X Head -->| Head -> A(10) ->| A(10) X+3 Head -> A(7) ->| Head -> B(5) -> A(2) ->| B(5) X+5 Head -> B(3) -> A(2) ->| Head -> B(3) -> C(1) -> A(1) ->| C(4) X+8 Head -> C(1) -> A(1) ->| Head -> C(1) -> A(1) -> D(4) ->| D(6) The granularity is one tick. THIS MUST BE CALLED WITH THE TIMER LOCK HELD. Arguments: Return Value: --*/ { PTIMERLIST pList, *ppList; ULONG DeltaTime = pListNew->tmr_AbsTime; // The DeltaTime is adjusted in every pass of the loop to reflect the // time after the previous entry that the new entry will schedule. for (ppList = &spxTimerList; (pList = *ppList) != NULL; ppList = &pList->tmr_Next) { CTEAssert(VALID_TMR(pList)); if (DeltaTime <= pList->tmr_RelDelta) { pList->tmr_RelDelta -= DeltaTime; break; } DeltaTime -= pList->tmr_RelDelta; } // Link this in the chain pListNew->tmr_RelDelta = DeltaTime; pListNew->tmr_Next = pList; pListNew->tmr_Prev = ppList; *ppList = pListNew; if (pList != NULL) { pList->tmr_Prev = &pListNew->tmr_Next; } // Now link it in the hash table pListNew->tmr_Overflow = spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE]; spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE] = pListNew; spxTimerCount ++; }
VOID spxTimerDpcRoutine( IN PKDPC pKDpc, IN PVOID pContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This is called in at DISPATCH_LEVEL when the timer expires. The entry at the head of the list is decremented and if ZERO unlinked and dispatched. If the list is non-empty, the timer is fired again. Arguments: Return Value: --*/ { PTIMERLIST pList, *ppList; BOOLEAN TimerStarted; ULONG ReEnqueueTime; CTELockHandle lockHandle; pKDpc; pContext; SystemArgument1; SystemArgument2; #if defined(_PNP_POWER) CTEGetLock(&spxTimerLock, &lockHandle); if (spxTimerStopped) { DBGPRINT(SYSTEM, ERR, ("spxTimerDpc: Enetered after Flush !!!\n")); CTEFreeLock(&spxTimerLock, lockHandle); return; } #else if (spxTimerStopped) { DBGPRINT(SYSTEM, ERR, ("spxTimerDpc: Enetered after Flush !!!\n")); return; } CTEGetLock(&spxTimerLock, &lockHandle); #endif _PNP_POWER SpxTimerCurrentTime ++; // Update our relative time #ifdef PROFILING // This is the only place where this is changed. And it always increases. SpxStatistics.stat_ElapsedTime = SpxTimerCurrentTime; #endif // We should never be here if we have no work to do if ((spxTimerList != NULL)) { // Careful here. If two guys wanna go off together - let them !! if (spxTimerList->tmr_RelDelta != 0) (spxTimerList->tmr_RelDelta)--; // Dispatch the entry if it is ready to go if (spxTimerList->tmr_RelDelta == 0) { pList = spxTimerList; CTEAssert(VALID_TMR(pList)); // Unlink from the list spxTimerList = pList->tmr_Next; if (spxTimerList != NULL) spxTimerList->tmr_Prev = &spxTimerList; // Unlink from the hash table now for (ppList = &spxTimerTable[pList->tmr_Id % TIMER_HASH_TABLE]; *ppList != NULL; ppList = &((*ppList)->tmr_Overflow)) { CTEAssert(VALID_TMR(*ppList)); if (*ppList == pList) { *ppList = pList->tmr_Overflow; break; } } CTEAssert (*ppList == pList->tmr_Overflow); DBGPRINT(SYSTEM, INFO, ("spxTimerDpcRoutine: Dispatching %lx\n", pList->tmr_Worker)); spxTimerDispatchCount ++; spxTimerCount --; spxTimerActive = pList; CTEFreeLock(&spxTimerLock, lockHandle); // If reenqueue time is 0, do not requeue. If 1, then requeue with // current value, else use value specified. ReEnqueueTime = (*pList->tmr_Worker)(pList->tmr_Context, FALSE); DBGPRINT(SYSTEM, INFO, ("spxTimerDpcRoutine: Reenequeu time %lx.%lx\n", ReEnqueueTime, pList->tmr_AbsTime)); CTEGetLock(&spxTimerLock, &lockHandle); spxTimerActive = NULL; spxTimerDispatchCount --; if (ReEnqueueTime != TIMER_DONT_REQUEUE) { // If this chappie was cancelled while it was running // and it wants to be re-queued, do it right away. if (pList->tmr_Cancelled) { (*pList->tmr_Worker)(pList->tmr_Context, FALSE); SpxBPFreeBlock(pList, BLKID_TIMERLIST); } else { if (ReEnqueueTime != TIMER_REQUEUE_CUR_VALUE) { pList->tmr_AbsTime = ReEnqueueTime/SPX_MS_TO_TICKS; if (pList->tmr_AbsTime == 0) { DBGPRINT(SYSTEM, INFO, ("SpxTimerDispatch: Requeue at %ld\n", pList->tmr_AbsTime)); } DBGPRINT(SYSTEM, INFO, ("SpxTimerDispatch: Requeue at %ld.%ld\n", ReEnqueueTime, pList->tmr_AbsTime)); } spxTimerEnqueue(pList); } } else { SpxBPFreeBlock(pList, BLKID_TIMERLIST); } } } #if defined(_PNP_POWER) if (!spxTimerStopped) { TimerStarted = KeSetTimer(&spxTimer, spxTimerTick, &spxTimerDpc); // it is possible that while we were here in Dpc, PNP_ADD_DEVICE // restarted the timer, so this assert is commented out for PnP // CTEAssert(!TimerStarted); } CTEFreeLock(&spxTimerLock, lockHandle); #else CTEFreeLock(&spxTimerLock, lockHandle); if (!spxTimerStopped) { TimerStarted = KeSetTimer(&spxTimer, spxTimerTick, &spxTimerDpc); CTEAssert(!TimerStarted); } #endif _PNP_POWER }
//---------------------------------------------------------------------------- NTSTATUS PassDgToRt ( IN PDEVICE pDevice, IN PIPX_DATAGRAM_OPTIONS2 pContext, IN ULONG Index, IN VOID UNALIGNED *pDgrm, IN ULONG uNumBytes ) /*++ Routine Description; This function is used to allow NBT to pass name query service Pdu's to RT. Rt posts a Rcv irp to Netbt. If the Irp is here then simply copy the data to the irp and return it, otherwise buffer the data up to a maximum # of bytes. Beyond that limit the datagrams are discarded. If Retstatus is not success then the pdu will also be processed by nbt. This allows nbt to process packets when wins pauses and its list of queued buffers is exceeded. Arguments; pDevice - card that the request can in on pSrcAddress - source address pDgrm - ptr to the datagram uNumBytes - length of datagram Return Value; STATUS_PENDING if the buffer is to be held on to , the normal case. Notes; --*/ { NTSTATUS status; PIPX_DATAGRAM_OPTIONS2 pRtBuffer; PIRP pIrp; CTELockHandle OldIrq; IpxPrint0("PassDgToRt - Entered\n"); // // Get the source port and ip address, since RT needs this information. // IpxPrint1("PassDgToRt: Index = (%d)\n", Index); CTEGetLock(&pRtInfo->Lock,&OldIrq); do { PRT_IRP pRtAF = &pRtInfo->AddFl[Index]; if (pRtAF->State != RT_OPEN) { CTEFreeLock(&pRtInfo->Lock,OldIrq); break; } IpxReferenceRt(pRtInfo, RT_BUFF); if (IsListEmpty(&pRtAF->RcvIrpList)) { IpxPrint0("PassDgToRt: No Rcv Irp\n"); if (pRtInfo->RcvMemoryAllocated < pRtInfo->RcvMemoryMax) { PRTRCV_BUFFER pBuffer; pBuffer = RtAllocMem(uNumBytes + sizeof(RTRCV_BUFFER)); if (pBuffer) { pBuffer->TotalAllocSize = uNumBytes + sizeof(RTRCV_BUFFER); // // Copy the user data // RtlCopyMemory( (PUCHAR)((PUCHAR)pBuffer + OFFSET_PKT_IN_RCVBUFF), (PVOID)pDgrm,uNumBytes); pBuffer->Options.DgrmOptions.LocalTarget.NicId = pContext->DgrmOptions.LocalTarget.NicId; pBuffer->Options.LengthOfExtraOpInfo = 0; // // total amount allocated for user // pBuffer->UserBufferLengthToPass = uNumBytes + OFFSET_PKT_IN_OPTIONS; CTEAssert(pContext->DgrmOptions.LocalTarget.NicId); IpxPrint2("PassDgToRt: Nic Id is (%d). BufferLength is (%lx)\n", pContext->DgrmOptions.LocalTarget.NicId, uNumBytes); // // Keep track of the total amount buffered so that we don't // eat up all non-paged pool buffering for RT // pRtInfo->RcvMemoryAllocated += pBuffer->TotalAllocSize; IpxPrint0("IpxRt;Buffering Rt Rcv - no Irp, status=%X\n"); InsertTailList(&pRtAF->RcvList,&pBuffer->Linkage); IpxPrint0("PassDgToRt: Buffer Queued\n"); status = STATUS_SUCCESS; } else { IpxPrint0("PassDgToRt; Could not allocate buffer\n"); status = STATUS_INSUFFICIENT_RESOURCES; } } else { // this ret status will allow netbt to process the packet. // IpxPrint0("PassDgToRt; Dropping Pkt\n"); status = STATUS_INSUFFICIENT_RESOURCES; } CTEFreeLock(&pRtInfo->Lock,OldIrq); } else { PMDL pMdl; ULONG CopyLength; ULONG DgrmLength; ULONG MdlBufferLength; ULONG BytesToCopy; PLIST_ENTRY pLE; // // The recv irp is here so copy the data to its buffer and // pass it up to RT // pLE = RemoveHeadList(&pRtAF->RcvIrpList); pIrp = CONTAINING_RECORD(pLE, IRP, Tail.Overlay.ListEntry); (*(REQUEST_LINKAGE(pIrp))).Flink = NULL; (*(REQUEST_LINKAGE(pIrp))).Blink = NULL; // // Copy the datagram and the source address to RT buffer and // return to RT // pMdl = pIrp->MdlAddress; IpxPrint2("PassDgToRt: Irp=(%lx); Mdl=(%lx)\n", pIrp, pMdl); CTEAssert(pMdl); pRtBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); MdlBufferLength = MmGetMdlByteCount(pMdl); DgrmLength = uNumBytes; BytesToCopy = DgrmLength + OFFSET_PKT_IN_OPTIONS; CopyLength = (BytesToCopy <= MdlBufferLength) ? BytesToCopy : MdlBufferLength; IpxPrint2("PassDgToRt: Copy Length = (%d); Mdl Buffer Length is (%d)\n", CopyLength, MdlBufferLength); // // Copy user datagram into pRtBuffer // RtlCopyMemory((PVOID)((PUCHAR)pRtBuffer + OFFSET_PKT_IN_OPTIONS), (PVOID)pDgrm, CopyLength-OFFSET_PKT_IN_OPTIONS); IpxPrint1("Data copied is (%.12s)\n", (PUCHAR)((PUCHAR)pRtBuffer + OFFSET_PKT_IN_OPTIONS + sizeof(IPX_HEADER))); pRtBuffer->DgrmOptions.LocalTarget.NicId = pContext->DgrmOptions.LocalTarget.NicId; pRtBuffer->LengthOfExtraOpInfo = 0; IpxPrint3("PassDgToRt: Copy to RcvIrp;Nic Id is (%d/%d). BufferLength is (%lx)\n", pContext->DgrmOptions.LocalTarget.NicId, pRtBuffer->DgrmOptions.LocalTarget.NicId, uNumBytes); CTEAssert(pContext->DgrmOptions.LocalTarget.NicId); // // pass the irp up to RT // if (CopyLength < BytesToCopy) { status = STATUS_BUFFER_OVERFLOW; } else { status = STATUS_SUCCESS; } InsertTailList(&pRtInfo->CompletedIrps, REQUEST_LINKAGE(pIrp)); pRtAF->NoOfRcvIrps--; IpxPrint4("PassDgToRt;Returning Rt Rcv Irp - data from net, Length=%X,pIrp=%X; status = (%d). NoOfRcvIrp = (%d)\n" ,uNumBytes,pIrp, status, pRtAF->NoOfRcvIrps); pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = CopyLength; CTEFreeLock(&pRtInfo->Lock,OldIrq); } IpxDereferenceRt(pRtInfo, RT_BUFF); } while (FALSE); IpxPrint0("PassDgToRt - Entered\n"); return(status); }
NTSTATUS RcvIrpFromRt ( IN PDEVICE pDevice, IN PIRP pIrp ) /*++ Routine Description; This function takes the rcv irp posted by RT and decides if there are any datagram queued waiting to go up to RT. If so then the datagram is copied to the RT buffer and passed back up. Otherwise the irp is held by Netbt until a datagram does come in. Arguments; pDevice - not used pIrp - Rt Rcv Irp Return Value; STATUS_PENDING if the buffer is to be held on to , the normal case. Notes; --*/ { NTSTATUS status; PRTRCV_BUFFER pBuffer; PLIST_ENTRY pEntry; CTELockHandle OldIrq; PRT_INFO pRt; PIPX_DATAGRAM_OPTIONS2 pRtBuffer; PRT_IRP pRtAF; ULONG Index; #if DBG ULONG NoOfRcvIrp; #endif IpxPrint0("RcvIrpfromRt - Entered\n"); // pRt = REQUEST_OPEN_CONTEXT(pIrp); pRt = pRtInfo; Index = RT_ADDRESS_INDEX(pIrp); IpxPrint1("RcvIrpFromRt: Index = (%d)\n", Index); IpxVerifyRt(pRt); CTEAssert(pRt && (pRt == pRtInfo)); CTEAssert(Index < IPX_RT_MAX_ADDRESSES); CTEGetLock (&pRt->Lock, &OldIrq); do { pRtAF = &pRt->AddFl[Index]; if (pRtAF->State != RT_OPEN) { status = STATUS_INVALID_HANDLE; CTEFreeLock (&pRt->Lock, OldIrq); break; } IpxReferenceRt(pRt, RT_IRPIN); if (!IsListEmpty(&pRtAF->RcvList)) { PMDL pMdl; ULONG CopyLength; ULONG UserBufferLengthToPass; ULONG MdlLength; // // There is at least one datagram waiting to be received // pEntry = RemoveHeadList(&pRtAF->RcvList); pBuffer = (PRTRCV_BUFFER)CONTAINING_RECORD(pEntry,RTRCV_BUFFER, Linkage); IpxPrint0("RcvIrpFromRt: Buffer dequeued\n"); // // Copy the datagram and the source address to RT buffer and // return to RT // pMdl = pIrp->MdlAddress; IpxPrint2("RcvIrpFromRt: Irp=(%lx); Mdl=(%lx)\n", pIrp, pMdl); CTEAssert(pMdl); if (!pMdl) { status = STATUS_BUFFER_TOO_SMALL; CTEFreeLock (&pRt->Lock, OldIrq); IpxDereferenceRt(pRtInfo, RT_IRPIN); break; } pRtBuffer = MmGetSystemAddressForMdl(pMdl); MdlLength = MmGetMdlByteCount(pMdl); UserBufferLengthToPass = pBuffer->UserBufferLengthToPass; CopyLength = (UserBufferLengthToPass <= MdlLength) ? UserBufferLengthToPass : MdlLength; IpxPrint0("RcvIrpFromRt: Copying Options\n"); RtlCopyMemory((PVOID)pRtBuffer, (PVOID)&pBuffer->Options, CopyLength); // // subtract from the total amount buffered for RT since we are // passing a datagram up to RT now. // pRtInfo->RcvMemoryAllocated -= pBuffer->TotalAllocSize; RtFreeMem(pBuffer, pBuffer->TotalAllocSize); CTEAssert(pRtBuffer->DgrmOptions.LocalTarget.NicId); // // pass the irp up to RT // if (CopyLength < UserBufferLengthToPass) { status = STATUS_BUFFER_OVERFLOW; } else { status = STATUS_SUCCESS; } #if DBG NoOfRcvIrp = pRtAF->NoOfRcvIrps; #endif CTEFreeLock (&pRt->Lock, OldIrq); IpxPrint3("Returning Rt rcv Irp immediately with queued dgram, status=%X,pIrp=%X. NoOfRcvIrp=(%d)\n" ,status,pIrp, NoOfRcvIrp); pIrp->IoStatus.Information = CopyLength; pIrp->IoStatus.Status = status; } else { status = NTCheckSetCancelRoutine(pIrp,RtIrpCancel,pDevice); if (!NT_SUCCESS(status)) { CTEFreeLock (&pRt->Lock, OldIrq); } else { if (pRtAF->NoOfRcvIrps++ > RT_IRP_MAX) { IpxPrint1("RcvIrpFromRt; REACHED LIMIT OF IRPS. NoOfRcvIrp=(%d)\n", pRtAF->NoOfRcvIrps); status = STATUS_INSUFFICIENT_RESOURCES; pRtAF->NoOfRcvIrps--; CTEFreeLock (&pRt->Lock, OldIrq); } else { InsertTailList(&pRtAF->RcvIrpList,REQUEST_LINKAGE(pIrp)); IpxPrint2("IpxRt;Holding onto Rt Rcv Irp, pIrp =%Xstatus=%X\n", status,pIrp); status = STATUS_PENDING; CTEFreeLock(&pRt->Lock,OldIrq); } } } IpxDereferenceRt(pRtInfo, RT_IRPIN); } while(FALSE); IpxPrint0("RcvIrpfromRt - Leaving\n"); return(status); }
BOOLEAN SpxTimerCancelEvent( IN ULONG TimerId, IN BOOLEAN ReEnqueue ) /*++ Routine Description: Cancel a previously scheduled timer event, if it hasn't fired already. Arguments: Return Value: --*/ { PTIMERLIST pList, *ppList; CTELockHandle lockHandle; DBGPRINT(SYSTEM, INFO, ("SpxTimerCancelEvent: Entered for TimerId %ld\n", TimerId)); CTEAssert(TimerId != 0); CTEGetLock(&spxTimerLock, &lockHandle); for (ppList = &spxTimerTable[TimerId % TIMER_HASH_TABLE]; (pList = *ppList) != NULL; ppList = &pList->tmr_Overflow) { CTEAssert(VALID_TMR(pList)); // If we find it, cancel it if (pList->tmr_Id == TimerId) { // Unlink this from the hash table *ppList = pList->tmr_Overflow; // ... and from the list if (pList->tmr_Next != NULL) { pList->tmr_Next->tmr_RelDelta += pList->tmr_RelDelta; pList->tmr_Next->tmr_Prev = pList->tmr_Prev; } *(pList->tmr_Prev) = pList->tmr_Next; spxTimerCount --; if (ReEnqueue) spxTimerEnqueue(pList); else SpxBPFreeBlock(pList, BLKID_TIMERLIST); break; } } // If we could not find it in the list, see if it currently running. // If so mark him to not reschedule itself, only if reenqueue was false. if (pList == NULL) { if ((spxTimerActive != NULL) && (spxTimerActive->tmr_Id == TimerId) && !ReEnqueue) { spxTimerActive->tmr_Cancelled = TRUE; } } CTEFreeLock(&spxTimerLock, lockHandle); DBGPRINT(SYSTEM, INFO, ("SpxTimerCancelEvent: %s for Id %ld\n", (pList != NULL) ? "Success" : "Failure", TimerId)); return (pList != NULL); }
NTSTATUS OpenRtAddress( IN PDEVICE pDevice, IN PREQUEST pIrp ) { PRT_INFO pRt; CTELockHandle OldIrq; NTSTATUS status; ULONG SaveReqCode; IpxPrint0("OpenRtAddress - entered\n"); // // if the RTINFO endpoint structure is not allocated, then allocate it // and initialize it. But first get the device lock. This gurantees that // we can not have two irps doing the creation at the same time // CTEGetLock(&pDevice->Lock, &OldIrq); if (!pRtInfo) { pRt = AllocMem(sizeof(RT_INFO)); // // Do this after locking the pagable rtns. // // pRtInfo = pRt; //store it in pRtInfo. When irps come down from RM, // we can compare pRt passed in them with pRtInfo if (pRt) { RtlZeroMemory(pRt,sizeof(RT_INFO)); IpxPrint1("OpenRtAddress: Initializing CompletedIrps for pRt=(%lx)\n", pRt); pRt->RcvMemoryMax = RT_MAX_BUFF_MEM; // max. memory we can allocate pRt->Type = IPX_RT_SIGNATURE; pRt->Size = sizeof(RT_INFO); pRt->pDevice = pDevice; IpxPrint1("OpenRtAddress: pRtInfo=(%lx)\n", pRt); IpxPrint1("Completed Irp list is (%lx)\n", IsListEmpty(&pRt->CompletedIrps)); #if DBG RtlCopyMemory(pRt->Signature, "RTIF", sizeof("RTIF") - 1); #endif InitializeListHead(&pRt->CompletedIrps); InitializeListHead(&pRt->HolderIrpsList); } CTEFreeLock(&pDevice->Lock, OldIrq); } else { pRt = pRtInfo; CTEFreeLock(&pDevice->Lock, OldIrq); IpxPrint1("OpenRtAddress: RTINFO found = (%lx)\n", pRtInfo); } if (pRt) { // Page in the Rt Code, if it hasn't already been paged in. // if (!IpxRtDiscardableCodeHandle) { IpxRtDiscardableCodeHandle = MmLockPagableCodeSection( CloseRtAddress ); pRtInfo = pRt; //store it in pRtInfo. When irps come down from RM, // we can compare pRt passed in them with pRtInfo } // // it could fail to lock the pages so check for that // if (IpxRtDiscardableCodeHandle) { ULONG i; status = STATUS_SUCCESS; IpxReferenceRt(pRtInfo, RT_CREATE); // // Find an empty slot and mark it open // CTEGetLock(&pRt->Lock, &OldIrq); for (i=0; i<IPX_RT_MAX_ADDRESSES; i++) { if (pRt->AddFl[i].State == RT_EMPTY) { break; } } if (i < IPX_RT_MAX_ADDRESSES) { pRt->AddFl[i].State = RT_OPEN; pRt->NoOfAdds++; InitializeListHead(&pRt->AddFl[i].RcvList); InitializeListHead(&pRt->AddFl[i].RcvIrpList); } else { CTEFreeLock(&pRt->Lock, OldIrq); IpxPrint1("OpenRtAddress; All %d slots used up\n", IPX_RT_MAX_ADDRESSES); IpxDereferenceRt(pRtInfo, RT_CREATE); status = STATUS_INSUFFICIENT_RESOURCES; goto RET; } CTEFreeLock(&pRt->Lock, OldIrq); // // Found an empty slot. Initialize all relevant info. and then // open an address object. // SaveReqCode = REQUEST_CODE(pIrp); REQUEST_CODE(pIrp) = MIPX_RT_CREATE; status = IpxOpenAddressM(pDevice, pIrp, i); REQUEST_CODE(pIrp) = SaveReqCode; IpxPrint1("After IpxOpenAddressM: Completed Irp list is (%lx)\n", IsListEmpty(&pRtInfo->CompletedIrps)); if (status != STATUS_SUCCESS) { IpxPrint0("OpenRtAddress; Access Denied due to OpenAddress\n"); IpxDereferenceRt(pRtInfo, RT_CREATE); CTEGetLock(&pRt->Lock, &OldIrq); pRt->AddFl[i].State = RT_EMPTY; pRt->NoOfAdds--; CTEFreeLock(&pRt->Lock, OldIrq); } else { CTEGetLock(&pRt->Lock, &OldIrq); pRt->AddFl[i].AddressFile = REQUEST_OPEN_CONTEXT(pIrp); CTEFreeLock(&pRt->Lock, OldIrq); // // No need to put pRt since it is global. We stick with the addressfile here. // // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)pRt; REQUEST_OPEN_TYPE(pIrp) = (PVOID)(ROUTER_ADDRESS_FILE + i); IpxPrint1("OpenRtAdd: Index = (%d)\n", RT_ADDRESS_INDEX(pIrp)); } } else { IpxPrint1("OpenRtAddress; All %d slots used up\n", IPX_RT_MAX_ADDRESSES); status = STATUS_INSUFFICIENT_RESOURCES; } } else { IpxPrint0("OpenRtCreate; Couldn't allocate a RT_INFO structure\n"); CTEAssert(FALSE); //should never happen unless system is running //out of non-paged pool status = STATUS_INSUFFICIENT_RESOURCES; } RET: IpxPrint1("OpenRtAddress status prior to return= %X\n",status); return(status); }