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