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