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.
 
 
 
 
 
 

1163 lines
32 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
init.c
Abstract:
This module contains code for the initialization phase of the
Soundblaster device driver.
Author:
Nigel Thompson (nigelt) 7-March-1991
Environment:
Kernel mode
Revision History:
Robin Speed (RobinSp) 29-Jan-1992
- Added new devices, cleanup and support for Soundblaster 1
Sameer Dekate ([email protected]) 19-Aug-1992
- Changes to support the MIPS sound board
--*/
#include "sound.h"
#include "stdlib.h"
BOOLEAN
SoundTestISR(
IN PKINTERRUPT pInterrupt,
IN PVOID Context
)
/*++
Routine Description:
Interrupt service routine for the MIPS sound card for
hardware detection testing
Arguments:
pInterrupt - our interrupt
Contest - Pointer to our global device info
Return Value:
TRUE if we handled the interrupt
--*/
{
PGLOBAL_DEVICE_INFO pGDI;
PSOUND_REGISTERS pSoundRegisters;
pGDI = (PGLOBAL_DEVICE_INFO)Context;
ASSERT(pGDI->Key == GDI_KEY);
//
// Find our sound registers
//
pSoundRegisters = pGDI->SoundHardware.SoundVirtualBase;
//
// Tell tester we interrupted OK
//
pGDI->Interrupts++;
//
// Clear interrupt
//
WRITEAUDIO_ENDIAN(&pSoundRegisters, 0xf8);
return TRUE;
}
NTSTATUS
SoundReportMemoryResourceUsage(
PDEVICE_OBJECT DeviceObject,
ULONG MemoryBase,
ULONG Size
)
/*++
Routine Description:
Report use of device-mapped memory and see if someone
else has already claimed it
Arguments:
DeviceObject - our device
MemoryBase - start of memory in use
Size - number of bytes being used
Return Value:
STATUS_SUCCESS if we got the memory to ourselves
--*/
{
BOOLEAN ResourceConflict;
CM_RESOURCE_LIST ResourceList;
NTSTATUS Status;
RtlZeroMemory((PVOID)&ResourceList, sizeof(ResourceList));
ResourceList.Count = 1;
ResourceList.List[0].InterfaceType = Internal;
// ResourceList.List[0].Busnumber = 0; Already 0
ResourceList.List[0].PartialResourceList.Count = 1;
ResourceList.List[0].PartialResourceList.PartialDescriptors[0].Type =
CmResourceTypeMemory;
ResourceList.List[0].PartialResourceList.PartialDescriptors[0].ShareDisposition =
CmResourceShareDriverExclusive;
ResourceList.List[0].PartialResourceList.PartialDescriptors[0].u.Memory.Start.LowPart =
MemoryBase;
ResourceList.List[0].PartialResourceList.PartialDescriptors[0].u.Memory.Length =
Size;
//
// Report our resource usage and detect conflicts
//
Status = IoReportResourceUsage(NULL,
DeviceObject->DriverObject,
&ResourceList,
sizeof(ResourceList),
DeviceObject,
NULL,
0,
FALSE,
&ResourceConflict);
//
// We might (for instance) get a conflict if another driver is loaded
// for the same hardware
//
#if DBG
if (ResourceConflict) {
dprintf1("Hardware is already in use by another driver !");
}
#endif // DBG
return !NT_SUCCESS(Status) ? Status :
ResourceConflict ?
STATUS_DEVICE_CONFIGURATION_ERROR :
STATUS_SUCCESS;
}
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING RegistryPathName
)
/*++
Routine Description:
This routine creates a device object for the record and
playback channels, an interrupt object
and initialises the DeviceExtension data.
A predeclaration for this exists in \nt\private\ntos\dd\init\ddpi386.h
Arguments:
pDriverObject - Pointer to a driver object.
Return Value:
The function value is the final status from the initialization operation.
--*/
{
PGLOBAL_DEVICE_INFO pGlobalInfo; // Card global info
DEVICE_DESCRIPTION DeviceDescription; //
PLOCAL_DEVICE_INFO pLocalInInfo, pLocalOutInfo;
PLOCAL_DEVICE_INFO pLocalAuxLineinInfo;
NTSTATUS Status;
ULONG NumberOfMapRegisters;
PSOUND_REGISTERS pSoundRegisters;
ULONG DmaEnable;
ULONG InterruptVector; // from configuration
KIRQL InterruptRequestLevel;
KAFFINITY Affinity;
//
// Get the system configuration information for this driver.
//
// DbgBreakPoint();
dprintf5("DriverEntry: Entering\n");
//
// Initialize the driver object dispatch table.
//
pDriverObject->DriverUnload = SoundUnload;
pDriverObject->MajorFunction[IRP_MJ_CREATE] = SoundDispatch;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = SoundDispatch;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SoundDispatch;
pDriverObject->MajorFunction[IRP_MJ_READ] = SoundDispatch;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = SoundDispatch;
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = SoundDispatch;
//
// Allocate some memory for the global device info
//
pGlobalInfo = (PGLOBAL_DEVICE_INFO)ExAllocatePool(NonPagedPoolMustSucceed,
sizeof(GLOBAL_DEVICE_INFO));
ASSERT(pGlobalInfo);
dprintf4(" GlobalInfo : %08lXH", pGlobalInfo);
RtlZeroMemory(pGlobalInfo, sizeof(GLOBAL_DEVICE_INFO));
pGlobalInfo->Key = GDI_KEY;
//
// Map the Sound device registers into the system virtual address space.
//
{
ULONG MemType;
PHYSICAL_ADDRESS RegisterAddress;
PHYSICAL_ADDRESS MappedAddress;
MemType = 0; // Memory space
RegisterAddress.LowPart = SOUND_PHYSICAL_BASE;
RegisterAddress.HighPart = 0;
HalTranslateBusAddress(
Internal,
0,
RegisterAddress,
&MemType,
&MappedAddress);
//
// Map memory type IO space into our address space
//
pGlobalInfo->SoundHardware.SoundVirtualBase =
MmMapIoSpace(
MappedAddress,
PAGE_SIZE,
FALSE);
}
if (pGlobalInfo->SoundHardware.SoundVirtualBase == NULL) {
dprintf1("Failed to map device registers into system space");
return STATUS_UNSUCCESSFUL;
}
//
// Find out what our request level and interrupt are
//
InterruptVector = HalGetInterruptVector(Internal,
0,
DEVICE_LEVEL,
SOUND_VECTOR,
&InterruptRequestLevel,
&Affinity);
if (pGlobalInfo->SoundHardware.SoundVirtualBase == NULL) {
dprintf1("Failed to map device registers into system space");
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
//
// Initialize some of the device global info
// This means that sndInitCleanup can find things it needs to
// free
//
pGlobalInfo->InterruptVector = SOUND_VECTOR;
pGlobalInfo->InterruptRequestLevel = DEVICE_LEVEL;
KeInitializeSpinLock(&pGlobalInfo->DeviceSpinLock);
pGlobalInfo->DMABusy = FALSE;
pGlobalInfo->Usage = SoundInterruptUsageIdle;
pGlobalInfo->NextHalf = LowerHalf;
pGlobalInfo->StartDMA = SoundInitiate;
//
// Create our devices
//
Status = sndCreateDevice(
DD_WAVE_IN_DEVICE_NAME_U,
FILE_DEVICE_WAVE_IN,
SoundInDeferred,
pDriverObject,
&pGlobalInfo->pWaveInDevObj);
if (!NT_SUCCESS(Status)) {
sndInitCleanup(pGlobalInfo);
return Status;
}
Status = sndCreateDevice(
DD_WAVE_OUT_DEVICE_NAME_U,
FILE_DEVICE_WAVE_OUT,
SoundOutDeferred,
pDriverObject,
&pGlobalInfo->pWaveOutDevObj);
if (!NT_SUCCESS(Status)) {
sndInitCleanup(pGlobalInfo);
return Status;
}
if (!NT_SUCCESS(Status)) {
dprintf1("Failed to create device 1");
sndInitCleanup(pGlobalInfo);
return Status;
}
// LINEIN Device
Status = sndCreateDevice(
DD_AUX_DEVICE_NAME_U,
FILE_DEVICE_SOUND,
NULL,
pDriverObject,
&pGlobalInfo->pAuxLineinDevObj);
if (!NT_SUCCESS(Status)) {
dprintf1("Failed to create device 2");
sndInitCleanup(pGlobalInfo);
return Status;
}
//
// Allocate a DMA buffer in physically contiguous memory.
// For now we allocate one page since this MUST be contiguous.
//
// Since there is no guarantee that a request will succeed
// we start by asking for DMA_MAX_BUFFER_SIZE and if that
// fails we ask to decreasing sizes until we get a buffer.
// We will always get at least 4k since that is a single
// page.
//
pGlobalInfo->DMABuffer[0].Buf = MmAllocateNonCachedMemory(DMA_BUFFER_SIZE);
dprintf4(" DMA Buffer : %08lXH", pGlobalInfo->DMABuffer[0].Buf);
if (pGlobalInfo->DMABuffer[0].Buf == NULL) {
sndInitCleanup(pGlobalInfo);
return STATUS_UNSUCCESSFUL;
}
pGlobalInfo->DMABuffer[1].Buf = pGlobalInfo->DMABuffer[0].Buf +
DMA_BUFFER_SIZE / 2;
//
// Allocate an Mdl to describe this buffer
//
pGlobalInfo->pDMABufferMDL[0] = IoAllocateMdl(pGlobalInfo->DMABuffer[0].Buf,
DMA_BUFFER_SIZE / 2,
FALSE, // not a secondary buffer
FALSE, // no charge of quota
NULL // no irp
);
pGlobalInfo->pDMABufferMDL[1] = IoAllocateMdl(pGlobalInfo->DMABuffer[1].Buf,
DMA_BUFFER_SIZE / 2,
FALSE, // not a secondary buffer
FALSE, // no charge of quota
NULL // no irp
);
if (pGlobalInfo->pDMABufferMDL[0] == NULL ||
pGlobalInfo->pDMABufferMDL[1] == NULL) {
sndInitCleanup(pGlobalInfo);
return STATUS_UNSUCCESSFUL;
}
//
// Lock all the pages down
//
MmBuildMdlForNonPagedPool(pGlobalInfo->pDMABufferMDL[0]);
MmBuildMdlForNonPagedPool(pGlobalInfo->pDMABufferMDL[1]);
//
// Initialise the local driver info for each device object
//
pLocalInInfo =
(PLOCAL_DEVICE_INFO)pGlobalInfo->pWaveInDevObj->DeviceExtension;
dprintf4(" LocalWaveInInfo : %08lXH", pLocalInInfo);
pLocalInInfo->Key = LDI_WAVE_IN_KEY;
pLocalInInfo->pGlobalInfo = pGlobalInfo;
pLocalInInfo->DeviceType = WAVE_IN;
pLocalInInfo->State = WAVE_DD_IDLE;
pLocalInInfo->SampleNumber = 0;
InitializeListHead(&pLocalInInfo->QueueHead);
pLocalOutInfo =
(PLOCAL_DEVICE_INFO)pGlobalInfo->pWaveOutDevObj->DeviceExtension;
dprintf4(" LocalWaveOutInfo : %08lXH", pLocalOutInfo);
pLocalOutInfo->Key = LDI_WAVE_OUT_KEY;
pLocalOutInfo->pGlobalInfo = pGlobalInfo;
pLocalOutInfo->DeviceType = WAVE_OUT;
pLocalOutInfo->State = WAVE_DD_IDLE;
pLocalOutInfo->SampleNumber = 0;
InitializeListHead(&pLocalOutInfo->QueueHead);
InitializeListHead(&pLocalOutInfo->TransitQueue);
InitializeListHead(&pLocalOutInfo->DeadQueue);
pLocalAuxLineinInfo =
(PLOCAL_DEVICE_INFO)pGlobalInfo->pAuxLineinDevObj->DeviceExtension;
dprintf4(" LocalAuxLineinInfo : %08lXH", pLocalAuxLineinInfo);
pLocalAuxLineinInfo->Key = LDI_AUX_LINEIN_KEY;
pLocalAuxLineinInfo->pGlobalInfo = pGlobalInfo;
pLocalAuxLineinInfo->DeviceType = AUX_LINEIN;
pLocalAuxLineinInfo->State = WAVE_DD_IDLE;
pLocalAuxLineinInfo->SampleNumber = 0;
InitializeListHead(&pLocalAuxLineinInfo->QueueHead);
InitializeListHead(&pLocalAuxLineinInfo->TransitQueue);
InitializeListHead(&pLocalAuxLineinInfo->DeadQueue);
//
// Intialize the volume (can be queried and set)
//
pGlobalInfo->WaveOutVol.Left = WAVE_DD_MID_VOLUME;
pGlobalInfo->WaveOutVol.Right = WAVE_DD_MID_VOLUME;
pGlobalInfo->WaveInVol.Left = WAVE_DD_MID_VOLUME;
pGlobalInfo->WaveInVol.Right = WAVE_DD_MID_VOLUME;
pGlobalInfo->AuxVol.Left = WAVE_DD_AUX_VOLUME;
pGlobalInfo->AuxVol.Right = WAVE_DD_AUX_VOLUME;
//
// Zero the device description structure.
//
RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
//
// Get the adapters for each channel.
//
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
DeviceDescription.Master = FALSE;
DeviceDescription.ScatterGather = FALSE;
DeviceDescription.DemandMode = FALSE;
DeviceDescription.AutoInitialize = FALSE;
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.BusNumber = 0;
DeviceDescription.InterfaceType = Internal;
DeviceDescription.DmaWidth = Width8Bits;
DeviceDescription.DmaSpeed = Compatible;
DeviceDescription.MaximumLength = SOUND_MAX_LENGTH;
DeviceDescription.DmaPort = 0;
//
// Note: Use NumberOfMapRegisters to determine the maximum length
// transfer.
//
NumberOfMapRegisters = SOUND_MAX_LENGTH >> PAGE_SHIFT;
DeviceDescription.DmaChannel = SOUND_CHANNEL_A;
pGlobalInfo->pAdapterObject[0] =
HalGetAdapter(&DeviceDescription,
&NumberOfMapRegisters);
DeviceDescription.DmaChannel = SOUND_CHANNEL_B;
pGlobalInfo->pAdapterObject[1] =
HalGetAdapter(&DeviceDescription,
&NumberOfMapRegisters);
DeviceDescription.DmaChannel = SOUND_CHANNEL_A+2;
pGlobalInfo->pAdapterObject[2] =
HalGetAdapter(&DeviceDescription,
&NumberOfMapRegisters);
DeviceDescription.DmaChannel = SOUND_CHANNEL_B+2;
pGlobalInfo->pAdapterObject[3] =
HalGetAdapter(&DeviceDescription,
&NumberOfMapRegisters);
//
// Check we got the device to ourself
//
Status = SoundReportMemoryResourceUsage(
pGlobalInfo->pWaveInDevObj,
SOUND_PHYSICAL_BASE,
sizeof(SOUND_REGISTERS));
if (!NT_SUCCESS(Status)) {
SoundUnload(pDriverObject);
return Status;
}
//
// try to connect it with the interrupt controller
//
Status = IoConnectInterrupt(
&pGlobalInfo->pInterrupt,
SoundTestISR,
(PVOID)pGlobalInfo,
(PKSPIN_LOCK)NULL,
pGlobalInfo->InterruptVector,
pGlobalInfo->InterruptRequestLevel,
pGlobalInfo->InterruptRequestLevel,
INTERRUPT_MODE,
IRQ_SHARABLE,
Affinity,
FALSE);
if (!NT_SUCCESS(Status)) {
//
// we didn't get the interrupt we wanted
//
dprintf1("Interrupt already in use?");
//
// clean up
//
SoundUnload(pDriverObject);
return Status;
}
//
// Initialize the audio registers pointer
//
pSoundRegisters = pGlobalInfo->SoundHardware.SoundVirtualBase ;
//
// Our hardware detection :
//
// (1) Write endianness register 0xf8
// Read it back (expected 00)
//
{
UCHAR EndianReg;
WRITEAUDIO_ENDIAN(&pSoundRegisters, 0xF8);
EndianReg = READAUDIO_ENDIAN(&pSoundRegisters);
if (EndianReg != 0) {
dprintf1("Endian register was %2X, expected 0",
(ULONG)EndianReg);
SoundUnload(pDriverObject);
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
}
//
// (2) Write endianness register 0xfe
// Should get an interrupt
//
// Try this a few times unless we get spurious interrupts
//
{
int i;
for (i = 0; i < 5; i++) {
pGlobalInfo->Interrupts = 0;
WRITEAUDIO_ENDIAN(&pSoundRegisters, 0xfe);
KeStallExecutionProcessor(100);
if (pGlobalInfo->Interrupts != (UCHAR)1) {
dprintf1("Interrupt count (%u) failed to match (1)",
(ULONG)pGlobalInfo->Interrupts);
SoundUnload(pDriverObject);
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
}
}
//
// We'll change our interrupt routine to the real one now
//
IoDisconnectInterrupt(pGlobalInfo->pInterrupt);
pGlobalInfo->pInterrupt = NULL; // For cleanup routine
//
// try to connect it with the interrupt controller
//
Status = IoConnectInterrupt(
&pGlobalInfo->pInterrupt,
SoundISR,
(PVOID)pGlobalInfo,
(PKSPIN_LOCK)NULL,
pGlobalInfo->InterruptVector,
pGlobalInfo->InterruptRequestLevel,
pGlobalInfo->InterruptRequestLevel,
INTERRUPT_MODE,
IRQ_SHARABLE,
Affinity,
FALSE);
if (!NT_SUCCESS(Status)) {
//
// we didn't get the interrupt we wanted
//
dprintf1("Interrupt already in use?");
//
// clean up
//
SoundUnload(pDriverObject);
return Status;
}
//
// Initialize the Sound controller.
//
//
// Turn off all the dma channels
//
DmaEnable = READ_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_A].Enable.Long);
((PDMA_CHANNEL_ENABLE)(&DmaEnable))->ChannelEnable= 0;
WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_A].Enable.Long, DmaEnable);
DmaEnable = READ_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_B].Enable.Long);
((PDMA_CHANNEL_ENABLE)(&DmaEnable))->ChannelEnable= 0;
WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_B].Enable.Long, DmaEnable);
DmaEnable = READ_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_A+2].Enable.Long);
((PDMA_CHANNEL_ENABLE)(&DmaEnable))->ChannelEnable= 0;
WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_A+2].Enable.Long, DmaEnable);
DmaEnable = READ_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_B+2].Enable.Long);
((PDMA_CHANNEL_ENABLE)(&DmaEnable))->ChannelEnable= 0;
WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_B+2].Enable.Long, DmaEnable);
// Dma Control Register
// Use DMA channels 4 & 5 for recording and 2 & 3 for playback
WRITEAUDIO_DMACNTRL(&pSoundRegisters, ((CH4_IN_USE << REC_CHANNEL_SHIFT )|
(CH2_IN_USE << PLAY_CHANNEL_SHIFT)));
// Config Register
// Mono 8 bit Wave for Playback and Recording
WRITEAUDIO_CONFIG(&pSoundRegisters, ((STEREO_16BIT << PLAY_XLATION_SHIFT)
|(STEREO_16BIT << REC_XLATION_SHIFT)));
// Endianness Register
// Disable Byte Swapping and Enable DMA Tranfer Count Interrupt
WRITEAUDIO_ENDIAN(&pSoundRegisters, DMA_TCINTR_ENABLE);
//
// Right Input Data Register
//
// 0x0F monitor attenuation and right channel input gain of 0x0f
// Full attenuation on linein pin (in our case CDROM, microphone & Linein)
// Since in default (microphone) we donot want to hear what we
// are saying into the microphone
// We Will of course have to turn off monitor attenuation when one
// wants to listen to the aux devices
WRITEAUDIO_RICNTRL(&pSoundRegisters, ((0x0F << MON_ATTN_SHIFT) | 0x02));
//
// Left Input Data Register
//
// We can choose ONLY ONE of the following inputs:
// (1) CDRom Input : DEFAULT
// (2) Line-In Input : AUXILIARY
// (3) Mic Input : AUXILLARY
// Monitor attenuation nibble in RICNTRL will control this input's
// Gain
// AUXILIARY INPUTS
// WRITEAUDIO_LICNTRL(&pSoundRegisters, (LINEIN_ENABLE | 0x0f));
// WRITEAUDIO_LICNTRL(&pSoundRegisters, (CDROM_ENABLE | 0x0f));
// DEFAULT INPUT
// Input from MICR jack and left channel input gain of 0x02
WRITEAUDIO_LICNTRL(&pSoundRegisters, (MICROPHONE_ENABLE | 0x02));
//
// Right Output Data Register
//
// Do not set to WAVE_DD_MAX_VOLUME since some apps may not
// use volume control and will not be able to turn it down
// We can choose two possible outputs:
// (1) Headphone Output: DEFAULT
// (2) Lineout Output: DEFAULT
// Attenuation of left(mono attenuation if so) and right(not used in mono)
// channels in 6 least significant bits in LOCNTRL and ROCNTRL resp.
// Disable the internal SPEAKER (CONNECTED BY HARDWARE!!)
// and set the right channel attenuation to 0x08
WRITEAUDIO_ROCNTRL(&pSoundRegisters, 0x08);
// Left Output Data Register
// Enable Headphone, Enable Lineout and set left channel attn to 0x08
WRITEAUDIO_LOCNTRL(&pSoundRegisters, (HEADPHONE_ENABLE | LINEOUT_ENABLE | 0x08));
// Parallel Port
WRITEAUDIO_PPORT(&pSoundRegisters, 0x40);
// Revision Register
WRITEAUDIO_REVISION(&pSoundRegisters, 0x00);
// Test Register
// No loopback mode, disable loopback test
WRITEAUDIO_TEST(&pSoundRegisters, 0x00);
// Serial Control Register
// set it to 11Khz stereo wave
WRITEAUDIO_SCNTRL(&pSoundRegisters, (CLKSRC_11KHZ | XMIT_CLOCK_SOURCE | XMIT_ENABLE));
// Data Format Register
// set it to 11Khz, default to 16bit linear stereo
// Always keep the CODEC in 16bit linear mode. Change formats
// using the CONFIG register.
WRITEAUDIO_DATAFMT(&pSoundRegisters, (CONFREQ_11KHZ | STEREO | LINEAR_16BIT ));
//
// Shift all the shadow registers to the CODEC
//
Status = sndSetControlRegisters(pGlobalInfo);
if (!NT_SUCCESS(Status)) {
SoundUnload(pDriverObject);
return Status;
}
return STATUS_SUCCESS;
}
NTSTATUS
sndSetControlRegisters(
IN PGLOBAL_DEVICE_INFO pGlobalInfo
)
/*++
Routine Description:
The following registers on the Audio Device are called
Control Registers:
Revision
ParallelPort
Test
Serial Control
Data Format
Status
To set one or more of these registers, the corresponding
values are first written into them. This routine is called
thereafter to freeze the values.
All dma channels need to be turned off and play enable and
record enable need to be set to zero.
Arguments:
pGlobalInfo - Pointer to Global Info for Audio Device
Return Value:
STATUS_SUCCESS if the Control Registers are set,
STATUS_UNSUCCESSFUL otherwise.
--*/
{
PSOUND_REGISTERS pSoundRegisters;
int count = 100000;
UCHAR regval;
pSoundRegisters = pGlobalInfo->SoundHardware.SoundVirtualBase ;
// Reset DCB bit in Control Mode Status Register
WRITEAUDIO_STATUS(&pSoundRegisters, 0x00);
// Reset Data/Control Bit in DMA Channel Control Register
// Put into Control Mode
regval = READAUDIO_DMACNTRL(&pSoundRegisters);
WRITEAUDIO_DMACNTRL(&pSoundRegisters, (regval & ~DATA_CONTROL));
// Delay for 500 microseconds
KeStallExecutionProcessor(500);
// Write DCB = 1 in Control Mode Status Register
WRITEAUDIO_STATUS(&pSoundRegisters, DATA_CTRL_HNDSHAKE);
// Wait for DCB bit to go high in DMA Channel Control Register
while ((!((regval = READAUDIO_DMACNTRL(&pSoundRegisters)) & DCB)) &&
(count--)){
// Delay for 10 microseconds
KeStallExecutionProcessor(10);
}
// Set Data/Control Bit in DMA Control Register
// Put back into Data Mode
WRITEAUDIO_DMACNTRL(&pSoundRegisters, (regval | DATA_CONTROL));
if (count) return STATUS_SUCCESS;
else return STATUS_DEVICE_CONFIGURATION_ERROR;
}
NTSTATUS
sndCreateDevice(
IN PWSTR PrototypeName, // Name to add a number to
IN DEVICE_TYPE DeviceType, // Type of device to create
IN PIO_DPC_ROUTINE DpcRoutine, // Dpc routine
IN PDRIVER_OBJECT pDriverObject, // Device object
OUT PDEVICE_OBJECT *ppDevObj // Pointer to device obj pointer
)
/*++
Routine Description:
Create a new device using a name derived from szPrototypeName
by adding a number on to the end such that the no device with the
qualified name exists.
Arguments:
Return Value:
An NTSTATUS code.
--*/
{
int DeviceNumber;
NTSTATUS Status;
UNICODE_STRING DeviceName;
UNICODE_STRING UnicodeNum;
WCHAR TestName[SOUND_MAX_DEVICE_NAME];
CHAR Number[8];
ANSI_STRING AnsiNum;
#ifdef SOUND_DIRECTORIES
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE DirectoryHandle = NULL;
//
// Create the directory for this device type.
//
RtlInitUnicodeString(&DeviceName, PrototypeName);
InitializeObjectAttributes(&ObjectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
NULL,
(PSECURITY_DESCRIPTOR)NULL);
//
// We create the directory if it doesn't exist.
// We must keep this handle open until we create something as
// we're not making it permanent. This means that if we unload
// the system may be able to get rid of the directory
//
Status = ZwCreateDirectoryObject(&DirectoryHandle,
GENERIC_READ,
&ObjectAttributes);
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) {
dprintf1("Return code from NtCreateDirectoryObject = %x", Status);
return Status;
} else {
//
// Directory is permanent so it won't go away.
//
ZwClose(DirectoryHandle);
}
#endif // SOUND_DIRECTORIES
for (DeviceNumber = 0; DeviceNumber < SOUND_MAX_DEVICES; DeviceNumber ++) {
//
// Create our test name
//
TestName[0] = 0;
RtlInitUnicodeString(&DeviceName, TestName);
DeviceName.MaximumLength = sizeof(TestName) - 3 * sizeof(WCHAR);
Status = RtlAppendUnicodeToString(&DeviceName, PrototypeName);
if (!NT_SUCCESS(Status)) {
return Status;
}
#ifdef SOUND_DIRECTORIES
//
// Create our unicode number
//
Number[0] = '\\';
_itoa(DeviceNumber, Number + 1, 10);
#else
_itoa(DeviceNumber, Number, 10);
#endif // SOUND_DIRECTORIES
RtlInitAnsiString(&AnsiNum, Number);
UnicodeNum.Buffer = TestName + DeviceName.Length/sizeof(WCHAR);
UnicodeNum.MaximumLength = 8 * sizeof(WCHAR);
RtlAnsiStringToUnicodeString(&UnicodeNum, &AnsiNum, FALSE);
DeviceName.Length += UnicodeNum.Length;
Status = IoCreateDevice(
pDriverObject,
sizeof(LOCAL_DEVICE_INFO),
&DeviceName,
DeviceType,
0,
FALSE, // Non-Exclusive
ppDevObj
);
if (NT_SUCCESS(Status)) {
dprintf2("Created device %d", DeviceNumber);
RtlZeroMemory((*ppDevObj)->DeviceExtension,
sizeof(LOCAL_DEVICE_INFO));
//
// Set up the rest of the device stuff
//
(*ppDevObj)->Flags |= DO_DIRECT_IO;
(*ppDevObj)->AlignmentRequirement = FILE_BYTE_ALIGNMENT;
((PLOCAL_DEVICE_INFO)(*ppDevObj)->DeviceExtension)->DeviceNumber =
DeviceNumber;
if (DpcRoutine) {
IoInitializeDpcRequest((*ppDevObj), DpcRoutine);
}
//
// Try to create a symbolic link object for this device
//
// No security
//
// We make (eg)
// \DosDevices\WaveOut0
// Point to
// \Device\WaveOut0
//
{
UNICODE_STRING LinkObject;
WCHAR LinkName[80];
#define DeviceSize (sizeof(L"\\Device") - sizeof(UNICODE_NULL))
LinkName[0] = UNICODE_NULL;
RtlInitUnicodeString(&LinkObject, LinkName);
LinkObject.MaximumLength = sizeof(LinkName);
RtlAppendUnicodeToString(&LinkObject, L"\\DosDevices");
DeviceName.Buffer += DeviceSize / sizeof(WCHAR);
DeviceName.Length -= DeviceSize;
RtlAppendUnicodeStringToString(&LinkObject, &DeviceName);
DeviceName.Buffer -= DeviceSize / sizeof(WCHAR);
DeviceName.Length += DeviceSize;
Status = IoCreateSymbolicLink(&LinkObject, &DeviceName);
if (!NT_SUCCESS(Status)) {
dprintf1(("Failed to create symbolic link object"));
IoDeleteDevice(*ppDevObj);
return Status;
}
}
return STATUS_SUCCESS;
}
}
//
// Failed !
//
return STATUS_INSUFFICIENT_RESOURCES;
}
VOID
SoundFreeDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PWSTR PrototypeName
)
/*++
Routine Description :
Free the all resources related to this device :
The device object itself
Any symbolic link related to this device
Arguments :
DeviceObject - the device to free
PrototypeName - Name used to create device
Return Value :
None
--*/
{
//
// Free the device if any
//
if (DeviceObject != NULL) {
//
// Remove the device's symbolic link
//
{
PLOCAL_DEVICE_INFO pLDI;
UNICODE_STRING DeviceName;
WCHAR TestName[SOUND_MAX_DEVICE_NAME];
pLDI = DeviceObject->DeviceExtension;
DeviceName.Buffer = TestName;
DeviceName.MaximumLength = sizeof(TestName);
DeviceName.Length = 0;
RtlAppendUnicodeToString(&DeviceName, L"\\DosDevices");
RtlAppendUnicodeToString(
&DeviceName,
PrototypeName +
(sizeof(L"\\Device") - sizeof(UNICODE_NULL)) /
sizeof(UNICODE_NULL));
{
UNICODE_STRING UnicodeNum;
WCHAR Number[8];
UnicodeNum.MaximumLength = sizeof(Number);
UnicodeNum.Buffer = Number;
RtlIntegerToUnicodeString(pLDI->DeviceNumber, 10, &UnicodeNum);
RtlAppendUnicodeStringToString(&DeviceName, &UnicodeNum);
}
IoDeleteSymbolicLink(&DeviceName);
}
//
// Delete the device object
//
IoDeleteDevice(DeviceObject);
}
}
VOID
sndInitCleanup(
IN PGLOBAL_DEVICE_INFO pGDI
)
/*++
Routine Description:
Clean up all resources allocated by our initialization
Arguments:
pGDI - Pointer to global data
Return Value:
NONE
--*/
{
if (pGDI->pInterrupt) {
IoDisconnectInterrupt(pGDI->pInterrupt);
}
if (pGDI->pDMABufferMDL[0]) {
IoFreeMdl(pGDI->pDMABufferMDL[0]);
}
if (pGDI->pDMABufferMDL[1]) {
IoFreeMdl(pGDI->pDMABufferMDL[1]);
}
if (pGDI->DMABuffer[0].Buf) {
MmFreeNonCachedMemory(pGDI->DMABuffer[0].Buf, DMA_BUFFER_SIZE);
}
SoundFreeDevice(pGDI->pWaveInDevObj,
DD_WAVE_IN_DEVICE_NAME_U);
SoundFreeDevice(pGDI->pWaveOutDevObj,
DD_WAVE_OUT_DEVICE_NAME_U);
SoundFreeDevice(pGDI->pAuxLineinDevObj,
DD_AUX_DEVICE_NAME_U);
if (pGDI->SoundHardware.SoundVirtualBase) {
MmUnmapIoSpace(pGDI->SoundHardware.SoundVirtualBase, PAGE_SIZE);
}
ExFreePool(pGDI);
}