/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_DriverCommandManager.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 22289 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include namespace nw { namespace snd { namespace internal { DriverCommandManager& DriverCommandManager::GetInstance() { static DriverCommandManager instance; return instance; } DriverCommandManager& DriverCommandManager::GetInstanceForTaskThread() { static DriverCommandManager instance; return instance; } DriverCommandManager::DriverCommandManager() { } void DriverCommandManager::Initialize( void* commandBuffer, u32 commandBufferSize ) { m_CommandListBegin = NULL; m_CommandListEnd = NULL; m_CommandTag = 0; m_CommandMemoryAreaBegin = 0; m_CommandMemoryAreaEnd = 0; m_CommandMemoryAreaZeroFlag = false; m_CommandMemoryArea = static_cast(commandBuffer); m_CommandMemoryAreaSize = commandBufferSize / sizeof(m_CommandMemoryArea[0]); m_SendCommandQueue.Initialize( m_SendCommandQueueBuffer, COMMAND_QUEUE_SIZE ); m_RecvCommandQueue.Initialize( m_RecvCommandQueueBuffer, COMMAND_QUEUE_SIZE ); } void DriverCommandManager::Finalize() { m_SendCommandQueue.Finalize(); m_RecvCommandQueue.Finalize(); } void* DriverCommandManager::AllocMemory( u32 count ) { NW_ASSERT( count <= m_CommandMemoryAreaSize ); void* ptr = TryAllocMemory( count ); if ( ptr != NULL ) return ptr; RecvCommandReply(); ptr = TryAllocMemory( count ); if ( ptr != NULL ) return ptr; // 強制フラッシュ while( ptr == NULL ) { u32 tag = FlushCommand( true ); WaitCommandReply( tag ); ptr = TryAllocMemory( count ); } return ptr; } void* DriverCommandManager::TryAllocMemory( u32 count ) { void* ptr = NULL; if ( m_CommandMemoryAreaZeroFlag ) return NULL; if ( m_CommandMemoryAreaEnd > m_CommandMemoryAreaBegin ) { // 連続領域の場合 if ( m_CommandMemoryAreaBegin + count <= m_CommandMemoryAreaEnd ) { ptr = &m_CommandMemoryArea[m_CommandMemoryAreaBegin]; m_CommandMemoryAreaBegin += count; } } else { // 非連続領域の場合 if ( m_CommandMemoryAreaBegin + count <= m_CommandMemoryAreaSize ) { ptr = &m_CommandMemoryArea[m_CommandMemoryAreaBegin]; m_CommandMemoryAreaBegin += count; } else if ( count <= m_CommandMemoryAreaEnd ) { ptr = &m_CommandMemoryArea[0]; m_CommandMemoryAreaBegin = count; } } if ( ptr != NULL && m_CommandMemoryAreaBegin == m_CommandMemoryAreaEnd ) { m_CommandMemoryAreaZeroFlag = true; } return ptr; } u32 DriverCommandManager::GetAllocatableCommandSize() const { if ( m_CommandMemoryAreaZeroFlag ) return 0; if ( m_CommandMemoryAreaEnd > m_CommandMemoryAreaBegin ) { // 連続領域の場合 return m_CommandMemoryAreaEnd - m_CommandMemoryAreaBegin; } else { // 非連続領域の場合 return ut::Max( m_CommandMemoryAreaSize - m_CommandMemoryAreaBegin, m_CommandMemoryAreaEnd ); } } u32 DriverCommandManager::PushCommand( DriverCommand* command ) { if ( m_CommandListEnd == NULL ) { m_CommandListBegin = command; m_CommandListEnd = command; } else { m_CommandListEnd->next = command; m_CommandListEnd = command; } command->next = NULL; return m_CommandTag; } //--------------------------------------------------------------------------- //! @brief コマンドをフラッシュします。 //! //! forceFlagがtrueの時は、必ず処理を行います。 //! コマンドが1つも登録されていない場合は、ダミーのコマンドを発行します。 //! 受信側の処理が滞っている場合は、空くまで待ちます。 //! //! forceFlagがfalseの時は、必要以上の処理は行いません。 //! コマンドが1つも登録されていない場合は、何もせずに返ります。 //! 受信側の処理が滞っている場合も、何もせずに返ります。 //! 何もせずに返った場合、返り値は0(無効値)になることに注意してください。 //! //! @param[in] forceFlag フラッシュ処理を強制するかどうかのフラグです。 //! //! @return コマンドタグを返します。 //! ただし、forceFlagがfalseの時に、コマンドフラッシュが //! 行われない場合は、0(無効値)を返します。 //! //--------------------------------------------------------------------------- u32 DriverCommandManager::FlushCommand( bool forceFlag ) { if ( m_CommandListBegin == NULL ) { if ( ! forceFlag ) return 0; // ダミーコマンド発行 DriverCommand* command = AllocCommand(); command->id = DRIVER_COMMAND_DUMMY; PushCommand(command); } uptr msg = reinterpret_cast(m_CommandListBegin); u32 tag = m_CommandTag; nn::os::DataMemoryBarrier(); if ( forceFlag ) { m_SendCommandQueue.Enqueue( msg ); } else { if ( ! m_SendCommandQueue.TryEnqueue( msg ) ) { return 0; } } m_CommandListBegin->tag = tag; m_CommandTag++; m_CommandListBegin = NULL; m_CommandListEnd = NULL; return tag; } void DriverCommandManager::FinalizeCommandList( DriverCommand* commandList ) { m_FinishCommandTag = commandList->tag; DriverCommand* command = commandList; while( command != NULL ) { if ( command->next == NULL ) { // コマンドメモリの解放 m_CommandMemoryAreaEnd = command->memory_next; m_CommandMemoryAreaZeroFlag = false; if ( m_CommandMemoryAreaBegin == m_CommandMemoryAreaEnd ) { // メモリの分断化を解消 m_CommandMemoryAreaBegin = m_CommandMemoryAreaEnd = 0; } break; } command = command->next; } } bool DriverCommandManager::IsFinishCommand( u32 tag ) const { static const u32 limit = (1 << ((sizeof(tag) * 8)-2) ); if ( tag < m_FinishCommandTag ) { return ( m_FinishCommandTag - tag < limit ); } else { return ( tag - m_FinishCommandTag < limit ); } } void DriverCommandManager::RecvCommandReply() { uptr msg; while( m_RecvCommandQueue.TryDequeue( &msg ) ) { DriverCommand* commandList = reinterpret_cast(msg); FinalizeCommandList( commandList ); } } void DriverCommandManager::WaitCommandReply( u32 tag ) { while( true ) { uptr msg = m_RecvCommandQueue.Dequeue(); DriverCommand* commandList = reinterpret_cast(msg); FinalizeCommandList( commandList ); if ( tag == commandList->tag ) break; } } void DriverCommandManager::ProcessCommand() { uptr msg; while( m_SendCommandQueue.TryDequeue( &msg ) ) { DriverCommand* commandList = reinterpret_cast(msg); DriverCommand::ProcessCommandList( commandList ); nn::os::DataMemoryBarrier(); m_RecvCommandQueue.Enqueue( msg ); } } } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw