|
1 /* |
|
2 * This component and the accompanying materials are made available |
|
3 * under the terms of the License "Eclipse Public License v1.0" |
|
4 * which accompanies this distribution, and is available |
|
5 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
6 * |
|
7 * Initial Contributors: |
|
8 * Accenture Ltd |
|
9 * |
|
10 * Contributors: |
|
11 * |
|
12 * Description: This file is a part of sound driver for Syborg adaptation. |
|
13 * |
|
14 */ |
|
15 |
|
16 #include "virtio_queue.h" |
|
17 |
|
18 #define ENABLE_QEMU_AUDIO_MODEL_BUG_WORKAROUND |
|
19 #define ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND |
|
20 #define PHYS_ADDR(x) Epoc::LinearToPhysical( reinterpret_cast<TUint32>(x)) |
|
21 |
|
22 namespace VirtIo |
|
23 { |
|
24 |
|
25 |
|
26 DQueue::DQueue( MIo& aVirtIo, TUint aId, TUint aCount ) |
|
27 : iVirtIo( aVirtIo ), iId( aId ), iCount( aCount ) |
|
28 { |
|
29 } |
|
30 |
|
31 TInt DQueue::Construct() |
|
32 { |
|
33 TInt r = AllocQueue(); |
|
34 if (r!=KErrNone) |
|
35 { return r; } |
|
36 PreInitQueue(); |
|
37 iVirtIo.SetQueueBase( iId, iDesc ); |
|
38 PostInitQueue(); |
|
39 return KErrNone; |
|
40 } |
|
41 |
|
42 void DQueue::Wipe() |
|
43 { |
|
44 if ( iChunk ) |
|
45 { |
|
46 iChunk->Close( NULL ); |
|
47 iChunk = NULL; |
|
48 } |
|
49 #ifndef ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND |
|
50 if ( iPhysAddr ) |
|
51 { Epoc::FreePhysicalRam( iPhysAddr ); } |
|
52 #endif |
|
53 iPhysAddr = 0; |
|
54 } |
|
55 |
|
56 DQueue::~DQueue() |
|
57 { |
|
58 Wipe(); |
|
59 } |
|
60 |
|
61 // |
|
62 // Implementation here is that descs are allocated from the one starting with lowest index, |
|
63 TInt DQueue::AddBuf( const TAddrLen aScatterList[], TUint32 aOutNum, TUint32 aInNum, Token aToken) |
|
64 { |
|
65 SYBORG_VIRTIO_DEBUG("AddBuf Q%x %x l%x %x,%x %x", |
|
66 iId, aScatterList[0].iAddr, aScatterList[0].iLen, aOutNum, aInNum, aToken ); |
|
67 TUint outLeft = aOutNum; |
|
68 TUint inLeft = aInNum; |
|
69 TInt first = -1; |
|
70 TInt last = -1; |
|
71 TUint totalLen = 0; |
|
72 // @TODO maintain freedescs counter to know if the below is going to success from the outset. |
|
73 // alloc and initialise descs |
|
74 for ( TUint i = 0, j = 0; (i < iCount) && (outLeft+inLeft); ++i ) |
|
75 { |
|
76 if (iDesc[i].iFlags == KFreeDescriptorMarker) |
|
77 { |
|
78 if (first<0) |
|
79 { first = i; } |
|
80 iDesc[i].iFlags = ((outLeft)? 0 : TRingDesc::EFlagWrite); |
|
81 iDesc[i].iNext = KFreeDescriptorMarker; |
|
82 iDesc[i].iAddr = aScatterList[j].iAddr; |
|
83 iDesc[i].iLen = aScatterList[j].iLen; |
|
84 totalLen += aScatterList[j].iLen; |
|
85 iToken[i].iToken = aToken; |
|
86 iToken[i].iTotalLen = 0; |
|
87 if ( last >=0 ) |
|
88 { |
|
89 iDesc[last].iNext = i; |
|
90 iDesc[last].iFlags |= TRingDesc::EFlagNext; |
|
91 } |
|
92 last = i; |
|
93 if (outLeft) |
|
94 { --outLeft; } |
|
95 else |
|
96 { --inLeft; } |
|
97 j++; |
|
98 } |
|
99 } |
|
100 if (outLeft+inLeft) // rollback descs if not all could have been claimed |
|
101 { |
|
102 if (first>=0) |
|
103 { FreeDescs(first); } |
|
104 return KErrNotReady; |
|
105 } |
|
106 iToken[first].iTotalLen = totalLen; |
|
107 |
|
108 // fill a slot in avail ring |
|
109 iAvail->iRing[Slot(iAvail->iIdx)] = first; |
|
110 ++iAvail->iIdx; |
|
111 |
|
112 return KErrNone; |
|
113 } |
|
114 |
|
115 // bases on the assumption that the lowest desc index with given aToken value is used (see addBuf) |
|
116 // @todo make sure the buffer is not yet posted |
|
117 TInt DQueue::DetachBuf( Token aToken ) |
|
118 { |
|
119 TUint myDescId = KFreeDescriptorMarker; |
|
120 for ( TIdx i = iNextAvailToSync; i != iAvail->iIdx ; ++i ) |
|
121 { |
|
122 TUint availSlot = Slot( i ); |
|
123 TUint descId = iAvail->iRing[availSlot]; |
|
124 if ( descId < iCount ) |
|
125 { |
|
126 if (iToken[descId].iToken == aToken) |
|
127 { |
|
128 myDescId = descId; |
|
129 break; |
|
130 } |
|
131 } |
|
132 } |
|
133 if ( myDescId != KFreeDescriptorMarker ) |
|
134 { return KErrNotFound; } |
|
135 FreeDescs( myDescId ); |
|
136 return KErrNone; |
|
137 } |
|
138 |
|
139 Token DQueue::GetBuf( TUint& aLen ) |
|
140 { |
|
141 TIdx usedIdx = iUsed->iIdx; |
|
142 ASSERT( ((TIdx)iNextUsedToRead) <= usedIdx ); |
|
143 if (usedIdx == ((TIdx)iNextUsedToRead)) |
|
144 { return 0; } |
|
145 TUint nextUsedSlot = Slot(iNextUsedToRead); |
|
146 TUint len = iUsed->iRing[nextUsedSlot].iLen; |
|
147 TUint descId = iUsed->iRing[nextUsedSlot].iId; |
|
148 ASSERT(descId<iCount); |
|
149 Token token = iToken[descId].iToken; |
|
150 TUint orderedLen = iToken[descId].iTotalLen; |
|
151 SYBORG_VIRTIO_DEBUG( "GetBuf Q%x %x ..%x t%x D%x L%x OL%x", iId, iNextUsedToRead, usedIdx, token, descId, len, orderedLen ); |
|
152 |
|
153 ++iNextUsedToRead; |
|
154 FreeDescs( descId ); |
|
155 |
|
156 #ifdef ENABLE_QEMU_AUDIO_MODEL_BUG_WORKAROUND |
|
157 aLen = len?len:orderedLen; // @TODO kind of a hack to solve virtio-audio's failure to report len by syborg on the side of qemu |
|
158 #endif |
|
159 return token; |
|
160 } |
|
161 |
|
162 TInt DQueue::Processing() |
|
163 { return ((TIdx)(iAvail->iIdx - iFirstEverToSync)) |
|
164 - ((TIdx)(iUsed->iIdx - iFirstEverToRead)); } |
|
165 |
|
166 TInt DQueue::Completed() |
|
167 { return ((TIdx)(iUsed->iIdx - iNextUsedToRead)); } |
|
168 |
|
169 void DQueue::Sync() |
|
170 { |
|
171 SYBORG_VIRTIO_DEBUG("Sync Q%d, %x..%x", |
|
172 iId, iNextAvailToSync, iAvail->iIdx ); |
|
173 if ( ((TIdx)iNextAvailToSync) == iAvail->iIdx) |
|
174 { return; } |
|
175 DumpAvailPending(); |
|
176 |
|
177 iNextAvailToSync = iAvail->iIdx; |
|
178 iVirtIo.PostQueue( iId ); |
|
179 } |
|
180 |
|
181 void DQueue::DumpUsedPending() |
|
182 { |
|
183 for (TIdx i = iNextUsedToRead; i != iUsed->iIdx; ++i ) |
|
184 { DumpUsed( Slot(i) ); } |
|
185 } |
|
186 |
|
187 void DQueue::DumpUsed(TUint usedSlot) |
|
188 { |
|
189 SYBORG_VIRTIO_DEBUG("Usedslot = %x, aLen = %x, descId=%x", usedSlot, (TUint32) iUsed->iRing[usedSlot].iLen, iUsed->iRing[usedSlot].iId); |
|
190 TUint descId = iUsed->iRing[usedSlot].iId; |
|
191 DumpDescs( descId ); |
|
192 } |
|
193 |
|
194 void DQueue::DumpAvailPending() |
|
195 { |
|
196 for (TIdx i = iNextAvailToSync; i != iAvail->iIdx; ++i ) |
|
197 { DumpAvail( Slot(i) ); } |
|
198 } |
|
199 |
|
200 void DQueue::DumpAvail(TUint availSlot) |
|
201 { |
|
202 SYBORG_VIRTIO_DEBUG("Q%d, availslot = %x", iId, availSlot); |
|
203 TUint descId = iAvail->iRing[availSlot]; |
|
204 DumpDescs( descId ); |
|
205 } |
|
206 |
|
207 void DQueue::DumpDescs(TUint descId ) |
|
208 { |
|
209 do { |
|
210 TRingDesc& d = iDesc[descId]; |
|
211 SYBORG_VIRTIO_DEBUG(" Desc %x,addr %x, len %x, flags %x, next %x", |
|
212 (TUint32)descId, (TUint32)d.iAddr, (TUint32)d.iLen, (TUint32)d.iFlags, (TUint32)d.iNext ); |
|
213 if ((d.iFlags&TRingDesc::EFlagNext)==0) |
|
214 { break; } |
|
215 descId = d.iNext; |
|
216 } while (ETrue); |
|
217 } |
|
218 |
|
219 void DQueue::DumpAll() |
|
220 { |
|
221 DumpAvailPending(); |
|
222 DumpUsedPending(); |
|
223 } |
|
224 |
|
225 void DQueue::FreeDescs( TUint firstDescIdx ) |
|
226 { |
|
227 TInt i = firstDescIdx; |
|
228 Token token = iToken[firstDescIdx].iToken; |
|
229 while (i>=0) |
|
230 { |
|
231 ASSERT( ( ((TUint)i) < iCount ) ); |
|
232 TUint flags = iDesc[i].iFlags; |
|
233 ASSERT( flags != KFreeDescriptorMarker ); |
|
234 iDesc[i].iFlags = KFreeDescriptorMarker; |
|
235 ASSERT( iToken[i].iToken == token ); |
|
236 iToken[i].iToken = 0; |
|
237 iToken[i].iTotalLen = 0; |
|
238 i = (flags&TRingDesc::EFlagNext) |
|
239 ? iDesc[i].iNext : -1; |
|
240 } |
|
241 } |
|
242 |
|
243 TUint8* DQueue::AllocMem( TUint aSize ) |
|
244 { |
|
245 TInt r = KErrNone; |
|
246 |
|
247 #ifdef ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND |
|
248 // note this is second part of workaround that deals |
|
249 // with the issue that memory once assigned to queuebase cannot be |
|
250 // changed, |
|
251 // if queuebase != 0 this is because it was set when the driver |
|
252 // was loaded previous time |
|
253 |
|
254 iPhysAddr = (TUint32) iVirtIo.GetQueueBase( iId ); |
|
255 iPhysMemReallyAllocated = (iPhysAddr == 0); |
|
256 if (iPhysMemReallyAllocated) |
|
257 { r = Epoc::AllocPhysicalRam(aSize, iPhysAddr, 0 ); } |
|
258 |
|
259 #endif |
|
260 |
|
261 if (r!=KErrNone ) |
|
262 { return NULL; } |
|
263 |
|
264 r = DPlatChunkHw::New( iChunk, iPhysAddr, aSize, |
|
265 EMapAttrSupRw | EMapAttrL2Uncached | EMapAttrL1Uncached ); |
|
266 |
|
267 if (r!=KErrNone ) |
|
268 { |
|
269 if ( iPhysMemReallyAllocated ) |
|
270 { Epoc::FreePhysicalRam( iPhysAddr, aSize ); } |
|
271 iChunk->Close( NULL ); |
|
272 return NULL; |
|
273 } |
|
274 |
|
275 ASSERT( r == KErrNone ); |
|
276 return reinterpret_cast<TUint8*>( iChunk->LinearAddress() ); |
|
277 } |
|
278 |
|
279 TInt DQueue::AllocQueue() |
|
280 { |
|
281 iDescSize = iCount * sizeof(TRingDesc); |
|
282 iAvailSize = sizeof(TRingAvail) + (iCount-1) * sizeof(((TRingAvail*)0)->iRing[0]); |
|
283 iTokenSize = iCount * sizeof(TTransactionInfo); |
|
284 TUint usedOffset = Align( iDescSize + iAvailSize, KVirtIoAlignment ); |
|
285 TUint iUsedSize = sizeof(TRingUsed) + (iCount-1) * sizeof(((TRingUsed*)0)->iRing[0]); |
|
286 TUint size = usedOffset + iUsedSize; |
|
287 TUint8* iMemAligned; |
|
288 |
|
289 iMemAligned = AllocMem( size ); |
|
290 |
|
291 if (!iMemAligned) |
|
292 { return KErrNoMemory; } |
|
293 |
|
294 iDesc = reinterpret_cast<TRingDesc*>( iMemAligned ); |
|
295 iAvail = reinterpret_cast<TRingAvail*>( iMemAligned + iDescSize ); |
|
296 iUsed = reinterpret_cast<TRingUsed*>( iMemAligned + usedOffset ); |
|
297 iToken = reinterpret_cast<TTransactionInfo*>( Kern::Alloc( iTokenSize ) ); |
|
298 SYBORG_VIRTIO_DEBUG("DQueue %d, Virt iDesc=%x,iAvail=%x,iToken=%x,iUsed=%x", |
|
299 iId, iDesc, iAvail, iToken, iUsed ); |
|
300 SYBORG_VIRTIO_DEBUG("DQueue %d, Phys iDesc=%x, iUsed=%x", |
|
301 iId, PHYS_ADDR(iDesc), PHYS_ADDR(iUsed) ); |
|
302 ASSERT( ((PHYS_ADDR(iUsed)-PHYS_ADDR(iDesc))) == ((TUint32)((TUint8*)iUsed-(TUint8*)iDesc)) ); |
|
303 return KErrNone; |
|
304 } |
|
305 |
|
306 void DQueue::PreInitQueue() |
|
307 { |
|
308 memset(iDesc, -1, iDescSize ); |
|
309 memset( ((TUint8*) iAvail) + 4, -1, iDescSize - 4 ); |
|
310 memset( ((TUint8*) iUsed) + 4, -1, iDescSize - 4 ); |
|
311 |
|
312 iAvail->iFlags = 0; // no notifications from control queue |
|
313 iUsed->iFlags = 0; |
|
314 if ( iPhysMemReallyAllocated ) |
|
315 { |
|
316 iAvail->iIdx = 0; |
|
317 iUsed->iIdx = 0; |
|
318 } |
|
319 } |
|
320 |
|
321 void DQueue::PostInitQueue() |
|
322 { |
|
323 iFirstEverToSync = iNextAvailToSync = iAvail->iIdx; |
|
324 iFirstEverToRead = iNextUsedToRead = iUsed->iIdx; |
|
325 } |
|
326 |
|
327 |
|
328 } // namespace VirtIo |