1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS NDIS User I/O driver 4 * FILE: ioctl.c 5 * PURPOSE: IOCTL handling 6 * PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org) 7 */ 8 9 #include "ndisuio.h" 10 11 //#define NDEBUG 12 #include <debug.h> 13 14 static 15 NTSTATUS 16 WaitForBind(PIRP Irp, PIO_STACK_LOCATION IrpSp) 17 { 18 /* I've seen several code samples that use this IOCTL but there's 19 * no official documentation on it. I'm just implementing it as a no-op 20 * right now because I don't see any reason we need it. We handle an open 21 * and bind just fine with IRP_MJ_CREATE and IOCTL_NDISUIO_OPEN_DEVICE */ 22 DPRINT("Wait for bind complete\n"); 23 24 Irp->IoStatus.Status = STATUS_SUCCESS; 25 Irp->IoStatus.Information = 0; 26 27 IoCompleteRequest(Irp, IO_NO_INCREMENT); 28 29 return STATUS_SUCCESS; 30 } 31 32 static 33 NTSTATUS 34 QueryBinding(PIRP Irp, PIO_STACK_LOCATION IrpSp) 35 { 36 PNDISUIO_ADAPTER_CONTEXT AdapterContext = NULL; 37 PNDISUIO_QUERY_BINDING QueryBinding = Irp->AssociatedIrp.SystemBuffer; 38 ULONG BindingLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; 39 NTSTATUS Status; 40 PLIST_ENTRY CurrentEntry; 41 KIRQL OldIrql; 42 ULONG i; 43 ULONG BytesCopied = 0; 44 45 if (QueryBinding && BindingLength >= sizeof(NDISUIO_QUERY_BINDING)) 46 { 47 KeAcquireSpinLock(&GlobalAdapterListLock, &OldIrql); 48 i = 0; 49 CurrentEntry = GlobalAdapterList.Flink; 50 while (CurrentEntry != &GlobalAdapterList) 51 { 52 if (i == QueryBinding->BindingIndex) 53 { 54 AdapterContext = CONTAINING_RECORD(CurrentEntry, NDISUIO_ADAPTER_CONTEXT, ListEntry); 55 break; 56 } 57 i++; 58 CurrentEntry = CurrentEntry->Flink; 59 } 60 KeReleaseSpinLock(&GlobalAdapterListLock, OldIrql); 61 if (AdapterContext) 62 { 63 DPRINT("Query binding for index %d is adapter %wZ\n", i, &AdapterContext->DeviceName); 64 BytesCopied = sizeof(NDISUIO_QUERY_BINDING); 65 if (AdapterContext->DeviceName.Length <= BindingLength - BytesCopied) 66 { 67 QueryBinding->DeviceNameOffset = BytesCopied; 68 QueryBinding->DeviceNameLength = AdapterContext->DeviceName.Length; 69 RtlCopyMemory((PUCHAR)QueryBinding + QueryBinding->DeviceNameOffset, 70 AdapterContext->DeviceName.Buffer, 71 QueryBinding->DeviceNameLength); 72 BytesCopied += AdapterContext->DeviceName.Length; 73 74 /* FIXME: Copy description too */ 75 QueryBinding->DeviceDescrOffset = BytesCopied; 76 QueryBinding->DeviceDescrLength = 0; 77 78 /* Successful */ 79 Status = STATUS_SUCCESS; 80 } 81 else 82 { 83 /* Not enough buffer space */ 84 Status = STATUS_BUFFER_TOO_SMALL; 85 } 86 } 87 else 88 { 89 /* Invalid index */ 90 Status = STATUS_NO_MORE_ENTRIES; 91 } 92 } 93 else 94 { 95 /* Invalid parameters */ 96 Status = STATUS_INVALID_PARAMETER; 97 } 98 99 Irp->IoStatus.Status = Status; 100 Irp->IoStatus.Information = BytesCopied; 101 102 IoCompleteRequest(Irp, IO_NO_INCREMENT); 103 104 return Status; 105 } 106 107 #if 0 108 static 109 NTSTATUS 110 CancelPacketRead(PIRP Irp, PIO_STACK_LOCATION IrpSp) 111 { 112 PNDISUIO_ADAPTER_CONTEXT AdapterContext = IrpSp->FileObject->FsContext; 113 PNDISUIO_PACKET_ENTRY PacketEntry; 114 NTSTATUS Status; 115 116 /* Indicate a 0-byte packet on the queue so one read returns 0 */ 117 PacketEntry = ExAllocatePool(PagedPool, sizeof(NDISUIO_PACKET_ENTRY)); 118 if (PacketEntry) 119 { 120 PacketEntry->PacketLength = 0; 121 122 ExInterlockedInsertHeadList(&AdapterContext->PacketList, 123 &PacketEntry->ListEntry, 124 &AdapterContext->Spinlock); 125 126 KeSetEvent(&AdapterContext->PacketReadEvent, IO_NO_INCREMENT, FALSE); 127 128 Status = STATUS_SUCCESS; 129 } 130 else 131 { 132 Status = STATUS_NO_MEMORY; 133 } 134 135 Irp->IoStatus.Status = Status; 136 Irp->IoStatus.Information = 0; 137 138 IoCompleteRequest(Irp, IO_NO_INCREMENT); 139 140 return Status; 141 } 142 #endif 143 144 static 145 NTSTATUS 146 SetAdapterOid(PIRP Irp, PIO_STACK_LOCATION IrpSp) 147 { 148 PNDISUIO_ADAPTER_CONTEXT AdapterContext = IrpSp->FileObject->FsContext; 149 PNDISUIO_SET_OID SetOidRequest; 150 NDIS_REQUEST Request; 151 ULONG RequestLength; 152 NDIS_STATUS Status; 153 154 Irp->IoStatus.Information = 0; 155 156 SetOidRequest = Irp->AssociatedIrp.SystemBuffer; 157 RequestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; 158 if (SetOidRequest && RequestLength >= sizeof(NDIS_OID)) 159 { 160 /* Setup the NDIS request */ 161 Request.RequestType = NdisRequestSetInformation; 162 Request.DATA.SET_INFORMATION.Oid = SetOidRequest->Oid; 163 Request.DATA.SET_INFORMATION.InformationBufferLength = RequestLength - sizeof(NDIS_OID); 164 if (Request.DATA.SET_INFORMATION.InformationBufferLength != 0) 165 { 166 Request.DATA.SET_INFORMATION.InformationBuffer = SetOidRequest->Data; 167 } 168 else 169 { 170 Request.DATA.SET_INFORMATION.InformationBuffer = NULL; 171 } 172 Request.DATA.SET_INFORMATION.BytesRead = 0; 173 174 DPRINT("Setting OID 0x%x on adapter %wZ\n", SetOidRequest->Oid, &AdapterContext->DeviceName); 175 176 /* Dispatch the request */ 177 NdisRequest(&Status, 178 AdapterContext->BindingHandle, 179 &Request); 180 181 /* Wait for the request */ 182 if (Status == NDIS_STATUS_PENDING) 183 { 184 KeWaitForSingleObject(&AdapterContext->AsyncEvent, 185 Executive, 186 KernelMode, 187 FALSE, 188 NULL); 189 Status = AdapterContext->AsyncStatus; 190 } 191 192 /* Return the bytes read */ 193 if (Status == NDIS_STATUS_INVALID_LENGTH || 194 Status == NDIS_STATUS_BUFFER_TOO_SHORT) 195 { 196 Status = STATUS_BUFFER_TOO_SMALL; 197 } 198 else if (Status == NDIS_STATUS_SUCCESS) 199 { 200 Irp->IoStatus.Information = sizeof(NDIS_OID) + Request.DATA.SET_INFORMATION.BytesRead; 201 } 202 203 DPRINT("Final request status: 0x%x (%d)\n", Status, Irp->IoStatus.Information); 204 } 205 else 206 { 207 /* Bad parameters */ 208 Status = STATUS_INVALID_PARAMETER; 209 } 210 211 Irp->IoStatus.Status = Status; 212 213 IoCompleteRequest(Irp, IO_NO_INCREMENT); 214 215 return Status; 216 } 217 218 static 219 NTSTATUS 220 QueryAdapterOid(PIRP Irp, PIO_STACK_LOCATION IrpSp) 221 { 222 PNDISUIO_ADAPTER_CONTEXT AdapterContext = IrpSp->FileObject->FsContext; 223 PNDISUIO_QUERY_OID QueryOidRequest; 224 NDIS_REQUEST Request; 225 ULONG RequestLength; 226 NDIS_STATUS Status; 227 228 Irp->IoStatus.Information = 0; 229 230 QueryOidRequest = Irp->AssociatedIrp.SystemBuffer; 231 RequestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; 232 if (QueryOidRequest && RequestLength >= sizeof(NDIS_OID)) 233 { 234 /* Setup the NDIS request */ 235 Request.RequestType = NdisRequestQueryInformation; 236 Request.DATA.QUERY_INFORMATION.Oid = QueryOidRequest->Oid; 237 Request.DATA.QUERY_INFORMATION.InformationBufferLength = RequestLength - sizeof(NDIS_OID); 238 if (Request.DATA.QUERY_INFORMATION.InformationBufferLength != 0) 239 { 240 Request.DATA.QUERY_INFORMATION.InformationBuffer = QueryOidRequest->Data; 241 } 242 else 243 { 244 Request.DATA.QUERY_INFORMATION.InformationBuffer = NULL; 245 } 246 Request.DATA.QUERY_INFORMATION.BytesWritten = 0; 247 248 DPRINT("Querying OID 0x%x on adapter %wZ\n", QueryOidRequest->Oid, &AdapterContext->DeviceName); 249 250 /* Dispatch the request */ 251 NdisRequest(&Status, 252 AdapterContext->BindingHandle, 253 &Request); 254 255 /* Wait for the request */ 256 if (Status == NDIS_STATUS_PENDING) 257 { 258 KeWaitForSingleObject(&AdapterContext->AsyncEvent, 259 Executive, 260 KernelMode, 261 FALSE, 262 NULL); 263 Status = AdapterContext->AsyncStatus; 264 } 265 266 /* Return the bytes written */ 267 if (Status == NDIS_STATUS_INVALID_LENGTH || 268 Status == NDIS_STATUS_BUFFER_TOO_SHORT) 269 { 270 Status = STATUS_BUFFER_TOO_SMALL; 271 } 272 else if (Status == NDIS_STATUS_SUCCESS) 273 { 274 Irp->IoStatus.Information = sizeof(NDIS_OID) + Request.DATA.QUERY_INFORMATION.BytesWritten; 275 } 276 277 DPRINT("Final request status: 0x%x (%d)\n", Status, Irp->IoStatus.Information); 278 } 279 else 280 { 281 /* Bad parameters */ 282 Status = STATUS_INVALID_PARAMETER; 283 } 284 285 Irp->IoStatus.Status = Status; 286 287 IoCompleteRequest(Irp, IO_NO_INCREMENT); 288 289 return Status; 290 } 291 292 static 293 NTSTATUS 294 OpenDeviceReadWrite(PIRP Irp, PIO_STACK_LOCATION IrpSp) 295 { 296 PFILE_OBJECT FileObject = IrpSp->FileObject; 297 UNICODE_STRING DeviceName; 298 ULONG NameLength; 299 NTSTATUS Status; 300 PNDISUIO_ADAPTER_CONTEXT AdapterContext; 301 PNDISUIO_OPEN_ENTRY OpenEntry; 302 KIRQL OldIrql; 303 304 NameLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; 305 if (NameLength != 0) 306 { 307 DeviceName.MaximumLength = DeviceName.Length = NameLength; 308 DeviceName.Buffer = Irp->AssociatedIrp.SystemBuffer; 309 310 /* Check if this already has a context */ 311 AdapterContext = FindAdapterContextByName(&DeviceName); 312 if (AdapterContext != NULL) 313 { 314 DPRINT("Binding file object 0x%x to device %wZ\n", FileObject, &AdapterContext->DeviceName); 315 316 /* Reference the adapter context */ 317 KeAcquireSpinLock(&AdapterContext->Spinlock, &OldIrql); 318 if (AdapterContext->OpenCount != 0) 319 { 320 /* An open for read-write is exclusive, 321 * so we can't have any other open handles */ 322 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql); 323 Status = STATUS_INVALID_PARAMETER; 324 } 325 else 326 { 327 /* Add a reference */ 328 ReferenceAdapterContext(AdapterContext); 329 Status = STATUS_SUCCESS; 330 } 331 } 332 else 333 { 334 /* Invalid device name */ 335 Status = STATUS_INVALID_PARAMETER; 336 } 337 338 /* Check that the bind succeeded */ 339 if (NT_SUCCESS(Status)) 340 { 341 OpenEntry = ExAllocatePool(NonPagedPool, sizeof(*OpenEntry)); 342 if (OpenEntry) 343 { 344 /* Set the file object pointer */ 345 OpenEntry->FileObject = FileObject; 346 347 /* Set the permissions */ 348 OpenEntry->WriteOnly = FALSE; 349 350 /* Associate this FO with the adapter */ 351 FileObject->FsContext = AdapterContext; 352 FileObject->FsContext2 = OpenEntry; 353 354 /* Add it to the adapter's list */ 355 InsertTailList(&AdapterContext->OpenEntryList, 356 &OpenEntry->ListEntry); 357 358 /* Success */ 359 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql); 360 Status = STATUS_SUCCESS; 361 } 362 else 363 { 364 /* Remove the reference we added */ 365 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql); 366 DereferenceAdapterContextWithOpenEntry(AdapterContext, NULL); 367 Status = STATUS_NO_MEMORY; 368 } 369 } 370 } 371 else 372 { 373 /* Invalid device name */ 374 Status = STATUS_INVALID_PARAMETER; 375 } 376 377 Irp->IoStatus.Status = Status; 378 Irp->IoStatus.Information = 0; 379 380 IoCompleteRequest(Irp, IO_NO_INCREMENT); 381 382 return Status; 383 } 384 385 #if 0 386 static 387 NTSTATUS 388 OpenDeviceWrite(PIRP Irp, PIO_STACK_LOCATION IrpSp) 389 { 390 PFILE_OBJECT FileObject = IrpSp->FileObject; 391 UNICODE_STRING DeviceName; 392 ULONG NameLength; 393 NTSTATUS Status; 394 PNDISUIO_ADAPTER_CONTEXT AdapterContext; 395 PNDISUIO_OPEN_ENTRY OpenEntry; 396 KIRQL OldIrql; 397 398 NameLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; 399 if (NameLength != 0) 400 { 401 DeviceName.MaximumLength = DeviceName.Length = NameLength; 402 DeviceName.Buffer = Irp->AssociatedIrp.SystemBuffer; 403 404 /* Check if this already has a context */ 405 AdapterContext = FindAdapterContextByName(&DeviceName); 406 if (AdapterContext != NULL) 407 { 408 /* Reference the adapter context */ 409 KeAcquireSpinLock(&AdapterContext->Spinlock, &OldIrql); 410 ReferenceAdapterContext(AdapterContext); 411 Status = STATUS_SUCCESS; 412 } 413 else 414 { 415 /* Invalid device name */ 416 Status = STATUS_INVALID_PARAMETER; 417 } 418 419 /* Check that the bind succeeded */ 420 if (NT_SUCCESS(Status)) 421 { 422 OpenEntry = ExAllocatePool(NonPagedPool, sizeof(*OpenEntry)); 423 if (OpenEntry) 424 { 425 /* Set the file object pointer */ 426 OpenEntry->FileObject = FileObject; 427 428 /* Associate this FO with the adapter */ 429 FileObject->FsContext = AdapterContext; 430 FileObject->FsContext2 = OpenEntry; 431 432 /* Set permissions */ 433 OpenEntry->WriteOnly = TRUE; 434 435 /* Add it to the adapter's list */ 436 InsertTailList(&AdapterContext->OpenEntryList, 437 &OpenEntry->ListEntry); 438 439 /* Success */ 440 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql); 441 Status = STATUS_SUCCESS; 442 } 443 else 444 { 445 /* Remove the reference we added */ 446 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql); 447 DereferenceAdapterContext(AdapterContext, NULL); 448 Status = STATUS_NO_MEMORY; 449 } 450 } 451 } 452 else 453 { 454 /* Invalid device name */ 455 Status = STATUS_INVALID_PARAMETER; 456 } 457 458 Irp->IoStatus.Status = Status; 459 Irp->IoStatus.Information = 0; 460 461 IoCompleteRequest(Irp, IO_NO_INCREMENT); 462 463 return Status; 464 } 465 #endif 466 467 NTSTATUS 468 NTAPI 469 NduDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, 470 PIRP Irp) 471 { 472 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 473 PNDISUIO_OPEN_ENTRY OpenEntry; 474 475 ASSERT(DeviceObject == GlobalDeviceObject); 476 477 /* Handle open IOCTLs first */ 478 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) 479 { 480 case IOCTL_NDISUIO_OPEN_DEVICE: 481 return OpenDeviceReadWrite(Irp, IrpSp); 482 #if 0 483 case IOCTL_NDISUIO_OPEN_WRITE_DEVICE: 484 return OpenDeviceWrite(Irp, IrpSp); 485 #endif 486 case IOCTL_NDISUIO_BIND_WAIT: 487 return WaitForBind(Irp, IrpSp); 488 489 case IOCTL_NDISUIO_QUERY_BINDING: 490 return QueryBinding(Irp, IrpSp); 491 492 default: 493 /* Fail if this file object has no adapter associated */ 494 if (IrpSp->FileObject->FsContext == NULL) 495 { 496 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 497 Irp->IoStatus.Information = 0; 498 IoCompleteRequest(Irp, IO_NO_INCREMENT); 499 500 return STATUS_INVALID_PARAMETER; 501 } 502 503 /* Now handle write IOCTLs */ 504 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) 505 { 506 case IOCTL_NDISUIO_SET_OID_VALUE: 507 return SetAdapterOid(Irp, IrpSp); 508 509 default: 510 /* Check that we have read permissions */ 511 OpenEntry = IrpSp->FileObject->FsContext2; 512 if (OpenEntry->WriteOnly) 513 { 514 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 515 Irp->IoStatus.Information = 0; 516 IoCompleteRequest(Irp, IO_NO_INCREMENT); 517 518 return STATUS_INVALID_PARAMETER; 519 } 520 521 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) 522 { 523 #if 0 524 case IOCTL_CANCEL_READ: 525 return CancelPacketRead(Irp, IrpSp); 526 #endif 527 528 case IOCTL_NDISUIO_QUERY_OID_VALUE: 529 return QueryAdapterOid(Irp, IrpSp); 530 531 default: 532 DPRINT1("Unimplemented\n"); 533 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; 534 Irp->IoStatus.Information = 0; 535 IoCompleteRequest(Irp, IO_NO_INCREMENT); 536 return STATUS_NOT_IMPLEMENTED; 537 } 538 } 539 break; 540 } 541 } 542