📄 merlin.cpp
字号:
/////////////////////////////////////////////////////////////////////////////////////////
//Merlin::uploadFirmware
//
// This function is dependant on the Merlin firmware file being present as
// \\Windows\system32\drivers\MerlinD.rom
//
// The firmware file consists of the following structures:
// 2 byte address
// 4 byte data length
// data (data length bytes)
//
// We read from the file until there are no more structures.
//
NTSTATUS Merlin::uploadFirmware()
{
HANDLE file_handle;
NTSTATUS status = openFirmwareFile(&file_handle);
if(NT_SUCCESS(status))
{
status = _p_registers->writeAudioFirmware(file_handle,_p_device->getRevID());
ZwClose(file_handle);
// // Uncomment the following lines if you want to verify firmware after upload
// // verify every byte just for Debuging.
// status = openFirmwareFile(&file_handle);
//
// if(NT_SUCCESS(status))
// {
// _p_registers->verifyAudioFirmware(file_handle);
// ZwClose(file_handle);
// }
// else
// {
// DbgLogError(("Audio firmware file open failed for verify\n"));
// }
}
else
{
DbgLogError(("Audio firmware file open failed for upload\n"));
}
return status;
}
VOID Merlin::initialize()
{
NTSTATUS status = uploadFirmware();
DbgLog(("Completed Merlin Firmware upload, status = %x\n", status));
//////////////////
//Initialize a few other registers necessary for audio functioning.
_p_registers->lock();
_p_registers->writeDword(I2S_IN_CTL, 0x000000A0);
//Audio output control 1 is set to Sony mode
//Audio output control 2 is set to 1 for master mode
_p_registers->writeDword(I2S_OUT_CTL, 0x000001A0);
_p_registers->writeDword(STD_DET_CTL, 0x000000F6);
setAudioInput(_current_audio_input);
//we can simply verify the FW version to tell whether FW is successfully downloaded.
GetVersion();
_p_registers->unlock();
}
/////////////////////////////////////////////////////////////////////////////////////////
VOID Merlin::setTVAudioMode(DWORD new_mode)
{
_p_registers->lock();
BYTE control_register;
_p_registers->readByte(STD_DET_CTL_PREF_MODE, &control_register);
control_register &= 0xF0;
//If the user selected stereo, select it
if(new_mode & KS_TVAUDIO_MODE_STEREO)
{
_tv_audio_preferred_mode = PREF_MODE_STEREO;
}
else
{
//If the user selected language B, select it
if(new_mode & KS_TVAUDIO_MODE_LANG_B)
{
_tv_audio_preferred_mode = PREF_MODE_MONO_LANGB;
}
else if(new_mode & KS_TVAUDIO_MODE_LANG_C)
{
//Read the detected modes
DWORD mode_detect_status;
_p_registers->readDword(STD_DET_STATUS, &mode_detect_status);
mode_detect_status &= 0xFF;
if(mode_detect_status & DETECT_TRI)
{
_tv_audio_preferred_mode = PREF_MODE_MONO_LANGC;
}
else
{
_tv_audio_preferred_mode = PREF_MODE_DUAL_LANG_AB;
}
}
else //Language A
{
//Make sure there is a selected language (to be checked when getting the current mode)
_tv_audio_preferred_mode = PREF_MODE_MONO_LANGA;
}
}
control_register |= _tv_audio_preferred_mode;
_p_registers->writeByte(STD_DET_CTL_PREF_MODE, control_register);
_p_registers->unlock();
}
/////////////////////////////////////////////////////////////////////////////////////////
DWORD Merlin::getCurrentMode()
{
if(!((_current_audio_input == AUDIO_INPUT_TUNER_TV) || (_current_audio_input == AUDIO_INPUT_MUTE)))
{
return KS_TVAUDIO_MODE_STEREO | KS_TVAUDIO_MODE_LANG_A;
}
//Read the detected modes
DWORD mode_detect_status;
_p_registers->readDword(STD_DET_STATUS, &mode_detect_status);
mode_detect_status &= 0xFF;
DWORD current_mode;
current_mode = KS_TVAUDIO_MODE_MONO | KS_TVAUDIO_MODE_LANG_A;
switch(_tv_audio_preferred_mode)
{
case PREF_MODE_STEREO:
if (mode_detect_status & DETECT_STEREO)
current_mode = KS_TVAUDIO_MODE_STEREO | KS_TVAUDIO_MODE_LANG_A;
break;
case PREF_MODE_MONO_LANGB:
if ((mode_detect_status & DETECT_DUAL) ||
(mode_detect_status & DETECT_SAP) ||
(mode_detect_status & DETECT_TRI))
current_mode = KS_TVAUDIO_MODE_MONO | KS_TVAUDIO_MODE_LANG_B;
else if (mode_detect_status & DETECT_STEREO)
current_mode = KS_TVAUDIO_MODE_STEREO | KS_TVAUDIO_MODE_LANG_A;
break;
case PREF_MODE_MONO_LANGC:
if (mode_detect_status & DETECT_TRI)
current_mode = KS_TVAUDIO_MODE_MONO | KS_TVAUDIO_MODE_LANG_C;
else if (mode_detect_status & DETECT_STEREO)
current_mode = KS_TVAUDIO_MODE_STEREO | KS_TVAUDIO_MODE_LANG_A;
break;
case PREF_MODE_DUAL_LANG_AB:
if ((mode_detect_status & DETECT_DUAL) ||
(mode_detect_status & DETECT_SAP) ||
(mode_detect_status & DETECT_TRI))
current_mode = KS_TVAUDIO_MODE_MONO | KS_TVAUDIO_MODE_LANG_C;
else if (mode_detect_status & DETECT_STEREO)
current_mode = KS_TVAUDIO_MODE_STEREO | KS_TVAUDIO_MODE_LANG_A;
break;
case PREF_MODE_MONO_LANGA:
break;
default:
break;
}
return current_mode;
}
/////////////////////////////////////////////////////////////////////////////////////////
DWORD Merlin::getAvailableModes()
{
if(!((_current_audio_input == AUDIO_INPUT_TUNER_TV) || (_current_audio_input == AUDIO_INPUT_MUTE)))
{
return KS_TVAUDIO_MODE_STEREO | KS_TVAUDIO_MODE_LANG_A;
}
//Read the detected modes
DWORD mode_detect_status;
_p_registers->readDword(STD_DET_STATUS, &mode_detect_status);
DbgLogInfo(("Merlin::getAvailableModes : \n\t\t status_reg = %x \n\t\t status & 0xFF = %x, Audio Standard = %x \n",
mode_detect_status, mode_detect_status & 0xFF, _audio_standard ));
if(((mode_detect_status >> 8) & 0xFF) == DETECT_NO_SIGNAL)
{
return KS_TVAUDIO_MODE_MONO | KS_TVAUDIO_MODE_LANG_A;
}
mode_detect_status &= 0xFF;
DWORD available_modes = KS_TVAUDIO_MODE_MONO | KS_TVAUDIO_MODE_LANG_A;
if(mode_detect_status & DETECT_STEREO)
{
available_modes |= KS_TVAUDIO_MODE_STEREO;
}
if((mode_detect_status & DETECT_SAP) || (mode_detect_status & DETECT_DUAL))
{
if((_audio_standard == AUDIO_STANDARD_BTSC) ||
(_audio_standard == AUDIO_STANDARD_EIAJ) ||
(_audio_standard == AUDIO_STANDARD_A2_M) ||
(_audio_standard == AUDIO_STANDARD_NICAM_BG) ||
(_audio_standard == AUDIO_STANDARD_NICAM_DK) ||
(_audio_standard == AUDIO_STANDARD_NICAM_L) ||
(_audio_standard == AUDIO_STANDARD_NICAM_I))
{
available_modes |= (KS_TVAUDIO_MODE_LANG_B | KS_TVAUDIO_MODE_LANG_C);
}
else
{
available_modes |= KS_TVAUDIO_MODE_LANG_B;
}
}
if(mode_detect_status & DETECT_TRI)
{
available_modes |= (KS_TVAUDIO_MODE_LANG_B | KS_TVAUDIO_MODE_LANG_C);
}
return available_modes;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Our default is AUDIO_INPUT_LINE. Since this getCapabilities is called only
// once when the graph is opened, if we change the input to Tuner, we are not
// able to send the new info back to MS.
// So for now, return everything.
// If an App wants to know the current avaliable modes, it will call the
// getAvailableModes function which returns the proper modes as reported by Merlin.
//
///////////////////////////////////////////////////////////////////////////////////
DWORD Merlin::getCapabilities()
{
// if((_current_audio_input == AUDIO_INPUT_TUNER_TV) || (_current_audio_input == AUDIO_INPUT_MUTE))
// {
return KS_TVAUDIO_MODE_LANG_A |
KS_TVAUDIO_MODE_LANG_B |
KS_TVAUDIO_MODE_LANG_C |
KS_TVAUDIO_MODE_STEREO |
KS_TVAUDIO_MODE_MONO;
// }
// else //Line in
// {
// return KS_TVAUDIO_MODE_LANG_A |
// KS_TVAUDIO_MODE_STEREO;
// }
}
VOID Merlin::powerUp()
{
DbgLog(("Merlin::powerUp\n"));
setAudioInput(_current_audio_input);
}
VOID Merlin::powerDown()
{ //[todo] finish this
DbgLog(("Merlin::powerDown\n"));
}
VOID Merlin::notifyPreChannelChange()
{
DbgLog(("Merlin::notifyPreChannelChange\n"));
_p_registers->stopAudioFirmware(FIRMWARE_STOP_REASON_CHANNEL_CHANGE);
}
VOID Merlin::notifyPostChannelChange()
{
DbgLog(("Merlin::notifyPostChannelChange\n"));
_p_registers->restartAudioFirmware(FIRMWARE_STOP_REASON_CHANNEL_CHANGE);
}
VOID Merlin::GetVersion()
{
UCHAR aud_fw_ver[2];
DWORD std_ctrl_reg;
DWORD dl_control = 0;
int MajRevHi,MajRevLo,MinRevlo;
// First save the old state
_p_registers->readDword(DL_CTL, &dl_control);
//Enable the firmware
_p_registers->writeDword(DL_CTL, FLD_START_8051 | FLD_DL_MAP);
_p_registers->readDword(STD_DET_CTL, &std_ctrl_reg);
//Write 0x00 on Audio Configuration register address
//and read from particular addresses to get the audio
//firmware Version & Build number.
_p_registers->writeByte(STD_DET_CTL_AUD_CTL, 0x00);
sleep(50); // wait for 50 ms
//Polaris remove this REG
_p_registers->readByte(AUD_VER_NUM, &aud_fw_ver[0]);
_p_registers->readByte(AUD_BUILD_NUM, &aud_fw_ver[1]);
MajRevHi = aud_fw_ver[0]>>4;
MajRevLo = aud_fw_ver[0]&0xF;
MinRevlo = aud_fw_ver[1];
_audio_fw_version = (((USHORT)aud_fw_ver[0]) & 0x00ff) | ((((USHORT)aud_fw_ver[1]) << 8) & 0xff00);
_p_registers->writeDword(STD_DET_CTL, std_ctrl_reg);
//restore the state.
_p_registers->writeDword(DL_CTL, dl_control);
DbgLog(("Merlin Firmware version = %d.%d.%d\n",MajRevHi, MajRevLo, MinRevlo));
}
BOOLEAN Merlin::isFMLocked()
{
BOOLEAN result = false;
DWORD status_reg = 0;
sleep(50);
_p_registers->readDword(STD_DET_STATUS, &status_reg);
DWORD fmLock = (status_reg >> 8) & 0xff;
DbgLogInfo(("Merlin::isFMLocked : status_reg = %x FM Lock = %x \n", status_reg, fmLock));
if(DETECT_FM_LOCKED == fmLock)
{
result = true;
}
return result;
}
VOID Merlin::setupFM()
{
//the following is just the normal FM Radio Merlin setup script
//except that it is modified to accept 5.5 MHz instead of 10.7 MHz IF
DWORD temp;
//autoconfig to FM Radio w/ 75 us preemphasis
_p_registers->readDword(AUTOCONFIG_REG, &temp);
temp &= 0xFFFFFFF0;
temp |= 0x00000001;
_p_registers->writeDword(AUTOCONFIG_REG, temp);
//set ROT1 to de-rotate 5.5 MHz carrier
//5.5/16.111616 * 2^16 = 0x5763
_p_registers->readDword(ANALOG_DEMOD_CTL, &temp);
temp &= 0x0000FFFF;
temp |= 0x57630000;
_p_registers->writeDword(ANALOG_DEMOD_CTL, temp);
//turn off AFC, turn off DC notch filter
_p_registers->readDword(FM_CTL, &temp);
temp &= 0xFF3FFF3F;
temp |= 0x00C000C0;
_p_registers->writeDword(FM_CTL, temp);
//setup dematrix for FM Radio Mono
_p_registers->readDword(DEMATRIX_CTL, &temp);
temp &= 0xFF00FFFF;
temp |= 0x00800000;
_p_registers->writeDword(DEMATRIX_CTL, temp);
//put the Merlin IF SRC into lowpass mode rather than highpass mode
_p_registers->writeDword(IF_SRC_CTL, 0x000031C0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -