mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 13:18:51 +00:00
542 lines
17 KiB
C
542 lines
17 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS NDIS User I/O driver
|
|
* FILE: ioctl.c
|
|
* PURPOSE: IOCTL handling
|
|
* PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
|
|
*/
|
|
|
|
#include "ndisuio.h"
|
|
|
|
//#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
static
|
|
NTSTATUS
|
|
WaitForBind(PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
/* I've seen several code samples that use this IOCTL but there's
|
|
* no official documentation on it. I'm just implementing it as a no-op
|
|
* right now because I don't see any reason we need it. We handle an open
|
|
* and bind just fine with IRP_MJ_CREATE and IOCTL_NDISUIO_OPEN_DEVICE */
|
|
DPRINT("Wait for bind complete\n");
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
QueryBinding(PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PNDISUIO_ADAPTER_CONTEXT AdapterContext = NULL;
|
|
PNDISUIO_QUERY_BINDING QueryBinding = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG BindingLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY CurrentEntry;
|
|
KIRQL OldIrql;
|
|
ULONG i;
|
|
ULONG BytesCopied = 0;
|
|
|
|
if (QueryBinding && BindingLength >= sizeof(NDISUIO_QUERY_BINDING))
|
|
{
|
|
KeAcquireSpinLock(&GlobalAdapterListLock, &OldIrql);
|
|
i = 0;
|
|
CurrentEntry = GlobalAdapterList.Flink;
|
|
while (CurrentEntry != &GlobalAdapterList)
|
|
{
|
|
if (i == QueryBinding->BindingIndex)
|
|
{
|
|
AdapterContext = CONTAINING_RECORD(CurrentEntry, NDISUIO_ADAPTER_CONTEXT, ListEntry);
|
|
break;
|
|
}
|
|
i++;
|
|
CurrentEntry = CurrentEntry->Flink;
|
|
}
|
|
KeReleaseSpinLock(&GlobalAdapterListLock, OldIrql);
|
|
if (AdapterContext)
|
|
{
|
|
DPRINT("Query binding for index %d is adapter %wZ\n", i, &AdapterContext->DeviceName);
|
|
BytesCopied = sizeof(NDISUIO_QUERY_BINDING);
|
|
if (AdapterContext->DeviceName.Length <= BindingLength - BytesCopied)
|
|
{
|
|
QueryBinding->DeviceNameOffset = BytesCopied;
|
|
QueryBinding->DeviceNameLength = AdapterContext->DeviceName.Length;
|
|
RtlCopyMemory((PUCHAR)QueryBinding + QueryBinding->DeviceNameOffset,
|
|
AdapterContext->DeviceName.Buffer,
|
|
QueryBinding->DeviceNameLength);
|
|
BytesCopied += AdapterContext->DeviceName.Length;
|
|
|
|
/* FIXME: Copy description too */
|
|
QueryBinding->DeviceDescrOffset = BytesCopied;
|
|
QueryBinding->DeviceDescrLength = 0;
|
|
|
|
/* Successful */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Not enough buffer space */
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Invalid index */
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Invalid parameters */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
Irp->IoStatus.Information = BytesCopied;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
|
|
#if 0
|
|
static
|
|
NTSTATUS
|
|
CancelPacketRead(PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PNDISUIO_ADAPTER_CONTEXT AdapterContext = IrpSp->FileObject->FsContext;
|
|
PNDISUIO_PACKET_ENTRY PacketEntry;
|
|
NTSTATUS Status;
|
|
|
|
/* Indicate a 0-byte packet on the queue so one read returns 0 */
|
|
PacketEntry = ExAllocatePool(PagedPool, sizeof(NDISUIO_PACKET_ENTRY));
|
|
if (PacketEntry)
|
|
{
|
|
PacketEntry->PacketLength = 0;
|
|
|
|
ExInterlockedInsertHeadList(&AdapterContext->PacketList,
|
|
&PacketEntry->ListEntry,
|
|
&AdapterContext->Spinlock);
|
|
|
|
KeSetEvent(&AdapterContext->PacketReadEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
static
|
|
NTSTATUS
|
|
SetAdapterOid(PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PNDISUIO_ADAPTER_CONTEXT AdapterContext = IrpSp->FileObject->FsContext;
|
|
PNDISUIO_SET_OID SetOidRequest;
|
|
NDIS_REQUEST Request;
|
|
ULONG RequestLength;
|
|
NDIS_STATUS Status;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
SetOidRequest = Irp->AssociatedIrp.SystemBuffer;
|
|
RequestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
if (SetOidRequest && RequestLength >= sizeof(NDIS_OID))
|
|
{
|
|
/* Setup the NDIS request */
|
|
Request.RequestType = NdisRequestSetInformation;
|
|
Request.DATA.SET_INFORMATION.Oid = SetOidRequest->Oid;
|
|
Request.DATA.SET_INFORMATION.InformationBufferLength = RequestLength - sizeof(NDIS_OID);
|
|
if (Request.DATA.SET_INFORMATION.InformationBufferLength != 0)
|
|
{
|
|
Request.DATA.SET_INFORMATION.InformationBuffer = SetOidRequest->Data;
|
|
}
|
|
else
|
|
{
|
|
Request.DATA.SET_INFORMATION.InformationBuffer = NULL;
|
|
}
|
|
Request.DATA.SET_INFORMATION.BytesRead = 0;
|
|
|
|
DPRINT("Setting OID 0x%x on adapter %wZ\n", SetOidRequest->Oid, &AdapterContext->DeviceName);
|
|
|
|
/* Dispatch the request */
|
|
NdisRequest(&Status,
|
|
AdapterContext->BindingHandle,
|
|
&Request);
|
|
|
|
/* Wait for the request */
|
|
if (Status == NDIS_STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&AdapterContext->AsyncEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
Status = AdapterContext->AsyncStatus;
|
|
}
|
|
|
|
/* Return the bytes read */
|
|
if (Status == NDIS_STATUS_INVALID_LENGTH ||
|
|
Status == NDIS_STATUS_BUFFER_TOO_SHORT)
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else if (Status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
Irp->IoStatus.Information = sizeof(NDIS_OID) + Request.DATA.SET_INFORMATION.BytesRead;
|
|
}
|
|
|
|
DPRINT("Final request status: 0x%x (%d)\n", Status, Irp->IoStatus.Information);
|
|
}
|
|
else
|
|
{
|
|
/* Bad parameters */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
QueryAdapterOid(PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PNDISUIO_ADAPTER_CONTEXT AdapterContext = IrpSp->FileObject->FsContext;
|
|
PNDISUIO_QUERY_OID QueryOidRequest;
|
|
NDIS_REQUEST Request;
|
|
ULONG RequestLength;
|
|
NDIS_STATUS Status;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
QueryOidRequest = Irp->AssociatedIrp.SystemBuffer;
|
|
RequestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
if (QueryOidRequest && RequestLength >= sizeof(NDIS_OID))
|
|
{
|
|
/* Setup the NDIS request */
|
|
Request.RequestType = NdisRequestQueryInformation;
|
|
Request.DATA.QUERY_INFORMATION.Oid = QueryOidRequest->Oid;
|
|
Request.DATA.QUERY_INFORMATION.InformationBufferLength = RequestLength - sizeof(NDIS_OID);
|
|
if (Request.DATA.QUERY_INFORMATION.InformationBufferLength != 0)
|
|
{
|
|
Request.DATA.QUERY_INFORMATION.InformationBuffer = QueryOidRequest->Data;
|
|
}
|
|
else
|
|
{
|
|
Request.DATA.QUERY_INFORMATION.InformationBuffer = NULL;
|
|
}
|
|
Request.DATA.QUERY_INFORMATION.BytesWritten = 0;
|
|
|
|
DPRINT("Querying OID 0x%x on adapter %wZ\n", QueryOidRequest->Oid, &AdapterContext->DeviceName);
|
|
|
|
/* Dispatch the request */
|
|
NdisRequest(&Status,
|
|
AdapterContext->BindingHandle,
|
|
&Request);
|
|
|
|
/* Wait for the request */
|
|
if (Status == NDIS_STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&AdapterContext->AsyncEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
Status = AdapterContext->AsyncStatus;
|
|
}
|
|
|
|
/* Return the bytes written */
|
|
if (Status == NDIS_STATUS_INVALID_LENGTH ||
|
|
Status == NDIS_STATUS_BUFFER_TOO_SHORT)
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else if (Status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
Irp->IoStatus.Information = sizeof(NDIS_OID) + Request.DATA.QUERY_INFORMATION.BytesWritten;
|
|
}
|
|
|
|
DPRINT("Final request status: 0x%x (%d)\n", Status, Irp->IoStatus.Information);
|
|
}
|
|
else
|
|
{
|
|
/* Bad parameters */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
OpenDeviceReadWrite(PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
UNICODE_STRING DeviceName;
|
|
ULONG NameLength;
|
|
NTSTATUS Status;
|
|
PNDISUIO_ADAPTER_CONTEXT AdapterContext;
|
|
PNDISUIO_OPEN_ENTRY OpenEntry;
|
|
KIRQL OldIrql;
|
|
|
|
NameLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
if (NameLength != 0)
|
|
{
|
|
DeviceName.MaximumLength = DeviceName.Length = NameLength;
|
|
DeviceName.Buffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
/* Check if this already has a context */
|
|
AdapterContext = FindAdapterContextByName(&DeviceName);
|
|
if (AdapterContext != NULL)
|
|
{
|
|
DPRINT("Binding file object 0x%x to device %wZ\n", FileObject, &AdapterContext->DeviceName);
|
|
|
|
/* Reference the adapter context */
|
|
KeAcquireSpinLock(&AdapterContext->Spinlock, &OldIrql);
|
|
if (AdapterContext->OpenCount != 0)
|
|
{
|
|
/* An open for read-write is exclusive,
|
|
* so we can't have any other open handles */
|
|
KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
/* Add a reference */
|
|
ReferenceAdapterContext(AdapterContext);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Invalid device name */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check that the bind succeeded */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
OpenEntry = ExAllocatePool(NonPagedPool, sizeof(*OpenEntry));
|
|
if (OpenEntry)
|
|
{
|
|
/* Set the file object pointer */
|
|
OpenEntry->FileObject = FileObject;
|
|
|
|
/* Set the permissions */
|
|
OpenEntry->WriteOnly = FALSE;
|
|
|
|
/* Associate this FO with the adapter */
|
|
FileObject->FsContext = AdapterContext;
|
|
FileObject->FsContext2 = OpenEntry;
|
|
|
|
/* Add it to the adapter's list */
|
|
InsertTailList(&AdapterContext->OpenEntryList,
|
|
&OpenEntry->ListEntry);
|
|
|
|
/* Success */
|
|
KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Remove the reference we added */
|
|
KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
|
|
DereferenceAdapterContextWithOpenEntry(AdapterContext, NULL);
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Invalid device name */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
|
|
#if 0
|
|
static
|
|
NTSTATUS
|
|
OpenDeviceWrite(PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
UNICODE_STRING DeviceName;
|
|
ULONG NameLength;
|
|
NTSTATUS Status;
|
|
PNDISUIO_ADAPTER_CONTEXT AdapterContext;
|
|
PNDISUIO_OPEN_ENTRY OpenEntry;
|
|
KIRQL OldIrql;
|
|
|
|
NameLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
if (NameLength != 0)
|
|
{
|
|
DeviceName.MaximumLength = DeviceName.Length = NameLength;
|
|
DeviceName.Buffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
/* Check if this already has a context */
|
|
AdapterContext = FindAdapterContextByName(&DeviceName);
|
|
if (AdapterContext != NULL)
|
|
{
|
|
/* Reference the adapter context */
|
|
KeAcquireSpinLock(&AdapterContext->Spinlock, &OldIrql);
|
|
ReferenceAdapterContext(AdapterContext);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Invalid device name */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check that the bind succeeded */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
OpenEntry = ExAllocatePool(NonPagedPool, sizeof(*OpenEntry));
|
|
if (OpenEntry)
|
|
{
|
|
/* Set the file object pointer */
|
|
OpenEntry->FileObject = FileObject;
|
|
|
|
/* Associate this FO with the adapter */
|
|
FileObject->FsContext = AdapterContext;
|
|
FileObject->FsContext2 = OpenEntry;
|
|
|
|
/* Set permissions */
|
|
OpenEntry->WriteOnly = TRUE;
|
|
|
|
/* Add it to the adapter's list */
|
|
InsertTailList(&AdapterContext->OpenEntryList,
|
|
&OpenEntry->ListEntry);
|
|
|
|
/* Success */
|
|
KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Remove the reference we added */
|
|
KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
|
|
DereferenceAdapterContext(AdapterContext, NULL);
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Invalid device name */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NduDispatchDeviceControl(PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PNDISUIO_OPEN_ENTRY OpenEntry;
|
|
|
|
ASSERT(DeviceObject == GlobalDeviceObject);
|
|
|
|
/* Handle open IOCTLs first */
|
|
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
case IOCTL_NDISUIO_OPEN_DEVICE:
|
|
return OpenDeviceReadWrite(Irp, IrpSp);
|
|
#if 0
|
|
case IOCTL_NDISUIO_OPEN_WRITE_DEVICE:
|
|
return OpenDeviceWrite(Irp, IrpSp);
|
|
#endif
|
|
case IOCTL_NDISUIO_BIND_WAIT:
|
|
return WaitForBind(Irp, IrpSp);
|
|
|
|
case IOCTL_NDISUIO_QUERY_BINDING:
|
|
return QueryBinding(Irp, IrpSp);
|
|
|
|
default:
|
|
/* Fail if this file object has no adapter associated */
|
|
if (IrpSp->FileObject->FsContext == NULL)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Now handle write IOCTLs */
|
|
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
case IOCTL_NDISUIO_SET_OID_VALUE:
|
|
return SetAdapterOid(Irp, IrpSp);
|
|
|
|
default:
|
|
/* Check that we have read permissions */
|
|
OpenEntry = IrpSp->FileObject->FsContext2;
|
|
if (OpenEntry->WriteOnly)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
#if 0
|
|
case IOCTL_CANCEL_READ:
|
|
return CancelPacketRead(Irp, IrpSp);
|
|
#endif
|
|
|
|
case IOCTL_NDISUIO_QUERY_OID_VALUE:
|
|
return QueryAdapterOid(Irp, IrpSp);
|
|
|
|
default:
|
|
DPRINT1("Unimplemented\n");
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|