Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1006 lines
20 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
beep.c
Abstract:
Beep driver.
Author:
Lee A. Smith (lees) 02-Aug-1991.
Environment:
Kernel mode only.
Notes:
Revision History:
--*/
#include "stdarg.h"
#include "stdio.h"
#include "ntddk.h"
#include <ntddbeep.h>
#include "beep.h"
// Private declaration of 'HalMakeBeep' because it's not in the ddk.
BOOLEAN
HalMakeBeep(
IN ULONG Frequency
);
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
VOID
BeepCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
BeepCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
#if DBG
VOID
BeepDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
);
//
// Declare the global debug flag for this driver.
//
ULONG BeepDebug = 0;
#define BeepPrint(x) BeepDebugPrint x
#else
#define BeepPrint(x)
#endif
NTSTATUS
BeepDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
BeepOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
BeepClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
BeepStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
BeepTimeOut(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
VOID
BeepUnload(
IN PDRIVER_OBJECT DriverObject
);
//
// Coud use the alloc_text pragma to specify the driver initialization
// routines (they can be paged out). Unfortunately, in this case that
// actually ends up using more space (because of the section boundaries?)/
// so don't bother. [Verify using coff -dump].
//
#if 0
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DriverEntry)
#endif
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine initializes the beep driver.
Arguments:
DriverObject - Pointer to driver object created by system.
RegistryPath - Pointer to the Unicode name of the registry path
for this driver.
Return Value:
The function value is the final status from the initialization operation.
--*/
{
UNICODE_STRING unicodeString;
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
NTSTATUS status;
BeepPrint((2,"\n\nBEEP-BeepInitialize: enter\n"));
//
// Create non-exclusive device object for beep device.
//
RtlInitUnicodeString(&unicodeString, DD_BEEP_DEVICE_NAME_U);
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
&unicodeString,
FILE_DEVICE_BEEP,
0,
FALSE,
&deviceObject
);
if (status != STATUS_SUCCESS) {
BeepPrint((
1,
"BEEP-BeepInitialize: Could not create device object\n"
));
return(status);
}
deviceObject->Flags |= DO_BUFFERED_IO;
deviceExtension =
(PDEVICE_EXTENSION)deviceObject->DeviceExtension;
//
// Initialize the timer DPC queue (we use the device object DPC) and
// the timer itself.
//
IoInitializeDpcRequest(
deviceObject,
(PKDEFERRED_ROUTINE) BeepTimeOut
);
KeInitializeTimer(&deviceExtension->Timer);
deviceExtension->TimerSet = FALSE;
//
// Initialize the fast mutex and set the reference count to zero.
//
ExInitializeFastMutex(&deviceExtension->Mutex);
deviceExtension->ReferenceCount = 0;
//
// Set the driver to be completely paged out.
//
MmPageEntireDriver(DriverEntry);
//
// Set up the device driver entry points.
//
DriverObject->DriverStartIo = BeepStartIo;
DriverObject->DriverUnload = BeepUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = BeepOpen;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = BeepClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
BeepDeviceControl;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = BeepCleanup;
#ifdef _PNP_POWER_
//
// The HAL is in charge of the beeping, it will take care
// of the power management on the device
//
deviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE;
#endif
BeepPrint((2,"BEEP-BeepInitialize: exit\n"));
return(STATUS_SUCCESS);
}
VOID
BeepCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called from the I/O system when a request is cancelled.
N.B. The cancel spinlock is already held upon entry to this routine.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet to be cancelled.
Return Value:
None.
--*/
{
BeepPrint((2,"BEEP-BeepCancel: enter\n"));
if (Irp == DeviceObject->CurrentIrp) {
//
// The current request is being cancelled. Set the CurrentIrp to
// null and release the cancel spinlock before starting the next packet.
//
DeviceObject->CurrentIrp = NULL;
IoReleaseCancelSpinLock(Irp->CancelIrql);
IoStartNextPacket(DeviceObject, TRUE);
} else {
//
// Cancel a request in the device queue. Remove it from queue and
// release the cancel spinlock.
//
if (TRUE != KeRemoveEntryDeviceQueue(
&DeviceObject->DeviceQueue,
&Irp->Tail.Overlay.DeviceQueueEntry
)) {
BeepPrint((
1,
"BEEP-BeepCancel: Irp 0x%x not in device queue?!?\n",
Irp
));
}
IoReleaseCancelSpinLock(Irp->CancelIrql);
}
//
// Complete the request with STATUS_CANCELLED.
//
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
BeepPrint((2,"BEEP-BeepCancel: exit\n"));
return;
}
NTSTATUS
BeepCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for cleanup requests.
All queued beep requests are completed with STATUS_CANCELLED,
and the speaker is stopped.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
KIRQL currentIrql;
KIRQL cancelIrql;
PKDEVICE_QUEUE_ENTRY packet;
PIRP currentIrp;
BeepPrint((2,"BEEP-BeepCleanup: enter\n"));
//
// Raise IRQL to DISPATCH_LEVEL.
//
KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
//
// Acquire the cancel spinlock.
//
IoAcquireCancelSpinLock(&cancelIrql);
//
// Complete all queued requests with STATUS_CANCELLED.
// Start with the real CurrentIrp, and run down the list of requests in the
// device queue. Be sure to set the real CurrentIrp to NULL, so that
// the interrupt service callback routine won't attempt to complete it.
//
currentIrp = DeviceObject->CurrentIrp;
DeviceObject->CurrentIrp = NULL;
while (currentIrp != NULL) {
//
// Remove the CurrentIrp from the cancellable state.
//
//
IoSetCancelRoutine(currentIrp, NULL);
//
// Set Status to CANCELLED, release the cancel spinlock,
// and complete the request. Note that the IRQL is reset to
// DISPATCH_LEVEL when we release the cancel spinlock.
//
currentIrp->IoStatus.Status = STATUS_CANCELLED;
currentIrp->IoStatus.Information = 0;
IoReleaseCancelSpinLock(cancelIrql);
IoCompleteRequest(currentIrp, IO_NO_INCREMENT);
//
// Reacquire the cancel spinlock.
//
IoAcquireCancelSpinLock(&cancelIrql);
//
// Dequeue the next packet (IRP) from the device work queue.
//
packet = KeRemoveDeviceQueue(&DeviceObject->DeviceQueue);
if (packet != NULL) {
currentIrp =
CONTAINING_RECORD(packet, IRP, Tail.Overlay.DeviceQueueEntry);
} else {
currentIrp = (PIRP) NULL;
}
} // end while
//
// Release the cancel spinlock and lower IRQL.
//
IoReleaseCancelSpinLock(cancelIrql);
KeLowerIrql(currentIrql);
//
// Complete the cleanup request with STATUS_SUCCESS.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
//
// Call HalMakeBeep() to stop any outstanding beep.
//
(VOID) HalMakeBeep(0);
BeepPrint((2,"BEEP-BeepCleanup: exit\n"));
return(STATUS_SUCCESS);
}
#if DBG
VOID
BeepDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
/*++
Routine Description:
Debug print routine.
Arguments:
Debug print level between 0 and 3, with 3 being the most verbose.
Return Value:
None.
--*/
{
va_list ap;
va_start(ap, DebugMessage);
if (DebugPrintLevel <= BeepDebug) {
char buffer[128];
(VOID) vsprintf(buffer, DebugMessage, ap);
DbgPrint(buffer);
}
va_end(ap);
}
#endif
NTSTATUS
BeepDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for device control requests.
The IOCTL_BEEP_SET subfunction is processed and completed
in this routine.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
PBEEP_SET_PARAMETERS beepParameters;
BeepPrint((2,"BEEP-BeepDeviceControl: enter\n"));
//
// Get a pointer to the current parameters for this request. The
// information is contained in the current stack location.
//
irpSp = IoGetCurrentIrpStackLocation(Irp);
//
// Case on the device control subfunction that is being performed by the
// requestor.
//
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
//
// Make a beep. Validate the beep function parameters and return
// status pending.
//
case IOCTL_BEEP_SET:
beepParameters = (PBEEP_SET_PARAMETERS)
(Irp->AssociatedIrp.SystemBuffer);
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(BEEP_SET_PARAMETERS)) {
status = STATUS_INVALID_PARAMETER;
} else if ((beepParameters->Frequency != 0)
&& (beepParameters->Duration == 0)) {
status = STATUS_SUCCESS;
} else {
status = STATUS_PENDING;
}
break;
//
// Unrecognized device control request.
//
default:
status = STATUS_INVALID_PARAMETER;
break;
}
//
// If status is pending, mark the packet pending and start the packet
// in a cancellable state. Otherwise, complete the request.
//
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
if (status == STATUS_PENDING) {
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, (PULONG)NULL, BeepCancel);
} else {
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
BeepPrint((2,"BEEP-BeepDeviceControl: exit\n"));
return(status);
}
NTSTATUS
BeepOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for create/open requests.
Open requests are completed here.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PFAST_MUTEX mutex;
BeepPrint((2,"BEEP-BeepOpenClose: enter\n"));
//
// Increment the reference count. If this is the first reference,
// reset the driver paging.
//
deviceExtension = DeviceObject->DeviceExtension;
mutex = &deviceExtension->Mutex;
ExAcquireFastMutex(mutex);
if (++deviceExtension->ReferenceCount == 1) {
MmResetDriverPaging(BeepOpen);
}
ExReleaseFastMutex(mutex);
//
// Complete the request and return status.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
BeepPrint((2,"BEEP-BeepOpenClose: exit\n"));
return(STATUS_SUCCESS);
}
NTSTATUS
BeepClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for close requests.
Close requests are completed here.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PFAST_MUTEX mutex;
BeepPrint((2,"BEEP-BeepOpenClose: enter\n"));
//
// Decrement the reference count. If this is the last reference,
// page the driver out
//
deviceExtension = DeviceObject->DeviceExtension;
mutex = &deviceExtension->Mutex;
ExAcquireFastMutex(mutex);
if (--deviceExtension->ReferenceCount == 0) {
//
// If there is a timer queued, attempt to cancel it before paging out
// the driver. If we cannot cancel it, it may already be queued for
// execution on another processor. This is highly unlikely, so just
// don't page out the entire driver if a timer has been set but cannot
// be canceled.
//
if ((!deviceExtension->TimerSet) ||
(deviceExtension->TimerSet &&
KeCancelTimer(&deviceExtension->Timer))) {
MmPageEntireDriver(BeepClose);
}
}
ExReleaseFastMutex(mutex);
//
// Complete the request and return status.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
BeepPrint((2,"BEEP-BeepOpenClose: exit\n"));
return(STATUS_SUCCESS);
}
VOID
BeepStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the StartIo routine. It is invoked to start a beep
request.
N.B. Requests enter BeepStartIo in a cancellable state.
Arguments:
DeviceObject - Pointer to the device object.
Irp - Pointer to the request packet.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpSp;
KIRQL cancelIrql;
PBEEP_SET_PARAMETERS beepParameters;
LARGE_INTEGER time;
NTSTATUS status;
BeepPrint((2,"BEEP-BeepStartIo: enter\n"));
deviceExtension = DeviceObject->DeviceExtension;
//
// Acquire the cancel spinlock and verify that the CurrentIrp has not been
// cancelled.
//
IoAcquireCancelSpinLock(&cancelIrql);
if (Irp == NULL) {
IoReleaseCancelSpinLock(cancelIrql);
return;
}
//
// Remove the request from the cancellable state and release the cancel
// spinlock.
//
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(cancelIrql);
//
// Get a pointer to the current parameters for this request. The
// information is contained in the current stack location.
//
irpSp = IoGetCurrentIrpStackLocation(Irp);
//
// Case on the device control subfunction that is being performed by the
// requestor.
//
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
//
// Make a beep. Call HalMakeBeep() to do the real work, and start
// a timer that will fire when the beep duration is reached. Finally,
// complete the request.
//
case IOCTL_BEEP_SET:
//
// Get the beep parameters.
//
beepParameters = (PBEEP_SET_PARAMETERS)
(Irp->AssociatedIrp.SystemBuffer);
BeepPrint((
3,
"BEEP-BeepStartIo: frequency %x, duration %d\n",
beepParameters->Frequency,
beepParameters->Duration
));
//
// Cancel the current timer (if any).
//
if (deviceExtension->TimerSet) {
if (KeCancelTimer(&deviceExtension->Timer)) {
//
// Timer successfully cancelled
//
deviceExtension->TimerSet = FALSE;
} else {
//
// The timer has already expired and
// been queued, it will reset the
// TimerSet flag when it runs.
//
}
}
//
// Call the HAL to actually start the beep (synchronizes
// access to the i8254 speaker.
//
if (HalMakeBeep(beepParameters->Frequency)) {
status = STATUS_SUCCESS;
//
// Set the timer so the beep will time out after
// the user-specified number of milliseconds (converted
// to 100ns resolution).
//
time.QuadPart = (LONGLONG)beepParameters->Duration * -10000;
BeepPrint((
3,
"BEEP-BeepStartIo: negative duration in 100ns %x.%x\n",
time.HighPart,
time.LowPart
));
deviceExtension->TimerSet = TRUE;
(VOID) KeSetTimer(
&deviceExtension->Timer,
time,
&DeviceObject->Dpc
);
} else {
status = STATUS_INVALID_PARAMETER;
}
break;
//
// Unrecognized device control request.
//
default:
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Start the next packet, and complete this request.
//
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoStartNextPacket(DeviceObject, TRUE);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
BeepPrint((2,"BEEP-BeepStartIo: exit\n"));
return;
}
VOID
BeepTimeOut(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This is the driver's timeout routine. It is called when the beep
duration expires. The timer is started in StartIo.
N.B. The request is removed from the cancellable state prior to
the timer start, so there is no need to check the cancellation status
here.
Arguments:
DeviceObject - Pointer to the device object.
Context - Unused.
SystemArgument1 - Unused.
SystemArgument2 - Unused.
Return Value:
--*/
{
PDEVICE_EXTENSION deviceExtension;
BeepPrint((2, "BEEP-BeepTimeOut: enter\n"));
deviceExtension = DeviceObject->DeviceExtension;
//
// Stop the beep.
//
(VOID) HalMakeBeep(0);
//
// Clear the TimerSet flag
//
deviceExtension->TimerSet = FALSE;
//
// We don't have a request at this point -- it was completed in StartIo
// when the beep was started. So, there's no more work to do here.
//
BeepPrint((2, "BEEP-BeepTimeOut: exit\n"));
}
VOID
BeepUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine is the beep driver unload routine.
Arguments:
DriverObject - Pointer to class driver object.
Return Value:
None.
--*/
{
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
BeepPrint((1,"BEEP-BeepUnload: enter\n"));
deviceObject = DriverObject->DeviceObject;
deviceExtension = deviceObject->DeviceExtension;
//
// Cancel the timer.
//
if (deviceExtension->TimerSet) {
if (KeCancelTimer(&deviceExtension->Timer)) {
//
// Timer successfully cancelled
//
deviceExtension->TimerSet = FALSE;
} else {
//
// The timer has already expired and
// been queued, it will reset the
// TimerSet flag when it runs.
//
}
}
//
// Delete the device object.
//
IoDeleteDevice(deviceObject);
BeepPrint((1,"BEEP-BeepUnload: exit\n"));
return;
}