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