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