xref: /reactos/drivers/base/beep/beep.c (revision c2c66aff)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            drivers/base/beep/beep.c
5  * PURPOSE:         Beep Device Driver
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Eric Kohl
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntddk.h>
13 #include <ntddbeep.h>
14 #ifndef NDEBUG
15 #define NDEBUG
16 #endif
17 #include <debug.h>
18 
19 /* TYPES *********************************************************************/
20 
21 typedef struct _BEEP_DEVICE_EXTENSION
22 {
23     LONG ReferenceCount;
24     FAST_MUTEX Mutex;
25     KTIMER Timer;
26     LONG TimerActive;
27     PVOID SectionHandle;
28 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
29 
30 /* FUNCTIONS *****************************************************************/
31 
32 VOID
33 NTAPI
BeepDPC(IN PKDPC Dpc,IN PDEVICE_OBJECT DeviceObject,IN PVOID SystemArgument1,IN PVOID SystemArgument2)34 BeepDPC(IN PKDPC Dpc,
35         IN PDEVICE_OBJECT DeviceObject,
36         IN PVOID SystemArgument1,
37         IN PVOID SystemArgument2)
38 {
39     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
40 
41     UNREFERENCED_PARAMETER(Dpc);
42     UNREFERENCED_PARAMETER(SystemArgument1);
43     UNREFERENCED_PARAMETER(SystemArgument2);
44 
45     /* Stop the beep */
46     HalMakeBeep(0);
47 
48     /* Disable the timer */
49     InterlockedDecrement(&DeviceExtension->TimerActive);
50 }
51 
52 DRIVER_DISPATCH BeepCreate;
53 NTSTATUS
54 NTAPI
BeepCreate(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)55 BeepCreate(IN PDEVICE_OBJECT DeviceObject,
56            IN PIRP Irp)
57 {
58     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
59 
60     /* Acquire the mutex and increase reference count */
61     ExAcquireFastMutex(&DeviceExtension->Mutex);
62     if (++DeviceExtension->ReferenceCount == 1)
63     {
64         /* First reference, lock the data section */
65         DeviceExtension->SectionHandle = MmLockPagableDataSection(BeepCreate);
66     }
67 
68     /* Release it */
69     ExReleaseFastMutex(&DeviceExtension->Mutex);
70 
71     /* Complete the request */
72     Irp->IoStatus.Status = STATUS_SUCCESS;
73     Irp->IoStatus.Information = 0;
74     IoCompleteRequest(Irp, IO_NO_INCREMENT);
75     return STATUS_SUCCESS;
76 }
77 
78 DRIVER_DISPATCH BeepClose;
79 NTSTATUS
80 NTAPI
BeepClose(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)81 BeepClose(IN PDEVICE_OBJECT DeviceObject,
82           IN PIRP Irp)
83 {
84     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
85 
86     /* Acquire the mutex and decrease reference count */
87     ExAcquireFastMutex(&DeviceExtension->Mutex);
88     if (!(--DeviceExtension->ReferenceCount))
89     {
90         /* Check for active timer */
91         if (DeviceExtension->TimerActive)
92         {
93             /* Cancel it */
94             if (KeCancelTimer(&DeviceExtension->Timer))
95             {
96                 /* Mark it as cancelled */
97                 InterlockedDecrement(&DeviceExtension->TimerActive);
98             }
99         }
100 
101         /* Page the driver */
102         MmUnlockPagableImageSection(DeviceExtension->SectionHandle);
103     }
104 
105     /* Release the lock */
106     ExReleaseFastMutex(&DeviceExtension->Mutex);
107 
108     /* Complete the request */
109     Irp->IoStatus.Status = STATUS_SUCCESS;
110     Irp->IoStatus.Information = 0;
111     IoCompleteRequest(Irp, IO_NO_INCREMENT);
112     return STATUS_SUCCESS;
113 }
114 
115 DRIVER_CANCEL BeepCancel;
116 VOID
117 NTAPI
BeepCancel(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)118 BeepCancel(IN PDEVICE_OBJECT DeviceObject,
119            IN PIRP Irp)
120 {
121     /* Check if this is the current request */
122     if (Irp == DeviceObject->CurrentIrp)
123     {
124         /* Clear it */
125         DeviceObject->CurrentIrp = NULL;
126 
127         /* Release the cancel lock and start the next packet */
128         IoReleaseCancelSpinLock(Irp->CancelIrql);
129         IoStartNextPacket(DeviceObject, TRUE);
130     }
131     else
132     {
133         /* Otherwise, remove the packet from the queue and release the lock */
134         KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue,
135                                  &Irp->Tail.Overlay.DeviceQueueEntry);
136         IoReleaseCancelSpinLock(Irp->CancelIrql);
137     }
138 
139     /* Complete the request */
140     Irp->IoStatus.Status = STATUS_CANCELLED;
141     Irp->IoStatus.Information = 0;
142     IoCompleteRequest (Irp, IO_NO_INCREMENT);
143 }
144 
145 DRIVER_DISPATCH BeepCleanup;
146 NTSTATUS
147 NTAPI
BeepCleanup(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)148 BeepCleanup(IN PDEVICE_OBJECT DeviceObject,
149             IN PIRP Irp)
150 {
151     KIRQL OldIrql, CancelIrql;
152     PKDEVICE_QUEUE_ENTRY Packet;
153     PIRP CurrentIrp;
154 
155     /* Raise IRQL and acquire the cancel lock */
156     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
157     IoAcquireCancelSpinLock(&CancelIrql);
158 
159     /* Get the current IRP */
160     CurrentIrp = DeviceObject->CurrentIrp;
161     DeviceObject->CurrentIrp = NULL;
162     while (CurrentIrp)
163     {
164         /* Clear its cancel routine */
165         (VOID)IoSetCancelRoutine(CurrentIrp, NULL);
166 
167         /* Cancel the IRP */
168         CurrentIrp->IoStatus.Status = STATUS_CANCELLED;
169         CurrentIrp->IoStatus.Information = 0;
170 
171         /* Release the cancel lock and complete it */
172         IoReleaseCancelSpinLock(CancelIrql);
173         IoCompleteRequest(CurrentIrp, IO_NO_INCREMENT);
174 
175         /* Reacquire the lock and get the next queue packet */
176         IoAcquireCancelSpinLock(&CancelIrql);
177         Packet = KeRemoveDeviceQueue(&DeviceObject->DeviceQueue);
178         if (Packet)
179         {
180             /* Get the IRP */
181             CurrentIrp = CONTAINING_RECORD(Packet,
182                                            IRP,
183                                            Tail.Overlay.DeviceQueueEntry);
184         }
185         else
186         {
187             /* No more IRPs */
188             CurrentIrp = NULL;
189         }
190     }
191 
192     /* Release lock and go back to low IRQL */
193     IoReleaseCancelSpinLock(CancelIrql);
194     KeLowerIrql(OldIrql);
195 
196     /* Complete the IRP */
197     Irp->IoStatus.Status = STATUS_SUCCESS;
198     Irp->IoStatus.Information = 0;
199     IoCompleteRequest(Irp, IO_NO_INCREMENT);
200 
201     /* Stop and beep and return */
202     HalMakeBeep(0);
203     return STATUS_SUCCESS;
204 }
205 
206 DRIVER_DISPATCH BeepDeviceControl;
207 NTSTATUS
208 NTAPI
BeepDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)209 BeepDeviceControl(IN PDEVICE_OBJECT DeviceObject,
210                   IN PIRP Irp)
211 {
212     PIO_STACK_LOCATION Stack;
213     PBEEP_SET_PARAMETERS BeepParam;
214     NTSTATUS Status;
215 
216     /* Get the stack location and parameters */
217     Stack = IoGetCurrentIrpStackLocation(Irp);
218     BeepParam = (PBEEP_SET_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
219 
220     /* We only support one IOCTL */
221     if (Stack->Parameters.DeviceIoControl.IoControlCode != IOCTL_BEEP_SET)
222     {
223         /* Unsupported command */
224         Status = STATUS_NOT_IMPLEMENTED;
225     }
226     else
227     {
228         /* Validate the input buffer length */
229         if (Stack->Parameters.DeviceIoControl.InputBufferLength <
230             sizeof(BEEP_SET_PARAMETERS))
231         {
232             /* Invalid buffer */
233             Status = STATUS_INVALID_PARAMETER;
234         }
235         else if ((BeepParam->Frequency != 0) && !(BeepParam->Duration))
236         {
237             /* No duration, return immediately */
238             Status = STATUS_SUCCESS;
239         }
240         else
241         {
242             /* We'll queue this request */
243             Status = STATUS_PENDING;
244         }
245     }
246 
247     /* Set packet information */
248     Irp->IoStatus.Status = Status;
249     Irp->IoStatus.Information = 0;
250 
251     /* Check if we're completing or queuing a packet */
252     if (Status == STATUS_PENDING)
253     {
254         /* Start the queue */
255         IoMarkIrpPending(Irp);
256         IoStartPacket(DeviceObject, Irp, NULL, BeepCancel);
257     }
258     else
259     {
260         /* Complete the request */
261         IoCompleteRequest(Irp, IO_NO_INCREMENT);
262     }
263 
264     /* Return */
265     return Status;
266 }
267 
268 DRIVER_UNLOAD BeepUnload;
269 VOID
270 NTAPI
BeepUnload(IN PDRIVER_OBJECT DriverObject)271 BeepUnload(IN PDRIVER_OBJECT DriverObject)
272 {
273     PDEVICE_EXTENSION DeviceExtension;
274     PDEVICE_OBJECT DeviceObject;
275 
276     /* Get DO and DE */
277     DeviceObject = DriverObject->DeviceObject;
278     DeviceExtension = DeviceObject->DeviceExtension;
279 
280     /* Check if the timer is active */
281     if (DeviceExtension->TimerActive)
282     {
283         /* Cancel it */
284         if (KeCancelTimer(&DeviceExtension->Timer))
285         {
286             /* All done */
287             InterlockedDecrement(&DeviceExtension->TimerActive);
288         }
289     }
290 
291     /* Delete the object */
292     IoDeleteDevice(DeviceObject);
293 }
294 
295 DRIVER_STARTIO BeepStartIo;
296 VOID
297 NTAPI
BeepStartIo(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)298 BeepStartIo(IN PDEVICE_OBJECT DeviceObject,
299             IN PIRP Irp)
300 {
301     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
302     KIRQL CancelIrql;
303     PIO_STACK_LOCATION IoStack;
304     PBEEP_SET_PARAMETERS BeepParam;
305     LARGE_INTEGER DueTime;
306     NTSTATUS Status;
307 
308     /* Acquire the cancel lock and make sure the IRP is valid */
309     IoAcquireCancelSpinLock(&CancelIrql);
310     if (!Irp)
311     {
312         /* It's not, release the lock and quit */
313         IoReleaseCancelSpinLock(CancelIrql);
314         return;
315     }
316 
317     /* Remove the cancel routine and release the lock */
318     (VOID)IoSetCancelRoutine(Irp, NULL);
319     IoReleaseCancelSpinLock(CancelIrql);
320 
321     /* Get the I/O Stack and make sure the request is valid */
322     BeepParam = (PBEEP_SET_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
323     IoStack = IoGetCurrentIrpStackLocation(Irp);
324     if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_BEEP_SET)
325     {
326         /* Check if we have an active timer */
327         if (DeviceExtension->TimerActive)
328         {
329             /* Cancel it */
330             if (KeCancelTimer(&DeviceExtension->Timer))
331             {
332                 /* Set the state */
333                 InterlockedDecrement(&DeviceExtension->TimerActive);
334             }
335         }
336 
337         /* Make the beep */
338         if (HalMakeBeep(BeepParam->Frequency))
339         {
340             /* Beep successful, queue a DPC to stop it */
341             Status = STATUS_SUCCESS;
342             DueTime.QuadPart = BeepParam->Duration * -10000LL;
343             InterlockedIncrement(&DeviceExtension->TimerActive);
344             KeSetTimer(&DeviceExtension->Timer, DueTime, &DeviceObject->Dpc);
345         }
346         else
347         {
348             /* Beep has failed */
349             Status = STATUS_INVALID_PARAMETER;
350         }
351     }
352     else
353     {
354         /* Invalid request */
355         Status = STATUS_INVALID_PARAMETER;
356     }
357 
358     /* Complete the request and start the next packet */
359     Irp->IoStatus.Status = Status;
360     Irp->IoStatus.Information = 0;
361     IoStartNextPacket(DeviceObject, TRUE);
362     IoCompleteRequest(Irp, IO_NO_INCREMENT);
363 }
364 
365 NTSTATUS
366 NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)367 DriverEntry(IN PDRIVER_OBJECT DriverObject,
368             IN PUNICODE_STRING RegistryPath)
369 {
370     PDEVICE_EXTENSION DeviceExtension;
371     PDEVICE_OBJECT DeviceObject;
372     UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Beep");
373     NTSTATUS Status;
374 
375     UNREFERENCED_PARAMETER(RegistryPath);
376 
377     /* Create the device */
378     Status = IoCreateDevice(DriverObject,
379                             sizeof(DEVICE_EXTENSION),
380                             &DeviceName,
381                             FILE_DEVICE_BEEP,
382                             0,
383                             FALSE,
384                             &DeviceObject);
385     if (!NT_SUCCESS(Status)) return Status;
386 
387     /* Make it use buffered I/O */
388     DeviceObject->Flags |= DO_BUFFERED_IO;
389 
390     /* Setup the Driver Object */
391     DriverObject->MajorFunction[IRP_MJ_CREATE] = BeepCreate;
392     DriverObject->MajorFunction[IRP_MJ_CLOSE] = BeepClose;
393     DriverObject->MajorFunction[IRP_MJ_CLEANUP] = BeepCleanup;
394     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = BeepDeviceControl;
395     DriverObject->DriverUnload = BeepUnload;
396     DriverObject->DriverStartIo = BeepStartIo;
397 
398     /* Set up device extension */
399     DeviceExtension = DeviceObject->DeviceExtension;
400     DeviceExtension->ReferenceCount = 0;
401     DeviceExtension->TimerActive = FALSE;
402     IoInitializeDpcRequest(DeviceObject, (PIO_DPC_ROUTINE)BeepDPC);
403     KeInitializeTimer(&DeviceExtension->Timer);
404     ExInitializeFastMutex(&DeviceExtension->Mutex);
405 
406     /* Page the entire driver */
407     MmPageEntireDriver(DriverEntry);
408     return STATUS_SUCCESS;
409 }
410 
411 /* EOF */
412