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