* Sync up to trunk HEAD (r62286).
[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 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
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
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
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 relelase 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
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
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 imemdiately */
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
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
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 = Int32x32To64(BeepParam->Duration, -10000);
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
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 */