/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: drivers/base/beep/beep.c * PURPOSE: Beep Device Driver * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Eric Kohl */ /* INCLUDES ******************************************************************/ #include #include #ifndef NDEBUG #define NDEBUG #endif #include /* TYPES *********************************************************************/ typedef struct _BEEP_DEVICE_EXTENSION { LONG ReferenceCount; FAST_MUTEX Mutex; KTIMER Timer; LONG TimerActive; PVOID SectionHandle; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; /* FUNCTIONS *****************************************************************/ VOID NTAPI BeepDPC(IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); /* Stop the beep */ HalMakeBeep(0); /* Disable the timer */ InterlockedDecrement(&DeviceExtension->TimerActive); } DRIVER_DISPATCH BeepCreate; NTSTATUS NTAPI BeepCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; /* Acquire the mutex and increase reference count */ ExAcquireFastMutex(&DeviceExtension->Mutex); if (++DeviceExtension->ReferenceCount == 1) { /* First reference, lock the data section */ DeviceExtension->SectionHandle = MmLockPagableDataSection(BeepCreate); } /* Release it */ ExReleaseFastMutex(&DeviceExtension->Mutex); /* Complete the request */ Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } DRIVER_DISPATCH BeepClose; NTSTATUS NTAPI BeepClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; /* Acquire the mutex and decrease reference count */ ExAcquireFastMutex(&DeviceExtension->Mutex); if (!(--DeviceExtension->ReferenceCount)) { /* Check for active timer */ if (DeviceExtension->TimerActive) { /* Cancel it */ if (KeCancelTimer(&DeviceExtension->Timer)) { /* Mark it as cancelled */ InterlockedDecrement(&DeviceExtension->TimerActive); } } /* Page the driver */ MmUnlockPagableImageSection(DeviceExtension->SectionHandle); } /* Release the lock */ ExReleaseFastMutex(&DeviceExtension->Mutex); /* Complete the request */ Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } DRIVER_CANCEL BeepCancel; VOID NTAPI BeepCancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { /* Check if this is the current request */ if (Irp == DeviceObject->CurrentIrp) { /* Clear it */ DeviceObject->CurrentIrp = NULL; /* Release the cancel lock and start the next packet */ IoReleaseCancelSpinLock(Irp->CancelIrql); IoStartNextPacket(DeviceObject, TRUE); } else { /* Otherwise, remove the packet from the queue and release the lock */ KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue, &Irp->Tail.Overlay.DeviceQueueEntry); IoReleaseCancelSpinLock(Irp->CancelIrql); } /* Complete the request */ Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); } DRIVER_DISPATCH BeepCleanup; NTSTATUS NTAPI BeepCleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { KIRQL OldIrql, CancelIrql; PKDEVICE_QUEUE_ENTRY Packet; PIRP CurrentIrp; /* Raise IRQL and acquire the cancel lock */ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); IoAcquireCancelSpinLock(&CancelIrql); /* Get the current IRP */ CurrentIrp = DeviceObject->CurrentIrp; DeviceObject->CurrentIrp = NULL; while (CurrentIrp) { /* Clear its cancel routine */ (VOID)IoSetCancelRoutine(CurrentIrp, NULL); /* Cancel the IRP */ CurrentIrp->IoStatus.Status = STATUS_CANCELLED; CurrentIrp->IoStatus.Information = 0; /* Release the cancel lock and complete it */ IoReleaseCancelSpinLock(CancelIrql); IoCompleteRequest(CurrentIrp, IO_NO_INCREMENT); /* Reacquire the lock and get the next queue packet */ IoAcquireCancelSpinLock(&CancelIrql); Packet = KeRemoveDeviceQueue(&DeviceObject->DeviceQueue); if (Packet) { /* Get the IRP */ CurrentIrp = CONTAINING_RECORD(Packet, IRP, Tail.Overlay.DeviceQueueEntry); } else { /* No more IRPs */ CurrentIrp = NULL; } } /* Release lock and go back to low IRQL */ IoReleaseCancelSpinLock(CancelIrql); KeLowerIrql(OldIrql); /* Complete the IRP */ Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); /* Stop and beep and return */ HalMakeBeep(0); return STATUS_SUCCESS; } DRIVER_DISPATCH BeepDeviceControl; NTSTATUS NTAPI BeepDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION Stack; PBEEP_SET_PARAMETERS BeepParam; NTSTATUS Status; /* Get the stack location and parameters */ Stack = IoGetCurrentIrpStackLocation(Irp); BeepParam = (PBEEP_SET_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; /* We only support one IOCTL */ if (Stack->Parameters.DeviceIoControl.IoControlCode != IOCTL_BEEP_SET) { /* Unsupported command */ Status = STATUS_NOT_IMPLEMENTED; } else { /* Validate the input buffer length */ if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(BEEP_SET_PARAMETERS)) { /* Invalid buffer */ Status = STATUS_INVALID_PARAMETER; } else if ((BeepParam->Frequency != 0) && !(BeepParam->Duration)) { /* No duration, return immediately */ Status = STATUS_SUCCESS; } else { /* We'll queue this request */ Status = STATUS_PENDING; } } /* Set packet information */ Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; /* Check if we're completing or queuing a packet */ if (Status == STATUS_PENDING) { /* Start the queue */ IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, BeepCancel); } else { /* Complete the request */ IoCompleteRequest(Irp, IO_NO_INCREMENT); } /* Return */ return Status; } DRIVER_UNLOAD BeepUnload; VOID NTAPI BeepUnload(IN PDRIVER_OBJECT DriverObject) { PDEVICE_EXTENSION DeviceExtension; PDEVICE_OBJECT DeviceObject; /* Get DO and DE */ DeviceObject = DriverObject->DeviceObject; DeviceExtension = DeviceObject->DeviceExtension; /* Check if the timer is active */ if (DeviceExtension->TimerActive) { /* Cancel it */ if (KeCancelTimer(&DeviceExtension->Timer)) { /* All done */ InterlockedDecrement(&DeviceExtension->TimerActive); } } /* Delete the object */ IoDeleteDevice(DeviceObject); } DRIVER_STARTIO BeepStartIo; VOID NTAPI BeepStartIo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; KIRQL CancelIrql; PIO_STACK_LOCATION IoStack; PBEEP_SET_PARAMETERS BeepParam; LARGE_INTEGER DueTime; NTSTATUS Status; /* Acquire the cancel lock and make sure the IRP is valid */ IoAcquireCancelSpinLock(&CancelIrql); if (!Irp) { /* It's not, release the lock and quit */ IoReleaseCancelSpinLock(CancelIrql); return; } /* Remove the cancel routine and release the lock */ (VOID)IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(CancelIrql); /* Get the I/O Stack and make sure the request is valid */ BeepParam = (PBEEP_SET_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; IoStack = IoGetCurrentIrpStackLocation(Irp); if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_BEEP_SET) { /* Check if we have an active timer */ if (DeviceExtension->TimerActive) { /* Cancel it */ if (KeCancelTimer(&DeviceExtension->Timer)) { /* Set the state */ InterlockedDecrement(&DeviceExtension->TimerActive); } } /* Make the beep */ if (HalMakeBeep(BeepParam->Frequency)) { /* Beep successful, queue a DPC to stop it */ Status = STATUS_SUCCESS; DueTime.QuadPart = BeepParam->Duration * -10000LL; InterlockedIncrement(&DeviceExtension->TimerActive); KeSetTimer(&DeviceExtension->Timer, DueTime, &DeviceObject->Dpc); } else { /* Beep has failed */ Status = STATUS_INVALID_PARAMETER; } } else { /* Invalid request */ Status = STATUS_INVALID_PARAMETER; } /* Complete the request and start the next packet */ Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoStartNextPacket(DeviceObject, TRUE); IoCompleteRequest(Irp, IO_NO_INCREMENT); } NTSTATUS NTAPI DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { PDEVICE_EXTENSION DeviceExtension; PDEVICE_OBJECT DeviceObject; UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Beep"); NTSTATUS Status; UNREFERENCED_PARAMETER(RegistryPath); /* Create the device */ Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_BEEP, 0, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) return Status; /* Make it use buffered I/O */ DeviceObject->Flags |= DO_BUFFERED_IO; /* Setup the Driver Object */ DriverObject->MajorFunction[IRP_MJ_CREATE] = BeepCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = BeepClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = BeepCleanup; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = BeepDeviceControl; DriverObject->DriverUnload = BeepUnload; DriverObject->DriverStartIo = BeepStartIo; /* Set up device extension */ DeviceExtension = DeviceObject->DeviceExtension; DeviceExtension->ReferenceCount = 0; DeviceExtension->TimerActive = FALSE; IoInitializeDpcRequest(DeviceObject, (PIO_DPC_ROUTINE)BeepDPC); KeInitializeTimer(&DeviceExtension->Timer); ExInitializeFastMutex(&DeviceExtension->Mutex); /* Page the entire driver */ MmPageEntireDriver(DriverEntry); return STATUS_SUCCESS; } /* EOF */