From 9e88919457b2cb6734b917293734801c93a3b730 Mon Sep 17 00:00:00 2001 From: Andrew Greenwood Date: Tue, 20 Jan 2004 19:58:48 +0000 Subject: [PATCH] 2nd time lucky - MPU401 driver. Sorry about the previous breakage of blue :( svn path=/trunk/; revision=7789 --- reactos/drivers/dd/mpu401/.cvsignore | 8 + reactos/drivers/dd/mpu401/makefile | 22 ++ reactos/drivers/dd/mpu401/mpu401.c | 406 +++++++++++++++++++++++++++ reactos/drivers/dd/mpu401/mpu401.h | 145 ++++++++++ reactos/drivers/dd/mpu401/mpu401.rc | 38 +++ reactos/drivers/dd/mpu401/portio.c | 97 +++++++ reactos/drivers/dd/mpu401/readme.txt | 28 ++ reactos/drivers/dd/mpu401/sbdebug.h | 24 ++ reactos/drivers/dd/mpu401/settings.c | 306 ++++++++++++++++++++ reactos/drivers/dd/mpu401/test.c | 70 +++++ 10 files changed, 1144 insertions(+) create mode 100644 reactos/drivers/dd/mpu401/.cvsignore create mode 100644 reactos/drivers/dd/mpu401/makefile create mode 100644 reactos/drivers/dd/mpu401/mpu401.c create mode 100644 reactos/drivers/dd/mpu401/mpu401.h create mode 100644 reactos/drivers/dd/mpu401/mpu401.rc create mode 100644 reactos/drivers/dd/mpu401/portio.c create mode 100644 reactos/drivers/dd/mpu401/readme.txt create mode 100644 reactos/drivers/dd/mpu401/sbdebug.h create mode 100644 reactos/drivers/dd/mpu401/settings.c create mode 100644 reactos/drivers/dd/mpu401/test.c diff --git a/reactos/drivers/dd/mpu401/.cvsignore b/reactos/drivers/dd/mpu401/.cvsignore new file mode 100644 index 00000000000..5f6c85b86cb --- /dev/null +++ b/reactos/drivers/dd/mpu401/.cvsignore @@ -0,0 +1,8 @@ +*.tmp +*.exp +*.coff +*.d +*.o +*.sym +*.sys +*.map diff --git a/reactos/drivers/dd/mpu401/makefile b/reactos/drivers/dd/mpu401/makefile new file mode 100644 index 00000000000..9c64f03f770 --- /dev/null +++ b/reactos/drivers/dd/mpu401/makefile @@ -0,0 +1,22 @@ +# $Id: makefile,v 1.1 2004/01/20 19:58:48 silverblade Exp $ + +PATH_TO_TOP = ../../.. + +TARGET_BOOTSTRAP = yes + +TARGET_TYPE = driver + +TARGET_NAME = mpu401 + +TARGET_OBJECTS = mpu401.o \ + portio.o \ + settings.o + +TARGET_CFLAGS = -Wall -Werror + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +DEP_OBJECTS := $(TARGET_OBJECTS) +include $(PATH_TO_TOP)/tools/depend.mk diff --git a/reactos/drivers/dd/mpu401/mpu401.c b/reactos/drivers/dd/mpu401/mpu401.c new file mode 100644 index 00000000000..ed274d4cd6f --- /dev/null +++ b/reactos/drivers/dd/mpu401/mpu401.c @@ -0,0 +1,406 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: services/dd/mpu401/mpu401.c + * PURPOSE: MPU-401 MIDI device driver + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Sept 26, 2003: Copied from beep.c as template + * Sept 27, 2003: Implemented MPU-401 init & playback + */ + +/* INCLUDES ****************************************************************/ + +#include +//#include + +//#define NDEBUG +#include + +#include "mpu401.h" + + +/* INTERNAL VARIABLES ******************************************************/ + +UINT DeviceCount = 0; + + +/* FUNCTIONS ***************************************************************/ + +NTSTATUS InitDevice( + IN PWSTR RegistryPath, + IN PVOID Context) +{ +// PDEVICE_INSTANCE Instance = Context; + PDEVICE_OBJECT DeviceObject; // = Context; + PDEVICE_EXTENSION Parameters; // = DeviceObject->DeviceExtension; + UNICODE_STRING DeviceName = UNICODE_STRING_INITIALIZER(L"\\Device\\MPU401_Out_0"); + UNICODE_STRING SymlinkName = UNICODE_STRING_INITIALIZER(L"\\??\\MPU401_Out_0"); +// CONFIG Config; + RTL_QUERY_REGISTRY_TABLE Table[2]; + NTSTATUS s; + + // This is TEMPORARY, to ensure that we don't process more than 1 device. + // I'll remove this limitation in the future. + if (DeviceCount > 0) + { + DPRINT("Sorry - only 1 device supported by MPU401 driver at present :(\n"); + return STATUS_NOT_IMPLEMENTED; + } + + DPRINT("Creating IO device\n"); + + s = IoCreateDevice(Context, // driverobject + sizeof(DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_SOUND, // Correct? + 0, + FALSE, + &DeviceObject); + + if (!NT_SUCCESS(s)) + return s; + + DPRINT("Device Extension at 0x%x\n", DeviceObject->DeviceExtension); + Parameters = DeviceObject->DeviceExtension; + + DPRINT("Creating DOS link\n"); + + /* Create the dos device link */ + IoCreateSymbolicLink(&SymlinkName, + &DeviceName); + + DPRINT("Initializing device\n"); + +// DPRINT("Allocating memory for parameters structure\n"); + // Bodged: +// Parameters = (PDEVICE_EXTENSION)ExAllocatePool(NonPagedPool, sizeof(DEVICE_EXTENSION)); +// DeviceObject->DeviceExtension = Parameters; +// Parameters = Instance->DriverObject->DriverExtension; + + DPRINT("DeviceObject at 0x%x, DeviceExtension at 0x%x\n", DeviceObject, Parameters); + + if (! Parameters) + { + DPRINT("NULL POINTER!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + +// Instance->DriverObject->DriverExtension = Parameters; + + DPRINT("Setting reg path\n"); + Parameters->RegistryPath = RegistryPath; +// Parameters->DriverObject = Instance->DriverObject; + + DPRINT("Zeroing table memory and setting query routine\n"); + RtlZeroMemory(Table, sizeof(Table)); + Table[0].QueryRoutine = LoadSettings; + + DPRINT("Setting port and IRQ defaults\n"); + Parameters->Port = DEFAULT_PORT; + Parameters->IRQ = DEFAULT_IRQ; + +// Only to be enabled once we can get support for multiple cards working :) +/* + DPRINT("Loading settings from: %S\n", RegistryPath); + + s = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RegistryPath, Table, + &Parameters, NULL); +*/ + + if (! NT_SUCCESS(s)) + return s; + + DPRINT("Port 0x%x IRQ %d\n", Parameters->Port, Parameters->IRQ); + +// Instance->P + + // Enter UART mode (should be done in init phase + if (! InitUARTMode(Parameters->Port)) + { + DPRINT("UART mode initialization FAILED!\n"); + // Set state indication somehow + // Failure - what error code do we give?! + // return STATUS_???? + } + + DeviceCount ++; + + return STATUS_SUCCESS; +} + + +static NTSTATUS STDCALL +MPU401Create(PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: Handles user mode requests + * ARGUMENTS: + * DeviceObject = Device for request + * Irp = I/O request packet describing request + * RETURNS: Success or failure + */ +{ + DPRINT("MPU401Create() called!\n"); + + // Initialize the MPU-401 + // ... do stuff ... + + + // Play a note to say we're alive: +// WaitToSend(MPU401_PORT); +// MPU401_WRITE_DATA(MPU401_PORT, 0x90); +// WaitToSend(MPU401_PORT); +// MPU401_WRITE_DATA(MPU401_PORT, 0x50); +// WaitToSend(MPU401_PORT); +// MPU401_WRITE_DATA(MPU401_PORT, 0x7f); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, + IO_NO_INCREMENT); + + return(STATUS_SUCCESS); +} + + +static NTSTATUS STDCALL +MPU401Close(PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: Handles user mode requests + * ARGUMENTS: + * DeviceObject = Device for request + * Irp = I/O request packet describing request + * RETURNS: Success or failure + */ +{ + PDEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + + DPRINT("MPU401Close() called!\n"); + + DeviceExtension = DeviceObject->DeviceExtension; + + Status = STATUS_SUCCESS; + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, + IO_NO_INCREMENT); + + return(Status); +} + + +static NTSTATUS STDCALL +MPU401Cleanup(PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: Handles user mode requests + * ARGUMENTS: + * DeviceObject = Device for request + * Irp = I/O request packet describing request + * RETURNS: Success or failure + */ +{ + UINT Channel; + DPRINT("MPU401Cleanup() called!\n"); + + // Reset the device (should we do this?) + for (Channel = 0; Channel <= 15; Channel ++) + { + // All notes off +// MPU401_WRITE_MESSAGE(MPU401_PORT, 0xb0 + Channel, 123, 0); + // All controllers off +// MPU401_WRITE_MESSAGE(MPU401_PORT, 0xb0 + Channel, 121, 0); + } + + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, + IO_NO_INCREMENT); + + return(STATUS_SUCCESS); +} + + +static NTSTATUS STDCALL +MPU401DeviceControl(PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: Handles user mode requests + * ARGUMENTS: + * DeviceObject = Device for request + * Irp = I/O request packet describing request + * RETURNS: Success or failure + */ +{ + PIO_STACK_LOCATION Stack; + PDEVICE_EXTENSION DeviceExtension; + UINT ByteCount; + PBYTE Data; + + DPRINT("MPU401DeviceControl() called!\n"); + + DeviceExtension = DeviceObject->DeviceExtension; + Stack = IoGetCurrentIrpStackLocation(Irp); + + switch(Stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_MIDI_PLAY : + { + DPRINT("Received IOCTL_MIDI_PLAY\n"); + Data = (PBYTE) Irp->AssociatedIrp.SystemBuffer; + + DPRINT("Sending %d bytes of MIDI data to 0x%d:\n", Stack->Parameters.DeviceIoControl.InputBufferLength, DeviceExtension->Port); + + for (ByteCount = 0; ByteCount < Stack->Parameters.DeviceIoControl.InputBufferLength; ByteCount ++) + { + DPRINT("0x%x ", Data[ByteCount]); + + MPU401_WRITE_BYTE(DeviceExtension->Port, Data[ByteCount]); +// if (WaitToSend(MPU401_PORT)) +// MPU401_WRITE_DATA(MPU401_PORT, Data[ByteCount]); + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return(STATUS_SUCCESS); + } + } + + return(STATUS_SUCCESS); + +/* + DeviceExtension = DeviceObject->DeviceExtension; + Stack = IoGetCurrentIrpStackLocation(Irp); + BeepParam = (PBEEP_SET_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; + + Irp->IoStatus.Information = 0; + + if (Stack->Parameters.DeviceIoControl.IoControlCode != IOCTL_BEEP_SET) + { + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, + IO_NO_INCREMENT); + return(STATUS_NOT_IMPLEMENTED); + } + + if ((Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(BEEP_SET_PARAMETERS)) + || (BeepParam->Frequency < BEEP_FREQUENCY_MINIMUM) + || (BeepParam->Frequency > BEEP_FREQUENCY_MAXIMUM)) + { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, + IO_NO_INCREMENT); + return(STATUS_INVALID_PARAMETER); + } + + DueTime.QuadPart = 0; +*/ + /* do the beep!! */ +/* DPRINT("Beep:\n Freq: %lu Hz\n Dur: %lu ms\n", + pbsp->Frequency, + pbsp->Duration); + + if (BeepParam->Duration >= 0) + { + DueTime.QuadPart = (LONGLONG)BeepParam->Duration * -10000; + + KeSetTimer(&DeviceExtension->Timer, + DueTime, + &DeviceExtension->Dpc); + + HalMakeBeep(BeepParam->Frequency); + DeviceExtension->BeepOn = TRUE; + KeWaitForSingleObject(&DeviceExtension->Event, + Executive, + KernelMode, + FALSE, + NULL); + } + else if (BeepParam->Duration == (DWORD)-1) + { + if (DeviceExtension->BeepOn == TRUE) + { + HalMakeBeep(0); + DeviceExtension->BeepOn = FALSE; + } + else + { + HalMakeBeep(BeepParam->Frequency); + DeviceExtension->BeepOn = TRUE; + } + } + + DPRINT("Did the beep!\n"); + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, + IO_NO_INCREMENT); + return(STATUS_SUCCESS); +*/ +} + + +static NTSTATUS STDCALL +MPU401Unload(PDRIVER_OBJECT DriverObject) +{ + DPRINT("MPU401Unload() called!\n"); + return(STATUS_SUCCESS); +} + + +NTSTATUS STDCALL +DriverEntry(PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath) +/* + * FUNCTION: Called by the system to initalize the driver + * ARGUMENTS: + * DriverObject = object describing this driver + * RegistryPath = path to our configuration entries + * RETURNS: Success or failure + */ +{ +// PDEVICE_EXTENSION DeviceExtension; +// PDEVICE_OBJECT DeviceObject; +// DEVICE_INSTANCE Instance; + // Doesn't support multiple instances (yet ...) + NTSTATUS Status; + + DPRINT("MPU401 Device Driver 0.0.1\n"); + + // Is this really necessary? Yes! (Talking to myself again...) +// Instance.DriverObject = DriverObject; + // previous instance = NULL... + +// DeviceExtension->RegistryPath = RegistryPath; + + DriverObject->Flags = 0; + DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)MPU401Create; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)MPU401Close; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)MPU401Cleanup; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)MPU401DeviceControl; + DriverObject->DriverUnload = (PDRIVER_UNLOAD)MPU401Unload; + + // Major hack to just get this damn thing working: + Status = InitDevice(RegistryPath, DriverObject); // ???? + +// DPRINT("Enumerating devices at %wZ\n", RegistryPath); + +// Status = EnumDeviceKeys(RegistryPath, PARMS_SUBKEY, InitDevice, (PVOID)&DeviceObject); // &Instance; + + // check error + + /* set up device extension */ +// DeviceExtension = DeviceObject->DeviceExtension; +// DeviceExtension->BeepOn = FALSE; + + return(STATUS_SUCCESS); +} + +/* EOF */ diff --git a/reactos/drivers/dd/mpu401/mpu401.h b/reactos/drivers/dd/mpu401/mpu401.h new file mode 100644 index 00000000000..4b2ce0b5f6e --- /dev/null +++ b/reactos/drivers/dd/mpu401/mpu401.h @@ -0,0 +1,145 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: services/dd/mpu401/mpu401.h + * PURPOSE: MPU-401 MIDI device driver header + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Sept 26, 2003: Created + */ + +#ifndef __INCLUDES_MPU401_H__ +#define __INCLUDES_MPU401_H__ + +#define DEFAULT_PORT 0x330 +#define DEFAULT_IRQ 9 + +#define DEVICE_SUBKEY L"Devices" +#define PARMS_SUBKEY L"Parameters" + +#define REGISTRY_PORT L"Port" + +// At the moment, we just support a single device with fixed parameters: +#define MPU401_PORT DEFAULT_PORT +#define MPU401_IRQ DEFAULT_IRQ + +#define MPU401_TIMEOUT 10000 + +#define IOCTL_SOUND_BASE FILE_DEVICE_SOUND +// wave base 0 +#define IOCTL_MIDI_BASE 0x0080 + +#define IOCTL_MIDI_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_SET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_GET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0003, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_PLAY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_RECORD CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0007, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_CACHE_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0008, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_CACHE_DRUM_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) + + +// The MPU-401 has 2 ports, usually 0x330 and 0x331, which are known as +// "data" and "status/command", respectively. These macros deal with +// reading from and writing to these ports: + +#define MPU401_WRITE_DATA(bp, x) WRITE_PORT_UCHAR((PUCHAR) bp, x) +#define MPU401_READ_DATA(bp) READ_PORT_UCHAR((PUCHAR) bp) +#define MPU401_WRITE_COMMAND(bp, x) WRITE_PORT_UCHAR((PUCHAR) bp+1, x) +#define MPU401_READ_STATUS(bp) READ_PORT_UCHAR((PUCHAR) bp+1) + + +// Flow control + +#define MPU401_READY_TO_SEND(bp) \ + MPU401_READ_STATUS(bp) & 0x80 + +#define MPU401_READY_TO_RECEIVE(bp) \ + MPU401_READ_STATUS(bp) & 0x40 + + +#define MPU401_WRITE_BYTE(bp, x) \ + if (WaitToSend(bp)) MPU401_WRITE_DATA(bp, x) + +#define MPU401_WRITE_MESSAGE(bp, status, da, db) \ + MPU401_WRITE(bp, status); \ + MPU401_WRITE(bp, da); \ + MPU401_WRITE(bp, db) + +//#define MPU401_READ(bp) +// if (WaitToRead(bp)) ... ??? + +/* + DEVICE_EXTENSION contains the settings for each individual device +*/ + +typedef struct _DEVICE_EXTENSION +{ + PWSTR RegistryPath; + PDRIVER_OBJECT DriverObject; + UINT Port; + UINT IRQ; +// KDPC Dpc; +// KTIMER Timer; +// KEVENT Event; +// BOOLEAN BeepOn; +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +/* + DEVICE_INSTANCE contains ??? +*/ + +typedef struct _DEVICE_INSTANCE +{ + // pPrevGDI + PDRIVER_OBJECT DriverObject; +} DEVICE_INSTANCE, *PDEVICE_INSTANCE; + +/* + CONFIG contains device parameters (port/IRQ) + THIS STRUCTURE IS REDUNDANT +*/ + +//typedef struct _CONFIG +//{ +// UINT Port; +// UINT IRQ; +//} CONFIG, *PCONFIG; + +/* + Some callback typedefs +*/ + +typedef NTSTATUS REGISTRY_CALLBACK_ROUTINE(PWSTR RegistryPath, PVOID Context); +typedef REGISTRY_CALLBACK_ROUTINE *PREGISTRY_CALLBACK_ROUTINE; + + +/* + Prototypes for functions in portio.c : +*/ + +BOOLEAN WaitToSend(UINT BasePort); +BOOLEAN WaitToReceive(UINT BasePort); +BOOLEAN InitUARTMode(UINT BasePort); + +/* + Prototypes for functions in settings.c : +*/ + +NTSTATUS EnumDeviceKeys( + IN PUNICODE_STRING RegistryPath, + IN PWSTR SubKey, + IN PREGISTRY_CALLBACK_ROUTINE Callback, + IN PVOID Context); + +NTSTATUS LoadSettings( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +#endif diff --git a/reactos/drivers/dd/mpu401/mpu401.rc b/reactos/drivers/dd/mpu401/mpu401.rc new file mode 100644 index 00000000000..e3cd6b443cf --- /dev/null +++ b/reactos/drivers/dd/mpu401/mpu401.rc @@ -0,0 +1,38 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "MPU-401 MIDI Driver\0" + VALUE "FileVersion", "0.0.1\0" + VALUE "InternalName", "mpu401\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalFilename", "mpu401.sys\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/drivers/dd/mpu401/portio.c b/reactos/drivers/dd/mpu401/portio.c new file mode 100644 index 00000000000..9e04b8a445f --- /dev/null +++ b/reactos/drivers/dd/mpu401/portio.c @@ -0,0 +1,97 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: services/dd/mpu401/portio.c (see also mpu401.h) + * PURPOSE: MPU-401 MIDI port I/O helper + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Sept 26, 2003: Created + */ + +#include +#include +#include "mpu401.h" + + +BOOLEAN WaitToSend(UINT BasePort) +{ + int TimeOut; + + DbgPrint("WaitToSend "); + + // Check if it's OK to send + for (TimeOut = MPU401_TIMEOUT; + ! MPU401_READY_TO_SEND(BasePort) && TimeOut > 0; + TimeOut --); + + // If a time-out occurs, we report failure + if (! TimeOut) + { + DbgPrint("FAILED\n"); + return FALSE; + } + + DbgPrint("SUCCEEDED\n"); + + return TRUE; +} + + +BOOLEAN WaitToReceive(UINT BasePort) +{ + int TimeOut; + + DbgPrint("WaitToSend "); + + // Check if it's OK to receive + for (TimeOut = MPU401_TIMEOUT; + ! MPU401_READY_TO_RECEIVE(BasePort) && TimeOut > 0; + TimeOut --); + + // If a time-out occurs, we report failure + if (! TimeOut) + { + DbgPrint("FAILED\n"); + return FALSE; + } + + DbgPrint("SUCCEEDED\n"); + + return TRUE; +} + + +BOOLEAN InitUARTMode(UINT BasePort) +{ + UINT TimeOut; + UCHAR Status = 0; + + DbgPrint("InitUARTMode() called\n"); + + // Check if it's OK to send + if (! WaitToSend(BasePort)) + return FALSE; + + DbgPrint("Resetting MPU401\n"); + + // Send an MPU reset: + MPU401_WRITE_COMMAND(BasePort, 0xff); + + // Check if it's OK to receive (some cards will ignore the above reset + // command and so will not issue an ACK, so time out is NOT an error) + DbgPrint("Waiting for an ACK\n"); + if (WaitToReceive(BasePort)) + { + // Check to make sure the reset was acknowledged: + for (TimeOut = MPU401_TIMEOUT; + Status = (MPU401_READ_DATA(BasePort) & 0xfe) && TimeOut > 0; + TimeOut --); + } + + DbgPrint("Entering UART mode\n"); + // Now we kick the MPU401 into UART ("dumb") mode + MPU401_WRITE_COMMAND(BasePort, 0x3f); + + return TRUE; +} diff --git a/reactos/drivers/dd/mpu401/readme.txt b/reactos/drivers/dd/mpu401/readme.txt new file mode 100644 index 00000000000..5a59256f737 --- /dev/null +++ b/reactos/drivers/dd/mpu401/readme.txt @@ -0,0 +1,28 @@ +---------------------------------- +REACTOS MPU-401 MIDI DEVICE DRIVER +by Andrew Greenwood +---------------------------------- + +This driver initializes the MPU-401 MIDI/joystick port found on +most sound cards, and allows the sending of simple messages. + +It's far from complete, and at present will only support 1 device. + +In Bochs, the MIDI output will be played using whatever device is +set up in Windows as your MIDI output. + +For real hardware, the output will be played to whatever device is +attached to your MIDI/joystick port, or, in some cases, the wave-table +or other synth on-board your card (note: this is NOT an FM synth +driver!) + + +Thanks to Vizzini and all the other great ReactOS developers for +helping me code this driver and also for giving me encouragement. + +I'd also like to thank Jeff Glatt, whose MIDI and MPU-401 +documentation has been a valuable resource to me over the past few +years, and who provided me with almost all of my knowledge of MIDI +and MPU-401. His site is at: www.borg.com/~jglatt/ + +- Andrew "Silver Blade" Greenwood diff --git a/reactos/drivers/dd/mpu401/sbdebug.h b/reactos/drivers/dd/mpu401/sbdebug.h new file mode 100644 index 00000000000..30e7feec961 --- /dev/null +++ b/reactos/drivers/dd/mpu401/sbdebug.h @@ -0,0 +1,24 @@ +#ifndef NDEBUG +#define TEST_STATUS(s) \ + if (! NT_SUCCESS(s)) \ + { \ + if (s == STATUS_NO_MORE_ENTRIES) \ + DPRINT("NTSTATUS == NO MORE ENTRIES\n") \ + else if (s == STATUS_BUFFER_OVERFLOW) \ + DPRINT("NTSTATUS == BUFFER OVERFLOW\n") \ + else if (s == STATUS_BUFFER_TOO_SMALL) \ + DPRINT("NTSTATUS == BUFFER TOO SMALL\n") \ + else if (s == STATUS_INVALID_PARAMETER) \ + DPRINT("NTSTATUS == INVALID PARAMETER\n") \ + else if (s == STATUS_OBJECT_NAME_NOT_FOUND) \ + DPRINT("NTSTATUS == OBJECT NAME NOT FOUND\n") \ + else if (s == STATUS_INVALID_HANDLE) \ + DPRINT("NTATATUS == INVALID_HANDLE\n") \ + else if (s == STATUS_ACCESS_DENIED) \ + DPRINT("NTSTATUS == ACCESS_DENIED\n") \ + else \ + DPRINT("NTSTATUS == FAILURE (???)\n"); \ + } +#else +#define TEST_STATUS(s) +#endif diff --git a/reactos/drivers/dd/mpu401/settings.c b/reactos/drivers/dd/mpu401/settings.c new file mode 100644 index 00000000000..ef6e31df714 --- /dev/null +++ b/reactos/drivers/dd/mpu401/settings.c @@ -0,0 +1,306 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: services/dd/mpu401/settings.c + * PURPOSE: MPU-401 MIDI device driver setting management + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Sept 27, 2003: Created + */ + +#include +#include + +#include "mpu401.h" + +// #define NDEBUG +#include +#include "sbdebug.h" // our own debug helper + + +NTSTATUS +OpenDevicesKey( + IN PWSTR RegistryPath, + OUT PHANDLE Key) +/* + Description: + Create a volatile key under this driver's Services node to contain + the device name list. + + Parameters: + RegistryPath The location of the registry entry + Key The key in the registry + + Return Value: + NT status STATUS_SUCCESS if successful (duh...) +*/ +{ + NTSTATUS s; + HANDLE hKey; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING uStr; + + // Attempt to open the key + + RtlInitUnicodeString(&uStr, RegistryPath); + + InitializeObjectAttributes(&oa, &uStr, OBJ_CASE_INSENSITIVE, NULL, + (PSECURITY_DESCRIPTOR)NULL); + + s = ZwOpenKey(&hKey, KEY_CREATE_SUB_KEY, &oa); + + if (! NT_SUCCESS(s)) + return s; // Problem + + + // Now create sub key + + RtlInitUnicodeString(&uStr, (PWSTR) DEVICE_SUBKEY); + + InitializeObjectAttributes(&oa, &uStr, OBJ_CASE_INSENSITIVE, hKey, + (PSECURITY_DESCRIPTOR)NULL); + + s = ZwCreateKey(Key, KEY_ALL_ACCESS, &oa, 0, NULL, REG_OPTION_VOLATILE, + NULL); + + ZwClose(hKey); + + return s; +} + + + +NTSTATUS EnumDeviceKeys( + IN PUNICODE_STRING RegistryPath, + IN PWSTR SubKey, + IN PREGISTRY_CALLBACK_ROUTINE Callback, + IN PVOID Context) +/* + Description: + Enumerate the device subkeys in the driver's registry entry, and + call the specified callback routine for each device. + + Parameters: + RegistryPath The location of the registry entry + Subkey The device's subkey + Callback A routine called for each device + Context ??? + + Return Value: + NT status STATUS_SUCCESS if successful +*/ +{ + NTSTATUS s; + OBJECT_ATTRIBUTES oa; + HANDLE hKey, hSubKey; + UNICODE_STRING SubkeyName; + ULONG i; + + // Attempt to open the key + + InitializeObjectAttributes(&oa, RegistryPath, OBJ_CASE_INSENSITIVE, + NULL, (PSECURITY_DESCRIPTOR)NULL); + + s = ZwOpenKey(&hKey, KEY_READ, &oa); + + TEST_STATUS(s); // debugging + + if (! NT_SUCCESS(s)) + return s; // Problem + + RtlInitUnicodeString(&SubkeyName, SubKey); + + DPRINT("Subkey: %wZ\n", &SubkeyName); + + InitializeObjectAttributes(&oa, &SubkeyName, OBJ_CASE_INSENSITIVE, + hKey, (PSECURITY_DESCRIPTOR)NULL); + + s = ZwOpenKey(&hSubKey, KEY_ENUMERATE_SUB_KEYS, &oa); + + ZwClose(hKey); + + TEST_STATUS(s); // debugging + + if (! NT_SUCCESS(s)) + return s; + + + // And now, the enumeration + + for (i = 0;; i ++) + { + KEY_BASIC_INFORMATION Info; + PKEY_BASIC_INFORMATION pInfo; + ULONG ResultLength = 0; + ULONG Size = 0; + PWSTR Pos; + PWSTR Name; + + // Find the length of the subkey data + +// Info.NameLength = 0; // TEMPORARY! + + s = ZwEnumerateKey(hSubKey, i, KeyBasicInformation, &Info, + sizeof(Info), &ResultLength); + + if (s == STATUS_NO_MORE_ENTRIES) + break; + + DPRINT("Found an entry, allocating memory...\n"); + +// Size = Info.NameLength + FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]); + Size = ResultLength + FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]); + + DPRINT("Size is %d\n", Size); + + pInfo = (PKEY_BASIC_INFORMATION) ExAllocatePool(PagedPool, Size); + + if (pInfo == NULL) + { + DPRINT("INSUFFICIENT RESOURCES!\n"); + s = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + DPRINT("Re-enumerating...\n"); + + s = ZwEnumerateKey(hSubKey, i, KeyBasicInformation, pInfo, Size, + &ResultLength); + +// TEST_STATUS(s); // debugging + + if (! NT_SUCCESS(s)) + { + ExFreePool((PVOID) pInfo); + s = STATUS_INTERNAL_ERROR; + break; + } + + DPRINT("Allocating memory for name...\n"); + + Name = ExAllocatePool(PagedPool, + RegistryPath->Length + sizeof(WCHAR) + + SubkeyName.Length + sizeof(WCHAR) + + pInfo->NameLength + sizeof(UNICODE_NULL)); + + if (Name == NULL) + { + DPRINT("INSUFFICIENT RESOURCES!"); + ExFreePool((PVOID) pInfo); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Copy the key name + RtlCopyMemory((PVOID)Name, (PVOID)RegistryPath->Buffer, RegistryPath->Length); + Pos = Name + (RegistryPath->Length / sizeof(WCHAR)); + Pos[0] = '\\'; + Pos++; + + // Copy the parameters sub key name + RtlCopyMemory((PVOID)Pos, (PVOID)SubKey, SubkeyName.Length); //SubkeyName? + Pos += SubkeyName.Length / sizeof(WCHAR); + Pos[0] = '\\'; + Pos ++; + + // Copy the device sub key name + RtlCopyMemory((PVOID)Pos, (PVOID)pInfo->Name, pInfo->NameLength); + Pos += pInfo->NameLength / sizeof(WCHAR); + Pos[0] = UNICODE_NULL; + + ExFreePool((PVOID)pInfo); + + DPRINT("Calling callback...\n"); + + s = (*Callback)(Name, Context); + + if (! NT_SUCCESS(s)) + { DPRINT("Callback FAILED\n"); + break;} + } + + ZwClose(hSubKey); + + DPRINT("%d device registry keys found\n", i); + + if ((i == 0) && (s == STATUS_NO_MORE_ENTRIES)) + return STATUS_DEVICE_CONFIGURATION_ERROR; + + return s == STATUS_NO_MORE_ENTRIES ? STATUS_SUCCESS : s; +} + + + +NTSTATUS LoadSettings( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +/* + Description: + Read the settings for a particular device + + Parameters: + ValueName The value to read from the registry + ValueType ? + ValueData ? + ValueLength ? + Context The configuration structure to write to + EntryContext ? + + Return Value: + NT status STATUS_SUCCESS if successful +*/ +{ + PDEVICE_EXTENSION DeviceInfo = Context; + + if (ValueType == REG_DWORD) + { + if (! _wcsicmp(ValueName, REGISTRY_PORT)) + { + DeviceInfo->Port = *(PULONG) ValueData; + DPRINT("Registry port = 0x%x\n", DeviceInfo->Port); + } + + // More to come... (config.c) + } + + else + { + // ? + } + + return STATUS_SUCCESS; +} + + + +NTSTATUS SaveSettings( + IN PWSTR RegistryPath, + IN ULONG Port, + IN ULONG IRQ, + IN ULONG DMA) +/* + Description: + Saves the settings for a particular device + + Parameters: + RegistryPath Where to save the settings to + Port The device's port number + IRQ The device's interrupt number + DMA The device's DMA channel + + Return Value: + NT status STATUS_SUCCESS if successful +*/ +{ + NTSTATUS s; + + DPRINT("SaveSettings() unimplemented\n"); + +// UNIMPLEMENTED; + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/dd/mpu401/test.c b/reactos/drivers/dd/mpu401/test.c new file mode 100644 index 00000000000..ff4aedc3362 --- /dev/null +++ b/reactos/drivers/dd/mpu401/test.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include "mpu401.h" + +int main() +{ +// NTSTATUS s; +// PHANDLE Handle; +// PIO_STATUS_BLOCK Status; + DWORD BytesReturned; + BYTE Test[3]; // Will store MIDI data + BYTE Notes[] = {50, 52, 54, 55, 57, 59, 61}; + HANDLE Device; + UINT Note; + UINT Junk; + + printf("Test program for MPU401 driver\n"); + + Device = CreateFile("\\\\.\\MPU401_Out_0", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING, + NULL); + + if (Device == INVALID_HANDLE_VALUE) + { + printf("Device is busy or could not be found.\n"); + return -1; + } + + printf("Device is open, let's play some music...\n"); + + Test[0] = 0x90; + Test[2] = 0x7f; + + for (Note = 0; Note < sizeof(Notes); Note ++) + { + Test[1] = Notes[Note]; + + DeviceIoControl( + Device, + IOCTL_MIDI_PLAY, + &Test, + sizeof(Test), + NULL, + 0, + &BytesReturned, + NULL + ); + + for (Junk = 0; Junk < 100000; Junk ++); // Pause + } + + +/* s = IoCreateFile(Handle, GENERIC_READ | GENERIC_WRITE, + OBJ_KERNEL_HANDLE, + Status, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE, + NULL, + 0, + CreateFileTypeNone, + NULL, + 0); +*/ +}