1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     snd_DriverCommandManager.cpp
4 
5   Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain proprietary
8   information of Nintendo and/or its licensed developers and are protected by
9   national and international copyright laws. They may not be disclosed to third
10   parties or copied or duplicated in any form, in whole or in part, without the
11   prior written consent of Nintendo.
12 
13   The content herein is highly confidential and should be handled accordingly.
14 
15   $Revision: 31311 $
16  *---------------------------------------------------------------------------*/
17 
18 #include "precompiled.h"
19 #include <nw/snd/snd_DriverCommandManager.h>
20 
21 namespace nw {
22 namespace snd {
23 namespace internal {
24 
GetInstance()25 DriverCommandManager& DriverCommandManager::GetInstance()
26 {
27     static DriverCommandManager instance;
28     return instance;
29 }
30 
GetInstanceForTaskThread()31 DriverCommandManager& DriverCommandManager::GetInstanceForTaskThread()
32 {
33     static DriverCommandManager instance;
34     return instance;
35 }
36 
DriverCommandManager()37 DriverCommandManager::DriverCommandManager()
38 {
39 }
40 
Initialize(void * commandBuffer,u32 commandBufferSize)41 void DriverCommandManager::Initialize( void* commandBuffer, u32 commandBufferSize )
42 {
43     m_CommandListBegin = NULL;
44     m_CommandListEnd = NULL;
45 
46     m_CommandTag = 0;
47 
48     m_CommandMemoryAreaBegin = 0;
49     m_CommandMemoryAreaEnd = 0;
50     m_CommandMemoryAreaZeroFlag = false;
51 
52     m_CommandMemoryArea = static_cast<u32*>(commandBuffer);
53     m_CommandMemoryAreaSize = commandBufferSize / sizeof(m_CommandMemoryArea[0]);
54 
55     m_SendCommandQueue.Initialize( m_SendCommandQueueBuffer, COMMAND_QUEUE_SIZE );
56     m_RecvCommandQueue.Initialize( m_RecvCommandQueueBuffer, COMMAND_QUEUE_SIZE );
57 }
58 
Finalize()59 void DriverCommandManager::Finalize()
60 {
61     m_SendCommandQueue.Finalize();
62     m_RecvCommandQueue.Finalize();
63 }
64 
AllocMemory(u32 count)65 void* DriverCommandManager::AllocMemory( u32 count )
66 {
67     NW_ASSERT( count <= m_CommandMemoryAreaSize );
68 
69     void* ptr = TryAllocMemory( count );
70     if ( ptr != NULL ) return ptr;
71 
72     RecvCommandReply();
73     ptr = TryAllocMemory( count );
74     if ( ptr != NULL ) return ptr;
75 
76     // 強制フラッシュ
77     while( ptr == NULL )
78     {
79         u32 tag = FlushCommand( true );
80         WaitCommandReply( tag );
81         ptr = TryAllocMemory( count );
82     }
83 
84     return ptr;
85 }
86 
TryAllocMemory(u32 count)87 void* DriverCommandManager::TryAllocMemory( u32 count )
88 {
89     void* ptr = NULL;
90 
91     if ( m_CommandMemoryAreaZeroFlag ) return NULL;
92 
93     if ( m_CommandMemoryAreaEnd > m_CommandMemoryAreaBegin ) {
94         // 連続領域の場合
95         if ( m_CommandMemoryAreaBegin + count <= m_CommandMemoryAreaEnd ) {
96             ptr = &m_CommandMemoryArea[m_CommandMemoryAreaBegin];
97             m_CommandMemoryAreaBegin += count;
98         }
99     }
100     else {
101         // 非連続領域の場合
102         if ( m_CommandMemoryAreaBegin + count <= m_CommandMemoryAreaSize ) {
103             ptr = &m_CommandMemoryArea[m_CommandMemoryAreaBegin];
104             m_CommandMemoryAreaBegin += count;
105         }
106         else if ( count <= m_CommandMemoryAreaEnd ) {
107             ptr = &m_CommandMemoryArea[0];
108             m_CommandMemoryAreaBegin = count;
109         }
110     }
111 
112     if ( ptr != NULL && m_CommandMemoryAreaBegin == m_CommandMemoryAreaEnd ) {
113         m_CommandMemoryAreaZeroFlag = true;
114     }
115     return ptr;
116 }
117 
GetAllocatableCommandSize() const118 u32 DriverCommandManager::GetAllocatableCommandSize() const
119 {
120     if ( m_CommandMemoryAreaZeroFlag ) return 0;
121 
122     if ( m_CommandMemoryAreaEnd > m_CommandMemoryAreaBegin ) {
123         // 連続領域の場合
124         return m_CommandMemoryAreaEnd - m_CommandMemoryAreaBegin;
125     }
126     else {
127         // 非連続領域の場合
128         return ut::Max(
129             m_CommandMemoryAreaSize - m_CommandMemoryAreaBegin,
130             m_CommandMemoryAreaEnd
131         );
132     }
133 }
134 
PushCommand(DriverCommand * command)135 u32 DriverCommandManager::PushCommand( DriverCommand* command )
136 {
137     if ( m_CommandListEnd == NULL )
138     {
139         m_CommandListBegin = command;
140         m_CommandListEnd = command;
141     }
142     else
143     {
144         m_CommandListEnd->next = command;
145         m_CommandListEnd = command;
146     }
147 
148     command->next = NULL;
149 
150     return m_CommandTag;
151 }
152 
153 //---------------------------------------------------------------------------
154 //! @brief    コマンドをフラッシュします。
155 //!
156 //!           forceFlagがtrueの時は、必ず処理を行います。
157 //!           コマンドが1つも登録されていない場合は、ダミーのコマンドを発行します。
158 //!           受信側の処理が滞っている場合は、空くまで待ちます。
159 //!
160 //!           forceFlagがfalseの時は、必要以上の処理は行いません。
161 //!           コマンドが1つも登録されていない場合は、何もせずに返ります。
162 //!           受信側の処理が滞っている場合も、何もせずに返ります。
163 //!           何もせずに返った場合、返り値は0(無効値)になることに注意してください。
164 //!
165 //! @param[in]    forceFlag  フラッシュ処理を強制するかどうかのフラグです。
166 //!
167 //! @return       コマンドタグを返します。
168 //!               ただし、forceFlagがfalseの時に、コマンドフラッシュが
169 //!               行われない場合は、0(無効値)を返します。
170 //!
171 //---------------------------------------------------------------------------
FlushCommand(bool forceFlag)172 u32 DriverCommandManager::FlushCommand( bool forceFlag )
173 {
174     if ( m_CommandListBegin == NULL ) {
175         if ( ! forceFlag ) return 0;
176 
177         // ダミーコマンド発行
178         DriverCommand* command = AllocCommand<DriverCommand>();
179         command->id = DRIVER_COMMAND_DUMMY;
180         PushCommand(command);
181     }
182 
183     uptr msg = reinterpret_cast<uptr>(m_CommandListBegin);
184     u32 tag = m_CommandTag;
185 
186     nn::os::DataMemoryBarrier();
187 
188     if ( forceFlag )
189     {
190         m_SendCommandQueue.Enqueue( msg );
191     }
192     else
193     {
194         if ( ! m_SendCommandQueue.TryEnqueue( msg ) ) {
195             return 0;
196         }
197     }
198 
199     m_CommandListBegin->tag = tag;
200     m_CommandTag++;
201 
202     m_CommandListBegin = NULL;
203     m_CommandListEnd = NULL;
204 
205     return tag;
206 }
207 
FinalizeCommandList(DriverCommand * commandList)208 void DriverCommandManager::FinalizeCommandList( DriverCommand* commandList )
209 {
210     m_FinishCommandTag = commandList->tag;
211 
212     DriverCommand* command = commandList;
213 
214     while( command != NULL )
215     {
216         if ( command->next == NULL ) {
217             // コマンドメモリの解放
218             m_CommandMemoryAreaEnd = command->memory_next;
219             m_CommandMemoryAreaZeroFlag = false;
220             if ( m_CommandMemoryAreaBegin == m_CommandMemoryAreaEnd ) {
221                 // メモリの分断化を解消
222                 m_CommandMemoryAreaBegin = m_CommandMemoryAreaEnd = 0;
223             }
224             break;
225         }
226 
227         command = command->next;
228     }
229 }
230 
IsFinishCommand(u32 tag) const231 bool DriverCommandManager::IsFinishCommand( u32 tag ) const
232 {
233     static const u32 limit = (1 << ((sizeof(tag) * 8)-2) );
234 
235     if ( tag < m_FinishCommandTag )
236     {
237         return ( m_FinishCommandTag - tag < limit );
238     }
239     else
240     {
241         return ( tag - m_FinishCommandTag < limit );
242     }
243 }
244 
RecvCommandReply()245 void DriverCommandManager::RecvCommandReply()
246 {
247     uptr msg;
248     while( m_RecvCommandQueue.TryDequeue( &msg ) )
249     {
250         DriverCommand* commandList = reinterpret_cast<DriverCommand*>(msg);
251         FinalizeCommandList( commandList );
252     }
253 }
254 
WaitCommandReply(u32 tag)255 void DriverCommandManager::WaitCommandReply( u32 tag )
256 {
257     while( true ) {
258         uptr msg = m_RecvCommandQueue.Dequeue();
259         DriverCommand* commandList = reinterpret_cast<DriverCommand*>(msg);
260         FinalizeCommandList( commandList );
261 
262         if ( tag == commandList->tag ) break;
263     }
264 }
265 
ProcessCommand()266 void DriverCommandManager::ProcessCommand()
267 {
268     uptr msg;
269     while( m_SendCommandQueue.TryDequeue( &msg ) )
270     {
271         DriverCommand* commandList = reinterpret_cast<DriverCommand*>(msg);
272 
273         DriverCommand::ProcessCommandList( commandList );
274 
275         nn::os::DataMemoryBarrier();
276 
277         m_RecvCommandQueue.Enqueue( msg );
278     }
279 }
280 
281 } // namespace nw::snd::internal
282 } // namespace nw::snd
283 } // namespace nw
284