/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: stdrvr.c Abstract: This module contains code which defines the NT Sample transport provider's device object. Environment: Kernel mode Revision History: --*/ #include "st.h" // // This is a list of all the device contexts that ST owns, // used while unloading. // LIST_ENTRY StDeviceList = {0,0}; // initialized for real at runtime. // // Forward declaration of various routines used in this module. // NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); VOID StUnload( IN PDRIVER_OBJECT DriverObject ); NTSTATUS StConfigureTransport ( IN PUNICODE_STRING RegistryPath, IN PCONFIG_DATA * ConfigData ); VOID StFreeConfigurationInfo ( IN PCONFIG_DATA ConfigurationInfo ); NTSTATUS StDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS StOpenAddress( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS StCloseAddress( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS StOpenConnection( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS StCloseConnection( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS StTdiAccept( IN PIRP Irp ); NTSTATUS StTdiConnect( IN PIRP Irp ); NTSTATUS StTdiDisconnect( IN PIRP Irp ); NTSTATUS StTdiDisassociateAddress ( IN PIRP Irp ); NTSTATUS StTdiAssociateAddress( IN PIRP Irp ); NTSTATUS StTdiListen( IN PIRP Irp ); NTSTATUS StTdiQueryInformation( IN PDEVICE_CONTEXT DeviceContext, IN PIRP Irp ); NTSTATUS StTdiReceive( IN PIRP Irp ); NTSTATUS StTdiReceiveDatagram( IN PIRP Irp ); NTSTATUS StTdiSend( IN PIRP Irp ); NTSTATUS StTdiSendDatagram( IN PIRP Irp ); NTSTATUS StTdiSetEventHandler( IN PIRP Irp ); NTSTATUS StTdiSetInformation( IN PIRP Irp ); VOID StDeallocateResources( IN PDEVICE_CONTEXT DeviceContext ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #endif NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine performs initialization of the sample transport driver. It creates the device objects for the transport provider and performs other driver initialization. Arguments: DriverObject - Pointer to driver object created by the system. RegistryPath - The name of ST's node in the registry. Return Value: The function value is the final status from the initialization operation. --*/ { ULONG i, j; STRING nameString; PDEVICE_CONTEXT DeviceContext; PTP_REQUEST Request; PTP_CONNECTION Connection; PTP_ADDRESS_FILE AddressFile; PTP_ADDRESS Address; PTP_PACKET Packet; PNDIS_PACKET NdisPacket; PRECEIVE_PACKET_TAG ReceiveTag; PBUFFER_TAG BufferTag; NTSTATUS status; UINT SuccessfulOpens; UINT MaxUserData; PCONFIG_DATA StConfig = NULL; ASSERT (sizeof (SHORT) == 2); // // This allocates the CONFIG_DATA structure and returns // it in StConfig. // status = StConfigureTransport(RegistryPath, &StConfig); if (!NT_SUCCESS (status)) { PANIC (" Failed to initialize transport, St initialization failed.\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // make ourselves known to the NDIS wrapper. // RtlInitString( &nameString, ST_DEVICE_NAME ); status = StRegisterProtocol (&nameString); if (!NT_SUCCESS (status)) { StFreeConfigurationInfo(StConfig); PANIC ("StInitialize: RegisterProtocol failed!\n"); StWriteGeneralErrorLog( (PVOID)DriverObject, EVENT_TRANSPORT_REGISTER_FAILED, 607, status, NULL, 0, NULL); return STATUS_INSUFFICIENT_RESOURCES; } // // Initialize the driver object with this driver's entry points. // DriverObject->MajorFunction [IRP_MJ_CREATE] = StDispatchOpenClose; DriverObject->MajorFunction [IRP_MJ_CLOSE] = StDispatchOpenClose; DriverObject->MajorFunction [IRP_MJ_CLEANUP] = StDispatchOpenClose; DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = StDispatchInternal; DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = StDispatch; DriverObject->DriverUnload = StUnload; // // Initialize the global list of devices. // InitializeListHead (&StDeviceList); SuccessfulOpens = 0; for (j=0;jNumAdapters;j++ ) { // // Loop through all the adapters that are in the configuration // information structure. Allocate a device object for each // one that we find. // status = StCreateDeviceContext (DriverObject, &StConfig->Names[StConfig->DevicesOffset+j], &DeviceContext); if (!NT_SUCCESS (status)) { continue; } // // Initialize our counter that records memory usage. // DeviceContext->MemoryUsage = 0; DeviceContext->MemoryLimit = StConfig->MaxMemoryUsage; // // Now fire up NDIS so this adapter talks // status = StInitializeNdis (DeviceContext, StConfig, j); if (!NT_SUCCESS (status)) { // // Log an error. // StWriteGeneralErrorLog( DeviceContext, EVENT_TRANSPORT_BINDING_FAILED, 601, status, StConfig->Names[j].Buffer, 0, NULL); StDereferenceDeviceContext ("Initialize NDIS failed", DeviceContext); continue; } // // Initialize our provider information structure; since it // doesn't change, we just keep it around and copy it to // whoever requests it. // MacReturnMaxDataSize( &DeviceContext->MacInfo, NULL, 0, DeviceContext->MaxSendPacketSize, &MaxUserData); DeviceContext->Information.Version = 0x0100; DeviceContext->Information.MaxSendSize = 0x1fffe; // 128k - 2 DeviceContext->Information.MaxConnectionUserData = 0; DeviceContext->Information.MaxDatagramSize = MaxUserData - sizeof(ST_HEADER); DeviceContext->Information.ServiceFlags = ST_SERVICE_FLAGS; DeviceContext->Information.MinimumLookaheadData = 128; DeviceContext->Information.MaximumLookaheadData = DeviceContext->MaxReceivePacketSize - sizeof(ST_HEADER); DeviceContext->Information.NumberOfResources = ST_TDI_RESOURCES; KeQuerySystemTime (&DeviceContext->Information.StartTime); // // Allocate various structures we will need. // // // The TP_PACKET structure has a CHAR[1] field at the end // which we expand upon to include all the headers needed; // the size of the MAC header depends on what the adapter // told us about its max header size. // DeviceContext->PacketHeaderLength = DeviceContext->MacInfo.MaxHeaderLength + sizeof (ST_HEADER); DeviceContext->PacketLength = FIELD_OFFSET(TP_PACKET, Header[0]) + DeviceContext->PacketHeaderLength; // // The BUFFER_TAG structure has a CHAR[1] field at the end // which we expand upong to include all the frame data. // DeviceContext->ReceiveBufferLength = DeviceContext->MaxReceivePacketSize + FIELD_OFFSET(BUFFER_TAG, Buffer[0]); for (i=0; iInitRequests; i++) { StAllocateRequest (DeviceContext, &Request); if (Request == NULL) { PANIC ("StInitialize: insufficient memory to allocate requests.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); } DeviceContext->RequestInitAllocated = StConfig->InitRequests; DeviceContext->RequestMaxAllocated = StConfig->MaxRequests; for (i=0; iInitConnections; i++) { StAllocateConnection (DeviceContext, &Connection); if (Connection == NULL) { PANIC ("StInitialize: insufficient memory to allocate connections.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } InsertTailList (&DeviceContext->ConnectionPool, &Connection->LinkList); } DeviceContext->ConnectionInitAllocated = StConfig->InitConnections; DeviceContext->ConnectionMaxAllocated = StConfig->MaxConnections; for (i=0; iInitAddressFiles; i++) { StAllocateAddressFile (DeviceContext, &AddressFile); if (AddressFile == NULL) { PANIC ("StInitialize: insufficient memory to allocate Address Files.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); } DeviceContext->AddressFileInitAllocated = StConfig->InitAddressFiles; DeviceContext->AddressFileMaxAllocated = StConfig->MaxAddressFiles; for (i=0; iInitAddresses; i++) { StAllocateAddress (DeviceContext, &Address); if (Address == NULL) { PANIC ("StInitialize: insufficient memory to allocate addresses.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); } DeviceContext->AddressInitAllocated = StConfig->InitAddresses; DeviceContext->AddressMaxAllocated = StConfig->MaxAddresses; for (i=0; iInitPackets; i++) { StAllocateSendPacket (DeviceContext, &Packet); if (Packet == NULL) { PANIC ("StInitialize: insufficient memory to allocate packets.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } PushEntryList (&DeviceContext->PacketPool, (PSINGLE_LIST_ENTRY)&Packet->Linkage); } DeviceContext->PacketInitAllocated = StConfig->InitPackets; for (i=0; iInitReceivePackets; i++) { StAllocateReceivePacket (DeviceContext, &NdisPacket); if (NdisPacket == NULL) { PANIC ("StInitialize: insufficient memory to allocate packet MDLs.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } ReceiveTag = (PRECEIVE_PACKET_TAG)NdisPacket->ProtocolReserved; PushEntryList (&DeviceContext->ReceivePacketPool, (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage); } DeviceContext->ReceivePacketInitAllocated = StConfig->InitReceivePackets; for (i=0; iInitReceiveBuffers; i++) { StAllocateReceiveBuffer (DeviceContext, &BufferTag); if (BufferTag == NULL) { PANIC ("StInitialize: Unable to allocate receive packet.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } PushEntryList (&DeviceContext->ReceiveBufferPool, &BufferTag->Linkage); } DeviceContext->ReceiveBufferInitAllocated = StConfig->InitReceiveBuffers; // // Now link the device into the global list. // InsertTailList (&StDeviceList, &DeviceContext->Linkage); DeviceContext->State = DEVICECONTEXT_STATE_OPEN; ++SuccessfulOpens; continue; cleanup: StWriteResourceErrorLog (DeviceContext, DeviceContext->MemoryUsage, 501); // // Cleanup whatever device context we were initializing // when we failed. // StFreeResources (DeviceContext); StCloseNdis (DeviceContext); StDereferenceDeviceContext ("Load failed", DeviceContext); } StFreeConfigurationInfo(StConfig); return ((SuccessfulOpens > 0) ? STATUS_SUCCESS : STATUS_DEVICE_DOES_NOT_EXIST); } VOID StUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine unloads the sample transport driver. It unbinds from any NDIS drivers that are open and frees all resources associated with the transport. The I/O system will not call us until nobody above has ST open. Arguments: DriverObject - Pointer to driver object created by the system. Return Value: None. When the function returns, the driver is unloaded. --*/ { PDEVICE_CONTEXT DeviceContext; PLIST_ENTRY p; UNREFERENCED_PARAMETER (DriverObject); // // Walk the list of device contexts. // while (!IsListEmpty (&StDeviceList)) { p = RemoveHeadList (&StDeviceList); DeviceContext = CONTAINING_RECORD (p, DEVICE_CONTEXT, Linkage); // // Remove all the storage associated with the device. // StFreeResources (DeviceContext); // // Free the packet pools, etc. and close the // adapter. // StCloseNdis (DeviceContext); // // And remove the creation reference from the device // context. // StDereferenceDeviceContext ("Unload", DeviceContext); } // // Finally, remove ourselves as an NDIS protocol. // StDeregisterProtocol(); return; } VOID StFreeResources ( IN PDEVICE_CONTEXT DeviceContext ) /*++ Routine Description: This routine is called by ST to clean up the data structures associated with a given DeviceContext. When this routine exits, the DeviceContext should be deleted as it no longer has any assocaited resources. Arguments: DeviceContext - Pointer to the DeviceContext we wish to clean up. Return Value: None. --*/ { PLIST_ENTRY p; PSINGLE_LIST_ENTRY s; PTP_PACKET packet; PTP_ADDRESS address; PTP_CONNECTION connection; PTP_REQUEST request; PTP_ADDRESS_FILE addressFile; PNDIS_PACKET ndisPacket; PBUFFER_TAG BufferTag; // // Clean up packet pool. // while ( DeviceContext->PacketPool.Next != NULL ) { s = PopEntryList( &DeviceContext->PacketPool ); packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); StDeallocateSendPacket (DeviceContext, packet); } // // Clean up address pool. // while ( !IsListEmpty (&DeviceContext->AddressPool) ) { p = RemoveHeadList (&DeviceContext->AddressPool); address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); StDeallocateAddress (DeviceContext, address); } // // Clean up address file pool. // while ( !IsListEmpty (&DeviceContext->AddressFilePool) ) { p = RemoveHeadList (&DeviceContext->AddressFilePool); addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); StDeallocateAddressFile (DeviceContext, addressFile); } // // Clean up connection pool. // while ( !IsListEmpty (&DeviceContext->ConnectionPool) ) { p = RemoveHeadList (&DeviceContext->ConnectionPool); connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); StDeallocateConnection (DeviceContext, connection); } // // Clean up request pool. // while ( !IsListEmpty( &DeviceContext->RequestPool ) ) { p = RemoveHeadList( &DeviceContext->RequestPool ); request = CONTAINING_RECORD (p, TP_REQUEST, Linkage ); StDeallocateRequest (DeviceContext, request); } // // Clean up receive packet pool // while ( DeviceContext->ReceivePacketPool.Next != NULL) { s = PopEntryList (&DeviceContext->ReceivePacketPool); // // HACK: This works because Linkage is the first field in // ProtocolReserved for a receive packet. // ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); StDeallocateReceivePacket (DeviceContext, ndisPacket); } // // Clean up receive buffer pool. // while ( DeviceContext->ReceiveBufferPool.Next != NULL ) { s = PopEntryList( &DeviceContext->ReceiveBufferPool ); BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); StDeallocateReceiveBuffer (DeviceContext, BufferTag); } return; } /* StFreeResources */ NTSTATUS StDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the main dispatch routine for the ST device driver. It accepts an I/O Request Packet, performs the request, and then returns with the appropriate status. Arguments: DeviceObject - Pointer to the device object for this driver. Irp - Pointer to the request packet representing the I/O request. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PDEVICE_CONTEXT DeviceContext; // // Check to see if ST has been initialized; if not, don't allow any use. // Note that this only covers any user mode code use; kernel TDI clients // will fail on their creation of an endpoint. // DeviceContext = (PDEVICE_CONTEXT)DeviceObject; if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); return STATUS_INVALID_DEVICE_STATE; } // // Make sure status information is consistent every time. // IoMarkIrpPending (Irp); Irp->IoStatus.Status = STATUS_PENDING; Irp->IoStatus.Information = 0; // // Get a pointer to the current stack location in the IRP. This is where // the function codes and parameters are stored. // IrpSp = IoGetCurrentIrpStackLocation (Irp); // // Case on the function that is being performed by the requestor. If the // operation is a valid one for this device, then make it look like it was // successfully completed, where possible. // switch (IrpSp->MajorFunction) { case IRP_MJ_DEVICE_CONTROL: Status = StDeviceControl (DeviceObject, Irp, IrpSp); break; default: Status = STATUS_INVALID_DEVICE_REQUEST; } /* major function switch */ if (Status != STATUS_PENDING) { IrpSp->Control &= ~SL_PENDING_RETURNED; Irp->IoStatus.Status = Status; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); } // // Return the immediate status code to the caller. // return Status; } /* StDispatch */ NTSTATUS StDispatchOpenClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the main dispatch routine for the ST device driver. It accepts an I/O Request Packet, performs the request, and then returns with the appropriate status. Arguments: DeviceObject - Pointer to the device object for this driver. Irp - Pointer to the request packet representing the I/O request. Return Value: The function value is the status of the operation. --*/ { KIRQL oldirql; PDEVICE_CONTEXT DeviceContext; NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PFILE_FULL_EA_INFORMATION openType; USHORT i; BOOLEAN found; PTP_ADDRESS_FILE AddressFile; PTP_CONNECTION Connection; // // Check to see if ST has been initialized; if not, don't allow any use. // Note that this only covers any user mode code use; kernel TDI clients // will fail on their creation of an endpoint. // DeviceContext = (PDEVICE_CONTEXT)DeviceObject; if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); return STATUS_INVALID_DEVICE_STATE; } // // Make sure status information is consistent every time. // IoMarkIrpPending (Irp); Irp->IoStatus.Status = STATUS_PENDING; Irp->IoStatus.Information = 0; // // Get a pointer to the current stack location in the IRP. This is where // the function codes and parameters are stored. // IrpSp = IoGetCurrentIrpStackLocation (Irp); // // Case on the function that is being performed by the requestor. If the // operation is a valid one for this device, then make it look like it was // successfully completed, where possible. // switch (IrpSp->MajorFunction) { // // The Create function opens a transport object (either address or // connection). Access checking is performed on the specified // address to ensure security of transport-layer addresses. // case IRP_MJ_CREATE: openType = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; if (openType != NULL) { found = TRUE; for (i=0;i<(USHORT)openType->EaNameLength;i++) { if (openType->EaName[i] == TdiTransportAddress[i]) { continue; } else { found = FALSE; break; } } if (found) { Status = StOpenAddress (DeviceObject, Irp, IrpSp); break; } // // Connection? // found = TRUE; for (i=0;i<(USHORT)openType->EaNameLength;i++) { if (openType->EaName[i] == TdiConnectionContext[i]) { continue; } else { found = FALSE; break; } } if (found) { Status = StOpenConnection (DeviceObject, Irp, IrpSp); break; } } else { ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); IrpSp->FileObject->FsContext = (PVOID)(DeviceContext->ControlChannelIdentifier); ++DeviceContext->ControlChannelIdentifier; if (DeviceContext->ControlChannelIdentifier == 0) { DeviceContext->ControlChannelIdentifier = 1; } RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); IrpSp->FileObject->FsContext2 = (PVOID)ST_FILE_TYPE_CONTROL; Status = STATUS_SUCCESS; } break; case IRP_MJ_CLOSE: // // The Close function closes a transport endpoint, terminates // all outstanding transport activity on the endpoint, and unbinds // the endpoint from its transport address, if any. If this // is the last transport endpoint bound to the address, then // the address is removed from the provider. // switch ((ULONG)IrpSp->FileObject->FsContext2) { case TDI_TRANSPORT_ADDRESS_FILE: AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; // // This creates a reference to AddressFile->Address // which is removed by StCloseAddress. // Status = StVerifyAddressObject(AddressFile); if (!NT_SUCCESS (Status)) { Status = STATUS_INVALID_HANDLE; } else { Status = StCloseAddress (DeviceObject, Irp, IrpSp); } break; case TDI_CONNECTION_FILE: // // This is a connection // Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; Status = StVerifyConnectionObject (Connection); if (NT_SUCCESS (Status)) { Status = StCloseConnection (DeviceObject, Irp, IrpSp); StDereferenceConnection ("Temporary Use",Connection); } break; case ST_FILE_TYPE_CONTROL: // // this always succeeds // Status = STATUS_SUCCESS; break; default: Status = STATUS_INVALID_HANDLE; } break; case IRP_MJ_CLEANUP: // // Handle the two stage IRP for a file close operation. When the first // stage hits, run down all activity on the object of interest. This // do everything to it but remove the creation hold. Then, when the // CLOSE irp hits, actually close the object. // switch ((ULONG)IrpSp->FileObject->FsContext2) { case TDI_TRANSPORT_ADDRESS_FILE: AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; Status = StVerifyAddressObject(AddressFile); if (!NT_SUCCESS (Status)) { Status = STATUS_INVALID_HANDLE; } else { StStopAddressFile (AddressFile, AddressFile->Address); StDereferenceAddress ("IRP_MJ_CLEANUP", AddressFile->Address); Status = STATUS_SUCCESS; } break; case TDI_CONNECTION_FILE: Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; Status = StVerifyConnectionObject (Connection); if (NT_SUCCESS (Status)) { StStopConnection (Connection, STATUS_LOCAL_DISCONNECT); Status = STATUS_SUCCESS; StDereferenceConnection ("Temporary Use",Connection); } break; case ST_FILE_TYPE_CONTROL: Status = STATUS_SUCCESS; break; default: Status = STATUS_INVALID_HANDLE; } break; default: Status = STATUS_INVALID_DEVICE_REQUEST; } /* major function switch */ if (Status != STATUS_PENDING) { IrpSp->Control &= ~SL_PENDING_RETURNED; Irp->IoStatus.Status = Status; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); } // // Return the immediate status code to the caller. // return Status; } /* StDispatchOpenClose */ NTSTATUS StDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine dispatches TDI request types to different handlers based on the minor IOCTL function code in the IRP's current stack location. In addition to cracking the minor function code, this routine also reaches into the IRP and passes the packetized parameters stored there as parameters to the various TDI request handlers so that they are not IRP-dependent. Arguments: DeviceObject - Pointer to the device object for this driver. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status; PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject; // // Branch to the appropriate request handler. Preliminary checking of // the size of the request block is performed here so that it is known // in the handlers that the minimum input parameters are readable. It // is *not* determined here whether variable length input fields are // passed correctly; this is a check which must be made within each routine. // switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { default: // // Convert the user call to the proper internal device call. // Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); if (Status == STATUS_SUCCESS) { // // If TdiMapUserRequest returns SUCCESS then the IRP // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL // IRP, so we dispatch it as usual. The IRP will // be completed by this call. // // StDispatchInternal expects to complete the IRP, // so we change Status to PENDING so we don't. // (VOID)StDispatchInternal (DeviceObject, Irp); Status = STATUS_PENDING; } } return Status; } /* StDeviceControl */ NTSTATUS StDispatchInternal ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine dispatches TDI request types to different handlers based on the minor IOCTL function code in the IRP's current stack location. In addition to cracking the minor function code, this routine also reaches into the IRP and passes the packetized parameters stored there as parameters to the various TDI request handlers so that they are not IRP-dependent. Arguments: DeviceObject - Pointer to the device object for this driver. Irp - Pointer to the request packet representing the I/O request. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status; PDEVICE_CONTEXT DeviceContext; PIO_STACK_LOCATION IrpSp; // // Get a pointer to the current stack location in the IRP. This is where // the function codes and parameters are stored. // IrpSp = IoGetCurrentIrpStackLocation (Irp); DeviceContext = (PDEVICE_CONTEXT)DeviceObject; if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); return STATUS_INVALID_DEVICE_STATE; } // // Make sure status information is consistent every time. // IoMarkIrpPending (Irp); Irp->IoStatus.Status = STATUS_PENDING; Irp->IoStatus.Information = 0; // // Branch to the appropriate request handler. Preliminary checking of // the size of the request block is performed here so that it is known // in the handlers that the minimum input parameters are readable. It // is *not* determined here whether variable length input fields are // passed correctly; this is a check which must be made within each routine. // switch (IrpSp->MinorFunction) { case TDI_ACCEPT: Status = StTdiAccept (Irp); break; case TDI_ACTION: Status = STATUS_SUCCESS; break; case TDI_ASSOCIATE_ADDRESS: Status = StTdiAssociateAddress (Irp); break; case TDI_DISASSOCIATE_ADDRESS: Status = StTdiDisassociateAddress (Irp); break; case TDI_CONNECT: Status = StTdiConnect (Irp); break; case TDI_DISCONNECT: Status = StTdiDisconnect (Irp); break; case TDI_LISTEN: Status = StTdiListen (Irp); break; case TDI_QUERY_INFORMATION: Status = StTdiQueryInformation (DeviceContext, Irp); break; case TDI_RECEIVE: Status = StTdiReceive (Irp); break; case TDI_RECEIVE_DATAGRAM: Status = StTdiReceiveDatagram (Irp); break; case TDI_SEND: Status = StTdiSend (Irp); break; case TDI_SEND_DATAGRAM: Status = StTdiSendDatagram (Irp); break; case TDI_SET_EVENT_HANDLER: // // Because this request will enable direct callouts from the // transport provider at DISPATCH_LEVEL to a client-specified // routine, this request is only valid in kernel mode, denying // access to this request in user mode. // Status = StTdiSetEventHandler (Irp); break; case TDI_SET_INFORMATION: Status = StTdiSetInformation (Irp); break; // // Something we don't know about was submitted. // default: Status = STATUS_INVALID_DEVICE_REQUEST; } if (Status != STATUS_PENDING) { IrpSp->Control &= ~SL_PENDING_RETURNED; Irp->IoStatus.Status = Status; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); } // // Return the immediate status code to the caller. // return Status; } /* StDispatchInternal */ VOID StWriteResourceErrorLog( IN PDEVICE_CONTEXT DeviceContext, IN ULONG BytesNeeded, IN ULONG UniqueErrorValue ) /*++ Routine Description: This routine allocates and writes an error log entry indicating an out of resources condition. Arguments: DeviceContext - Pointer to the device context. BytesNeeded - If applicable, the number of bytes that could not be allocated. UniqueErrorValue - Used as the UniqueErrorValue in the error log packet. Return Value: None. --*/ { PIO_ERROR_LOG_PACKET errorLogEntry; UCHAR EntrySize; PUCHAR StringLoc; ULONG TempUniqueError; static WCHAR UniqueErrorBuffer[4] = L"000"; UINT i; EntrySize = sizeof(IO_ERROR_LOG_PACKET) + DeviceContext->DeviceNameLength + sizeof(UniqueErrorBuffer); errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( (PDEVICE_OBJECT)DeviceContext, EntrySize ); // // Convert the error value into a buffer. // TempUniqueError = UniqueErrorValue; for (i=1; i>=0; i--) { UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); TempUniqueError /= 10; } if (errorLogEntry != NULL) { errorLogEntry->MajorFunctionCode = (UCHAR)-1; errorLogEntry->RetryCount = (UCHAR)-1; errorLogEntry->DumpDataSize = sizeof(ULONG); errorLogEntry->NumberOfStrings = 2; errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); errorLogEntry->EventCategory = 0; errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; errorLogEntry->UniqueErrorValue = UniqueErrorValue; errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; errorLogEntry->SequenceNumber = (ULONG)-1; errorLogEntry->IoControlCode = 0; errorLogEntry->DumpData[0] = BytesNeeded; StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); StringLoc += DeviceContext->DeviceNameLength; RtlCopyMemory (StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); IoWriteErrorLogEntry(errorLogEntry); } } /* StWriteResourceErrorLog */ VOID StWriteGeneralErrorLog( IN PDEVICE_CONTEXT DeviceContext, IN NTSTATUS ErrorCode, IN ULONG UniqueErrorValue, IN NTSTATUS FinalStatus, IN PWSTR SecondString, IN ULONG DumpDataCount, IN ULONG DumpData[] ) /*++ Routine Description: This routine allocates and writes an error log entry indicating a general problem as indicated by the parameters. It handles event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these events have messages with one or two strings in them. Arguments: DeviceContext - Pointer to the device context, or this may be a driver object instead. ErrorCode - The transport event code. UniqueErrorValue - Used as the UniqueErrorValue in the error log packet. FinalStatus - Used as the FinalStatus in the error log packet. SecondString - If not NULL, the string to use as the %3 value in the error log packet. DumpDataCount - The number of ULONGs of dump data. DumpData - Dump data for the packet. Return Value: None. --*/ { PIO_ERROR_LOG_PACKET errorLogEntry; UCHAR EntrySize; ULONG SecondStringSize; PUCHAR StringLoc; static WCHAR DriverName[3] = L"St"; EntrySize = sizeof(IO_ERROR_LOG_PACKET) + (DumpDataCount * sizeof(ULONG)); if (DeviceContext->Type == IO_TYPE_DEVICE) { EntrySize += (UCHAR)DeviceContext->DeviceNameLength; } else { EntrySize += sizeof(DriverName); } if (SecondString) { SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); EntrySize += (UCHAR)SecondStringSize; } errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( (PDEVICE_OBJECT)DeviceContext, EntrySize ); if (errorLogEntry != NULL) { errorLogEntry->MajorFunctionCode = (UCHAR)-1; errorLogEntry->RetryCount = (UCHAR)-1; errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); errorLogEntry->EventCategory = 0; errorLogEntry->ErrorCode = ErrorCode; errorLogEntry->UniqueErrorValue = UniqueErrorValue; errorLogEntry->FinalStatus = FinalStatus; errorLogEntry->SequenceNumber = (ULONG)-1; errorLogEntry->IoControlCode = 0; if (DumpDataCount) { RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); } StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; if (DeviceContext->Type == IO_TYPE_DEVICE) { RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); StringLoc += DeviceContext->DeviceNameLength; } else { RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); StringLoc += sizeof(DriverName); } if (SecondString) { RtlCopyMemory (StringLoc, SecondString, SecondStringSize); } IoWriteErrorLogEntry(errorLogEntry); } } /* StWriteGeneralErrorLog */ VOID StWriteOidErrorLog( IN PDEVICE_CONTEXT DeviceContext, IN NTSTATUS ErrorCode, IN NTSTATUS FinalStatus, IN PWSTR AdapterString, IN ULONG OidValue ) /*++ Routine Description: This routine allocates and writes an error log entry indicating a problem querying or setting an OID on an adapter. It handles event codes SET_OID_FAILED and QUERY_OID_FAILED. Arguments: DeviceContext - Pointer to the device context. ErrorCode - Used as the ErrorCode in the error log packet. FinalStatus - Used as the FinalStatus in the error log packet. AdapterString - The name of the adapter we were bound to. OidValue - The OID which could not be set or queried. Return Value: None. --*/ { PIO_ERROR_LOG_PACKET errorLogEntry; UCHAR EntrySize; ULONG AdapterStringSize; PUCHAR StringLoc; static WCHAR OidBuffer[9] = L"00000000"; UINT i; UINT CurrentDigit; AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); EntrySize = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG) + DeviceContext->DeviceNameLength + AdapterStringSize + sizeof(OidBuffer); errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( (PDEVICE_OBJECT)DeviceContext, EntrySize ); // // Convert the OID into a buffer. // for (i=7; i>=0; i--) { CurrentDigit = OidValue & 0xf; OidValue >>= 4; if (CurrentDigit >= 0xa) { OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); } else { OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); } } if (errorLogEntry != NULL) { errorLogEntry->MajorFunctionCode = (UCHAR)-1; errorLogEntry->RetryCount = (UCHAR)-1; errorLogEntry->DumpDataSize = 0; errorLogEntry->NumberOfStrings = 3; errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); errorLogEntry->EventCategory = 0; errorLogEntry->ErrorCode = ErrorCode; errorLogEntry->UniqueErrorValue = 0; errorLogEntry->FinalStatus = FinalStatus; errorLogEntry->SequenceNumber = (ULONG)-1; errorLogEntry->IoControlCode = 0; StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); StringLoc += DeviceContext->DeviceNameLength; RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); StringLoc += sizeof(OidBuffer); RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); IoWriteErrorLogEntry(errorLogEntry); } } /* StWriteOidErrorLog */