xref: /reactos/drivers/network/ndisuio/ioctl.c (revision bbabe248)
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