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