[NDISUIO]
[reactos.git] / drivers / network / ndisuio / ioctl.c
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.InformationBuffer = SetOidRequest->Data;
164 Request.DATA.SET_INFORMATION.InformationBufferLength = RequestLength - sizeof(NDIS_OID);
165
166 DPRINT("Setting OID 0x%x on adapter %wZ\n", SetOidRequest->Oid, &AdapterContext->DeviceName);
167
168 /* Dispatch the request */
169 NdisRequest(&Status,
170 AdapterContext->BindingHandle,
171 &Request);
172
173 /* Wait for the request */
174 if (Status == NDIS_STATUS_PENDING)
175 {
176 KeWaitForSingleObject(&AdapterContext->AsyncEvent,
177 Executive,
178 KernelMode,
179 FALSE,
180 NULL);
181 Status = AdapterContext->AsyncStatus;
182 }
183
184 /* Return the bytes read */
185 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = Request.DATA.SET_INFORMATION.BytesRead;
186 }
187 else
188 {
189 /* Bad parameters */
190 Status = STATUS_INVALID_PARAMETER;
191 }
192
193 Irp->IoStatus.Status = Status;
194
195 IoCompleteRequest(Irp, IO_NO_INCREMENT);
196
197 return Status;
198 }
199
200 static
201 NTSTATUS
202 QueryAdapterOid(PIRP Irp, PIO_STACK_LOCATION IrpSp)
203 {
204 PNDISUIO_ADAPTER_CONTEXT AdapterContext = IrpSp->FileObject->FsContext;
205 PNDISUIO_QUERY_OID QueryOidRequest;
206 NDIS_REQUEST Request;
207 ULONG RequestLength;
208 NDIS_STATUS Status;
209
210 Irp->IoStatus.Information = 0;
211
212 QueryOidRequest = Irp->AssociatedIrp.SystemBuffer;
213 RequestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
214 if (QueryOidRequest && RequestLength >= sizeof(NDIS_OID))
215 {
216 /* Setup the NDIS request */
217 Request.RequestType = NdisRequestQueryInformation;
218 Request.DATA.QUERY_INFORMATION.Oid = QueryOidRequest->Oid;
219 Request.DATA.QUERY_INFORMATION.InformationBuffer = QueryOidRequest->Data;
220 Request.DATA.QUERY_INFORMATION.InformationBufferLength = RequestLength - sizeof(NDIS_OID);
221
222 DPRINT("Querying OID 0x%x on adapter %wZ\n", QueryOidRequest->Oid, &AdapterContext->DeviceName);
223
224 /* Dispatch the request */
225 NdisRequest(&Status,
226 AdapterContext->BindingHandle,
227 &Request);
228
229 /* Wait for the request */
230 if (Status == NDIS_STATUS_PENDING)
231 {
232 KeWaitForSingleObject(&AdapterContext->AsyncEvent,
233 Executive,
234 KernelMode,
235 FALSE,
236 NULL);
237 Status = AdapterContext->AsyncStatus;
238 }
239
240 /* Return the bytes written */
241 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = Request.DATA.QUERY_INFORMATION.BytesWritten;
242 }
243 else
244 {
245 /* Bad parameters */
246 Status = STATUS_INVALID_PARAMETER;
247 }
248
249 Irp->IoStatus.Status = Status;
250
251 IoCompleteRequest(Irp, IO_NO_INCREMENT);
252
253 return Status;
254 }
255
256 static
257 NTSTATUS
258 OpenDeviceReadWrite(PIRP Irp, PIO_STACK_LOCATION IrpSp)
259 {
260 PFILE_OBJECT FileObject = IrpSp->FileObject;
261 UNICODE_STRING DeviceName;
262 ULONG NameLength;
263 NTSTATUS Status;
264 PNDISUIO_ADAPTER_CONTEXT AdapterContext;
265 PNDISUIO_OPEN_ENTRY OpenEntry;
266 KIRQL OldIrql;
267
268 NameLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
269 if (NameLength != 0)
270 {
271 DeviceName.MaximumLength = DeviceName.Length = NameLength;
272 DeviceName.Buffer = Irp->AssociatedIrp.SystemBuffer;
273
274 /* Check if this already has a context */
275 AdapterContext = FindAdapterContextByName(&DeviceName);
276 if (AdapterContext != NULL)
277 {
278 DPRINT("Binding file object 0x%x to device %wZ\n", FileObject, &AdapterContext->DeviceName);
279
280 /* Reference the adapter context */
281 KeAcquireSpinLock(&AdapterContext->Spinlock, &OldIrql);
282 if (AdapterContext->OpenCount != 0)
283 {
284 /* An open for read-write is exclusive,
285 * so we can't have any other open handles */
286 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
287 Status = STATUS_INVALID_PARAMETER;
288 }
289 else
290 {
291 /* Add a reference */
292 ReferenceAdapterContext(AdapterContext);
293 Status = STATUS_SUCCESS;
294 }
295 }
296 else
297 {
298 /* Invalid device name */
299 Status = STATUS_INVALID_PARAMETER;
300 }
301
302 /* Check that the bind succeeded */
303 if (NT_SUCCESS(Status))
304 {
305 OpenEntry = ExAllocatePool(NonPagedPool, sizeof(*OpenEntry));
306 if (OpenEntry)
307 {
308 /* Set the file object pointer */
309 OpenEntry->FileObject = FileObject;
310
311 /* Set the permissions */
312 OpenEntry->WriteOnly = FALSE;
313
314 /* Associate this FO with the adapter */
315 FileObject->FsContext = AdapterContext;
316 FileObject->FsContext2 = OpenEntry;
317
318 /* Add it to the adapter's list */
319 InsertTailList(&AdapterContext->OpenEntryList,
320 &OpenEntry->ListEntry);
321
322 /* Success */
323 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
324 Status = STATUS_SUCCESS;
325 }
326 else
327 {
328 /* Remove the reference we added */
329 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
330 DereferenceAdapterContextWithOpenEntry(AdapterContext, NULL);
331 Status = STATUS_NO_MEMORY;
332 }
333 }
334 }
335 else
336 {
337 /* Invalid device name */
338 Status = STATUS_INVALID_PARAMETER;
339 }
340
341 Irp->IoStatus.Status = Status;
342 Irp->IoStatus.Information = 0;
343
344 IoCompleteRequest(Irp, IO_NO_INCREMENT);
345
346 return Status;
347 }
348
349 #if 0
350 static
351 NTSTATUS
352 OpenDeviceWrite(PIRP Irp, PIO_STACK_LOCATION IrpSp)
353 {
354 PFILE_OBJECT FileObject = IrpSp->FileObject;
355 UNICODE_STRING DeviceName;
356 ULONG NameLength;
357 NTSTATUS Status;
358 PNDISUIO_ADAPTER_CONTEXT AdapterContext;
359 PNDISUIO_OPEN_ENTRY OpenEntry;
360 KIRQL OldIrql;
361
362 NameLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
363 if (NameLength != 0)
364 {
365 DeviceName.MaximumLength = DeviceName.Length = NameLength;
366 DeviceName.Buffer = Irp->AssociatedIrp.SystemBuffer;
367
368 /* Check if this already has a context */
369 AdapterContext = FindAdapterContextByName(&DeviceName);
370 if (AdapterContext != NULL)
371 {
372 /* Reference the adapter context */
373 KeAcquireSpinLock(&AdapterContext->Spinlock, &OldIrql);
374 ReferenceAdapterContext(AdapterContext);
375 Status = STATUS_SUCCESS;
376 }
377 else
378 {
379 /* Invalid device name */
380 Status = STATUS_INVALID_PARAMETER;
381 }
382
383 /* Check that the bind succeeded */
384 if (NT_SUCCESS(Status))
385 {
386 OpenEntry = ExAllocatePool(NonPagedPool, sizeof(*OpenEntry));
387 if (OpenEntry)
388 {
389 /* Set the file object pointer */
390 OpenEntry->FileObject = FileObject;
391
392 /* Associate this FO with the adapter */
393 FileObject->FsContext = AdapterContext;
394 FileObject->FsContext2 = OpenEntry;
395
396 /* Set permissions */
397 OpenEntry->WriteOnly = TRUE;
398
399 /* Add it to the adapter's list */
400 InsertTailList(&AdapterContext->OpenEntryList,
401 &OpenEntry->ListEntry);
402
403 /* Success */
404 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
405 Status = STATUS_SUCCESS;
406 }
407 else
408 {
409 /* Remove the reference we added */
410 KeReleaseSpinLock(&AdapterContext->Spinlock, OldIrql);
411 DereferenceAdapterContext(AdapterContext, NULL);
412 Status = STATUS_NO_MEMORY;
413 }
414 }
415 }
416 else
417 {
418 /* Invalid device name */
419 Status = STATUS_INVALID_PARAMETER;
420 }
421
422 Irp->IoStatus.Status = Status;
423 Irp->IoStatus.Information = 0;
424
425 IoCompleteRequest(Irp, IO_NO_INCREMENT);
426
427 return Status;
428 }
429 #endif
430
431 NTSTATUS
432 NTAPI
433 NduDispatchDeviceControl(PDEVICE_OBJECT DeviceObject,
434 PIRP Irp)
435 {
436 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
437 PNDISUIO_OPEN_ENTRY OpenEntry;
438
439 ASSERT(DeviceObject == GlobalDeviceObject);
440
441 /* Handle open IOCTLs first */
442 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
443 {
444 case IOCTL_NDISUIO_OPEN_DEVICE:
445 return OpenDeviceReadWrite(Irp, IrpSp);
446 #if 0
447 case IOCTL_NDISUIO_OPEN_WRITE_DEVICE:
448 return OpenDeviceWrite(Irp, IrpSp);
449 #endif
450 case IOCTL_NDISUIO_BIND_WAIT:
451 return WaitForBind(Irp, IrpSp);
452
453 case IOCTL_NDISUIO_QUERY_BINDING:
454 return QueryBinding(Irp, IrpSp);
455
456 default:
457 /* Fail if this file object has no adapter associated */
458 if (IrpSp->FileObject->FsContext == NULL)
459 {
460 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
461 Irp->IoStatus.Information = 0;
462 IoCompleteRequest(Irp, IO_NO_INCREMENT);
463
464 return STATUS_INVALID_PARAMETER;
465 }
466
467 /* Now handle write IOCTLs */
468 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
469 {
470 case IOCTL_NDISUIO_SET_OID_VALUE:
471 return SetAdapterOid(Irp, IrpSp);
472
473 default:
474 /* Check that we have read permissions */
475 OpenEntry = IrpSp->FileObject->FsContext2;
476 if (OpenEntry->WriteOnly)
477 {
478 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
479 Irp->IoStatus.Information = 0;
480 IoCompleteRequest(Irp, IO_NO_INCREMENT);
481
482 return STATUS_INVALID_PARAMETER;
483 }
484
485 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
486 {
487 #if 0
488 case IOCTL_CANCEL_READ:
489 return CancelPacketRead(Irp, IrpSp);
490 #endif
491
492 case IOCTL_NDISUIO_QUERY_OID_VALUE:
493 return QueryAdapterOid(Irp, IrpSp);
494
495 default:
496 DPRINT1("Unimplemented\n");
497 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
498 Irp->IoStatus.Information = 0;
499 IoCompleteRequest(Irp, IO_NO_INCREMENT);
500 return STATUS_NOT_IMPLEMENTED;
501 }
502 }
503 break;
504 }
505 }