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