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