2nd time lucky - MPU401 driver. Sorry about the previous breakage of blue :(

svn path=/trunk/; revision=7789
This commit is contained in:
Andrew Greenwood 2004-01-20 19:58:48 +00:00
parent 5c32f0ba7f
commit 9e88919457
10 changed files with 1144 additions and 0 deletions

View file

@ -0,0 +1,8 @@
*.tmp
*.exp
*.coff
*.d
*.o
*.sym
*.sys
*.map

View file

@ -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

View file

@ -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 <ddk/ntddk.h>
//#include <ddk/ntddbeep.h>
//#define NDEBUG
#include <debug.h>
#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 */

View file

@ -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

View file

@ -0,0 +1,38 @@
#include <defines.h>
#include <reactos/resource.h>
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

View file

@ -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 <windows.h>
#include <ddk/ntddk.h>
#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;
}

View file

@ -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

View file

@ -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

View file

@ -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 <ntddk.h>
#include <windows.h>
#include "mpu401.h"
// #define NDEBUG
#include <debug.h>
#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;
}

View file

@ -0,0 +1,70 @@
#include <stdio.h>
#include <windows.h>
#include <ddk/ntddk.h>
#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);
*/
}