|
1 // Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // \e32\drivers\pbus\mmc\stack.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include <drivers/mmc.h> |
|
19 #include <kernel/kern_priv.h> |
|
20 #include <drivers/locmedia.h> |
|
21 #include "stackbody.h" |
|
22 |
|
23 #ifdef __SMP__ |
|
24 TSpinLock MMCLock(TSpinLock::EOrderGenericIrqHigh0); |
|
25 #endif |
|
26 |
|
27 #define ASSERT_NOT_ISR_CONTEXT __ASSERT_DEBUG(NKern::CurrentContext()!=NKern::EInterrupt,DMMCSocket::Panic(DMMCSocket::EMMCUnblockingInWrongContext)); |
|
28 |
|
29 #if !defined(__WINS__) |
|
30 #define DISABLEPREEMPTION TUint irq = __SPIN_LOCK_IRQSAVE(MMCLock); |
|
31 #define RESTOREPREEMPTION __SPIN_UNLOCK_IRQRESTORE(MMCLock,irq); |
|
32 #else |
|
33 #define DISABLEPREEMPTION |
|
34 #define RESTOREPREEMPTION |
|
35 #endif |
|
36 |
|
37 //#define ENABLE_DETAILED_SD_COMMAND_TRACE |
|
38 |
|
39 // default length of minor buffer - must have at least enough space for one sector |
|
40 const TInt KMinMinorBufSize = 512; |
|
41 |
|
42 // MultiMedia Card Controller - Generic level code for controller, intermediate |
|
43 // level code for media change and power supply handling |
|
44 |
|
45 EXPORT_C TUint TCSD::CSDField(const TUint& aTopBit, const TUint& aBottomBit) const |
|
46 /** |
|
47 * Extract bitfield from CSD |
|
48 */ |
|
49 { |
|
50 const TUint indexT=KMMCCSDLength-1-aTopBit/8; |
|
51 const TUint indexB=KMMCCSDLength-1-aBottomBit/8; |
|
52 return(((indexT==indexB ? iData[indexT] |
|
53 : (indexT+1)==indexB ? ((iData[indexT]<<8) | iData[indexT+1]) |
|
54 : ((iData[indexT]<<16) | (iData[indexT+1]<<8) | iData[indexT+2]) |
|
55 ) >> (aBottomBit&7)) & ((1<<(aTopBit-aBottomBit+1))-1)); |
|
56 } |
|
57 |
|
58 |
|
59 // -------- class TCSD -------- |
|
60 // Raw field accessor functions are defined in mmc.inl. These functions return |
|
61 // values that require extra computation, such as memory capacity. |
|
62 |
|
63 |
|
64 EXPORT_C TUint TCSD::DeviceSize() const |
|
65 /** |
|
66 * |
|
67 * Calculate device capacity from CSD |
|
68 * |
|
69 * Section 5.3, MMCA Spec 2.2 (Jan 2000) |
|
70 * |
|
71 * memory capacity = BLOCKNR * BLOCK_LEN |
|
72 * where |
|
73 * BLOCKNR = (C_SIZE + 1) * MULT; MULT = 2 ** (C_MULT_SIZE + 2); |
|
74 * BLOCK_LEN = 2 ** (READ_BL_LEN) |
|
75 * |
|
76 * memory capacity = (C_SIZE + 1) * (2 ** (C_MULT_SIZE + 2)) * (2 ** READ_BL_LEN) |
|
77 * = (C_SIZE + 1) * (2 ** (C_MULT_SIZE + 2 + READ_BL_LEN)) |
|
78 * |
|
79 * @return Device Capacity |
|
80 */ |
|
81 { |
|
82 __KTRACE_OPT(KPBUS1, Kern::Printf("csd:ds:0x%x,0x%x,0x%x", ReadBlLen(), CSize(), CSizeMult())); |
|
83 |
|
84 const TUint blockLog = ReadBlLen(); |
|
85 if( blockLog > 11 ) |
|
86 return( 0 ); |
|
87 |
|
88 const TUint size = (CSize() + 1) << (2 + CSizeMult() + blockLog); |
|
89 |
|
90 if( size == 0 ) |
|
91 return( 0xFFF00000 ); |
|
92 |
|
93 return( size ); |
|
94 } |
|
95 |
|
96 EXPORT_C TMMCMediaTypeEnum TCSD::MediaType() const |
|
97 /** |
|
98 * This function makes a rough approximation if media type based on supported |
|
99 * command classes (CCC). |
|
100 * |
|
101 * @return TMMCMediaTypeEnum describing the type of media. |
|
102 */ |
|
103 { |
|
104 struct mediaTableEntry |
|
105 { |
|
106 TUint iMask; |
|
107 TUint iValue; |
|
108 TMMCMediaTypeEnum iMedia; |
|
109 }; |
|
110 |
|
111 const TUint testMask = (KMMCCmdClassBlockRead|KMMCCmdClassBlockWrite|KMMCCmdClassErase|KMMCCmdClassIOMode); |
|
112 static const mediaTableEntry mediaTable[] = |
|
113 { |
|
114 {KMMCCmdClassBasic, 0, EMultiMediaNotSupported}, |
|
115 {testMask, (KMMCCmdClassBlockRead|KMMCCmdClassBlockWrite|KMMCCmdClassErase), EMultiMediaFlash}, |
|
116 {testMask, KMMCCmdClassBlockRead|KMMCCmdClassBlockWrite, EMultiMediaFlash}, |
|
117 {testMask, KMMCCmdClassBlockRead|KMMCCmdClassErase, EMultiMediaROM}, |
|
118 {testMask, KMMCCmdClassBlockRead, EMultiMediaROM}, |
|
119 {KMMCCmdClassIOMode,KMMCCmdClassIOMode, EMultiMediaIO}, |
|
120 {0, 0, EMultiMediaOther} |
|
121 }; |
|
122 |
|
123 const TUint ccc = CCC(); |
|
124 const mediaTableEntry* ptr = mediaTable; |
|
125 |
|
126 while( (ccc & ptr->iMask) != (ptr->iValue) ) |
|
127 ptr++; |
|
128 |
|
129 if (ptr->iMedia == EMultiMediaFlash) |
|
130 { |
|
131 // Further check PERM_WRITE_PROTECT and TMP_WRITE_PROTECT bits |
|
132 if (PermWriteProtect() || TmpWriteProtect()) |
|
133 return EMultiMediaROM; |
|
134 } |
|
135 |
|
136 return( ptr->iMedia ); |
|
137 } |
|
138 |
|
139 EXPORT_C TUint TCSD::ReadBlockLength() const |
|
140 /** |
|
141 * Calculates the read block length from the CSD. |
|
142 * READ_BL_LEN is encoded as a logarithm. |
|
143 * |
|
144 * @return The read block length |
|
145 */ |
|
146 { |
|
147 const TUint blockLog = ReadBlLen(); |
|
148 |
|
149 //SD version 2.0 or less the range is 0-11 |
|
150 //MMC version 4.1 or less the range is 0-11 |
|
151 //MMC version 4.2 the range is 0-14 (15 is reserved for future use) |
|
152 //But we cannot differentiate among 4.x |
|
153 //Hence , 0-14 is supported for 4.x |
|
154 if (SpecVers() < 4) |
|
155 { |
|
156 if( blockLog > 11 ) |
|
157 return( 0 ); |
|
158 } |
|
159 else |
|
160 { |
|
161 if(blockLog > 14) |
|
162 return ( 0 ); |
|
163 } |
|
164 |
|
165 return( 1 << blockLog ); |
|
166 } |
|
167 |
|
168 EXPORT_C TUint TCSD::WriteBlockLength() const |
|
169 /** |
|
170 * Calculates the write block length from the CSD. |
|
171 * WRITE_BL_LEN is encoded as a logarithm. |
|
172 * |
|
173 * @return The write block length |
|
174 */ |
|
175 { |
|
176 const TUint blockLog = WriteBlLen(); |
|
177 if( blockLog > 11 ) |
|
178 return( 0 ); |
|
179 |
|
180 return( 1 << blockLog ); |
|
181 } |
|
182 |
|
183 EXPORT_C TUint TCSD::EraseSectorSize() const |
|
184 /** |
|
185 * Calculates the erase sector size from the CSD. |
|
186 * SECTOR_SIZE is a 5 bit value, which is one less than the number of write |
|
187 * |
|
188 * @return The erase sector size |
|
189 */ |
|
190 { |
|
191 if (SpecVers() < 3) |
|
192 { |
|
193 // V2.2 and earlier supports erase sectors. Read sector size from CSD(46:42) - confusingly now reassigned as |
|
194 // erase group size. |
|
195 return( (EraseGrpSize()+1) * WriteBlockLength() ); |
|
196 } |
|
197 else |
|
198 { |
|
199 // Support for erase sectors removed from V3.1 onwards |
|
200 return(0); |
|
201 } |
|
202 |
|
203 } |
|
204 |
|
205 EXPORT_C TUint TCSD::EraseGroupSize() const |
|
206 /** |
|
207 * Calculates the erase group size from the CSD. |
|
208 * ERASE_GRP_SIZE is a 5 bit value, which is one less than the number of erase |
|
209 * sectors in an erase group. |
|
210 * |
|
211 * @return The erase group size |
|
212 */ |
|
213 { |
|
214 if (SpecVers() < 3) |
|
215 { |
|
216 // For V2.2 and earlier, the erase group size is held in CSD(41:37) - confusingly now reassigned as the erase |
|
217 // group multiplier. The units for this are erase sectors, so need to convert to write blocks and then bytes. |
|
218 TUint erSecSizeInBytes=(EraseGrpSize()+1) * WriteBlockLength(); |
|
219 return( (EraseGrpMult()+1) * erSecSizeInBytes ); |
|
220 } |
|
221 else |
|
222 { |
|
223 // For V3.1 onwards, the erase group size is determined by multiplying the erase group size - CSD(41:37) by the |
|
224 // erase group multiplier - CSD(46:42)). The units for this are write blocks, so need to convert to bytes. |
|
225 TUint erGrpSizeInWrBlk = (EraseGrpSize()+1) * (EraseGrpMult()+1); |
|
226 return(erGrpSizeInWrBlk * WriteBlockLength()); |
|
227 } |
|
228 } |
|
229 |
|
230 EXPORT_C TUint TCSD::MinReadCurrentInMilliamps() const |
|
231 /** |
|
232 * Calculates the minimum read current from the CSD. |
|
233 * VDD_R_CURR_MIN is a three bit value which is mapped to a number of mA. |
|
234 * 0 actually maps to 0.5mA, but has been rounded up. |
|
235 * |
|
236 * @return The minimum read current, in Milliamps |
|
237 */ |
|
238 { |
|
239 static const TUint8 minConsumptionTable[] = {1,1,5,10,25,35,60,100}; |
|
240 return( minConsumptionTable[VDDRCurrMin()] ); |
|
241 } |
|
242 |
|
243 EXPORT_C TUint TCSD::MinWriteCurrentInMilliamps() const |
|
244 /** |
|
245 * Calculates the minimum write current from the CSD. |
|
246 * VDD_W_CURR_MIN is a three bit value which is mapped to a number of mA. |
|
247 * |
|
248 * @return The minimum write current, in Milliamps |
|
249 */ |
|
250 { |
|
251 static const TUint8 minConsumptionTable[] = {1,1,5,10,25,35,60,100}; |
|
252 return( minConsumptionTable[VDDWCurrMin()] ); |
|
253 } |
|
254 |
|
255 EXPORT_C TUint TCSD::MaxReadCurrentInMilliamps() const |
|
256 /** |
|
257 * Calculates the maximum read current from the CSD. |
|
258 * VDD_R_CURR_MAX is a three bit value which is mapped to a number of mA. |
|
259 * 0 actually maps to 0.5mA, but has been rounded up. |
|
260 * |
|
261 * @return The maximum read current, in Milliamps |
|
262 */ |
|
263 { |
|
264 static const TUint8 maxConsumptionTable[] = {1,5,10,25,35,45,80,200}; |
|
265 return( maxConsumptionTable[VDDRCurrMax()] ); |
|
266 } |
|
267 |
|
268 EXPORT_C TUint TCSD::MaxWriteCurrentInMilliamps() const |
|
269 /** |
|
270 * Calculates the maximum write current from the CSD. |
|
271 * VDD_W_CURR_MAX is a three bit value which is mapped to a number of mA. |
|
272 * |
|
273 * @return The maximum write current, in Milliamps |
|
274 */ |
|
275 { |
|
276 static const TUint8 maxConsumptionTable[] = {1,5,10,25,35,45,80,200}; |
|
277 return( maxConsumptionTable[VDDWCurrMax()] ); |
|
278 } |
|
279 |
|
280 EXPORT_C TUint TCSD::MaxTranSpeedInKilohertz() const |
|
281 /** |
|
282 * TRAN_SPEED is an eight bit value which encodes three fields. |
|
283 * Section 5.3, MMCA Spec 2.2 (Jan 2000) |
|
284 * |
|
285 * 2:0 transfer rate unit values 4 to 7 are reserved. |
|
286 * 6:3 time value |
|
287 * |
|
288 * @return Speed, in Kilohertz |
|
289 */ |
|
290 { |
|
291 // tranRateUnits entries are all divided by ten so tranRateValues can be integers |
|
292 static const TUint tranRateUnits[8] = {10,100,1000,10000,10,10,10,10}; |
|
293 static const TUint8 tranRateValues[16] = {10,10,12,13,15,20,25,30,35,40,45,50,55,60,70,80}; |
|
294 const TUint ts = TranSpeed(); |
|
295 return( tranRateUnits[ts&7] * tranRateValues[(ts>>3)&0xF] ); |
|
296 } |
|
297 |
|
298 // -------- class TMMCard -------- |
|
299 |
|
300 TMMCard::TMMCard() |
|
301 : iIndex(0), iUsingSessionP(0), iFlags(0), iBusWidth(1) |
|
302 { |
|
303 // empty. |
|
304 } |
|
305 |
|
306 EXPORT_C TBool TMMCard::IsReady() const |
|
307 /** |
|
308 * Predicate for if card is mounted and in standby/transfer/sleep state. |
|
309 * |
|
310 * @return ETrue if ready, EFalse otherwise. |
|
311 */ |
|
312 { |
|
313 const TUint state = iStatus.State(); |
|
314 __KTRACE_OPT(KPBUS1, Kern::Printf("=mcc:ir:%d,0x%08x", IsPresent(), state)); |
|
315 return IsPresent() && (state == ECardStateStby || state == ECardStateTran || state == ECardStateSlp); |
|
316 } |
|
317 |
|
318 EXPORT_C TBool TMMCard::IsLocked() const |
|
319 /** |
|
320 * Predicate for if card is locked |
|
321 * |
|
322 * It would be useful to check if the CSD supports the password protection |
|
323 * feature. Password protection was introduced in c3.1, 05/99 and SPEC_VERS |
|
324 * is encoded 0 |-> 1.0 - 1.2, 1 |-> 1.4, 3 |-> 2.2. Some cards support |
|
325 * password locking but their CSD reports SPEC_VERS == 1. |
|
326 * |
|
327 * @return ETrue if locked, EFalse otherwise. |
|
328 */ |
|
329 { |
|
330 if ( !IsPresent() ) |
|
331 return( EFalse ); |
|
332 |
|
333 return( (TUint32(iStatus) & KMMCStatCardIsLocked) != 0 ); |
|
334 } |
|
335 |
|
336 TInt64 TMMCard::DeviceSize64() const |
|
337 /** |
|
338 * Returns the size of the MMC card in bytes |
|
339 * @return The size of the MMC card in bytes. |
|
340 */ |
|
341 { |
|
342 const TBool highCapacity = IsHighCapacity(); |
|
343 const TUint32 sectorCount = ExtendedCSD().SectorCount(); |
|
344 |
|
345 return ((highCapacity && sectorCount) ? (((TInt64)ExtendedCSD().SectorCount()) * 512) : (TInt64)CSD().DeviceSize()); |
|
346 } |
|
347 |
|
348 TUint32 TMMCard::PreferredWriteGroupLength() const |
|
349 /** |
|
350 * Returns the write group length. Provided by the variant. |
|
351 * Default implementation returns a multiple of the write block length, as indicated by the CSD. |
|
352 * @return The preferred write group length. |
|
353 */ |
|
354 { |
|
355 return(CSD().WriteBlockLength() << 5); // 16K for a standard 512byte block length |
|
356 } |
|
357 |
|
358 TInt TMMCard::GetFormatInfo(TLDFormatInfo& /*aFormatInfo*/) const |
|
359 /** |
|
360 * Returns the preferred format parametersm for the partition. |
|
361 * Implemented at the Variant layer. |
|
362 * @return Standard Symbian OS error code. |
|
363 */ |
|
364 { |
|
365 return KErrNotSupported; |
|
366 } |
|
367 |
|
368 TUint32 TMMCard::MinEraseSectorSize() const |
|
369 /** |
|
370 * Returns the minimum erase sector size. Provided by the variant. |
|
371 * Default implementation returns the erase sector size, as indicated by the CSD. |
|
372 * @return The minimum erase sector size. |
|
373 */ |
|
374 { |
|
375 return CSD().EraseSectorSize(); |
|
376 } |
|
377 |
|
378 TUint32 TMMCard::EraseSectorSize() const |
|
379 /** |
|
380 * Returns the recommended erase sector size. Provided by the variant. |
|
381 * Default implementation returns the erase sector size, as indicated by the CSD. |
|
382 * @return The recommended erase sector size. |
|
383 */ |
|
384 { |
|
385 return CSD().EraseSectorSize(); |
|
386 } |
|
387 |
|
388 LOCAL_C TBool IsPowerOfTwo(TInt aNum) |
|
389 // |
|
390 // Returns ETrue if aNum is a power of two |
|
391 // |
|
392 { |
|
393 return (aNum != 0 && (aNum & -aNum) == aNum); |
|
394 } |
|
395 |
|
396 TInt TMMCard::GetEraseInfo(TMMCEraseInfo& aEraseInfo) const |
|
397 /** |
|
398 * Return info. on erase services for this card |
|
399 * @param aEraseInfo A reference to the TMMCEraseInfo to be filled in with the erase information. |
|
400 * @return Symbian OS error code. |
|
401 */ |
|
402 { |
|
403 |
|
404 // Check whether this card supports Erase Class Commands. Also, validate the erase group size |
|
405 if ((CSD().CCC() & KMMCCmdClassErase) && IsPowerOfTwo(CSD().EraseGroupSize())) |
|
406 { |
|
407 // This card supports erase cmds. Also, all versions of MMC cards support Erase Group commands (i.e. CMD35, CMD36). |
|
408 aEraseInfo.iEraseFlags=(KMMCEraseClassCmdsSupported|KMMCEraseGroupCmdsSupported); |
|
409 |
|
410 // Return the preferred size to be used as the unit for format operations. We need to return a sensible |
|
411 // multiple of the erase group size - as calculated by the CSD. A value around 1/32th of the total disk |
|
412 // size generally results in an appropriate number of individual format calls. |
|
413 const TInt64 devSizeDividedBy32=(DeviceSize64()>>5); |
|
414 aEraseInfo.iPreferredEraseUnitSize=CSD().EraseGroupSize(); |
|
415 while (aEraseInfo.iPreferredEraseUnitSize < devSizeDividedBy32) |
|
416 aEraseInfo.iPreferredEraseUnitSize<<=1; |
|
417 |
|
418 // Return the smallest size that can be used as the unit for erase operations. For erase group commands, this |
|
419 // is the erase group size. |
|
420 aEraseInfo.iMinEraseSectorSize=CSD().EraseGroupSize(); |
|
421 } |
|
422 else |
|
423 aEraseInfo.iEraseFlags=0; |
|
424 |
|
425 return(KErrNone); |
|
426 } |
|
427 |
|
428 TUint TMMCard::MaxTranSpeedInKilohertz() const |
|
429 /** |
|
430 * Returns the maximum supported clock rate for the card, in Kilohertz. |
|
431 * @return Speed, in Kilohertz |
|
432 */ |
|
433 { |
|
434 // Default implementation obtains the transaction speed from the CSD |
|
435 TUint32 highSpeedClock = HighSpeedClock(); |
|
436 return(highSpeedClock ? highSpeedClock : iCSD.MaxTranSpeedInKilohertz()); |
|
437 } |
|
438 |
|
439 |
|
440 TInt TMMCard::MaxReadBlLen() const |
|
441 /** |
|
442 * Returns the maximum read block length supported by the card encoded as a logarithm |
|
443 * Normally this is the same as the READ_BL_LEN field in the CSD register, |
|
444 * but for high capacity cards (>- 2GB) this is set to a maximum of 512 bytes, |
|
445 * if possible, to try to avoid compatibility issues. |
|
446 */ |
|
447 { |
|
448 const TInt KDefaultReadBlockLen = 9; // 2^9 = 512 bytes |
|
449 const TCSD& csd = CSD(); |
|
450 |
|
451 TInt blkLenLog2 = csd.ReadBlLen(); |
|
452 |
|
453 if (blkLenLog2 > KDefaultReadBlockLen) |
|
454 { |
|
455 __KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl %d", blkLenLog2)); |
|
456 if (csd.ReadBlPartial() || CSD().SpecVers() >= 4) |
|
457 { |
|
458 // |
|
459 // MMC System Spec 4.2 states that 512 bytes blocks are always supported, |
|
460 // regardless of the state of READ_BL_PARTIAL |
|
461 // |
|
462 blkLenLog2 = KDefaultReadBlockLen; |
|
463 __KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl -> %d", blkLenLog2)); |
|
464 } |
|
465 } |
|
466 |
|
467 return blkLenLog2; |
|
468 |
|
469 } |
|
470 |
|
471 TInt TMMCard::MaxWriteBlLen() const |
|
472 /** |
|
473 * Returns the maximum write block length supported by the card encoded as a logarithm |
|
474 * Normally this is the same as the WRITE_BL_LEN field in the CSD register, |
|
475 * but for high capacity cards (>- 2GB) this is set to a maximum of 512 bytes, |
|
476 * if possible, to try to avoid compatibility issues. |
|
477 */ |
|
478 { |
|
479 const TInt KDefaultWriteBlockLen = 9; // 2^9 = 512 bytes |
|
480 const TCSD& csd = CSD(); |
|
481 |
|
482 TInt blkLenLog2 = csd.WriteBlLen(); |
|
483 |
|
484 if (blkLenLog2 > KDefaultWriteBlockLen) |
|
485 { |
|
486 __KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl %d", blkLenLog2)); |
|
487 if (csd.WriteBlPartial() || CSD().SpecVers() >= 4) |
|
488 { |
|
489 // |
|
490 // MMC System Spec 4.2 states that 512 bytes blocks are always supported, |
|
491 // regardless of the state of READ_BL_PARTIAL |
|
492 // |
|
493 blkLenLog2 = KDefaultWriteBlockLen; |
|
494 __KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl -> %d", blkLenLog2)); |
|
495 } |
|
496 } |
|
497 |
|
498 return blkLenLog2; |
|
499 |
|
500 } |
|
501 |
|
502 // -------- class TMMCardArray -------- |
|
503 |
|
504 EXPORT_C TInt TMMCardArray::AllocCards() |
|
505 /** |
|
506 * Allocate TMMCard objects for iCards and iNewCardsArray. |
|
507 * This function is called at bootup as part of stack allocation so there |
|
508 * is no cleanup if it fails. |
|
509 * |
|
510 * @return KErrNone if successful, Standard Symbian OS error code otherwise. |
|
511 */ |
|
512 { |
|
513 for (TUint i = 0; i < KMaxMMCardsPerStack; ++i) |
|
514 { |
|
515 // zeroing the card data used to be implicit because embedded in |
|
516 // CBase-derived DMMCStack. |
|
517 if ((iCards[i] = new TMMCard) == 0) |
|
518 return KErrNoMemory; |
|
519 iCards[i]->iUsingSessionP = 0; |
|
520 if ((iNewCards[i] = new TMMCard) == 0) |
|
521 return KErrNoMemory; |
|
522 iNewCards[i]->iUsingSessionP = 0; |
|
523 } |
|
524 |
|
525 return KErrNone; |
|
526 } |
|
527 |
|
528 void TMMCardArray::InitNewCardScan() |
|
529 /** |
|
530 * Prepare card array for new scan. |
|
531 */ |
|
532 { |
|
533 iNewCardsCount=0; |
|
534 } |
|
535 |
|
536 void TMMCardArray::MoveCardAndLockRCA(TMMCard& aSrcCard,TMMCard& aDestCard,TInt aDestIndex) |
|
537 /** |
|
538 * Copy card object and lock RCA. |
|
539 */ |
|
540 { |
|
541 __KTRACE_OPT(KPBUS1, Kern::Printf("=mca:mclr:%d", aDestIndex)); |
|
542 |
|
543 aDestCard.iCID=aSrcCard.iCID; |
|
544 aDestCard.iRCA=aSrcCard.iRCA; |
|
545 aDestCard.iCSD=aSrcCard.iCSD; |
|
546 aDestCard.iIndex=aDestIndex; // Mark card as being present |
|
547 aDestCard.iFlags=aSrcCard.iFlags; |
|
548 aDestCard.iBusWidth=aSrcCard.iBusWidth; |
|
549 aDestCard.iHighSpeedClock = aSrcCard.iHighSpeedClock; |
|
550 |
|
551 iOwningStack->iRCAPool.LockRCA(aDestCard.iRCA); |
|
552 |
|
553 // Now that we have transferred ownership, reset the source card |
|
554 aSrcCard.iRCA = aSrcCard.iIndex = aSrcCard.iFlags = 0; |
|
555 aSrcCard.iBusWidth = 1; |
|
556 aSrcCard.iHighSpeedClock = 0; |
|
557 |
|
558 aSrcCard.iUsingSessionP = NULL; |
|
559 } |
|
560 |
|
561 EXPORT_C void TMMCardArray::AddNewCard(const TUint8* aCID,TRCA* aNewRCA) |
|
562 /** |
|
563 * Found a new card to add to the array. Add it to a separate array for now |
|
564 * since we need to know all the cards present before we start replacing old |
|
565 * entries. |
|
566 */ |
|
567 { |
|
568 // Store the CID in the next free slot |
|
569 NewCard(iNewCardsCount).iCID = aCID; |
|
570 |
|
571 *aNewRCA=0; |
|
572 |
|
573 // Now let's look if we've seen this card before |
|
574 for ( TUint i=0 ; i<iOwningStack->iMaxCardsInStack ; i++ ) |
|
575 { |
|
576 if ( Card(i).iCID==NewCard(iNewCardsCount).iCID ) |
|
577 { |
|
578 *aNewRCA=Card(i).iRCA; |
|
579 NewCard(iNewCardsCount).iIndex=(i+1); |
|
580 break; |
|
581 } |
|
582 } |
|
583 |
|
584 if ( *aNewRCA==0 ) |
|
585 { |
|
586 // Not seen this one before so get a new RCA for the card |
|
587 NewCard(iNewCardsCount).iIndex=0; |
|
588 __ASSERT_ALWAYS( (*aNewRCA=iOwningStack->iRCAPool.GetFreeRCA())!=0,DMMCSocket::Panic(DMMCSocket::EMMCNoFreeRCA) ); |
|
589 } |
|
590 |
|
591 __KTRACE_OPT(KPBUS1, Kern::Printf("mca:adn: assigning new card %d rca 0x%04x", iNewCardsCount, TUint16(*aNewRCA) )); |
|
592 NewCard(iNewCardsCount).iRCA=*aNewRCA; |
|
593 iNewCardsCount++; |
|
594 } |
|
595 |
|
596 TInt TMMCardArray::MergeCards(TBool aFirstPass) |
|
597 /** |
|
598 * This function places newly acquired cards from the new card array into free |
|
599 * slots of the main card array. |
|
600 * Returns KErrNotFound if not able to successfully place all the new cards. |
|
601 */ |
|
602 { |
|
603 |
|
604 __KTRACE_OPT(KPBUS1,Kern::Printf("=mca:mc:%d,%d", aFirstPass, iNewCardsCount)); |
|
605 TUint i; // New card index |
|
606 TUint j; // Main card index |
|
607 |
|
608 // Only do this on first pass. Setup any new cards which were already there |
|
609 if (aFirstPass) |
|
610 { |
|
611 for ( i=0 ; i<iNewCardsCount ; i++ ) |
|
612 { |
|
613 __KTRACE_OPT(KPBUS1, Kern::Printf("-mca:fp,i=%d,idx=0x%x", i, NewCard(i).iIndex)); |
|
614 if( NewCard(i).iIndex != 0 ) // Signifies card was here before (iIndex has old slot number +1) |
|
615 { |
|
616 // Put it in the same slot as before |
|
617 j=(NewCard(i).iIndex-1); |
|
618 MoveCardAndLockRCA(NewCard(i),Card(j),(j+1)); |
|
619 } |
|
620 } |
|
621 } |
|
622 |
|
623 for ( i=0,j=0 ; i<iNewCardsCount ; i++ ) |
|
624 { |
|
625 __KTRACE_OPT(KPBUS1, Kern::Printf("-mca:i=%d,j=%d,rca=0x%4x", i, j, TUint16(NewCard(i).iRCA) )); |
|
626 if ( NewCard(i).iRCA != 0 ) |
|
627 { |
|
628 // Find a spare slot in main array for this new card |
|
629 while ( Card(j).IsPresent() ) |
|
630 if ( ++j==iOwningStack->iMaxCardsInStack ) |
|
631 return(KErrNotFound); |
|
632 |
|
633 // Found a free slot; move the card info there |
|
634 __KTRACE_OPT(KPBUS1, Kern::Printf("-mca:freej=%d,rca=0x%04x", j, TUint16(Card(j).iRCA) )); |
|
635 if ( Card(j).iRCA != 0 ) |
|
636 iOwningStack->iRCAPool.UnlockRCA(Card(j).iRCA); |
|
637 |
|
638 __KTRACE_OPT(KPBUS1, Kern::Printf("merging new card %d to card %d dest index %d", i, j, j+1)); |
|
639 MoveCardAndLockRCA(NewCard(i),Card(j),(j+1)); |
|
640 } |
|
641 } |
|
642 return(KErrNone); |
|
643 } |
|
644 |
|
645 void TMMCardArray::UpdateAcquisitions(TUint* aMaxClock) |
|
646 /** |
|
647 * Called when we have successfully stored a new set of cards in the card array. |
|
648 * This performs final initialisation of the card entries and determines the |
|
649 * maximum bus clock that can be employed - by checking the CSD of each card. |
|
650 */ |
|
651 { |
|
652 |
|
653 __KTRACE_OPT(KPBUS1,Kern::Printf("=mca:uda")); |
|
654 iCardsPresent=0; |
|
655 TUint maxClk = iOwningStack->iMultiplexedBus ? 1 : 800000; // ??? |
|
656 for ( TUint i=0 ; i < (iOwningStack->iMaxCardsInStack) ; i++ ) |
|
657 { |
|
658 if ( Card(i).IsPresent() ) |
|
659 { |
|
660 // General initialisation |
|
661 iCardsPresent++; |
|
662 Card(i).iSetBlockLen=0; |
|
663 Card(i).iLastCommand=ECmdSendStatus; |
|
664 |
|
665 // Check each card present to determine appropriate bus clock |
|
666 TUint maxTS = iOwningStack->MaxTranSpeedInKilohertz(Card(i)); |
|
667 if(iOwningStack->iMultiplexedBus) |
|
668 { |
|
669 if ( maxTS > maxClk ) |
|
670 maxClk = maxTS; |
|
671 } |
|
672 else |
|
673 { |
|
674 if ( maxTS < maxClk ) |
|
675 maxClk = maxTS; |
|
676 } |
|
677 } |
|
678 } |
|
679 // ??? Should also calculate here and return the data timeout and busy timeout |
|
680 // instead of relying on ASSP defaults. |
|
681 |
|
682 *aMaxClock=maxClk; |
|
683 } |
|
684 |
|
685 EXPORT_C void TMMCardArray::DeclareCardAsGone(TUint aCardNumber) |
|
686 /** |
|
687 * Clears up a card info object in the main card array |
|
688 */ |
|
689 { |
|
690 |
|
691 __KTRACE_OPT(KPBUS1,Kern::Printf("=mca:dcag")); |
|
692 // If we thought this one was present then mark it as not present |
|
693 TMMCard& card = Card(aCardNumber); |
|
694 if (card.IsPresent()) |
|
695 { |
|
696 card.iIndex=0; // Mark card as not present |
|
697 iCardsPresent--; |
|
698 } |
|
699 |
|
700 // If this card is in use by a session then flag that card has now gone |
|
701 if( card.iUsingSessionP != NULL ) |
|
702 card.iUsingSessionP->iState |= KMMCSessStateCardIsGone; |
|
703 |
|
704 card.iUsingSessionP=NULL; |
|
705 card.iSetBlockLen=0; |
|
706 card.iFlags=0; // Reset 'has password' and 'write protected' bit fields |
|
707 card.iHighSpeedClock=0; |
|
708 card.iBusWidth=1; |
|
709 } |
|
710 |
|
711 // return this card's index in the array or KErrNotFound if not found |
|
712 TInt TMMCardArray::CardIndex(const TMMCard* aCard) |
|
713 { |
|
714 TInt i; |
|
715 for (i = KMaxMMCardsPerStack-1; i>= 0; i--) |
|
716 { |
|
717 if (iCards[i] == aCard) |
|
718 break; |
|
719 } |
|
720 return i; |
|
721 } |
|
722 |
|
723 // -------- class TMMCCommandDesc -------- |
|
724 |
|
725 EXPORT_C TInt TMMCCommandDesc::Direction() const |
|
726 /** |
|
727 * returns -1, 0 or +1 for DT directions read, none or write respectively |
|
728 */ |
|
729 { |
|
730 TUint dir = iSpec.iDirection; |
|
731 TInt result = dir; |
|
732 |
|
733 if( dir == 0 ) |
|
734 return( 0 ); |
|
735 |
|
736 if( dir & KMMCCmdDirWBitArgument ) |
|
737 result = TUint(iArgument) >> (dir & KMMCCmdDirIndBitPosition); |
|
738 |
|
739 if( dir & KMMCCmdDirNegate ) |
|
740 result = ~result; |
|
741 |
|
742 return( ((result&1)-1)|1 ); |
|
743 } |
|
744 |
|
745 |
|
746 TBool TMMCCommandDesc::AdjustForBlockOrByteAccess(const DMMCSession& aSession) |
|
747 { |
|
748 /** |
|
749 * The MMC session provides both block and byte based IO methods, all of which can |
|
750 * be used on both block and byte based MMC cards. This method adjusts the command |
|
751 * arguments so that they match the underlying cards access mode. |
|
752 * |
|
753 * @return ETrue if the address is valid or successfully converted, EFalse otherwise |
|
754 */ |
|
755 TUint32 blockLength = BlockLength(); |
|
756 |
|
757 if(iTotalLength == 0 || |
|
758 blockLength == 0 || |
|
759 iTotalLength % KMMCardHighCapBlockSize != 0 || // always aligned on 512 bytes |
|
760 blockLength % KMMCardHighCapBlockSize != 0) |
|
761 { |
|
762 return(EFalse); |
|
763 } |
|
764 |
|
765 if(aSession.CardP()->IsHighCapacity()) |
|
766 { |
|
767 // high capacity (block-based) card |
|
768 if((iFlags & KMMCCmdFlagBlockAddress) == 0) |
|
769 { |
|
770 // The command arguments are using byte based addressing |
|
771 // - adjust to block-based addressing |
|
772 if(iArgument % KMMCardHighCapBlockSize != 0) |
|
773 { |
|
774 // Block based media does not support misaligned access |
|
775 return(EFalse); |
|
776 } |
|
777 |
|
778 // adjust for block based access |
|
779 iArgument = iArgument >> KMMCardHighCapBlockSizeLog2; |
|
780 iFlags |= KMMCCmdFlagBlockAddress; |
|
781 } |
|
782 } |
|
783 else |
|
784 { |
|
785 // standard (byte based) card |
|
786 if((iFlags & KMMCCmdFlagBlockAddress) != 0) |
|
787 { |
|
788 // The command arguments are using block based addressing |
|
789 // - adjust to byte-based addressing |
|
790 const TUint32 maxBlocks = 4 * 1024 * ((1024 * 1024) >> KMMCardHighCapBlockSizeLog2); |
|
791 |
|
792 if(iArgument > maxBlocks) |
|
793 { |
|
794 // The address is out of range (>2G) - cannot convert |
|
795 return(EFalse); |
|
796 } |
|
797 |
|
798 // adjust for byte-based access |
|
799 iArgument = iArgument << KMMCardHighCapBlockSizeLog2; |
|
800 iFlags &= ~KMMCCmdFlagBlockAddress; |
|
801 } |
|
802 else if(iArgument % KMMCardHighCapBlockSize != 0) |
|
803 { |
|
804 // byte addressing, unaligned address |
|
805 return(EFalse); |
|
806 } |
|
807 } |
|
808 |
|
809 return(ETrue); |
|
810 } |
|
811 |
|
812 void TMMCCommandDesc::Dump(TUint8* aResponseP, TMMCErr aErr) |
|
813 { |
|
814 |
|
815 Kern::Printf("------------------------------------------------------------------"); |
|
816 Kern::Printf("CMD %02d(0x%08x) - ",TUint(iCommand),TUint(iArgument)); |
|
817 |
|
818 switch(iCommand) |
|
819 { |
|
820 case 0 : Kern::Printf(" | GO_IDLE_STATE"); break; |
|
821 case 1 : Kern::Printf(" | SEND_OP_COND"); break; |
|
822 case 2 : Kern::Printf(" | ALL_SEND_CID"); break; |
|
823 case 3 : Kern::Printf(" | SET_RELATIVE_ADDR"); break; |
|
824 case 4 : Kern::Printf(" | SET_DSR"); break; |
|
825 case 5 : Kern::Printf(" | SLEEP/AWAKE"); break; |
|
826 case 6 : Kern::Printf(" | SWITCH"); break; |
|
827 case 7 : Kern::Printf(" | SELECT/DESELECT_CARD"); break; |
|
828 case 8 : Kern::Printf(" | SEND_EXT_CSD"); break; |
|
829 case 9 : Kern::Printf(" | SEND_CSD"); break; |
|
830 case 10 : Kern::Printf(" | SEND_CID"); break; |
|
831 case 11 : Kern::Printf(" | READ_DAT_UNTIL_STOP"); break; |
|
832 case 12 : Kern::Printf(" | STOP_TRANSMISSION"); break; |
|
833 case 13 : Kern::Printf(" | SEND_STATUS"); break; |
|
834 case 14 : Kern::Printf(" | BUSTEST_R"); break; |
|
835 case 15 : Kern::Printf(" | GO_INACTIVE_STATE"); break; |
|
836 case 16 : Kern::Printf(" | SET_BLOCKLEN"); break; |
|
837 case 17 : Kern::Printf(" | READ_SINGLE_BLOCK"); break; |
|
838 case 18 : Kern::Printf(" | READ_MULTIPLE_BLOCK"); break; |
|
839 case 19 : Kern::Printf(" | BUSTEST_W"); break; |
|
840 case 20 : Kern::Printf(" | WRITE_DAT_UNTIL_STOP"); break; |
|
841 case 23 : Kern::Printf(" | SET_BLOCK_COUNT"); break; |
|
842 case 24 : Kern::Printf(" | WRITE_BLOCK"); break; |
|
843 case 25 : Kern::Printf(" | WRITE_MULTIPLE_BLOCK"); break; |
|
844 case 26 : Kern::Printf(" | PROGRAM_CID"); break; |
|
845 case 27 : Kern::Printf(" | PROGRAM_CSD"); break; |
|
846 case 28 : Kern::Printf(" | SET_WRITE_PROT"); break; |
|
847 case 29 : Kern::Printf(" | CLR_WRITE_PROT"); break; |
|
848 case 30 : Kern::Printf(" | SEND_WRITE_PROT"); break; |
|
849 case 32 : Kern::Printf(" | ERASE_WR_BLK_START"); break; // SD |
|
850 case 33 : Kern::Printf(" | ERASE_WR_BLK_END"); break; // SD |
|
851 case 35 : Kern::Printf(" | ERASE_GROUP_START"); break; |
|
852 case 36 : Kern::Printf(" | ERASE_GROUP_END"); break; |
|
853 case 38 : Kern::Printf(" | ERASE"); break; |
|
854 case 39 : Kern::Printf(" | FAST_IO"); break; |
|
855 case 40 : Kern::Printf(" | GO_IRQ_STATE"); break; |
|
856 case 42 : Kern::Printf(" | LOCK_UNLOCK"); break; |
|
857 case 55 : Kern::Printf(" | APP_CMD"); break; |
|
858 case 56 : Kern::Printf(" | GEN_CMD"); break; |
|
859 default : Kern::Printf(" | *** UNKNOWN COMMAND ***"); break; |
|
860 } |
|
861 |
|
862 switch(iSpec.iResponseType) |
|
863 { |
|
864 case ERespTypeNone: Kern::Printf(" RSP - NONE"); break; |
|
865 case ERespTypeUnknown: Kern::Printf(" RSP - UNKNOWN"); break; |
|
866 case ERespTypeR1: Kern::Printf(" RSP - R1"); break; |
|
867 case ERespTypeR1B: Kern::Printf(" RSP - R1b"); break; |
|
868 case ERespTypeR2: Kern::Printf(" RSP - R2"); break; |
|
869 case ERespTypeR3: Kern::Printf(" RSP - R3"); break; |
|
870 case ERespTypeR4: Kern::Printf(" RSP - R4"); break; |
|
871 case ERespTypeR5: Kern::Printf(" RSP - R5"); break; |
|
872 case ERespTypeR6: Kern::Printf(" RSP - R6"); break; |
|
873 default : Kern::Printf(" RSP - *** UNKNOWN RESPONSE ***"); break; |
|
874 } |
|
875 |
|
876 switch(iSpec.iResponseLength) |
|
877 { |
|
878 case 0 : break; |
|
879 case 4 : Kern::Printf(" | 0x%08x", TMMC::BigEndian32(aResponseP)); break; |
|
880 case 16 : Kern::Printf(" | 0x%08x 0x%08x 0x%08x 0x%08x", ((TUint32*)aResponseP)[0], ((TUint32*)aResponseP)[1], ((TUint32*)aResponseP)[2], ((TUint32*)aResponseP)[3]); break; |
|
881 default : Kern::Printf(" | *** RESPONSE NOT PARSED ***"); break; |
|
882 } |
|
883 Kern::Printf(" ERR - 0x%08x", aErr); |
|
884 if(aErr & KMMCErrResponseTimeOut) Kern::Printf(" | KMMCErrResponseTimeOut"); |
|
885 if(aErr & KMMCErrDataTimeOut) Kern::Printf(" | KMMCErrDataTimeOut"); |
|
886 if(aErr & KMMCErrBusyTimeOut) Kern::Printf(" | KMMCErrBusyTimeOut"); |
|
887 if(aErr & KMMCErrBusTimeOut) Kern::Printf(" | KMMCErrBusTimeOut"); |
|
888 if(aErr & KMMCErrTooManyCards) Kern::Printf(" | KMMCErrTooManyCards"); |
|
889 if(aErr & KMMCErrResponseCRC) Kern::Printf(" | KMMCErrResponseCRC"); |
|
890 if(aErr & KMMCErrDataCRC) Kern::Printf(" | KMMCErrDataCRC"); |
|
891 if(aErr & KMMCErrCommandCRC) Kern::Printf(" | KMMCErrCommandCRC"); |
|
892 if(aErr & KMMCErrStatus) Kern::Printf(" | KMMCErrStatus"); |
|
893 if(aErr & KMMCErrNoCard) Kern::Printf(" | KMMCErrNoCard"); |
|
894 if(aErr & KMMCErrBrokenLock) Kern::Printf(" | KMMCErrBrokenLock"); |
|
895 if(aErr & KMMCErrPowerDown) Kern::Printf(" | KMMCErrPowerDown"); |
|
896 if(aErr & KMMCErrAbort) Kern::Printf(" | KMMCErrAbort"); |
|
897 if(aErr & KMMCErrStackNotReady) Kern::Printf(" | KMMCErrStackNotReady"); |
|
898 if(aErr & KMMCErrNotSupported) Kern::Printf(" | KMMCErrNotSupported"); |
|
899 if(aErr & KMMCErrHardware) Kern::Printf(" | KMMCErrHardware"); |
|
900 if(aErr & KMMCErrBusInconsistent) Kern::Printf(" | KMMCErrBusInconsistent"); |
|
901 if(aErr & KMMCErrBypass) Kern::Printf(" | KMMCErrBypass"); |
|
902 if(aErr & KMMCErrInitContext) Kern::Printf(" | KMMCErrInitContext"); |
|
903 if(aErr & KMMCErrArgument) Kern::Printf(" | KMMCErrArgument"); |
|
904 if(aErr & KMMCErrSingleBlock) Kern::Printf(" | KMMCErrSingleBlock"); |
|
905 if(aErr & KMMCErrUpdPswd) Kern::Printf(" | KMMCErrUpdPswd"); |
|
906 if(aErr & KMMCErrLocked) Kern::Printf(" | KMMCErrLocked"); |
|
907 if(aErr & KMMCErrNotFound) Kern::Printf(" | KMMCErrNotFound"); |
|
908 if(aErr & KMMCErrAlreadyExists) Kern::Printf(" | KMMCErrAlreadyExists"); |
|
909 if(aErr & KMMCErrGeneral) Kern::Printf(" | KMMCErrGeneral"); |
|
910 |
|
911 |
|
912 if(iSpec.iResponseType == ERespTypeR1 || iSpec.iResponseType == ERespTypeR1B) |
|
913 { |
|
914 const TUint32 stat = TMMC::BigEndian32(aResponseP); |
|
915 |
|
916 Kern::Printf(" STAT - 0x%08x", stat); |
|
917 if(stat & KMMCStatAppCmd) Kern::Printf(" | KMMCStatAppCmd"); |
|
918 if(stat & KMMCStatSwitchError) Kern::Printf(" | KMMCStatSwitchError"); |
|
919 if(stat & KMMCStatReadyForData) Kern::Printf(" | KMMCStatReadyForData"); |
|
920 if(stat & KMMCStatCurrentStateMask){ Kern::Printf(" | KMMCStatCurrentStateMask"); |
|
921 const TMMCardStateEnum cardState = (TMMCardStateEnum)(stat & KMMCStatCurrentStateMask); |
|
922 switch (cardState){ |
|
923 case ECardStateIdle : Kern::Printf(" | ECardStateIdle"); break; |
|
924 case ECardStateReady : Kern::Printf(" | ECardStateReady"); break; |
|
925 case ECardStateIdent : Kern::Printf(" | ECardStateIdent"); break; |
|
926 case ECardStateStby : Kern::Printf(" | ECardStateStby"); break; |
|
927 case ECardStateTran : Kern::Printf(" | ECardStateTran"); break; |
|
928 case ECardStateData : Kern::Printf(" | ECardStateData"); break; |
|
929 case ECardStateRcv : Kern::Printf(" | ECardStateRcv"); break; |
|
930 case ECardStatePrg : Kern::Printf(" | ECardStatePrg"); break; |
|
931 case ECardStateDis : Kern::Printf(" | ECardStateDis"); break; |
|
932 case ECardStateBtst : Kern::Printf(" | ECardStateBtst"); break; |
|
933 case ECardStateSlp : Kern::Printf(" | ECardStateSlp"); break; |
|
934 default : Kern::Printf(" | ECardStateUnknown"); break; |
|
935 } |
|
936 } |
|
937 if(stat & KMMCStatEraseReset) Kern::Printf(" | KMMCStatEraseReset"); |
|
938 if(stat & KMMCStatCardECCDisabled) Kern::Printf(" | KMMCStatCardECCDisabled"); |
|
939 if(stat & KMMCStatWPEraseSkip) Kern::Printf(" | KMMCStatWPEraseSkip"); |
|
940 if(stat & KMMCStatErrCSDOverwrite) Kern::Printf(" | KMMCStatErrCSDOverwrite"); |
|
941 if(stat & KMMCStatErrOverrun) Kern::Printf(" | KMMCStatErrOverrun"); |
|
942 if(stat & KMMCStatErrUnderrun) Kern::Printf(" | KMMCStatErrUnderrun"); |
|
943 if(stat & KMMCStatErrUnknown) Kern::Printf(" | KMMCStatErrUnknown"); |
|
944 if(stat & KMMCStatErrCCError) Kern::Printf(" | KMMCStatErrCCError"); |
|
945 if(stat & KMMCStatErrCardECCFailed) Kern::Printf(" | KMMCStatErrCardECCFailed"); |
|
946 if(stat & KMMCStatErrIllegalCommand) Kern::Printf(" | KMMCStatErrIllegalCommand"); |
|
947 if(stat & KMMCStatErrComCRCError) Kern::Printf(" | KMMCStatErrComCRCError"); |
|
948 if(stat & KMMCStatErrLockUnlock) Kern::Printf(" | KMMCStatErrLockUnlock"); |
|
949 if(stat & KMMCStatCardIsLocked) Kern::Printf(" | KMMCStatCardIsLocked"); |
|
950 if(stat & KMMCStatErrWPViolation) Kern::Printf(" | KMMCStatErrWPViolation"); |
|
951 if(stat & KMMCStatErrEraseParam) Kern::Printf(" | KMMCStatErrEraseParam"); |
|
952 if(stat & KMMCStatErrEraseSeqError) Kern::Printf(" | KMMCStatErrEraseSeqError"); |
|
953 if(stat & KMMCStatErrBlockLenError) Kern::Printf(" | KMMCStatErrBlockLenError"); |
|
954 if(stat & KMMCStatErrAddressError) Kern::Printf(" | KMMCStatErrAddressError"); |
|
955 if(stat & KMMCStatErrOutOfRange) Kern::Printf(" | KMMCStatErrOutOfRange"); |
|
956 } |
|
957 |
|
958 Kern::Printf(" -----------------------------------------------"); |
|
959 } |
|
960 |
|
961 // -------- class TMMCRCAPool -------- |
|
962 |
|
963 TRCA TMMCRCAPool::GetFreeRCA() |
|
964 /** |
|
965 * Returns a free RCA number from the pool or zero if none is available |
|
966 */ |
|
967 { |
|
968 TUint32 seekm = (iPool | iLocked) + 1; |
|
969 iPool |= (seekm & ~iLocked); |
|
970 |
|
971 if( (seekm & 0xFFFFFFFF) == 0 ) |
|
972 return( 0 ); |
|
973 |
|
974 TUint16 pos = 1; |
|
975 |
|
976 if ((seekm & 0xFFFF) == 0) { seekm >>= 16; pos = 17; } |
|
977 if ((seekm & 0xFF) == 0) { seekm >>= 8; pos += 8; } |
|
978 if ((seekm & 0xF) == 0) { seekm >>= 4; pos += 4; } |
|
979 if ((seekm & 0x3) == 0) { seekm >>= 2; pos += 2; } |
|
980 if ((seekm & 0x1) == 0) pos++; |
|
981 |
|
982 // Multiply return value by 257 so that 1 is never returned. (0x0001 is the default RCA value.) |
|
983 // The RCA integer value is divided by 257 in LockRCA() and UnlockRCA() to compensate |
|
984 // for this adjustment. These functions are only ever called in this file with the iRCA |
|
985 // field of a TMMCard object, and not with arbitrary values. |
|
986 // The iRCA field itself is only assigned values from iNewCards[] or zero. iNewCards |
|
987 // in turn is fed values from this function, in DMMCStack::CIMUpdateAcqSM() / EStSendCIDIssued. |
|
988 |
|
989 return TUint16(pos << 8 | pos); |
|
990 } |
|
991 |
|
992 |
|
993 |
|
994 // -------- class TMMCSessRing -------- |
|
995 |
|
996 TMMCSessRing::TMMCSessRing() |
|
997 /** |
|
998 * Constructor |
|
999 */ |
|
1000 : iPMark(NULL),iPoint(NULL),iPrevP(NULL),iSize(0) |
|
1001 {} |
|
1002 |
|
1003 |
|
1004 void TMMCSessRing::Erase() |
|
1005 /** |
|
1006 * Erases all the ring content |
|
1007 */ |
|
1008 {iPMark = iPoint = iPrevP = NULL; iSize = 0;} |
|
1009 |
|
1010 |
|
1011 DMMCSession* TMMCSessRing::operator++(TInt) |
|
1012 /** |
|
1013 * Post increment of Point |
|
1014 */ |
|
1015 { |
|
1016 if( iPoint == NULL ) |
|
1017 return( NULL ); |
|
1018 |
|
1019 if( (iPrevP=iPoint) == iPMark ) |
|
1020 iPoint = NULL; |
|
1021 else |
|
1022 iPoint = iPoint->iLinkP; |
|
1023 |
|
1024 return( iPrevP ); |
|
1025 } |
|
1026 |
|
1027 |
|
1028 TBool TMMCSessRing::Point(DMMCSession* aSessP) |
|
1029 /** |
|
1030 * Finds aSessP and sets Point to that position |
|
1031 */ |
|
1032 { |
|
1033 Point(); |
|
1034 |
|
1035 while( iPoint != NULL ) |
|
1036 if( iPoint == aSessP ) |
|
1037 return( ETrue ); |
|
1038 else |
|
1039 this->operator++(0); |
|
1040 |
|
1041 return( EFalse ); |
|
1042 } |
|
1043 |
|
1044 void TMMCSessRing::Add(DMMCSession* aSessP) |
|
1045 /** |
|
1046 * Inserts aSessP before Marker. Point is moved into the Marker position. |
|
1047 */ |
|
1048 { |
|
1049 if( iSize == 0 ) |
|
1050 { |
|
1051 iPMark = iPrevP = iPoint = aSessP; |
|
1052 aSessP->iLinkP = aSessP; |
|
1053 iSize = 1; |
|
1054 return; |
|
1055 } |
|
1056 |
|
1057 iPoint = iPMark->iLinkP; |
|
1058 iPMark->iLinkP = aSessP; |
|
1059 aSessP->iLinkP = iPoint; |
|
1060 iPMark = iPrevP = aSessP; |
|
1061 iSize++; |
|
1062 } |
|
1063 |
|
1064 |
|
1065 void TMMCSessRing::Add(TMMCSessRing& aRing) |
|
1066 /** |
|
1067 * Inserts aRing before Marker. Point is moved into the Marker position. |
|
1068 * aRing Marker becomes the fisrt inserted element. |
|
1069 * Erases aRing. |
|
1070 */ |
|
1071 { |
|
1072 Point(); |
|
1073 |
|
1074 if( aRing.iSize == 0 ) |
|
1075 return; |
|
1076 |
|
1077 if( iSize == 0 ) |
|
1078 { |
|
1079 iPrevP = iPMark = aRing.iPMark; |
|
1080 iPoint = iPrevP->iLinkP; |
|
1081 iSize = aRing.iSize; |
|
1082 } |
|
1083 else |
|
1084 { |
|
1085 iPrevP->iLinkP = aRing.iPMark->iLinkP; |
|
1086 iPMark = iPrevP = aRing.iPMark; |
|
1087 iPrevP->iLinkP = iPoint; |
|
1088 iSize += aRing.iSize; |
|
1089 } |
|
1090 |
|
1091 aRing.Erase(); |
|
1092 } |
|
1093 |
|
1094 DMMCSession* TMMCSessRing::Remove() |
|
1095 /** |
|
1096 * Removes an element pointed to by Point. |
|
1097 * Point (and possibly Marker) move forward as in operator++ |
|
1098 */ |
|
1099 { |
|
1100 DMMCSession* remS = iPrevP; |
|
1101 |
|
1102 if( iSize < 2 ) |
|
1103 Erase(); |
|
1104 else |
|
1105 { |
|
1106 remS = remS->iLinkP; |
|
1107 iPrevP->iLinkP = remS->iLinkP; |
|
1108 iSize--; |
|
1109 |
|
1110 if( iPoint != NULL ) |
|
1111 iPoint = iPrevP->iLinkP; |
|
1112 |
|
1113 if( iPMark == remS ) |
|
1114 { |
|
1115 iPMark = iPrevP; |
|
1116 iPoint = NULL; |
|
1117 } |
|
1118 } |
|
1119 |
|
1120 return( remS ); |
|
1121 } |
|
1122 |
|
1123 |
|
1124 void TMMCSessRing::Remove(DMMCSession* aSessP) |
|
1125 /** |
|
1126 * Removes a specified session from the ring |
|
1127 */ |
|
1128 { |
|
1129 if( Point(aSessP) ) |
|
1130 Remove(); |
|
1131 else |
|
1132 DMMCSocket::Panic(DMMCSocket::EMMCSessRingNoSession); |
|
1133 } |
|
1134 |
|
1135 |
|
1136 |
|
1137 // -------- class TMMCStateMachine -------- |
|
1138 |
|
1139 |
|
1140 /** |
|
1141 Removes all state from the state machine. |
|
1142 |
|
1143 It also resets the stack and the exit code. |
|
1144 */ |
|
1145 EXPORT_C void TMMCStateMachine::Reset() |
|
1146 { |
|
1147 iAbort = EFalse; |
|
1148 iSP = 0; iExitCode = 0; |
|
1149 iStack[0].iState = 0; iStack[0].iTrapMask = 0; |
|
1150 } |
|
1151 |
|
1152 |
|
1153 |
|
1154 |
|
1155 /** |
|
1156 The state machine dispatcher. |
|
1157 |
|
1158 @return The MultiMediaCard error code. |
|
1159 */ |
|
1160 EXPORT_C TMMCErr TMMCStateMachine::Dispatch() |
|
1161 { |
|
1162 |
|
1163 // If a state machine returns non-zero, i.e. a non-empty error set, then the second |
|
1164 // inner while loop is broken. The errors are thrown like an exception where the |
|
1165 // stack is unravelled until it reaches a state machine which can handle at least |
|
1166 // one of the error codes, else this function returns with the exit code or'd with |
|
1167 // KMMCErrBypass. If the state machine returns zero, then this function returns |
|
1168 // zero if iSuspend is set, i.e., if the stack is waiting on an asynchronous event. |
|
1169 // If suspend is not set, then the next state machine is called. This may be the |
|
1170 // same as the current state machine, or its caller if the current state machine |
|
1171 // ended called Pop() before exiting, e.g., via SMF_END. |
|
1172 |
|
1173 while( iSP >= 0 && !iAbort ) |
|
1174 { |
|
1175 // If there is an un-trapped error, wind back down the stack, either |
|
1176 // to the end of the stack or until the error becomes trapped. |
|
1177 while( iSP >= 0 && (iExitCode & ~iStack[iSP].iTrapMask) != 0 ) |
|
1178 iSP--; |
|
1179 |
|
1180 iExitCode &= ~KMMCErrBypass; |
|
1181 |
|
1182 if ( iExitCode ) |
|
1183 { |
|
1184 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Err %x",iExitCode)); |
|
1185 } |
|
1186 |
|
1187 while( iSP >= 0 && !iAbort ) |
|
1188 { |
|
1189 __KTRACE_OPT(KPBUS1,Kern::Printf("-msm:dsp:%02x:%08x.%02x",iSP, TUint32(iStack[iSP].iFunction), State())); |
|
1190 |
|
1191 iSuspend = ETrue; |
|
1192 const TMMCErr signal = iStack[iSP].iFunction(iContextP); |
|
1193 |
|
1194 if (signal) |
|
1195 { |
|
1196 iExitCode = signal; |
|
1197 break; |
|
1198 } |
|
1199 |
|
1200 if( iSuspend ) |
|
1201 { |
|
1202 __KTRACE_OPT(KPBUS1,Kern::Printf("<msm:dsp:exitslp")); |
|
1203 return(0); |
|
1204 } |
|
1205 } |
|
1206 } |
|
1207 |
|
1208 __KTRACE_OPT(KPBUS1,Kern::Printf("<msm:dsp:exit%08x", iExitCode)); |
|
1209 return( KMMCErrBypass | iExitCode ); |
|
1210 } |
|
1211 |
|
1212 |
|
1213 |
|
1214 |
|
1215 /** |
|
1216 Pushes another state machine entry onto the stack. |
|
1217 |
|
1218 Typically, this is invoked using one of the macros: |
|
1219 SMF_CALL, SMF_CALLWAIT, SMF_INVOKES, SMF_INVOKEWAITS |
|
1220 |
|
1221 @param anEntry The state machine function to be run; this will start at |
|
1222 the initial state (EStBegin), with no exception handling defined. |
|
1223 @param aSuspend Indicates whether the state machine is to block on return to the dispatcher; |
|
1224 Specify ETrue to block; EFalse not to block. |
|
1225 EFalse is the default, if not explicitly stated. |
|
1226 |
|
1227 @return KMMCErrNone |
|
1228 |
|
1229 @panic PBUS-MMC 0 if the maximum depth of nested state machine entries is being exeeded. |
|
1230 |
|
1231 @see SMF_CALL |
|
1232 @see SMF_CALLWAIT |
|
1233 @see SMF_INVOKES |
|
1234 @see SMF_INVOKEWAITS |
|
1235 */ |
|
1236 EXPORT_C TMMCErr TMMCStateMachine::Push(TMMCErr (*anEntry)(TAny*), TBool aSuspend) |
|
1237 { |
|
1238 iSP++; |
|
1239 __ASSERT_ALWAYS(TUint(iSP)<KMaxMMCMachineStackDepth, |
|
1240 DMMCSocket::Panic(DMMCSocket::EMMCMachineStack)); |
|
1241 iStack[iSP].iFunction = anEntry; |
|
1242 iStack[iSP].iState = 0; |
|
1243 iStack[iSP].iTrapMask = 0; |
|
1244 if( !aSuspend ) |
|
1245 iSuspend = EFalse; |
|
1246 return( 0 ); |
|
1247 } |
|
1248 |
|
1249 |
|
1250 |
|
1251 |
|
1252 /** |
|
1253 Jumps to the specified state machine function in the current state machine entry. |
|
1254 |
|
1255 @param anEntry The state machine function to be run; this will start at |
|
1256 the initial state (EStBegin), with no exception handling defined. |
|
1257 @param aSuspend Indicates whether the state machine is to block on return to the dispatcher; |
|
1258 Specify ETrue to block; EFalse not to block. |
|
1259 EFalse is the default, if not explicitly stated. |
|
1260 |
|
1261 @return KMMCErrNone |
|
1262 */ |
|
1263 EXPORT_C TMMCErr TMMCStateMachine::Jump(TMMCErr (*anEntry)(TAny*), TBool aSuspend) |
|
1264 { |
|
1265 iStack[iSP].iFunction = anEntry; |
|
1266 iStack[iSP].iState = 0; |
|
1267 iStack[iSP].iTrapMask = 0; |
|
1268 if( !aSuspend ) |
|
1269 iSuspend = EFalse; |
|
1270 return( 0 ); |
|
1271 } |
|
1272 |
|
1273 |
|
1274 |
|
1275 |
|
1276 // -------- class DMMCStack -------- |
|
1277 |
|
1278 #pragma warning( disable : 4355 ) // this used in initializer list |
|
1279 EXPORT_C DMMCStack::DMMCStack(TInt /*aBus*/, DMMCSocket* aSocket) |
|
1280 /** |
|
1281 * Constructs a DMMCStack object |
|
1282 * @param aBus Unused |
|
1283 * @param aSocket A pointer to the associated socket. |
|
1284 */ |
|
1285 : iWorkSet(), |
|
1286 iReadyQueue(), |
|
1287 iEntryQueue(), |
|
1288 iStackDFC(DMMCStack::StackDFC, this, 1), |
|
1289 iSelectedCard(TUint16(~0)), |
|
1290 iSocket(aSocket), |
|
1291 iStackSession(NULL), |
|
1292 iAutoUnlockSession(TMMCCallBack(AutoUnlockCBST, this)), |
|
1293 iInitState(EISPending), |
|
1294 iInitialise(ETrue), |
|
1295 iCurrentDSR(), |
|
1296 iConfig(), |
|
1297 iRCAPool(), |
|
1298 iMasterConfig() |
|
1299 { |
|
1300 // iStackState(0), |
|
1301 // iLockingSessionP(NULL), |
|
1302 // iAttention(EFalse), |
|
1303 // iAbortReq(EFalse), |
|
1304 // iCompReq(EFalse), |
|
1305 // iDoorOpened(EFalse), |
|
1306 // iPoweredUp(EFalse), |
|
1307 // iDFCRunning(EFalse), |
|
1308 // iAbortAll(EFalse), |
|
1309 // iAllExitCode(0), |
|
1310 // iSessionP(NULL), |
|
1311 // iCurrentOpRange(0), |
|
1312 // iCardsPresent(0), |
|
1313 // iMaxCardsInStack(0) |
|
1314 } |
|
1315 #pragma warning( default : 4355 ) |
|
1316 |
|
1317 EXPORT_C TInt DMMCStack::Init() |
|
1318 /** |
|
1319 * Initialises the generic MMC stack. |
|
1320 * @return KErrNone if successful, standard error code otherwise. |
|
1321 */ |
|
1322 { |
|
1323 // allocate and initialize session object |
|
1324 if ((iStackSession = AllocSession(TMMCCallBack(StackSessionCBST, this))) == 0) |
|
1325 return(KErrNoMemory); |
|
1326 |
|
1327 // create helper class |
|
1328 if ((iBody = new DBody(*this)) == NULL) |
|
1329 return(KErrNoMemory); |
|
1330 |
|
1331 iStackSession->SetStack(this); |
|
1332 |
|
1333 iStackDFC.SetDfcQ(&iSocket->iDfcQ); |
|
1334 |
|
1335 // Get the maximal number of cards from ASSP layer |
|
1336 iMaxCardsInStack = iSocket->TotalSupportedCards(); |
|
1337 if ( iMaxCardsInStack > KMaxMMCardsPerStack ) |
|
1338 iMaxCardsInStack=KMaxMMCardsPerStack; |
|
1339 |
|
1340 TInt r = iCardArray->AllocCards(); |
|
1341 |
|
1342 return(r); |
|
1343 } |
|
1344 |
|
1345 EXPORT_C void DMMCStack::PowerUpStack() |
|
1346 /** |
|
1347 * Enforce stack power-up and initialisation. |
|
1348 * This is an asynchronous operation, which calls DMMCSocket::PowerUpSequenceComplete upon completion. |
|
1349 */ |
|
1350 { |
|
1351 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:pus")); |
|
1352 |
|
1353 if (iPSLBuf == NULL) |
|
1354 { |
|
1355 GetBufferInfo(&iPSLBuf, &iPSLBufLen); |
|
1356 iMinorBufLen = KMinMinorBufSize; |
|
1357 } |
|
1358 |
|
1359 ReportPowerDown(); // ensure power will be switch on regardless |
|
1360 |
|
1361 Scheduler( iInitialise ); |
|
1362 } |
|
1363 |
|
1364 void DMMCStack::QSleepStack() |
|
1365 /** |
|
1366 * Schedules a session to place media in Sleep State |
|
1367 */ |
|
1368 { |
|
1369 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:qsleep")); |
|
1370 |
|
1371 Scheduler( iSleep ); |
|
1372 } |
|
1373 |
|
1374 EXPORT_C void DMMCStack::PowerDownStack() |
|
1375 /** |
|
1376 * Enforce stack power down. |
|
1377 * Clients generally shouldn't need to concern themselves with powering down a stack |
|
1378 * unless they specifically need to perform a power reset of a card. If a driver fails to |
|
1379 * open then normal practise is for that driver to leave the card powered so that any subsequent |
|
1380 * driver which may attempt to open immediately after this failed attempt won't have to re-power the card. |
|
1381 * If no driver successfully opens on the card then the Controllers inactivity/not in use |
|
1382 * timeout system can be left to power it down. |
|
1383 */ |
|
1384 { |
|
1385 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:pds")); |
|
1386 |
|
1387 ReportPowerDown(); |
|
1388 iInitState = EISPending; |
|
1389 DoPowerDown(); |
|
1390 |
|
1391 TBool cardRemoved = (iStackState & KMMCStackStateCardRemoved); |
|
1392 for (TUint i=0;i<iMaxCardsInStack;i++) |
|
1393 { |
|
1394 TMMCard& card = iCardArray->Card(i); |
|
1395 card.SetBusWidth(1); |
|
1396 card.SetHighSpeedClock(0); |
|
1397 if (cardRemoved) |
|
1398 { |
|
1399 iCardArray->DeclareCardAsGone(i); |
|
1400 } |
|
1401 else |
|
1402 { |
|
1403 // set the locked bit if the card has a password - need to do this |
|
1404 // now that RLocalDrive::Caps() no longer powers up the stack |
|
1405 if (card.HasPassword()) |
|
1406 { |
|
1407 TMapping* pmp = iSocket->iPasswordStore->FindMappingInStore(card.CID()); |
|
1408 if (!pmp || pmp->iState != TMapping::EStValid) |
|
1409 { |
|
1410 *((TUint32*) &card.iStatus) |= KMMCStatCardIsLocked; |
|
1411 } |
|
1412 } |
|
1413 |
|
1414 // Remove card state flags, after a power cycle all cards are in idle state |
|
1415 *((TUint32*) &card.iStatus) &= ~KMMCStatCurrentStateMask; |
|
1416 } |
|
1417 } |
|
1418 if (cardRemoved) |
|
1419 iStackState &= ~KMMCStackStateCardRemoved; |
|
1420 |
|
1421 |
|
1422 iSocket->iVcc->SetState(EPsuOff); |
|
1423 if (iSocket->iVccCore) |
|
1424 iSocket->iVccCore->SetState(EPsuOff); |
|
1425 |
|
1426 // Cancel timers, reset ASSP, cancel stack DFC & remove session from workset |
|
1427 // to ensure stack doesn't wake up again & attempt to dereference iSessionP |
|
1428 if (iSessionP) |
|
1429 Abort(iSessionP); |
|
1430 |
|
1431 iStackDFC.Cancel(); |
|
1432 |
|
1433 // The stack may have powered down while attempting to power up (e.g. because a card has not responded), |
|
1434 // so ensure stack doesn't attempt to initialize itself again until next PowerUpStack() |
|
1435 iInitialise = EFalse; |
|
1436 iStackState &= ~(KMMCStackStateInitInProgress | KMMCStackStateInitPending | KMMCStackStateBusInconsistent | KMMCStackStateWaitingDFC); |
|
1437 iSessionP = NULL; |
|
1438 } |
|
1439 |
|
1440 // |
|
1441 // DMMCStack:: --- Stack Scheduler and its supplementary functions --- |
|
1442 // |
|
1443 DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedGetOnDFC() |
|
1444 /** |
|
1445 * Initiates stack DFC. Returns either Continue or Loop. |
|
1446 */ |
|
1447 { |
|
1448 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sgd")); |
|
1449 |
|
1450 if( iDFCRunning ) |
|
1451 return( ESchedContinue ); |
|
1452 |
|
1453 if( (iStackState & KMMCStackStateWaitingDFC) == 0 ) |
|
1454 { |
|
1455 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sgd:q")); |
|
1456 iStackState |= KMMCStackStateWaitingDFC; |
|
1457 if (NKern::CurrentContext()==NKern::EInterrupt) |
|
1458 iStackDFC.Add(); |
|
1459 else |
|
1460 iStackDFC.Enque(); |
|
1461 } |
|
1462 |
|
1463 return( ESchedLoop ); |
|
1464 } |
|
1465 |
|
1466 void DMMCStack::SchedSetContext(DMMCSession* aSessP) |
|
1467 /** |
|
1468 * Sets up the specified session as the current session. |
|
1469 * Invoked by JobChooser and Initialiser. |
|
1470 * @param aSessP A pointer to the session. |
|
1471 */ |
|
1472 { |
|
1473 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ssc")); |
|
1474 |
|
1475 if( (iStackState & (KMMCStackStateInitPending|KMMCStackStateBusInconsistent)) != 0 && |
|
1476 aSessP->iSessionID != ECIMInitStack ) |
|
1477 { |
|
1478 iInitialise = ETrue; |
|
1479 return; |
|
1480 } |
|
1481 |
|
1482 if( iSessionP != aSessP ) |
|
1483 { |
|
1484 iStackState |= KMMCStackStateReScheduled; |
|
1485 MergeConfig( aSessP ); |
|
1486 |
|
1487 if( aSessP->iSessionID == ECIMInitStack ) |
|
1488 iInitialise = ETrue; |
|
1489 else |
|
1490 if( InitStackInProgress() ) |
|
1491 MarkComplete( aSessP, KMMCErrStackNotReady ); |
|
1492 else |
|
1493 if( aSessP->iBrokenLock ) |
|
1494 MarkComplete( aSessP, KMMCErrBrokenLock ); |
|
1495 |
|
1496 iSessionP = aSessP; |
|
1497 } |
|
1498 |
|
1499 iSessionP->iState &= ~KMMCSessStateDoReSchedule; |
|
1500 } |
|
1501 |
|
1502 void DMMCStack::SchedDoAbort(DMMCSession* aSessP) |
|
1503 /** |
|
1504 * Aborts asynchronous activities of a session aSessP |
|
1505 * @param aSessP A pointer to the session to be aborted. |
|
1506 */ |
|
1507 { |
|
1508 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sda")); |
|
1509 |
|
1510 #ifdef __EPOC32__ |
|
1511 if( aSessP->iBlockOn & KMMCBlockOnPollTimer ) |
|
1512 aSessP->iPollTimer.Cancel(); |
|
1513 |
|
1514 if( aSessP->iBlockOn & KMMCBlockOnRetryTimer ) |
|
1515 aSessP->iRetryTimer.Cancel(); |
|
1516 |
|
1517 if( aSessP->iBlockOn & KMMCBlockOnPgmTimer ) |
|
1518 aSessP->iProgramTimer.Cancel(); |
|
1519 #endif // #ifdef __EPOC32__ |
|
1520 |
|
1521 if( aSessP->iBlockOn & KMMCBlockOnWaitToLock ) |
|
1522 iStackState &= ~KMMCStackStateWaitingToLock; |
|
1523 |
|
1524 if( aSessP->iBlockOn & (KMMCBlockOnASSPFunction | KMMCBlockOnInterrupt | KMMCBlockOnDataTransfer) ) |
|
1525 ASSPReset(); |
|
1526 |
|
1527 if( (aSessP->iState & (KMMCSessStateInProgress|KMMCSessStateCritical)) == |
|
1528 (KMMCSessStateInProgress|KMMCSessStateCritical) ) |
|
1529 iStackState |= KMMCStackStateInitPending; |
|
1530 |
|
1531 |
|
1532 (void)__e32_atomic_and_ord32(&aSessP->iBlockOn, ~(KMMCBlockOnPollTimer | KMMCBlockOnRetryTimer | |
|
1533 KMMCBlockOnWaitToLock | KMMCBlockOnASSPFunction | |
|
1534 KMMCBlockOnInterrupt | KMMCBlockOnDataTransfer) ); |
|
1535 } |
|
1536 |
|
1537 DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedResolveStatBlocks(DMMCSession* aSessP) |
|
1538 /** |
|
1539 * Checks static blocking conditions and removes them as necessary |
|
1540 * @param aSessP A pointer to the session. |
|
1541 * @return EschedContinue or ESchedLoop (if scheduler is to be restarted) |
|
1542 */ |
|
1543 { |
|
1544 |
|
1545 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:srsb")); |
|
1546 |
|
1547 if( (aSessP->iBlockOn & KMMCBlockOnCardInUse) && aSessP->iCardP->iUsingSessionP == NULL ) |
|
1548 aSessP->SynchUnBlock( KMMCBlockOnCardInUse ); |
|
1549 |
|
1550 if( (aSessP->iBlockOn & KMMCBlockOnWaitToLock) && iWorkSet.Size() == 1 ) |
|
1551 { |
|
1552 // ECIMLockStack processed here |
|
1553 iLockingSessionP = aSessP; // in this order |
|
1554 iStackState &= ~KMMCStackStateWaitingToLock; |
|
1555 aSessP->SynchUnBlock( KMMCBlockOnWaitToLock ); |
|
1556 MarkComplete( aSessP, KMMCErrNone ); |
|
1557 return( ESchedLoop ); |
|
1558 } |
|
1559 |
|
1560 return( ESchedContinue ); |
|
1561 } |
|
1562 |
|
1563 DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedGroundDown(DMMCSession* aSessP, TMMCErr aReason) |
|
1564 /** |
|
1565 * Aborts all asynchronous activities of session aSessP with |
|
1566 * iExitCode = aReason. This function conserns itself with asynchronous |
|
1567 * activities only; session static state (eg Critical) is not taken into |
|
1568 * account. Session dynamic state and action flags (i.e. SafeInGaps, |
|
1569 * DoReSchedule and DoDFC) are cleared. |
|
1570 * @param aSessP A pointer to the session. |
|
1571 * @param aReason The reason for aborting. |
|
1572 * @return EschedContinue if everything's done OK. |
|
1573 * @return ESchedLoop if the session can not be safely grounded (eg |
|
1574 * iStackSession) and should therefore be aborted and/or completed by a |
|
1575 * separate scheduler pass. |
|
1576 */ |
|
1577 { |
|
1578 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sgdn")); |
|
1579 |
|
1580 if( (aSessP == iStackSession) || InitStackInProgress() ) |
|
1581 return( ESchedLoop ); |
|
1582 |
|
1583 if( aSessP->iState & KMMCSessStateInProgress ) |
|
1584 { |
|
1585 SchedDoAbort( aSessP ); |
|
1586 //coverity[check_return] |
|
1587 //return value is not saved or checked because there is no further uses. |
|
1588 aSessP->iMachine.SetExitCode( aReason ); |
|
1589 aSessP->iState &= ~(KMMCSessStateSafeInGaps | KMMCSessStateDoReSchedule | |
|
1590 KMMCSessStateDoDFC); |
|
1591 } |
|
1592 |
|
1593 return( ESchedContinue ); |
|
1594 } |
|
1595 |
|
1596 DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedEnqueStackSession(TMMCSessionTypeEnum aSessID) |
|
1597 /** |
|
1598 * Prepare internal session for InitStack and enque it into WorkSet. |
|
1599 * @return EschedContinue or ESchedLoop |
|
1600 */ |
|
1601 { |
|
1602 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sess")); |
|
1603 |
|
1604 if( iStackSession->IsEngaged() ) |
|
1605 { |
|
1606 MarkComplete( iStackSession, KMMCErrAbort ); |
|
1607 return( ESchedLoop ); |
|
1608 } |
|
1609 |
|
1610 iStackSession->SetupCIMControl( aSessID ); |
|
1611 iWorkSet.Add( iStackSession ); |
|
1612 iStackSession->iState |= KMMCSessStateEngaged; |
|
1613 return( ESchedContinue ); |
|
1614 } |
|
1615 |
|
1616 void DMMCStack::SchedGrabEntries() |
|
1617 /** |
|
1618 * Merges Entry queue into Ready queue. Invoked at the scheduler entry and |
|
1619 * after the completion pass |
|
1620 */ |
|
1621 { |
|
1622 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sge")); |
|
1623 |
|
1624 iAttention = EFalse; // Strictly in this order |
|
1625 if( !iEntryQueue.IsEmpty() ) |
|
1626 { |
|
1627 DISABLEPREEMPTION |
|
1628 iReadyQueue.Add( iEntryQueue ); |
|
1629 RESTOREPREEMPTION |
|
1630 } |
|
1631 } |
|
1632 |
|
1633 void DMMCStack::SchedDisengage() |
|
1634 /** |
|
1635 * This function is called by AbortPass() and CompletionPass() to remove the session |
|
1636 * at WorkSet Point, to abort its asynchronous activities (if any) and |
|
1637 * clear up the dependent resources |
|
1638 */ |
|
1639 { |
|
1640 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sd")); |
|
1641 |
|
1642 DMMCSession* sessP = iWorkSet.Remove(); |
|
1643 |
|
1644 SchedDoAbort( sessP ); |
|
1645 |
|
1646 if( sessP == iSessionP ) |
|
1647 iSessionP = NULL; |
|
1648 |
|
1649 if( sessP->iCardP != NULL && sessP->iCardP->iUsingSessionP == sessP ) |
|
1650 sessP->iCardP->iUsingSessionP = NULL; |
|
1651 |
|
1652 // iAutoUnlockSession may attach to more than once card, so need to iterate |
|
1653 // through all cards and clear their session pointers if they match sessP |
|
1654 if (sessP == &iAutoUnlockSession) |
|
1655 { |
|
1656 for (TUint i = 0; i < iMaxCardsInStack; i++) |
|
1657 { |
|
1658 TMMCard& cd = *(iCardArray->CardP(i)); |
|
1659 if (cd.iUsingSessionP == sessP) |
|
1660 cd.iUsingSessionP = NULL; |
|
1661 } |
|
1662 } |
|
1663 |
|
1664 if( sessP->iState & KMMCSessStateASSPEngaged ) |
|
1665 ASSPDisengage(); |
|
1666 |
|
1667 sessP->iState = 0; |
|
1668 } |
|
1669 |
|
1670 inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedAbortPass() |
|
1671 /** |
|
1672 * DMMCStack Scheduler private inline functions. These functions were separated as inline functions |
|
1673 * only for the sake of Scheduler() clarity. |
|
1674 */ |
|
1675 { |
|
1676 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sap")); |
|
1677 |
|
1678 iAbortReq = EFalse; |
|
1679 SchedGrabEntries(); |
|
1680 DMMCSession* sessP; |
|
1681 |
|
1682 iWorkSet.Point(); |
|
1683 |
|
1684 while( (sessP = iWorkSet) != NULL ) |
|
1685 if( iAbortAll || sessP->iDoAbort ) |
|
1686 SchedDisengage(); |
|
1687 else |
|
1688 iWorkSet++; |
|
1689 |
|
1690 iReadyQueue.Point(); |
|
1691 |
|
1692 while( (sessP = iReadyQueue) != NULL ) |
|
1693 if( iAbortAll || sessP->iDoAbort ) |
|
1694 { |
|
1695 iReadyQueue.Remove(); |
|
1696 sessP->iState = 0; |
|
1697 } |
|
1698 else |
|
1699 iReadyQueue++; |
|
1700 |
|
1701 if( iAbortReq ) |
|
1702 return( ESchedLoop ); |
|
1703 |
|
1704 // Clearing iAbortAll here is a bit dodgy. It wouldn't work if somebody interrupted us |
|
1705 // at this point, enqued a session and then immediately called Reset() - that session |
|
1706 // would not be discarded. However, the correct solution (enque Reset() requests |
|
1707 // and process them in the Scheduler main loop) seems to be too expensive just to avoid |
|
1708 // this particular effect. |
|
1709 iAbortAll = EFalse; |
|
1710 return( ESchedContinue ); |
|
1711 } |
|
1712 |
|
1713 inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedCompletionPass() |
|
1714 /** |
|
1715 * This function calls back all the sessions waiting to be completed |
|
1716 * Returns either Continue or Loop. |
|
1717 */ |
|
1718 { |
|
1719 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:scp")); |
|
1720 |
|
1721 iCompReq = EFalse; |
|
1722 DMMCSession* sessP; |
|
1723 |
|
1724 if( iCompleteAllExitCode ) |
|
1725 { |
|
1726 SchedGrabEntries(); |
|
1727 iWorkSet.Add( iReadyQueue ); |
|
1728 } |
|
1729 |
|
1730 iWorkSet.Point(); |
|
1731 |
|
1732 while( (sessP = iWorkSet) != NULL ) |
|
1733 if( iCompleteAllExitCode || sessP->iDoComplete ) |
|
1734 { |
|
1735 if( (EffectiveModes(sessP->iConfig) & KMMCModeCompleteInStackDFC) != 0 && |
|
1736 SchedGetOnDFC() ) |
|
1737 { |
|
1738 // DFC has been queued so return back to main loop. Next time |
|
1739 // SchedGetOnDfc() will return EFalse, and the callback will be called. |
|
1740 iCompReq = ETrue; |
|
1741 return( ESchedLoop ); |
|
1742 } |
|
1743 |
|
1744 SchedDisengage(); // calls iWorkSet.Remove |
|
1745 sessP->iMMCExitCode |= iCompleteAllExitCode; |
|
1746 // Update the controller store if a password operation was in progress. |
|
1747 TBool doCallback = ETrue; |
|
1748 if (sessP->iSessionID == ECIMLockUnlock) |
|
1749 { |
|
1750 iSocket->PasswordControlEnd(sessP, sessP->EpocErrorCode()); |
|
1751 |
|
1752 if(sessP->EpocErrorCode() == KErrNone) |
|
1753 { |
|
1754 sessP->SetupCIMInitStackAfterUnlock(); |
|
1755 if(sessP->Engage() == KErrNone) |
|
1756 { |
|
1757 doCallback = EFalse; |
|
1758 } |
|
1759 } |
|
1760 } |
|
1761 |
|
1762 if(sessP->iSessionID == ECIMInitStackAfterUnlock) |
|
1763 { |
|
1764 // After unlocking the stack, cards may have switched into HS mode |
|
1765 // (HS switch commands are only valid when the card is unlocked). |
|
1766 // |
|
1767 // Therefore, we need to re-negotiate the maximum bus clock again. |
|
1768 // |
|
1769 // The PSL will use this to set the master config (limiting the clock if |
|
1770 // appropriate). |
|
1771 // |
|
1772 // Note that the clock may change when a specific card is selected. |
|
1773 // |
|
1774 TUint maxClk; |
|
1775 iCardArray->UpdateAcquisitions(&maxClk); |
|
1776 SetBusConfigDefaults( iMasterConfig.iBusConfig, maxClk ); |
|
1777 DoSetClock(maxClk); |
|
1778 } |
|
1779 |
|
1780 if(doCallback) |
|
1781 { |
|
1782 // call media driver completion routine or StackSessionCBST(). |
|
1783 sessP->iCallBack.CallBack(); |
|
1784 } |
|
1785 } |
|
1786 else |
|
1787 iWorkSet++; |
|
1788 |
|
1789 if( iCompReq ) |
|
1790 return( ESchedLoop ); |
|
1791 |
|
1792 iCompleteAllExitCode = 0; |
|
1793 |
|
1794 return( ESchedContinue ); |
|
1795 } |
|
1796 |
|
1797 inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedInitStack() |
|
1798 /** |
|
1799 * "Immediate" InitStack initiator. Returns either Continue or Loop. |
|
1800 */ |
|
1801 { |
|
1802 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sis")); |
|
1803 |
|
1804 if( SchedGetOnDFC() ) |
|
1805 return( ESchedLoop ); |
|
1806 |
|
1807 if( iSessionP != NULL && (iStackState & KMMCStackStateJobChooser) == 0 ) |
|
1808 { |
|
1809 if( (iSessionP->iState & KMMCSessStateInProgress) ) |
|
1810 { |
|
1811 if( SchedGroundDown(iSessionP, KMMCErrPowerDown) ) |
|
1812 { |
|
1813 MarkComplete( iSessionP, KMMCErrPowerDown ); |
|
1814 return( ESchedLoop ); |
|
1815 } |
|
1816 } |
|
1817 else |
|
1818 iSessionP->iMachine.Reset(); |
|
1819 } |
|
1820 |
|
1821 // NB if the current session was InitStack InProgress, JobChooser can not be active; |
|
1822 // so we are not going to continue another InitStack as if nothing happened. |
|
1823 |
|
1824 iStackState &= ~(KMMCStackStateInitInProgress|KMMCStackStateInitPending); |
|
1825 |
|
1826 // If there is no current session (e.g. called from PowerUpStack()) or the current |
|
1827 // session isn't specifically ECIMInitStack (which it rarely will be) then we have to use |
|
1828 // the stack session to perform the stack init. |
|
1829 if( iSessionP == NULL || iSessionP->iSessionID != ECIMInitStack ) |
|
1830 { |
|
1831 if( SchedEnqueStackSession(ECIMInitStack) ) |
|
1832 return( ESchedLoop ); |
|
1833 |
|
1834 SchedSetContext( iStackSession ); // make the internal session to be current job |
|
1835 } |
|
1836 |
|
1837 // Neither client nor internal session could be blocked here, not even on "BrokenLock" |
|
1838 __ASSERT_ALWAYS( (iSessionP->iBlockOn)==0, |
|
1839 DMMCSocket::Panic(DMMCSocket::EMMCInitStackBlocked) ); |
|
1840 |
|
1841 iStackState |= KMMCStackStateInitInProgress; |
|
1842 // nothing can stop this session now; it's safe to clear iInitialise here. |
|
1843 iInitialise = EFalse; |
|
1844 return( ESchedContinue ); |
|
1845 } |
|
1846 |
|
1847 inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedSleepStack() |
|
1848 /** |
|
1849 * "Immediate" Stack sleep mode. Returns either Continue or Loop. |
|
1850 */ |
|
1851 { |
|
1852 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:SchdSlp!")); |
|
1853 |
|
1854 // Make sure Stack DFC is Running! |
|
1855 if( SchedGetOnDFC() ) |
|
1856 { |
|
1857 __KTRACE_OPT(KPBUS1, Kern::Printf("mst:SchdSlp - DFC not running")); |
|
1858 return( ESchedLoop ); |
|
1859 } |
|
1860 |
|
1861 if( iSessionP != NULL && (iStackState & KMMCStackStateJobChooser) == 0 ) |
|
1862 { |
|
1863 if( (iSessionP->iState & KMMCSessStateInProgress) ) |
|
1864 { |
|
1865 // A session has been queued before sleep, |
|
1866 // cancel sleep and loop for next session |
|
1867 iSleep = EFalse; |
|
1868 return( ESchedLoop ); |
|
1869 } |
|
1870 } |
|
1871 |
|
1872 // Use the stack session to perform the stack sleep. |
|
1873 if( SchedEnqueStackSession(ECIMSleep) ) |
|
1874 { |
|
1875 __KTRACE_OPT(KPBUS1,Kern::Printf("SchdSlp: already Enqued")); |
|
1876 // Stack already busy cancel sleep |
|
1877 iSleep = EFalse; |
|
1878 return( ESchedLoop ); |
|
1879 } |
|
1880 |
|
1881 SchedSetContext( iStackSession ); // make the internal session to be current job |
|
1882 |
|
1883 // Sleep has now been queued |
|
1884 iSleep = EFalse; |
|
1885 iStackState |= KMMCStackStateSleepinProgress; |
|
1886 __KTRACE_OPT(KPBUS1, Kern::Printf("<mst:SchdSlp")); |
|
1887 |
|
1888 return( ESchedLoop ); |
|
1889 } |
|
1890 |
|
1891 |
|
1892 inline TBool DMMCStack::SchedPreemptable() |
|
1893 /** |
|
1894 * Checks if the current session can be preempted |
|
1895 */ |
|
1896 { // strictly in the following order |
|
1897 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:spe")); |
|
1898 |
|
1899 if( (iStackState & KMMCStackStateJobChooser) || |
|
1900 (iSessionP->iState & KMMCSessStateDoReSchedule) ) |
|
1901 return( ETrue ); |
|
1902 |
|
1903 if( (iSessionP->iBlockOn & KMMCBlockOnASSPFunction) ) |
|
1904 return( EFalse ); |
|
1905 |
|
1906 TBool preemptDC = EFalse; |
|
1907 |
|
1908 if (iSessionP->iBlockOn & KMMCBlockOnYielding) |
|
1909 { |
|
1910 // Added to support yielding the stack for a specific command. |
|
1911 preemptDC = ETrue; |
|
1912 } |
|
1913 else if( (iSessionP->iBlockOn & KMMCBlockOnDataTransfer) ) |
|
1914 { |
|
1915 // Added for SDIO Read/Wait and SDC support. This condition |
|
1916 // is set at the variant, and determines whether commands may be |
|
1917 // issued during the data transfer period. |
|
1918 if(!(iSessionP->iState & KMMCSessStateAllowDirectCommands)) |
|
1919 return( EFalse ); |
|
1920 |
|
1921 // We must consider the remaining blocking conditions |
|
1922 // before being sure that we can enable pre-emtion of this session |
|
1923 preemptDC = ETrue; |
|
1924 } |
|
1925 |
|
1926 if( (iSessionP->iBlockOn & (KMMCBlockOnCardInUse | KMMCBlockOnNoRun)) ) |
|
1927 return( ETrue ); |
|
1928 |
|
1929 if( (iConfig.iModes & KMMCModeEnablePreemption) == 0 ) |
|
1930 return( EFalse ); |
|
1931 |
|
1932 if( (iSessionP->iBlockOn & KMMCBlockOnGapTimersMask) && |
|
1933 (iConfig.iModes & KMMCModePreemptInGaps) && |
|
1934 (iSessionP->iState & KMMCSessStateSafeInGaps) ) |
|
1935 return( ETrue ); |
|
1936 |
|
1937 if( iSessionP->iBlockOn & KMMCBlockOnInterrupt ) |
|
1938 return( ETrue ); |
|
1939 |
|
1940 if(preemptDC) |
|
1941 return( ETrue ); |
|
1942 |
|
1943 return( EFalse ); |
|
1944 } |
|
1945 |
|
1946 inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedSession() |
|
1947 /** |
|
1948 * Current context analyser. Returns Exit, Loop or ChooseJob. |
|
1949 */ |
|
1950 { |
|
1951 |
|
1952 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ss")); |
|
1953 |
|
1954 // If no current session selected then we need to choose one |
|
1955 if (iSessionP == NULL) |
|
1956 return(ESchedChooseJob); |
|
1957 |
|
1958 // Check any static blocking conditions on the current session and remove if possible |
|
1959 if (SchedResolveStatBlocks(iSessionP)==ESchedLoop) |
|
1960 return(ESchedLoop); |
|
1961 |
|
1962 // If current session is still blocked, see if we could pre-empt the session |
|
1963 if (iSessionP->iBlockOn) |
|
1964 { |
|
1965 if( SchedPreemptable() ) |
|
1966 return(ESchedChooseJob); |
|
1967 |
|
1968 return( ESchedExit ); // No preemption possible |
|
1969 } |
|
1970 |
|
1971 // If the current session has been marked to be 'un-scheduled' then we |
|
1972 // need to choose another session if ones available |
|
1973 if ( (iSessionP->iState & KMMCSessStateDoReSchedule) ) |
|
1974 return( ESchedChooseJob ); |
|
1975 |
|
1976 // Check if this session requires to be run in DFC context - loop if necessary |
|
1977 if ( (iSessionP->iState & KMMCSessStateDoDFC) ) |
|
1978 { |
|
1979 iSessionP->iState &= ~KMMCSessStateDoDFC; |
|
1980 if( SchedGetOnDFC()==ESchedLoop ) |
|
1981 return( ESchedLoop ); |
|
1982 } |
|
1983 |
|
1984 // Now we actually execute the current session |
|
1985 if( iLockingSessionP != NULL ) |
|
1986 { |
|
1987 if( (iStackState & KMMCStackStateLocked) ) |
|
1988 { |
|
1989 if( iSessionP != iLockingSessionP ) |
|
1990 { |
|
1991 iLockingSessionP->iBrokenLock = ETrue; |
|
1992 iLockingSessionP = NULL; |
|
1993 DeselectsToIssue(KMMCIdleCommandsAtRestart); // use it for the number of deselects as well |
|
1994 } |
|
1995 } |
|
1996 else |
|
1997 if( iSessionP == iLockingSessionP ) |
|
1998 iStackState |= KMMCStackStateLocked; |
|
1999 } |
|
2000 |
|
2001 if( iSessionP->iInitContext != iInitContext ) |
|
2002 { |
|
2003 // If the current session's init_stack pass number is set but isn't the same as the current |
|
2004 // pass number, it indicates this session is being resumed having tried to recover from |
|
2005 // a bus inconsitency by re-initialising the stack. Set the exit code to a special |
|
2006 // value so this session can un-wind from where the initial error occured, back to the start. |
|
2007 if( iSessionP->iInitContext != 0 ) |
|
2008 //coverity[check_return] |
|
2009 //return value is not saved or checked because there is no further uses. |
|
2010 iSessionP->iMachine.SetExitCode(KMMCErrInitContext | iSessionP->iMachine.ExitCode()); |
|
2011 |
|
2012 iSessionP->iInitContext = iInitContext; |
|
2013 } |
|
2014 |
|
2015 iStackState &= ~KMMCStackStateJobChooser; |
|
2016 iSessionP->iState &= ~KMMCSessStateSafeInGaps; |
|
2017 |
|
2018 // Execute the session state machine until it completes, is blocked or is aborted. |
|
2019 TMMCErr exitCode = iSessionP->iMachine.Dispatch(); |
|
2020 |
|
2021 iStackState &= ~KMMCStackStateReScheduled; |
|
2022 |
|
2023 if( exitCode ) |
|
2024 MarkComplete( iSessionP, (exitCode & ~KMMCErrBypass) ); |
|
2025 |
|
2026 return(ESchedLoop); |
|
2027 } |
|
2028 |
|
2029 TBool DMMCStack::SchedYielding(DMMCSession* aSessP) |
|
2030 /** |
|
2031 * Check whether the scheduler should yield to another command |
|
2032 */ |
|
2033 { |
|
2034 // Test whether a full loop through the sessions has occurred during a yield |
|
2035 if ((aSessP->iBlockOn & KMMCBlockOnYielding) && (iStackState & KMMCStackStateYielding)) |
|
2036 { |
|
2037 // We've looped, now stop yielding |
|
2038 aSessP->iBlockOn &= ~KMMCBlockOnYielding; |
|
2039 iStackState &= ~KMMCStackStateYielding; |
|
2040 } |
|
2041 return(iStackState & KMMCStackStateYielding) != 0; |
|
2042 } |
|
2043 |
|
2044 TBool DMMCStack::SchedAllowDirectCommands(DMMCSession* aSessP) |
|
2045 /** |
|
2046 * Check whether direct only commands can be run. |
|
2047 */ |
|
2048 { |
|
2049 TBool allowDirectCommands = EFalse; |
|
2050 |
|
2051 // Test the remaining sessions to see if they have a DMA data transfer blockage which allow direct commands only |
|
2052 DMMCSession* testSessP = aSessP; |
|
2053 do |
|
2054 { |
|
2055 if ((testSessP->iBlockOn & KMMCBlockOnDataTransfer) && (testSessP->iState & KMMCSessStateAllowDirectCommands)) |
|
2056 allowDirectCommands = ETrue; |
|
2057 testSessP = testSessP->iLinkP; |
|
2058 } |
|
2059 while((aSessP != testSessP) && (testSessP != NULL)); |
|
2060 |
|
2061 return(allowDirectCommands); |
|
2062 } |
|
2063 |
|
2064 inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedChooseJob() |
|
2065 /** |
|
2066 * Find an unblocked job to run. Returns Exit or Loop. |
|
2067 */ |
|
2068 { |
|
2069 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:scj")); |
|
2070 |
|
2071 iStackState |= KMMCStackStateJobChooser; |
|
2072 SchedGrabEntries(); |
|
2073 DMMCSession* sessP = NULL; |
|
2074 |
|
2075 if( iLockingSessionP != NULL ) // if stack is already locked we accept only locking session |
|
2076 { |
|
2077 if( iWorkSet.IsEmpty() && iReadyQueue.Point(iLockingSessionP) ) |
|
2078 sessP = iReadyQueue.Remove(); |
|
2079 } |
|
2080 else // otherwise we might add a fresh session from reserve |
|
2081 { |
|
2082 iStackState &= ~KMMCStackStateLocked; |
|
2083 if( iWorkSet.Size() < KMMCMaxJobsInStackWorkSet && // if work set is not too big |
|
2084 !iReadyQueue.IsEmpty() && // and there are ready sessions |
|
2085 (iStackState & KMMCStackStateWaitingToLock) == 0 ) // and nobody waits to lock us |
|
2086 { |
|
2087 iReadyQueue.Point(); // at marker to preserve FIFO |
|
2088 sessP = iReadyQueue.Remove(); |
|
2089 } |
|
2090 } |
|
2091 |
|
2092 if( sessP != NULL ) |
|
2093 { |
|
2094 iWorkSet.Add( sessP ); |
|
2095 |
|
2096 if( sessP->iSessionID == ECIMLockStack ) |
|
2097 { |
|
2098 sessP->SynchBlock( KMMCBlockOnWaitToLock | KMMCBlockOnNoRun ); |
|
2099 sessP->iBrokenLock = EFalse; |
|
2100 iStackState |= KMMCStackStateWaitingToLock; |
|
2101 } |
|
2102 } |
|
2103 |
|
2104 if( iSessionP != NULL ) |
|
2105 iWorkSet.AdvanceMarker(); // move current session to the end of the queue |
|
2106 |
|
2107 iWorkSet.Point(); |
|
2108 |
|
2109 while( (sessP = iWorkSet) != NULL ) |
|
2110 { |
|
2111 // first, remove all static blocking conditions |
|
2112 if( SchedResolveStatBlocks(sessP) ) |
|
2113 return( ESchedLoop ); |
|
2114 |
|
2115 TBool scheduleSession = ETrue; |
|
2116 // Test whether we are yielding |
|
2117 if (SchedYielding(sessP) && (sessP->Command().iSpec.iCommandType != iYieldCommandType)) |
|
2118 scheduleSession = EFalse; |
|
2119 // Test whether this session is blocked |
|
2120 else if (sessP->iBlockOn) |
|
2121 scheduleSession = EFalse; |
|
2122 // Test whether we can only handle direct commands |
|
2123 else if (SchedAllowDirectCommands(sessP) && (sessP->Command().iSpec.iCommandType != ECmdTypeADC)) |
|
2124 scheduleSession = EFalse; |
|
2125 |
|
2126 if (scheduleSession) |
|
2127 { |
|
2128 iWorkSet.SetMarker(); |
|
2129 SchedSetContext( sessP ); |
|
2130 return( ESchedLoop ); |
|
2131 } |
|
2132 |
|
2133 iWorkSet++; |
|
2134 } |
|
2135 |
|
2136 return( ESchedExit ); |
|
2137 } |
|
2138 |
|
2139 void DMMCStack::StackDFC(TAny* aStackP) |
|
2140 /** |
|
2141 * This DFC is used to startup Stack Scheduler from the background. |
|
2142 */ |
|
2143 { |
|
2144 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sdf")); |
|
2145 |
|
2146 DMMCStack* const stackP = static_cast<DMMCStack*>(aStackP); |
|
2147 stackP->Scheduler( stackP->iDFCRunning ); |
|
2148 } |
|
2149 |
|
2150 void DMMCStack::Scheduler(volatile TBool& aFlag) |
|
2151 /** |
|
2152 * This is the main function which controls, monitors and synchronises session execution. |
|
2153 * It's divided into the entry function Scheduler() and the scheduling mechanism itself, |
|
2154 * DoSchedule() |
|
2155 */ |
|
2156 { |
|
2157 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sch")); |
|
2158 |
|
2159 DISABLEPREEMPTION |
|
2160 aFlag = ETrue; |
|
2161 |
|
2162 if( iStackState & KMMCStackStateRunning ) |
|
2163 { |
|
2164 RESTOREPREEMPTION |
|
2165 return; |
|
2166 } |
|
2167 |
|
2168 iStackState |= KMMCStackStateRunning; |
|
2169 RESTOREPREEMPTION |
|
2170 DoSchedule(); |
|
2171 } |
|
2172 |
|
2173 void DMMCStack::DoSchedule() |
|
2174 { |
|
2175 __KTRACE_OPT(KPBUS1,Kern::Printf(">mst:dos")); |
|
2176 |
|
2177 for(;;) |
|
2178 { |
|
2179 for(;;) |
|
2180 { |
|
2181 if( iAbortReq && SchedAbortPass() ) |
|
2182 continue; |
|
2183 |
|
2184 if( iDFCRunning ) |
|
2185 iStackState &= ~KMMCStackStateWaitingDFC; |
|
2186 else |
|
2187 if( iStackState & KMMCStackStateWaitingDFC ) |
|
2188 break; |
|
2189 |
|
2190 if( iCompReq && SchedCompletionPass() ) |
|
2191 continue; |
|
2192 |
|
2193 if( iInitialise && SchedInitStack() ) |
|
2194 continue; |
|
2195 |
|
2196 if( iSleep && SchedSleepStack() ) |
|
2197 continue; |
|
2198 |
|
2199 iAttention = EFalse; |
|
2200 |
|
2201 DMMCStack::TMMCStackSchedStateEnum toDo = SchedSession(); |
|
2202 |
|
2203 if( toDo == ESchedLoop ) |
|
2204 continue; |
|
2205 |
|
2206 if( toDo == ESchedExit ) |
|
2207 break; |
|
2208 |
|
2209 if( SchedChooseJob() == ESchedExit ) |
|
2210 break; |
|
2211 } |
|
2212 |
|
2213 DISABLEPREEMPTION |
|
2214 |
|
2215 if( !iAbortReq && |
|
2216 ((iStackState & KMMCStackStateWaitingDFC) || |
|
2217 (iCompReq | iInitialise | iAttention)==0) || |
|
2218 ((iSessionP) && (iSessionP->iState & KMMCSessStateAllowDirectCommands))) |
|
2219 { |
|
2220 // Clear DFC flag here in case somebody was running scheduler in the background |
|
2221 // when DFC turned up. This should never really happen, but with EPOC who knows |
|
2222 iStackState &= ~KMMCStackStateRunning; |
|
2223 iDFCRunning = EFalse; |
|
2224 |
|
2225 RESTOREPREEMPTION |
|
2226 __KTRACE_OPT(KPBUS1,Kern::Printf("<mst:dos")); |
|
2227 return; |
|
2228 } |
|
2229 |
|
2230 RESTOREPREEMPTION |
|
2231 } |
|
2232 } |
|
2233 |
|
2234 // |
|
2235 // DMMCStack:: --- Session service --- |
|
2236 // |
|
2237 void DMMCStack::Add(DMMCSession* aSessP) |
|
2238 /** |
|
2239 * Adds session aSessP to the EntryQueue (asynchronous function) |
|
2240 */ |
|
2241 { |
|
2242 ASSERT_NOT_ISR_CONTEXT |
|
2243 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Add %d",TUint(aSessP->iSessionID))); |
|
2244 |
|
2245 DISABLEPREEMPTION |
|
2246 iEntryQueue.Add( aSessP ); |
|
2247 aSessP->iState |= KMMCSessStateEngaged; |
|
2248 RESTOREPREEMPTION |
|
2249 Scheduler( iAttention ); |
|
2250 } |
|
2251 |
|
2252 void DMMCStack::Abort(DMMCSession* aSessP) |
|
2253 /** |
|
2254 * Aborts a session |
|
2255 */ |
|
2256 { |
|
2257 ASSERT_NOT_ISR_CONTEXT |
|
2258 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:abt")); |
|
2259 |
|
2260 if( !aSessP->IsEngaged() ) |
|
2261 return; |
|
2262 |
|
2263 aSessP->iDoAbort = ETrue; |
|
2264 aSessP->iMachine.Abort(); |
|
2265 |
|
2266 Scheduler( iAbortReq ); |
|
2267 } |
|
2268 |
|
2269 void DMMCStack::Stop(DMMCSession* aSessP) |
|
2270 /** |
|
2271 * Signals session to stop |
|
2272 */ |
|
2273 { |
|
2274 ASSERT_NOT_ISR_CONTEXT |
|
2275 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:stp")); |
|
2276 |
|
2277 if( !aSessP->IsEngaged() ) |
|
2278 return; |
|
2279 |
|
2280 aSessP->iDoStop = ETrue; |
|
2281 } |
|
2282 |
|
2283 EXPORT_C void DMMCStack::Block(DMMCSession* aSessP, TUint32 aFlag) |
|
2284 { |
|
2285 ASSERT_NOT_ISR_CONTEXT |
|
2286 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:blk")); |
|
2287 |
|
2288 if( !aSessP->IsEngaged() ) |
|
2289 return; |
|
2290 |
|
2291 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:blk:[aFlag=%08x, iBlockOn=%08x]", aFlag, aSessP->iBlockOn)); |
|
2292 |
|
2293 (void)__e32_atomic_ior_ord32(&aSessP->iBlockOn, aFlag); |
|
2294 } |
|
2295 |
|
2296 EXPORT_C void DMMCStack::UnBlock(DMMCSession* aSessP, TUint32 aFlag, TMMCErr anExitCode) |
|
2297 /** |
|
2298 * aFlag is a bitset of KMMCBlockOnXXX events that have occured. If the stack's |
|
2299 * session is waiting on all of these events, then it is scheduled. |
|
2300 */ |
|
2301 { |
|
2302 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ubl")); |
|
2303 |
|
2304 if (aSessP != NULL) |
|
2305 { |
|
2306 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ubl:[aFlag=%08x, iBlockOn=%08x", aFlag, aSessP->iBlockOn)); |
|
2307 |
|
2308 if( (aSessP->iBlockOn & aFlag) == 0 ) |
|
2309 return; |
|
2310 |
|
2311 // Must be either in a DFC or have the KMMCSessStateDoDFC flag set |
|
2312 __ASSERT_DEBUG( |
|
2313 (aSessP->iState & KMMCSessStateDoDFC) != 0 || |
|
2314 NKern::CurrentContext() != NKern::EInterrupt, |
|
2315 DMMCSocket::Panic(DMMCSocket::EMMCUnblockingInWrongContext)); |
|
2316 |
|
2317 (void)__e32_atomic_and_ord32(&aSessP->iBlockOn, ~aFlag); |
|
2318 aSessP->iMachine.SetExitCode( anExitCode ); |
|
2319 |
|
2320 if( aSessP->iBlockOn == 0 ) |
|
2321 Scheduler( iAttention ); |
|
2322 } |
|
2323 } |
|
2324 |
|
2325 void DMMCStack::UnlockStack(DMMCSession* aSessP) |
|
2326 /** |
|
2327 * Removes stack lock. Asynchronous function. |
|
2328 */ |
|
2329 { |
|
2330 ASSERT_NOT_ISR_CONTEXT |
|
2331 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ust")); |
|
2332 |
|
2333 aSessP->iBrokenLock = EFalse; |
|
2334 |
|
2335 if( aSessP == iLockingSessionP ) |
|
2336 { |
|
2337 iLockingSessionP = NULL; |
|
2338 Scheduler( iAttention ); |
|
2339 } |
|
2340 } |
|
2341 |
|
2342 EXPORT_C TInt DMMCStack::Stop(TMMCard* aCardP) |
|
2343 /** |
|
2344 * Completes all sessions operating with a specified card with KMMCErrAbort. |
|
2345 * Returns either KErrNone or KErrServerBusy. |
|
2346 */ |
|
2347 { |
|
2348 ASSERT_NOT_ISR_CONTEXT |
|
2349 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:stp")); |
|
2350 |
|
2351 DISABLEPREEMPTION |
|
2352 |
|
2353 if( iStackState & KMMCStackStateRunning ) |
|
2354 { |
|
2355 RESTOREPREEMPTION |
|
2356 return( KErrServerBusy ); // can not operate in foreground |
|
2357 } |
|
2358 |
|
2359 iStackState |= KMMCStackStateRunning; |
|
2360 RESTOREPREEMPTION |
|
2361 |
|
2362 DMMCSession* sessP; |
|
2363 SchedGrabEntries(); |
|
2364 |
|
2365 iWorkSet.Point(); |
|
2366 |
|
2367 while( (sessP = iWorkSet++) != NULL ) |
|
2368 if( sessP->iCardP == aCardP ) |
|
2369 MarkComplete( sessP, KMMCErrAbort ); |
|
2370 |
|
2371 iReadyQueue.Point(); |
|
2372 |
|
2373 while( (sessP = iReadyQueue) != NULL ) |
|
2374 if( sessP->iCardP == aCardP ) |
|
2375 { |
|
2376 MarkComplete( sessP, KMMCErrAbort ); |
|
2377 iReadyQueue.Remove(); |
|
2378 iWorkSet.Add( sessP ); |
|
2379 } |
|
2380 else |
|
2381 iReadyQueue++; |
|
2382 |
|
2383 SchedGetOnDFC(); |
|
2384 DoSchedule(); |
|
2385 return( KErrNone ); |
|
2386 } |
|
2387 |
|
2388 void DMMCStack::MarkComplete(DMMCSession* aSessP, TMMCErr anExitCode) |
|
2389 /** |
|
2390 * Marks session to be completed on the next scheduler pass. |
|
2391 */ |
|
2392 { |
|
2393 ASSERT_NOT_ISR_CONTEXT |
|
2394 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:mcp")); |
|
2395 |
|
2396 aSessP->SynchBlock( KMMCBlockOnNoRun ); |
|
2397 aSessP->iMMCExitCode = anExitCode; |
|
2398 aSessP->iDoComplete = ETrue; |
|
2399 iCompReq = ETrue; |
|
2400 } |
|
2401 |
|
2402 // |
|
2403 // DMMCStack:: --- Miscellaneous --- |
|
2404 // |
|
2405 EXPORT_C TUint32 DMMCStack::EffectiveModes(const TMMCStackConfig& aClientConfig) |
|
2406 /** |
|
2407 * Calculates effective client modes as real client modes merged with iMasterConfig modes |
|
2408 */ |
|
2409 { |
|
2410 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:em")); |
|
2411 |
|
2412 const TUint32 masterMode = (iMasterConfig.iModes & iMasterConfig.iUpdateMask) | |
|
2413 (KMMCModeDefault & ~iMasterConfig.iUpdateMask); |
|
2414 |
|
2415 const TUint32 c = aClientConfig.iClientMask; |
|
2416 const TUint32 u = aClientConfig.iUpdateMask; |
|
2417 const TUint32 m = aClientConfig.iModes; |
|
2418 const TUint32 userMode = (c & ((m & u) | ~u)) | (m & KMMCModeMask); |
|
2419 const TUint32 userMask = (u | KMMCModeClientMask) & |
|
2420 ((masterMode & KMMCModeMasterOverrides) | ~KMMCModeMasterOverrides); |
|
2421 |
|
2422 const TUint32 effectiveMode = (userMode & userMask) | (masterMode & ~userMask); |
|
2423 |
|
2424 if( effectiveMode & KMMCModeEnableClientConfig ) |
|
2425 return( effectiveMode ); |
|
2426 else |
|
2427 return( (effectiveMode & KMMCModeClientOverrides) | |
|
2428 (masterMode & ~(KMMCModeClientOverrides | KMMCModeClientMask)) ); |
|
2429 } |
|
2430 |
|
2431 void DMMCStack::MergeConfig(DMMCSession* aSessP) |
|
2432 /** |
|
2433 * Merges client and master configuration into iConfig |
|
2434 */ |
|
2435 { |
|
2436 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:mc")); |
|
2437 |
|
2438 TMMCStackConfig& cC = aSessP->iConfig; |
|
2439 TMMCStackConfig& mC = iMasterConfig; |
|
2440 const TUint32 modes = EffectiveModes( cC ); |
|
2441 const TUint32 mastM = mC.iClientMask; |
|
2442 |
|
2443 iConfig.iModes = modes; |
|
2444 |
|
2445 iConfig.iPollAttempts = |
|
2446 (modes & KMMCModeClientPollAttempts) |
|
2447 ? cC.iPollAttempts |
|
2448 : ((mastM & KMMCModeClientPollAttempts) ? mC.iPollAttempts : KMMCMaxPollAttempts); |
|
2449 |
|
2450 iConfig.iTimeOutRetries = |
|
2451 (modes & KMMCModeClientTimeOutRetries) |
|
2452 ? cC.iTimeOutRetries |
|
2453 : ((mastM & KMMCModeClientTimeOutRetries) ? mC.iTimeOutRetries : KMMCMaxTimeOutRetries); |
|
2454 |
|
2455 iConfig.iCRCRetries = |
|
2456 (modes & KMMCModeClientCRCRetries) |
|
2457 ? cC.iCRCRetries |
|
2458 : ((mastM & KMMCModeClientCRCRetries) ? mC.iCRCRetries : KMMCMaxCRCRetries); |
|
2459 |
|
2460 iConfig.iUnlockRetries = |
|
2461 (modes & KMMCModeClientUnlockRetries) |
|
2462 ? cC.iUnlockRetries |
|
2463 : ((mastM & KMMCModeClientUnlockRetries) ? mC.iUnlockRetries : KMMCMaxUnlockRetries); |
|
2464 |
|
2465 iConfig.iOpCondBusyTimeout = |
|
2466 (modes & KMMCModeClientiOpCondBusyTimeout) |
|
2467 ? cC.iOpCondBusyTimeout |
|
2468 : ((mastM & KMMCModeClientiOpCondBusyTimeout) ? mC.iOpCondBusyTimeout : KMMCMaxOpCondBusyTimeout); |
|
2469 |
|
2470 // There are no default constants defining BusConfig parameters. |
|
2471 // iMasterConfig.iBusConfig must be initialised by ASSP layer |
|
2472 |
|
2473 // _?_? The code below can be modified later for a card controlled session |
|
2474 // to include CSD analisys and calculate time-out and clock parameters on that basis. |
|
2475 // As it written now, the defaults for all cards will be the same. |
|
2476 |
|
2477 if( modes & KMMCModeClientBusClock ) |
|
2478 { |
|
2479 TUint clock = cC.iBusConfig.iBusClock; |
|
2480 if( clock > mC.iBusConfig.iBusClock ) |
|
2481 clock = mC.iBusConfig.iBusClock; |
|
2482 if( clock < KMMCBusClockFOD ) |
|
2483 clock = KMMCBusClockFOD; |
|
2484 DoSetClock(clock); |
|
2485 } |
|
2486 else if( modes & KMMCModeCardControlled && aSessP->CardP() ) |
|
2487 { |
|
2488 TUint clock = MaxTranSpeedInKilohertz(*aSessP->CardP()); |
|
2489 if( clock > mC.iBusConfig.iBusClock ) |
|
2490 clock = mC.iBusConfig.iBusClock; |
|
2491 if( clock < KMMCBusClockFOD ) |
|
2492 clock = KMMCBusClockFOD; |
|
2493 DoSetClock(clock); |
|
2494 } |
|
2495 else |
|
2496 DoSetClock(mC.iBusConfig.iBusClock); |
|
2497 |
|
2498 iConfig.iBusConfig.iClockIn = (modes & KMMCModeClientClockIn) |
|
2499 ? cC.iBusConfig.iClockIn |
|
2500 : mC.iBusConfig.iClockIn; |
|
2501 |
|
2502 iConfig.iBusConfig.iClockOut = (modes & KMMCModeClientClockOut) |
|
2503 ? cC.iBusConfig.iClockOut |
|
2504 : mC.iBusConfig.iClockOut; |
|
2505 |
|
2506 iConfig.iBusConfig.iResponseTimeOut = (modes & KMMCModeClientResponseTimeOut) |
|
2507 ? cC.iBusConfig.iResponseTimeOut |
|
2508 : mC.iBusConfig.iResponseTimeOut; |
|
2509 |
|
2510 iConfig.iBusConfig.iDataTimeOut = (modes & KMMCModeClientDataTimeOut) |
|
2511 ? cC.iBusConfig.iDataTimeOut |
|
2512 : mC.iBusConfig.iDataTimeOut; |
|
2513 |
|
2514 iConfig.iBusConfig.iBusyTimeOut = (modes & KMMCModeClientBusyTimeOut) |
|
2515 ? cC.iBusConfig.iBusyTimeOut |
|
2516 : mC.iBusConfig.iBusyTimeOut; |
|
2517 } |
|
2518 |
|
2519 TBool DMMCStack::StaticBlocks() |
|
2520 /** |
|
2521 * This function realises the potential blocking conditions of the current session. |
|
2522 * Returns ETrue if the session has to be stopped right now |
|
2523 */ |
|
2524 { |
|
2525 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:stb")); |
|
2526 |
|
2527 if( iSessionP->iDoStop ) |
|
2528 { |
|
2529 MarkComplete( iSessionP, KMMCErrAbort ); |
|
2530 return( ETrue ); |
|
2531 } |
|
2532 |
|
2533 if( !iDFCRunning && (iSessionP->iState & KMMCSessStateDoDFC) ) |
|
2534 return( ETrue ); |
|
2535 |
|
2536 return( (iSessionP->iState & KMMCSessStateDoReSchedule) != 0 ); |
|
2537 } |
|
2538 |
|
2539 |
|
2540 EXPORT_C TBool DMMCStack::CardDetect(TUint /*aCardNumber*/) |
|
2541 /** |
|
2542 * Returns ETrue when a card is present in the card socket 'aCardNumber'. |
|
2543 * Default implementation when not provided by ASSP layer. |
|
2544 */ |
|
2545 { |
|
2546 return(ETrue); |
|
2547 } |
|
2548 |
|
2549 EXPORT_C TBool DMMCStack::WriteProtected(TUint /*aCardNumber*/) |
|
2550 /** |
|
2551 * Returns ETrue when the card in socket 'aCardNumber' is mechanically write |
|
2552 * protected. |
|
2553 * Default implementation when not provided by ASSP layer. |
|
2554 */ |
|
2555 { |
|
2556 return(EFalse); |
|
2557 } |
|
2558 |
|
2559 // -------- DMMCStack State Machine functions -------- |
|
2560 // |
|
2561 |
|
2562 // Auxiliary SM function service |
|
2563 |
|
2564 void DMMCStack::StackSessionCBST(TAny* aStackP) |
|
2565 /** |
|
2566 * Stack Session completion routine. |
|
2567 */ |
|
2568 { |
|
2569 |
|
2570 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sscbs")); |
|
2571 static_cast<DMMCStack *>(aStackP)->StackSessionCB(); |
|
2572 } |
|
2573 |
|
2574 |
|
2575 TInt DMMCStack::StackSessionCB() |
|
2576 { |
|
2577 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sscb ")); |
|
2578 |
|
2579 if (iStackState & KMMCStackStateSleepinProgress) |
|
2580 { |
|
2581 // Sleep completed update stack state |
|
2582 iStackState &= ~KMMCStackStateSleepinProgress; |
|
2583 return( 0 ); |
|
2584 } |
|
2585 |
|
2586 TMMCErr mmcError = iStackSession->MMCExitCode(); |
|
2587 iStackState &= ~KMMCStackStateInitInProgress; |
|
2588 |
|
2589 TInt errCode = KErrNone; |
|
2590 TBool anyLocked = EFalse; |
|
2591 |
|
2592 if (mmcError != KMMCErrNone) |
|
2593 { |
|
2594 // |
|
2595 // StackSessionCB is the completion callback for the internal initialisation/power-up |
|
2596 // session, so we never expect a callback while initialisation is still in progress. |
|
2597 // - unless a card has failed to respond and the controller has not detected the error (!) |
|
2598 // |
|
2599 errCode = KErrTimedOut; // this error code is not sticky, so should allow stack to be powered up again |
|
2600 iInitialise = EFalse; |
|
2601 iStackState &= ~(KMMCStackStateInitInProgress | KMMCStackStateInitPending | KMMCStackStateBusInconsistent); |
|
2602 } |
|
2603 else |
|
2604 { |
|
2605 if (! iCardArray->CardsPresent()) |
|
2606 { |
|
2607 errCode = KErrNotReady; |
|
2608 } |
|
2609 else |
|
2610 { |
|
2611 // Stack initialized ok, so complete request or start auto-unlock |
|
2612 |
|
2613 iInitState = EISDone; |
|
2614 |
|
2615 // remove bindings from password store for cards that do not have passwords |
|
2616 TUint i; |
|
2617 for (i = 0; i < iMaxCardsInStack; ++i) |
|
2618 { |
|
2619 TMMCard& cd = *(iCardArray->CardP(i)); |
|
2620 if (cd.IsPresent()) |
|
2621 { |
|
2622 if (cd.HasPassword()) |
|
2623 anyLocked = ETrue; |
|
2624 else |
|
2625 { |
|
2626 TMapping* pmp = iSocket->iPasswordStore->FindMappingInStore(cd.CID()); |
|
2627 if (pmp) |
|
2628 pmp->iState = TMapping::EStInvalid; |
|
2629 } |
|
2630 } // if (cd.IsPresent()) |
|
2631 } // for (i = 0; i < iMaxCardsInStack; ++i) |
|
2632 |
|
2633 // if any cards are locked then launch auto-unlock mechanism |
|
2634 if (anyLocked) |
|
2635 { |
|
2636 // |
|
2637 // During power up (stack session context), we use the iAutoUnlockSession |
|
2638 // to perform auto-unlock of the cards. Upon completion of the AutoUnlock |
|
2639 // state machine, the local media subsystem is notified via ::PowerUpSequenceComplete |
|
2640 // |
|
2641 iAutoUnlockSession.SetStack(this); |
|
2642 iAutoUnlockSession.SetupCIMAutoUnlock(); |
|
2643 |
|
2644 errCode = iAutoUnlockSession.Engage(); |
|
2645 if(errCode == KErrNone) |
|
2646 { |
|
2647 // don't complete power up request yet |
|
2648 // - This will be done in DMMCStack::AutoUnlockCB() |
|
2649 return 0; |
|
2650 } |
|
2651 } |
|
2652 } // else ( !iCardArray->CardsPresent() ) |
|
2653 } |
|
2654 |
|
2655 if(errCode == KErrNone) |
|
2656 { |
|
2657 // |
|
2658 // No cards are locked (otherwise we will have engaged iAutoUnlockSession) and we |
|
2659 // have encountered no error, so can now continue with the second-stage initialisation |
|
2660 // phase (InitStackAfterUnlock). This performs initialisation that can only be |
|
2661 // performed when a card is unlocked (such as setting bus width, speed class etc..) |
|
2662 // |
|
2663 // iAutoUnlockSession::AutoUnlockCB will complete power-up by calling ::PowerUpSequenceComplete |
|
2664 // |
|
2665 iAutoUnlockSession.SetStack(this); |
|
2666 iAutoUnlockSession.iCardP = NULL; |
|
2667 iAutoUnlockSession.SetupCIMInitStackAfterUnlock(); |
|
2668 errCode = iAutoUnlockSession.Engage(); |
|
2669 } |
|
2670 |
|
2671 if(errCode != KErrNone) |
|
2672 { |
|
2673 // |
|
2674 // We have encountered an error during power up initialisation |
|
2675 // - Complete the request and notify the local media subsystem. |
|
2676 // |
|
2677 |
|
2678 // Calling PowerUpSequenceComplete() with an error may result in the media driver being closed which will delete |
|
2679 // the media driver's session, so the stack must be made re-entrant here to allow all references to any engaged |
|
2680 // sessions to be removed from the stack immediately to prevent the stack from referencing a deleted object |
|
2681 __ASSERT_ALWAYS(iStackState & KMMCStackStateRunning, DMMCSocket::Panic(DMMCSocket::EMMCNotInDfcContext)); |
|
2682 iStackState &= ~KMMCStackStateRunning; |
|
2683 iSocket->PowerUpSequenceComplete(errCode); |
|
2684 iStackState |= KMMCStackStateRunning; |
|
2685 |
|
2686 } |
|
2687 |
|
2688 return( 0 ); |
|
2689 } |
|
2690 |
|
2691 void DMMCStack::AutoUnlockCBST(TAny *aStackP) |
|
2692 { |
|
2693 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:aucbs")); |
|
2694 |
|
2695 static_cast<DMMCStack *>(aStackP)->AutoUnlockCB(); |
|
2696 } |
|
2697 |
|
2698 |
|
2699 TInt DMMCStack::AutoUnlockCB() |
|
2700 { |
|
2701 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:aucb")); |
|
2702 |
|
2703 // This is the session end callback for iAutoUnlockSession, |
|
2704 // called at the end of the power up and initialisation process. |
|
2705 |
|
2706 TInt epocErr = iAutoUnlockSession.EpocErrorCode(); |
|
2707 |
|
2708 // Calling PowerUpSequenceComplete() with an error may result in the media driver being closed which will delete |
|
2709 // the media driver's session, so the stack must be made re-entrant here to allow all references to any engaged |
|
2710 // sessions to be removed from the stack immediately to prevent the stack from referencing a deleted object |
|
2711 __ASSERT_ALWAYS(iStackState & KMMCStackStateRunning, DMMCSocket::Panic(DMMCSocket::EMMCNotInDfcContext)); |
|
2712 if (epocErr != KErrNone) |
|
2713 iStackState &= ~KMMCStackStateRunning; |
|
2714 iSocket->PowerUpSequenceComplete(epocErr); |
|
2715 iStackState |= KMMCStackStateRunning; |
|
2716 |
|
2717 return 0; |
|
2718 } |
|
2719 |
|
2720 |
|
2721 inline TMMCErr DMMCStack::AttachCardSM() |
|
2722 /** |
|
2723 * This SM function must be invoked by every session which is CardControlled. |
|
2724 * |
|
2725 * Some commands require that the data held by the stack for a given card is up to date. |
|
2726 * |
|
2727 * These are card controlled commands. Before such commands are issued, this function should |
|
2728 * first be invoked which performs the SEND_STATUS (CMD13) command. |
|
2729 * |
|
2730 * @return MMC error code |
|
2731 */ |
|
2732 { |
|
2733 __KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ac")); |
|
2734 |
|
2735 enum states |
|
2736 { |
|
2737 EStBegin=0, |
|
2738 EStAttStatus, |
|
2739 EStEnd |
|
2740 }; |
|
2741 |
|
2742 DMMCSession& s=Session(); |
|
2743 |
|
2744 SMF_BEGIN |
|
2745 |
|
2746 if( s.iCardP == NULL ) |
|
2747 return( KMMCErrNoCard ); |
|
2748 |
|
2749 if( s.iCardP->iUsingSessionP != NULL && s.iCardP->iUsingSessionP != &s ) |
|
2750 { |
|
2751 s.SynchBlock( KMMCBlockOnCardInUse ); |
|
2752 SMF_WAIT |
|
2753 } |
|
2754 |
|
2755 if( s.iCardP->IsPresent() && s.iCardP->iCID == s.iCID ) |
|
2756 s.iCardP->iUsingSessionP = &s; |
|
2757 else |
|
2758 return( KMMCErrNoCard ); |
|
2759 |
|
2760 s.iConfig.SetMode( KMMCModeCardControlled ); // for future context switching |
|
2761 iConfig.SetMode( KMMCModeCardControlled ); // for this context |
|
2762 |
|
2763 // read card status if there are sticky bits in it |
|
2764 if( (TUint32(s.iCardP->iStatus) & KMMCStatClearByReadMask) == 0 || |
|
2765 s.iCardP->iLastCommand == ECmdSendStatus || |
|
2766 s.iSessionID == ECIMNakedSession ) |
|
2767 SMF_EXIT |
|
2768 |
|
2769 s.PushCommandStack(); |
|
2770 s.FillCommandDesc( ECmdSendStatus, 0 ); |
|
2771 m.SetTraps( KMMCErrBasic ); // to restore command stack position to its original level |
|
2772 SMF_INVOKES( ExecCommandSMST, EStAttStatus ) |
|
2773 |
|
2774 SMF_STATE(EStAttStatus) |
|
2775 |
|
2776 s.PopCommandStack(); |
|
2777 SMF_RETURN( err ) |
|
2778 |
|
2779 SMF_END |
|
2780 } |
|
2781 |
|
2782 inline TMMCErr DMMCStack::CIMInitStackSM() |
|
2783 /** |
|
2784 * Performs the Perform the CIM_INIT_STACK macro. |
|
2785 * @return MMC error code |
|
2786 */ |
|
2787 { |
|
2788 enum states |
|
2789 { |
|
2790 EStBegin=0, |
|
2791 EStInitDone, |
|
2792 EStEnd |
|
2793 }; |
|
2794 |
|
2795 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:InitStackSM")); |
|
2796 |
|
2797 DMMCSession& s=Session(); |
|
2798 |
|
2799 SMF_BEGIN |
|
2800 |
|
2801 m.SetTraps( KMMCErrAll ); // to prevent this macro from infinite restarts via iInitialise |
|
2802 |
|
2803 SMF_INVOKES( CIMUpdateAcqSMST, EStInitDone ) |
|
2804 |
|
2805 SMF_STATE(EStInitDone) |
|
2806 |
|
2807 s.iState &= ~KMMCSessStateInProgress; // now we won't be restarted |
|
2808 SchedGetOnDFC(); // StackSessionCB must be on DFC |
|
2809 SMF_RETURN( err ) // _?_ power cycles can be performed here if error |
|
2810 |
|
2811 SMF_END |
|
2812 } |
|
2813 |
|
2814 TMMCErr DMMCStack::CIMUpdateAcqSM() |
|
2815 /** |
|
2816 * Performs an identification of a card stack. New cards are always |
|
2817 * initialised but if KMMCStackStateInitInProgress is FALSE then existing |
|
2818 * cards keep their configuration. |
|
2819 * After successful execution of this function, all cards will be in standby |
|
2820 * state. |
|
2821 * If iPoweredUp is FALSE then the stack is powered up and a full INIT_STACK |
|
2822 * is performed (i.e all cards set to idle and then initialized). |
|
2823 * @return MMC error code |
|
2824 */ |
|
2825 { |
|
2826 enum states |
|
2827 { |
|
2828 EStBegin=0, |
|
2829 EStPoweredUp, |
|
2830 EStClockOn, |
|
2831 EStStartInterrogation, |
|
2832 EStCheckStack, |
|
2833 EStCardCap, |
|
2834 EStIssueDSR, |
|
2835 EStFinishUp, |
|
2836 EStEnd |
|
2837 }; |
|
2838 |
|
2839 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:UpdAcqSM")); |
|
2840 |
|
2841 DMMCSession& s=Session(); |
|
2842 DMMCPsu* psu=(DMMCPsu*)iSocket->iVcc; |
|
2843 |
|
2844 SMF_BEGIN |
|
2845 |
|
2846 // This macro works naked and must not be preempted |
|
2847 iConfig.RemoveMode( KMMCModeEnablePreemption | KMMCModeCardControlled ); |
|
2848 // Ensure DFC is running before and after powering up |
|
2849 if( SchedGetOnDFC() ) // Such a direct synchronisation with Scheduler() can only |
|
2850 SMF_WAIT // be used in this macro |
|
2851 |
|
2852 s.iState |= (KMMCSessStateInProgress | KMMCSessStateCritical); |
|
2853 |
|
2854 if (iPoweredUp) |
|
2855 SMF_GOTOS( EStPoweredUp ) |
|
2856 |
|
2857 // The bus is not powered so all cards need initialising - enforce INIT_STACK. |
|
2858 iStackState |= KMMCStackStateInitInProgress; |
|
2859 |
|
2860 // Need to turn on the PSU at it's default voltage. Let the ASSP layer choose |
|
2861 // this voltage by calling SetVoltage() with the full range the ASSP supports. |
|
2862 iCurrentOpRange=(psu->VoltageSupported() & ~KMMCAdjustableOpVoltage); |
|
2863 psu->SetVoltage(iCurrentOpRange); |
|
2864 SMF_INVOKES( DoPowerUpSMST, EStPoweredUp ) |
|
2865 |
|
2866 SMF_STATE(EStPoweredUp) |
|
2867 |
|
2868 // Switch on the bus clock in identification mode |
|
2869 SetBusConfigDefaults(iMasterConfig.iBusConfig, KMMCBusClockFOD); |
|
2870 DoSetClock(KMMCBusClockFOD); |
|
2871 |
|
2872 // Make sure controller is in 1-bit bus width mode |
|
2873 DoSetBusWidth(EBusWidth1); |
|
2874 |
|
2875 MergeConfig(&s); // This might take some time, but we are running in DFC here |
|
2876 // Reinstate config bits after the merge |
|
2877 iConfig.RemoveMode( KMMCModeEnablePreemption | KMMCModeCardControlled ); |
|
2878 SMF_INVOKES( InitClockOnSMST, EStClockOn ) // Feed init clock to the bus |
|
2879 |
|
2880 SMF_STATE(EStClockOn) |
|
2881 |
|
2882 // Check if there are any cards present in the stack |
|
2883 if (!HasCardsPresent()) |
|
2884 SMF_GOTOS( EStCheckStack ) |
|
2885 |
|
2886 if( !InitStackInProgress() ) |
|
2887 SMF_GOTOS( EStStartInterrogation ) |
|
2888 |
|
2889 // Increment the stack's initialiser pass number. Set the current session's pass |
|
2890 // number to the new value. Pass number may be used later on to detect sessions |
|
2891 // which have been re-initialized due to problems on the bus. |
|
2892 if ((++iInitContext) == 0) |
|
2893 iInitContext++; // Pass number must never be zero |
|
2894 s.iInitContext = iInitContext; // this session is always in a proper context |
|
2895 |
|
2896 SMF_STATE(EStStartInterrogation) |
|
2897 |
|
2898 // NB: RCAs are not unlocked here. They will be unlocked one by one during the update of card info array. |
|
2899 SMF_INVOKES( AcquireStackSMST, EStCheckStack ) |
|
2900 |
|
2901 SMF_STATE(EStCheckStack) |
|
2902 |
|
2903 // Check that all known cards are still present by issuing select/deselect |
|
2904 SMF_INVOKES( CheckStackSMST, EStCardCap ) |
|
2905 |
|
2906 SMF_STATE(EStCardCap) |
|
2907 |
|
2908 // Call a licencee-specific state machine to allow card capabilities to be modified. |
|
2909 SMF_INVOKES( ModifyCardCapabilitySMST, EStIssueDSR ) |
|
2910 |
|
2911 SMF_STATE(EStIssueDSR) |
|
2912 |
|
2913 // Now that we have updated the card entries, do any final initialisation |
|
2914 // of the card entries and determine the maximum bus clock that can be employed. |
|
2915 // |
|
2916 // If the bus is not multiplexed (ie - MMC stack), then the max clock is set to |
|
2917 // the lowest common denominator of all cards in the stack. Otherwise (in the case |
|
2918 // of a multiplexed bus such as SD), the highest clock is returned and the clock |
|
2919 // rate is changed when a new card is selected. |
|
2920 // |
|
2921 TUint maxClk; |
|
2922 iCardArray->UpdateAcquisitions(&maxClk); |
|
2923 SetBusConfigDefaults( iMasterConfig.iBusConfig, maxClk ); |
|
2924 DoSetClock(maxClk); |
|
2925 |
|
2926 // merge clock from iMasterConfig.iBusConfig.iBusClock to |
|
2927 // iConfig.iBusConfig.iBusClock - which the PSL should use to configure it's clock |
|
2928 MergeConfig(&s); |
|
2929 |
|
2930 // switch to normal iConfig clock mode |
|
2931 InitClockOff(); |
|
2932 |
|
2933 SMF_STATE(EStFinishUp) |
|
2934 |
|
2935 s.iState &= ~(KMMCSessStateInProgress | KMMCSessStateCritical); |
|
2936 |
|
2937 // Update/Init stack has been completed. |
|
2938 |
|
2939 SMF_END |
|
2940 } |
|
2941 |
|
2942 |
|
2943 #define MHZ_TO_KHZ(valInMhz) ((valInMhz) * 1000) |
|
2944 |
|
2945 EXPORT_C TMMCErr DMMCStack::InitStackAfterUnlockSM() |
|
2946 /** |
|
2947 * Perform last-stage initialisation of the MMC card. |
|
2948 * This implements initialiation that must occur only when the card |
|
2949 * is unlocked (ie - immediately after unlocking, or during initialisation |
|
2950 * if the card is already unlocked) |
|
2951 */ |
|
2952 { |
|
2953 enum states |
|
2954 { |
|
2955 EStBegin=0, |
|
2956 EStTestNextCard, |
|
2957 EStGetExtendedCSD, |
|
2958 EStGotExtendedCSD, |
|
2959 EStGotModifiedExtendedCSD, |
|
2960 EStEraseGroupDefSet, |
|
2961 EStDetermineBusWidthAndClock, |
|
2962 EStGotBusWidthAndClock, |
|
2963 EStNoMoreCards, |
|
2964 EStExit, |
|
2965 EStEnd |
|
2966 }; |
|
2967 |
|
2968 DMMCSession& s = Session(); |
|
2969 TBool initSingleCard = (s.CardP() == NULL) ? (TBool)EFalse : (TBool)ETrue; |
|
2970 |
|
2971 SMF_BEGIN |
|
2972 |
|
2973 if(initSingleCard) |
|
2974 { |
|
2975 iSelectedCardIndex = iCxCardCount; |
|
2976 TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex); |
|
2977 |
|
2978 // if card not present or is locked, exit |
|
2979 if ((!cardP->IsPresent()) || (cardP->IsLocked())) |
|
2980 SMF_GOTOS(EStExit); |
|
2981 |
|
2982 s.SetCard(cardP); |
|
2983 |
|
2984 // If a card is currently indexed for initialisation, then only configure this one. |
|
2985 // We assume that this has been called from the SD stack, so only one MMC card will be present on the bus |
|
2986 |
|
2987 SMF_GOTOS(EStGetExtendedCSD); |
|
2988 } |
|
2989 |
|
2990 // Initialising the entire MMC stack - start with the first card |
|
2991 iSelectedCardIndex = -1; |
|
2992 |
|
2993 // ...fall through... |
|
2994 |
|
2995 SMF_STATE(EStTestNextCard) |
|
2996 |
|
2997 // any more cards ? |
|
2998 if (++iSelectedCardIndex >= iCxCardCount) |
|
2999 SMF_GOTOS(EStNoMoreCards); |
|
3000 |
|
3001 // if no card in this slot, try next one |
|
3002 if (!iCardArray->CardP(iSelectedCardIndex)->IsPresent()) |
|
3003 SMF_GOTOS(EStTestNextCard); |
|
3004 |
|
3005 TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex); |
|
3006 s.SetCard(cardP); |
|
3007 |
|
3008 // if card is locked, try the next one |
|
3009 if(cardP->IsLocked()) |
|
3010 SMF_GOTOS(EStTestNextCard); |
|
3011 |
|
3012 SMF_STATE(EStGetExtendedCSD) |
|
3013 |
|
3014 // Get the Extended CSD if this is an MMC version 4 card |
|
3015 |
|
3016 __KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), SpecVers() %u", s.CardP()->CSD().SpecVers())); |
|
3017 |
|
3018 // clear the Extended CSD contents in case this is a pre-version 4 card or the read fails. |
|
3019 memset(s.CardP()->iExtendedCSD.Ptr(), 0, KMMCExtendedCSDLength); |
|
3020 |
|
3021 if (s.CardP()->CSD().SpecVers() < 4) |
|
3022 SMF_GOTOS(EStTestNextCard); |
|
3023 |
|
3024 m.SetTraps(KMMCErrResponseTimeOut | KMMCErrStatus | KMMCErrDataCRC | KMMCErrBypass); // KMMCErrDataCRC will pick up if the card is not in 1-bit mode |
|
3025 |
|
3026 s.FillCommandDesc(ECmdSendExtendedCSD); |
|
3027 s.FillCommandArgs(0, KMMCExtendedCSDLength, iPSLBuf, KMMCExtendedCSDLength); |
|
3028 |
|
3029 __KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Sending ECmdSendExtendedCSD")); |
|
3030 SMF_INVOKES(CIMReadWriteBlocksSMST, EStGotExtendedCSD) |
|
3031 |
|
3032 SMF_STATE(EStGotExtendedCSD) |
|
3033 |
|
3034 if (err != KMMCErrNone) |
|
3035 { |
|
3036 SMF_GOTOS(EStExit); |
|
3037 } |
|
3038 |
|
3039 memcpy(s.CardP()->iExtendedCSD.Ptr(), iPSLBuf, KMMCExtendedCSDLength); |
|
3040 |
|
3041 // Call a licencee-specific state machine to allow the Extended CSD register to be modified. |
|
3042 SMF_INVOKES( ModifyCardCapabilitySMST, EStGotModifiedExtendedCSD ) |
|
3043 |
|
3044 SMF_STATE(EStGotModifiedExtendedCSD) |
|
3045 |
|
3046 __KTRACE_OPT(KPBUS1, Kern::Printf("Extended CSD")); |
|
3047 __KTRACE_OPT(KPBUS1, Kern::Printf("CSDStructureVer: %u", s.CardP()->ExtendedCSD().CSDStructureVer())); |
|
3048 __KTRACE_OPT(KPBUS1, Kern::Printf("ExtendedCSDRev: %u", s.CardP()->ExtendedCSD().ExtendedCSDRev())); |
|
3049 __KTRACE_OPT(KPBUS1, Kern::Printf("-------------------------------")); |
|
3050 __KTRACE_OPT(KPBUS1, Kern::Printf("SupportedCmdSet: %u", s.CardP()->ExtendedCSD().SupportedCmdSet())); |
|
3051 __KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass26Mhz360V: 0x%02X", s.CardP()->ExtendedCSD().PowerClass26Mhz360V())); |
|
3052 __KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass52Mhz360V: 0x%02X", s.CardP()->ExtendedCSD().PowerClass52Mhz360V())); |
|
3053 __KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass26Mhz195V: 0x%02X", s.CardP()->ExtendedCSD().PowerClass26Mhz195V())); |
|
3054 __KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass52Mhz195V: 0x%02X", s.CardP()->ExtendedCSD().PowerClass52Mhz195V())); |
|
3055 __KTRACE_OPT(KPBUS1, Kern::Printf("CardType: %u", s.CardP()->ExtendedCSD().CardType())); |
|
3056 __KTRACE_OPT(KPBUS1, Kern::Printf("CmdSet: %u", s.CardP()->ExtendedCSD().CmdSet())); |
|
3057 __KTRACE_OPT(KPBUS1, Kern::Printf("CmdSetRev: %u", s.CardP()->ExtendedCSD().CmdSetRev())); |
|
3058 __KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass: %u", s.CardP()->ExtendedCSD().PowerClass())); |
|
3059 __KTRACE_OPT(KPBUS1, Kern::Printf("HighSpeedTiming: %u", s.CardP()->ExtendedCSD().HighSpeedTiming())); |
|
3060 __KTRACE_OPT(KPBUS1, Kern::Printf("HighCapacityEraseGroupSize: %u", s.CardP()->ExtendedCSD().HighCapacityEraseGroupSize())); |
|
3061 __KTRACE_OPT(KPBUS1, Kern::Printf("AccessSize: %u", s.CardP()->ExtendedCSD().AccessSize())); |
|
3062 __KTRACE_OPT(KPBUS1, Kern::Printf("BootInfo: %u", s.CardP()->ExtendedCSD().BootInfo() )); |
|
3063 __KTRACE_OPT(KPBUS1, Kern::Printf("BootSizeMultiple: %u", s.CardP()->ExtendedCSD().BootSizeMultiple() )); |
|
3064 __KTRACE_OPT(KPBUS1, Kern::Printf("EraseTimeoutMultiple: %u", s.CardP()->ExtendedCSD().EraseTimeoutMultiple() )); |
|
3065 __KTRACE_OPT(KPBUS1, Kern::Printf("ReliableWriteSector: %u", s.CardP()->ExtendedCSD().ReliableWriteSector() )); |
|
3066 __KTRACE_OPT(KPBUS1, Kern::Printf("HighCapWriteProtGroupSize: %u", s.CardP()->ExtendedCSD().HighCapacityWriteProtectGroupSize() )); |
|
3067 __KTRACE_OPT(KPBUS1, Kern::Printf("SleepCurrentVcc: %u", s.CardP()->ExtendedCSD().SleepCurrentVcc() )); |
|
3068 __KTRACE_OPT(KPBUS1, Kern::Printf("SleepCurrentVccQ: %u", s.CardP()->ExtendedCSD().SleepCurrentVccQ())); |
|
3069 __KTRACE_OPT(KPBUS1, Kern::Printf("SleepAwakeTimeout: %u", s.CardP()->ExtendedCSD().SleepAwakeTimeout())); |
|
3070 __KTRACE_OPT(KPBUS1, Kern::Printf("BootConfig: %u", s.CardP()->ExtendedCSD().BootConfig())); |
|
3071 __KTRACE_OPT(KPBUS1, Kern::Printf("BootBusWidth: %u", s.CardP()->ExtendedCSD().BootBusWidth())); |
|
3072 __KTRACE_OPT(KPBUS1, Kern::Printf("EraseGroupDef: %u", s.CardP()->ExtendedCSD().EraseGroupDef())); |
|
3073 |
|
3074 if (s.CardP()->ExtendedCSD().ExtendedCSDRev() >= 3) |
|
3075 { |
|
3076 if (!(s.CardP()->ExtendedCSD().EraseGroupDef()) && s.CardP()->ExtendedCSD().HighCapacityEraseGroupSize()) |
|
3077 { |
|
3078 // Need to ensure that media is using correct erase group sizes. |
|
3079 TMMCArgument arg = TExtendedCSD::GetWriteArg( |
|
3080 TExtendedCSD::EWriteByte, |
|
3081 TExtendedCSD::EEraseGroupDefIndex, |
|
3082 TExtendedCSD::EEraseGrpDefEnableHighCapSizes, |
|
3083 0); |
|
3084 |
|
3085 __KTRACE_OPT(KPBUS1, Kern::Printf(">Writing to EXT_CSD (EEraseGroupDefIndex), arg %08X", (TUint32) arg)); |
|
3086 s.FillCommandDesc(ECmdSwitch, arg); |
|
3087 |
|
3088 SMF_INVOKES(ExecSwitchCommandST, EStEraseGroupDefSet) |
|
3089 } |
|
3090 } |
|
3091 |
|
3092 SMF_GOTOS(EStDetermineBusWidthAndClock) |
|
3093 |
|
3094 SMF_STATE(EStEraseGroupDefSet) |
|
3095 |
|
3096 if (err == KMMCErrNone) |
|
3097 { |
|
3098 // EEraseGroupDef has been updated succussfully, |
|
3099 // update the Extended CSD to reflect this |
|
3100 memset( s.CardP()->iExtendedCSD.Ptr()+TExtendedCSD::EEraseGroupDefIndex, TExtendedCSD::EEraseGrpDefEnableHighCapSizes, 1); |
|
3101 } |
|
3102 |
|
3103 SMF_STATE(EStDetermineBusWidthAndClock) |
|
3104 |
|
3105 SMF_INVOKES( DetermineBusWidthAndClockSMST, EStGotBusWidthAndClock ) |
|
3106 |
|
3107 SMF_STATE(EStGotBusWidthAndClock) |
|
3108 |
|
3109 SMF_NEXTS(initSingleCard ? EStExit : EStTestNextCard) |
|
3110 |
|
3111 if(iMultiplexedBus || iCardArray->CardsPresent() == 1) |
|
3112 { |
|
3113 SMF_CALL( ConfigureHighSpeedSMST ) |
|
3114 } |
|
3115 |
|
3116 SMF_GOTONEXTS |
|
3117 |
|
3118 SMF_STATE(EStNoMoreCards) |
|
3119 |
|
3120 |
|
3121 SMF_STATE(EStExit) |
|
3122 m.ResetTraps(); |
|
3123 |
|
3124 SMF_END |
|
3125 } |
|
3126 |
|
3127 |
|
3128 |
|
3129 /** |
|
3130 DetermineBusWidthAndClockSM() |
|
3131 |
|
3132 Reads the extended CSD register for all MMCV4 cards in the stack. |
|
3133 If there is only one MMCV4 card, then an attempt is made to switch |
|
3134 both the card and the controller to a higher clock rate (either 26MHz of 52MHz) |
|
3135 and to a wider bus width (4 or 8 bits). |
|
3136 The clock speed & bus width chosen depend on : |
|
3137 |
|
3138 - whether the card supports it |
|
3139 - whether the controller supports it |
|
3140 - whether the controller has the ability to supply enough current (the current used |
|
3141 by the card can be read from the Extended CSD register) |
|
3142 |
|
3143 */ |
|
3144 TMMCErr DMMCStack::DetermineBusWidthAndClockSM() |
|
3145 { |
|
3146 enum states |
|
3147 { |
|
3148 EStBegin=0, |
|
3149 EStWritePowerClass, |
|
3150 EStStartBusTest, |
|
3151 EStExit, |
|
3152 EStEnd |
|
3153 }; |
|
3154 |
|
3155 DMMCSession& s = Session(); |
|
3156 TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex); |
|
3157 |
|
3158 SMF_BEGIN |
|
3159 // Trap Switch errors & no-response errors |
|
3160 m.SetTraps(KMMCErrResponseTimeOut | KMMCErrStatus); |
|
3161 |
|
3162 __KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), iCxCardCount %u", iCxCardCount)); |
|
3163 |
|
3164 |
|
3165 SMF_STATE(EStWritePowerClass) |
|
3166 |
|
3167 // Check the card type is valid |
|
3168 // The only currently valid values for this field are 0x01 or 0x03 |
|
3169 TUint cardType = cardP->iExtendedCSD.CardType(); |
|
3170 if (cardType != (TExtendedCSD::EHighSpeedCard26Mhz) && |
|
3171 cardType != (TExtendedCSD::EHighSpeedCard26Mhz | TExtendedCSD::EHighSpeedCard52Mhz)) |
|
3172 { |
|
3173 __KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardType)); |
|
3174 SMF_GOTOS(EStExit); |
|
3175 } |
|
3176 |
|
3177 // determine the optimum bus width & clock speed which match the power constraints |
|
3178 TUint powerClass; |
|
3179 DetermineBusWidthAndClock( |
|
3180 *cardP, |
|
3181 (iCurrentOpRange == KMMCOCRLowVoltage), |
|
3182 powerClass, |
|
3183 iBusWidthAndClock); |
|
3184 |
|
3185 // If the power class for the chosen width is different from the default, |
|
3186 // send SWITCH cmd and write the POWER_CLASS byte of the EXT_CSD register |
|
3187 if (powerClass > 0) |
|
3188 { |
|
3189 TMMCArgument arg = TExtendedCSD::GetWriteArg( |
|
3190 TExtendedCSD::EWriteByte, |
|
3191 TExtendedCSD::EPowerClassIndex, |
|
3192 powerClass, |
|
3193 0); |
|
3194 |
|
3195 __KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Writing to EXT_CSD (EPowerClass), arg %08X", (TUint32) arg)); |
|
3196 s.FillCommandDesc(ECmdSwitch, arg); |
|
3197 SMF_INVOKES(ExecSwitchCommandST, EStStartBusTest) |
|
3198 } |
|
3199 |
|
3200 SMF_STATE(EStStartBusTest) |
|
3201 |
|
3202 if (err != KMMCErrNone) |
|
3203 { |
|
3204 SMF_GOTOS(EStExit); |
|
3205 } |
|
3206 |
|
3207 // We have determined the capabilities of the host and card. |
|
3208 // - Before switching to the required bus width, perform the BUSTEST sequence |
|
3209 SMF_INVOKES(ExecBusTestSMST, EStExit); |
|
3210 |
|
3211 SMF_STATE(EStExit) |
|
3212 m.ResetTraps(); |
|
3213 |
|
3214 SMF_END |
|
3215 } |
|
3216 |
|
3217 |
|
3218 /** |
|
3219 ConfigureHighSpeedSM() |
|
3220 |
|
3221 Reads the extended CSD register for all MMCV4 cards in the stack. |
|
3222 If there is only one MMCV4 card, then an attempt is made to switch |
|
3223 both the card and the controller to a higher clock rate (either 26MHz of 52MHz) |
|
3224 and to a wider bus width (4 or 8 bits). |
|
3225 The clock speed & bus width chosen depend on : |
|
3226 |
|
3227 - whether the card supports it |
|
3228 - whether the controller supports it |
|
3229 - whether the controller has the ability to supply enough current (the current used |
|
3230 by the card can be read from the Extended CSD register) |
|
3231 |
|
3232 */ |
|
3233 TMMCErr DMMCStack::ConfigureHighSpeedSM() |
|
3234 { |
|
3235 enum states |
|
3236 { |
|
3237 EStBegin=0, |
|
3238 EStConfigureBusWidth, |
|
3239 EStWriteHsTiming, |
|
3240 EStConfigureClock, |
|
3241 EStExit, |
|
3242 EStEnd |
|
3243 }; |
|
3244 |
|
3245 DMMCSession& s = Session(); |
|
3246 TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex); |
|
3247 |
|
3248 SMF_BEGIN |
|
3249 |
|
3250 // Trap Switch errors & no-response errors |
|
3251 m.SetTraps(KMMCErrResponseTimeOut | KMMCErrStatus); |
|
3252 |
|
3253 __KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), iCxCardCount %u", iCxCardCount)); |
|
3254 |
|
3255 cardP->SetHighSpeedClock(0); |
|
3256 |
|
3257 // Check the card type is valid |
|
3258 // The only currently valid values for this field are 0x01 or 0x03 |
|
3259 TUint cardType = cardP->iExtendedCSD.CardType(); |
|
3260 if (cardType != (TExtendedCSD::EHighSpeedCard26Mhz) && |
|
3261 cardType != (TExtendedCSD::EHighSpeedCard26Mhz | TExtendedCSD::EHighSpeedCard52Mhz)) |
|
3262 { |
|
3263 __KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardType)); |
|
3264 SMF_GOTOS(EStExit); |
|
3265 } |
|
3266 |
|
3267 // If the bus width is 4 or 8, send SWITCH cmd and write the BUS_WIDTH byte of the EXT_CSD register |
|
3268 |
|
3269 if (iBusWidthAndClock != E1Bit20Mhz) |
|
3270 { |
|
3271 TMMCArgument arg = TExtendedCSD::GetWriteArg( |
|
3272 TExtendedCSD::EWriteByte, |
|
3273 TExtendedCSD::EBusWidthModeIndex, |
|
3274 (iBusWidthAndClock & E4BitMask) ? TExtendedCSD::EExtCsdBusWidth4 : TExtendedCSD::EExtCsdBusWidth8, |
|
3275 0); |
|
3276 |
|
3277 __KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Writing to EXT_CSD (EBusWidthMode), arg %08X", (TUint32) arg)); |
|
3278 s.FillCommandDesc(ECmdSwitch, arg); |
|
3279 SMF_INVOKES(ExecSwitchCommandST, EStConfigureBusWidth) |
|
3280 } |
|
3281 |
|
3282 SMF_STATE(EStConfigureBusWidth) |
|
3283 |
|
3284 if (err != KMMCErrNone) |
|
3285 { |
|
3286 SMF_GOTOS(EStExit); |
|
3287 } |
|
3288 |
|
3289 // Ensure that the controller is configured for an 4 or 8 bit bus |
|
3290 // - BUSTEST should have already done this |
|
3291 if (iBusWidthAndClock & E4BitMask) |
|
3292 { |
|
3293 DoSetBusWidth(EBusWidth4); |
|
3294 } |
|
3295 else if (iBusWidthAndClock & E8BitMask) |
|
3296 { |
|
3297 DoSetBusWidth(EBusWidth8); |
|
3298 } |
|
3299 // fall through to next state |
|
3300 |
|
3301 SMF_STATE(EStWriteHsTiming) |
|
3302 if (iBusWidthAndClock == E1Bit20Mhz) |
|
3303 SMF_GOTOS(EStExit); |
|
3304 |
|
3305 TMMCArgument arg = TExtendedCSD::GetWriteArg( |
|
3306 TExtendedCSD::EWriteByte, |
|
3307 TExtendedCSD::EHighSpeedInterfaceTimingIndex, |
|
3308 1, // turn on high speed (26 or 52 Mhz, depending on the card type) |
|
3309 0); |
|
3310 |
|
3311 __KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Writing to EXT_CSD (EHighSpeedInterfaceTiming), arg %08X", (TUint32) arg)); |
|
3312 s.FillCommandDesc(ECmdSwitch, arg); |
|
3313 SMF_INVOKES(ExecSwitchCommandST, EStConfigureClock) |
|
3314 |
|
3315 |
|
3316 SMF_STATE(EStConfigureClock) |
|
3317 |
|
3318 if (err != KMMCErrNone) |
|
3319 { |
|
3320 DoSetBusWidth(EBusWidth1); |
|
3321 SMF_GOTOS(EStExit); |
|
3322 } |
|
3323 |
|
3324 cardP->SetHighSpeedClock( |
|
3325 MHZ_TO_KHZ(((iBusWidthAndClock & E52MhzMask) ? |
|
3326 TMMCMachineInfoV4::EClockSpeed52Mhz: |
|
3327 TMMCMachineInfoV4::EClockSpeed26Mhz))); |
|
3328 |
|
3329 SMF_STATE(EStExit) |
|
3330 m.ResetTraps(); |
|
3331 |
|
3332 SMF_END |
|
3333 } |
|
3334 |
|
3335 // Issue a switch command and then wait while he card is in prg state |
|
3336 // |
|
3337 TMMCErr DMMCStack::ExecSwitchCommand() |
|
3338 { |
|
3339 enum states |
|
3340 { |
|
3341 EStBegin=0, |
|
3342 EStSendStatus, |
|
3343 EStGetStatus, |
|
3344 EStEnd |
|
3345 }; |
|
3346 |
|
3347 DMMCSession& s=Session(); |
|
3348 |
|
3349 SMF_BEGIN |
|
3350 SMF_INVOKES(ExecCommandSMST, EStSendStatus) |
|
3351 |
|
3352 SMF_STATE(EStSendStatus) |
|
3353 s.FillCommandDesc(ECmdSendStatus, 0); |
|
3354 SMF_INVOKES(ExecCommandSMST, EStGetStatus) |
|
3355 |
|
3356 SMF_STATE(EStGetStatus) |
|
3357 const TMMCStatus st(s.ResponseP()); |
|
3358 |
|
3359 const TMMCardStateEnum st1 = st.State(); |
|
3360 if (st1 == ECardStatePrg) |
|
3361 { |
|
3362 SMF_INVOKES(ProgramTimerSMST, EStSendStatus); |
|
3363 } |
|
3364 |
|
3365 // Fall through if CURRENT_STATE is not PGM |
|
3366 |
|
3367 SMF_END |
|
3368 } |
|
3369 |
|
3370 // Issue CMD5 to change device status to Sleep mode |
|
3371 // |
|
3372 TMMCErr DMMCStack::ExecSleepCommandSM() |
|
3373 { |
|
3374 enum states |
|
3375 { |
|
3376 EStBegin=0, |
|
3377 EStIndexNxtCard, |
|
3378 EStIssueSleepAwake, |
|
3379 EStSleepAwakeIssued, |
|
3380 EStUpdateStackState, |
|
3381 EStDone, |
|
3382 EStEnd |
|
3383 }; |
|
3384 |
|
3385 DMMCSession& s=Session(); |
|
3386 |
|
3387 SMF_BEGIN |
|
3388 |
|
3389 __KTRACE_OPT(KPBUS1, Kern::Printf(">ExecSleepCommandSM()")); |
|
3390 |
|
3391 iAutoUnlockIndex = -1; |
|
3392 // drop through.... |
|
3393 |
|
3394 SMF_STATE(EStIndexNxtCard) |
|
3395 |
|
3396 __KTRACE_OPT(KPBUS1, Kern::Printf(">EStIndexNxtCard")); |
|
3397 // the cycle is finished when iAutoUnlockIndex == KMaxMultiMediaCardsPerStack |
|
3398 if(iAutoUnlockIndex >= TInt(KMaxMMCardsPerStack)) |
|
3399 { |
|
3400 SMF_GOTOS(EStUpdateStackState); |
|
3401 } |
|
3402 |
|
3403 // Potentionaly more than one eMMC device attached to Controller |
|
3404 // need to select each device and determine if Sleep can be issued |
|
3405 TBool useIndex = EFalse; |
|
3406 for (++iAutoUnlockIndex; iAutoUnlockIndex < TInt(KMaxMMCardsPerStack); ++iAutoUnlockIndex) |
|
3407 { |
|
3408 // card must be present and a valid 4.3 device |
|
3409 TMMCard* cardP = iCardArray->CardP(iAutoUnlockIndex); |
|
3410 useIndex = ( (cardP->IsPresent()) && |
|
3411 (cardP->ExtendedCSD().ExtendedCSDRev() >= 3) && |
|
3412 (cardP->iStatus != ECardStateSlp) ); |
|
3413 |
|
3414 // don't increment iAutoUnlockIndex in continuation loop |
|
3415 if (useIndex) |
|
3416 { |
|
3417 __KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: is v4.3 device",iAutoUnlockIndex)); |
|
3418 break; |
|
3419 } |
|
3420 } |
|
3421 |
|
3422 if (!useIndex) |
|
3423 { |
|
3424 SMF_GOTOS(EStUpdateStackState); |
|
3425 } |
|
3426 |
|
3427 TMMCard &cd = *(iCardArray->CardP(iAutoUnlockIndex)); |
|
3428 s.SetCard(&cd); |
|
3429 |
|
3430 s.PushCommandStack(); |
|
3431 s.FillCommandDesc(ECmdSleepAwake, KBit15); |
|
3432 |
|
3433 // CMD5 is an AC command, ExecCommandSMST will automatically issue a deselect |
|
3434 SMF_INVOKES(ExecCommandSMST, EStSleepAwakeIssued) |
|
3435 |
|
3436 SMF_STATE(EStSleepAwakeIssued) |
|
3437 |
|
3438 __KTRACE_OPT(KPBUS1, Kern::Printf(">EStSleepAwakeIssued!")); |
|
3439 |
|
3440 const TMMCStatus status(s.ResponseP()); |
|
3441 |
|
3442 s.PopCommandStack(); |
|
3443 |
|
3444 if(status.State() == ECardStateStby || status.State() == ECardStateSlp) |
|
3445 { |
|
3446 // R1b is issued before Sleep state is achieved and |
|
3447 // will therefore return the previous state which was Standby |
|
3448 __KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: SLEEP",iAutoUnlockIndex)); |
|
3449 |
|
3450 // Ensure card status is ECardStateSlp |
|
3451 s.CardP()->iStatus.UpdateState(ECardStateSlp); |
|
3452 |
|
3453 // Update Stack state to indicate media is sleep mode |
|
3454 iStackState |= KMMCStackStateSleep; |
|
3455 } |
|
3456 else |
|
3457 { |
|
3458 __KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: UNKNOWN",iAutoUnlockIndex)); |
|
3459 return (KMMCErrStatus); |
|
3460 } |
|
3461 |
|
3462 SMF_GOTOS(EStIndexNxtCard) |
|
3463 |
|
3464 SMF_STATE(EStUpdateStackState) |
|
3465 |
|
3466 if (iStackState & KMMCStackStateSleep) |
|
3467 { |
|
3468 // Media has been transitioned to sleep state |
|
3469 iStackState &= ~KMMCStackStateSleep; |
|
3470 |
|
3471 // VccCore may now be switched off |
|
3472 iSocket->iVccCore->SetState(EPsuOff); |
|
3473 } |
|
3474 // else |
|
3475 // No media transitioned to sleep state or media was already in sleep state, |
|
3476 // nothing to do... |
|
3477 |
|
3478 SMF_STATE(EStDone) |
|
3479 |
|
3480 __KTRACE_OPT(KPBUS1, Kern::Printf("<ExecSleepCommandSM()")); |
|
3481 |
|
3482 SMF_END |
|
3483 } |
|
3484 |
|
3485 |
|
3486 //Issue CMD5 to change devices into STANDBY state |
|
3487 TMMCErr DMMCStack::ExecAwakeCommandSM() |
|
3488 { |
|
3489 enum states |
|
3490 { |
|
3491 EStBegin=0, |
|
3492 EStPoweredUp, |
|
3493 EStAwakeIssued, |
|
3494 EStDone, |
|
3495 EStEnd |
|
3496 }; |
|
3497 |
|
3498 DMMCSession& s=Session(); |
|
3499 |
|
3500 SMF_BEGIN |
|
3501 |
|
3502 __KTRACE_OPT(KPBUS1, Kern::Printf(">ExecAwakeCommandSM()")); |
|
3503 |
|
3504 // Call PSL to ensure VccQ is powered up before continuing |
|
3505 SMF_INVOKES( DoWakeUpSMST, EStPoweredUp ) |
|
3506 |
|
3507 SMF_STATE(EStPoweredUp) |
|
3508 |
|
3509 __KTRACE_OPT(KPBUS1, Kern::Printf("VccQ Powered Up")); |
|
3510 |
|
3511 //Issue CMD5 to awaken media |
|
3512 s.PushCommandStack(); |
|
3513 s.FillCommandDesc(ECmdSleepAwake); |
|
3514 s.Command().iArgument.SetRCA(s.CardP()->RCA()); |
|
3515 |
|
3516 SMF_INVOKES(IssueCommandCheckResponseSMST, EStAwakeIssued) |
|
3517 |
|
3518 SMF_STATE(EStAwakeIssued) |
|
3519 |
|
3520 __KTRACE_OPT(KPBUS1, Kern::Printf(">>EStAwakeIssued!")); |
|
3521 |
|
3522 TMMCStatus status(s.ResponseP()); |
|
3523 |
|
3524 if(status.State() == ECardStateStby || status.State() == ECardStateSlp) |
|
3525 { |
|
3526 // R1b is issued before Standby state is achieved and |
|
3527 // will therefore return the previous state which was Sleep |
|
3528 __KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: STANDBY",iAutoUnlockIndex)); |
|
3529 |
|
3530 s.CardP()->iStatus.UpdateState(ECardStateStby); |
|
3531 } |
|
3532 else |
|
3533 { |
|
3534 __KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: UNKNOWN",iAutoUnlockIndex)); |
|
3535 return (KMMCErrStatus); |
|
3536 } |
|
3537 |
|
3538 s.PopCommandStack(); |
|
3539 |
|
3540 SMF_STATE(EStDone) |
|
3541 |
|
3542 __KTRACE_OPT(KPBUS1, Kern::Printf("<ExecAwakeCommandSM()")); |
|
3543 |
|
3544 SMF_END |
|
3545 } |
|
3546 |
|
3547 |
|
3548 // determine the maximum bus width and clock speed supported by both the controller |
|
3549 // and the card which fits the power constraints. |
|
3550 void DMMCStack::DetermineBusWidthAndClock( |
|
3551 const TMMCard& aCard, |
|
3552 TBool aLowVoltage, |
|
3553 TUint& aPowerClass, |
|
3554 TBusWidthAndClock& aBusWidthAndClock) |
|
3555 { |
|
3556 |
|
3557 // Set default return values - in case power constraints aren't matched |
|
3558 aPowerClass = 0; |
|
3559 aBusWidthAndClock = E1Bit20Mhz; |
|
3560 |
|
3561 // Get the bus widths & clocks supported by the controller |
|
3562 // NB If the PSL doesn not support TMMCMachineInfoV4, return |
|
3563 TMMCMachineInfoV4 machineInfo; |
|
3564 TMMCMachineInfoV4Pckg machineInfoPckg(machineInfo); |
|
3565 MachineInfo(machineInfoPckg); |
|
3566 if (machineInfo.iVersion < TMMCMachineInfoV4::EVersion4) |
|
3567 return; |
|
3568 |
|
3569 TBusWidth maxBusWidth = machineInfo.iMaxBusWidth; |
|
3570 TInt maxClockSpeedInMhz = machineInfo.iMaxClockSpeedInMhz; |
|
3571 |
|
3572 TUint32 controllerWidthAndClock = E1Bit20Mhz; |
|
3573 |
|
3574 if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed26Mhz && maxBusWidth >= EBusWidth4) |
|
3575 controllerWidthAndClock|= E4Bits26Mhz; |
|
3576 if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed52Mhz && maxBusWidth >= EBusWidth4) |
|
3577 controllerWidthAndClock|= E4Bits52Mhz; |
|
3578 |
|
3579 if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed26Mhz && maxBusWidth >= EBusWidth8) |
|
3580 controllerWidthAndClock|= E8Bits26Mhz; |
|
3581 if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed52Mhz && maxBusWidth >= EBusWidth8) |
|
3582 controllerWidthAndClock|= E8Bits52Mhz; |
|
3583 |
|
3584 // Get the bus widths & clocks supported by the card |
|
3585 TUint32 cardWidthAndClock = E1Bit20Mhz; |
|
3586 |
|
3587 if (aCard.ExtendedCSD().CardType() & TExtendedCSD::EHighSpeedCard26Mhz) |
|
3588 cardWidthAndClock|= E4Bits26Mhz | E8Bits26Mhz; |
|
3589 if (aCard.ExtendedCSD().CardType() & TExtendedCSD::EHighSpeedCard52Mhz) |
|
3590 cardWidthAndClock|= E4Bits52Mhz | E8Bits52Mhz; |
|
3591 |
|
3592 |
|
3593 // Get the bus widths & clocks supported by both the controller & card |
|
3594 // by AND-ing them together, |
|
3595 TUint32 supportedWidthAndClock = controllerWidthAndClock & cardWidthAndClock; |
|
3596 |
|
3597 // Iterate through all the modes (starting at the fastest) until we find one |
|
3598 // that is supported by both card & controller and fits the power constraints |
|
3599 TUint powerClass = 0; |
|
3600 for (TUint targetWidthAndClock = E8Bits52Mhz; targetWidthAndClock != 0; targetWidthAndClock>>= 1) |
|
3601 { |
|
3602 if ((supportedWidthAndClock & targetWidthAndClock) == 0) |
|
3603 continue; |
|
3604 |
|
3605 powerClass = GetPowerClass(aCard, TBusWidthAndClock(targetWidthAndClock), aLowVoltage); |
|
3606 |
|
3607 // can the controller support this power class ? |
|
3608 if (powerClass > (aLowVoltage ? machineInfo.iLowVoltagePowerClass : machineInfo.iHighVoltagePowerClass )) |
|
3609 continue; |
|
3610 |
|
3611 aPowerClass = powerClass; |
|
3612 aBusWidthAndClock = TBusWidthAndClock(targetWidthAndClock); |
|
3613 break; |
|
3614 } |
|
3615 |
|
3616 __KTRACE_OPT(KPBUS1, Kern::Printf("aPowerClass %u, targetWidthAndClock = %08X", aPowerClass, aBusWidthAndClock)); |
|
3617 } |
|
3618 |
|
3619 TUint DMMCStack::GetPowerClass(const TMMCard& aCard, TBusWidthAndClock aWidthAndClock, TBool aLowVoltage) |
|
3620 { |
|
3621 // The power class for 4 bit bus configurations is in the low nibble, |
|
3622 // The power class for 8 bit bus configurations is in the high nibble, |
|
3623 #define LO_NIBBLE(val) (val & 0x0F) |
|
3624 #define HI_NIBBLE(val) ((val >> 4) & 0x0F) |
|
3625 |
|
3626 const TExtendedCSD& extendedCSD = aCard.ExtendedCSD(); |
|
3627 |
|
3628 TUint powerClass = 0; |
|
3629 |
|
3630 if (aLowVoltage) |
|
3631 { |
|
3632 switch( aWidthAndClock) |
|
3633 { |
|
3634 case E4Bits26Mhz: |
|
3635 powerClass = LO_NIBBLE(extendedCSD.PowerClass26Mhz195V()); |
|
3636 break; |
|
3637 case E4Bits52Mhz: |
|
3638 powerClass = LO_NIBBLE(extendedCSD.PowerClass52Mhz195V()); |
|
3639 break; |
|
3640 case E8Bits26Mhz: |
|
3641 powerClass = HI_NIBBLE(extendedCSD.PowerClass26Mhz195V()); |
|
3642 break; |
|
3643 case E8Bits52Mhz: |
|
3644 powerClass = HI_NIBBLE(extendedCSD.PowerClass52Mhz195V()); |
|
3645 break; |
|
3646 case E1Bit20Mhz: |
|
3647 powerClass = 0; |
|
3648 break; |
|
3649 } |
|
3650 } |
|
3651 else |
|
3652 { |
|
3653 switch( aWidthAndClock) |
|
3654 { |
|
3655 case E4Bits26Mhz: |
|
3656 powerClass = LO_NIBBLE(extendedCSD.PowerClass26Mhz360V()); |
|
3657 break; |
|
3658 case E4Bits52Mhz: |
|
3659 powerClass = LO_NIBBLE(extendedCSD.PowerClass52Mhz360V()); |
|
3660 break; |
|
3661 case E8Bits26Mhz: |
|
3662 powerClass = HI_NIBBLE(extendedCSD.PowerClass26Mhz360V()); |
|
3663 break; |
|
3664 case E8Bits52Mhz: |
|
3665 powerClass = HI_NIBBLE(extendedCSD.PowerClass52Mhz360V()); |
|
3666 break; |
|
3667 case E1Bit20Mhz: |
|
3668 powerClass = 0; |
|
3669 break; |
|
3670 } |
|
3671 } |
|
3672 |
|
3673 return powerClass; |
|
3674 } |
|
3675 |
|
3676 // |
|
3677 // Execute the BUSTEST procedure for a given bus width |
|
3678 // |
|
3679 TMMCErr DMMCStack::ExecBusTestSM() |
|
3680 { |
|
3681 enum states |
|
3682 { |
|
3683 EStBegin=0, |
|
3684 EstSendBusTest_W, |
|
3685 EstSendBusTest_R, |
|
3686 EstGotBusTest_R, |
|
3687 EStExit, |
|
3688 EStEnd |
|
3689 }; |
|
3690 |
|
3691 DMMCSession& s = Session(); |
|
3692 |
|
3693 SMF_BEGIN |
|
3694 // |
|
3695 // Start the BUSTEST sequence at the maximum supported by the PSL |
|
3696 // - iSpare[0] keeps track of the current bus width |
|
3697 // |
|
3698 if (iBusWidthAndClock & E8BitMask) |
|
3699 { |
|
3700 iSpare[0] = EBusWidth8; |
|
3701 __KTRACE_OPT(KPBUS1, Kern::Printf("...Hardware supports 8-bit bus")); |
|
3702 } |
|
3703 else if(iBusWidthAndClock & E4BitMask) |
|
3704 { |
|
3705 iSpare[0] = EBusWidth4; |
|
3706 __KTRACE_OPT(KPBUS1, Kern::Printf("...Hardware supports 4-bit bus")); |
|
3707 } |
|
3708 else |
|
3709 { |
|
3710 __KTRACE_OPT(KPBUS1, Kern::Printf("...Hardware supports 1-bit bus")); |
|
3711 iSpare[0] = EBusWidth1; |
|
3712 } |
|
3713 |
|
3714 // remove KMMCModeCardControlled so that IssueCommandCheckResponseSMST doesn't try to |
|
3715 // override the bus width & clock rate using the card settings |
|
3716 iConfig.RemoveMode( KMMCModeCardControlled ); |
|
3717 |
|
3718 SMF_STATE(EstSendBusTest_W) |
|
3719 // |
|
3720 // Issue the BUSTEST_W command |
|
3721 // |
|
3722 TInt length = 2; |
|
3723 switch(iSpare[0]) |
|
3724 { |
|
3725 case EBusWidth8: |
|
3726 // Set the host to 8-bit mode |
|
3727 __KTRACE_OPT(KPBUS1, Kern::Printf("BUSTEST : EBusWidth8")); |
|
3728 DoSetBusWidth(EBusWidth8); |
|
3729 iPSLBuf[0] = 0x55; |
|
3730 iPSLBuf[1] = 0xaa; |
|
3731 break; |
|
3732 |
|
3733 case EBusWidth4: |
|
3734 // Set the host to 4-bit mode |
|
3735 __KTRACE_OPT(KPBUS1, Kern::Printf("BUSTEST : EBusWidth4")); |
|
3736 DoSetBusWidth(EBusWidth4); |
|
3737 iPSLBuf[0] = 0x5a; |
|
3738 iPSLBuf[1] = 0x00; |
|
3739 break; |
|
3740 |
|
3741 case EBusWidth1: |
|
3742 default: |
|
3743 // Set the host to 1-bit mode |
|
3744 __KTRACE_OPT(KPBUS1, Kern::Printf("BUSTEST : EBusWidth1")); |
|
3745 DoSetBusWidth(EBusWidth1); |
|
3746 iPSLBuf[0] = 0x40; |
|
3747 iPSLBuf[1] = 0x00; |
|
3748 break; |
|
3749 } |
|
3750 |
|
3751 // Issue BUSTEST_W |
|
3752 |
|
3753 __KTRACE_OPT(KPBUS1, Kern::Printf("...Issue BUSTEST_W [%02x:%02x]", iPSLBuf[1], iPSLBuf[0])); |
|
3754 |
|
3755 m.SetTraps(KMMCErrDataCRC); // CRC check is optional for BUSTEST |
|
3756 |
|
3757 s.FillCommandDesc(ECmdBustest_W); |
|
3758 s.FillCommandArgs(0, length, &iPSLBuf[0], length); |
|
3759 SMF_INVOKES(IssueCommandCheckResponseSMST, EstSendBusTest_R) |
|
3760 |
|
3761 SMF_STATE(EstSendBusTest_R) |
|
3762 // |
|
3763 // Issue the BUSTEST_R command |
|
3764 // |
|
3765 __KTRACE_OPT(KPBUS1, Kern::Printf("...got BUSTEST_W response : %02x", err)); |
|
3766 |
|
3767 if(err == KMMCErrNone || err == KMMCErrDataCRC) |
|
3768 { |
|
3769 __KTRACE_OPT(KPBUS1, Kern::Printf("...sending BUSTEST_R")); |
|
3770 |
|
3771 iPSLBuf[0] = 0; |
|
3772 iPSLBuf[1] = 0; |
|
3773 |
|
3774 s.FillCommandDesc(ECmdBustest_R); |
|
3775 s.FillCommandArgs(0, 2, &iPSLBuf[0], 2); |
|
3776 SMF_INVOKES(IssueCommandCheckResponseSMST, EstGotBusTest_R) |
|
3777 } |
|
3778 else |
|
3779 { |
|
3780 SMF_RETURN(KMMCErrNotSupported); |
|
3781 } |
|
3782 |
|
3783 SMF_STATE(EstGotBusTest_R) |
|
3784 // |
|
3785 // Validate the BUSTEST_R data with that issued by BUSTEST_W |
|
3786 // |
|
3787 __KTRACE_OPT(KPBUS1, Kern::Printf("...got BUSTEST_R response [%02x:%02x] : err(%02x)", iPSLBuf[1], iPSLBuf[0], err)); |
|
3788 |
|
3789 TBool retry = EFalse; |
|
3790 TBool is52MHzSupported = (iBusWidthAndClock & E52MhzMask) ? (TBool)ETrue : (TBool)EFalse; |
|
3791 |
|
3792 switch(iSpare[0]) |
|
3793 { |
|
3794 case EBusWidth8: |
|
3795 { |
|
3796 if(iPSLBuf[0] == 0xAA && iPSLBuf[1] == 0x55) |
|
3797 { |
|
3798 // 8-Bit bus supported |
|
3799 iBusWidthAndClock = is52MHzSupported ? E8Bits52Mhz : E8Bits26Mhz; |
|
3800 } |
|
3801 else |
|
3802 { |
|
3803 // 8-Bit bus not supported - retry with 4-Bit |
|
3804 retry = ETrue; |
|
3805 iSpare[0] = EBusWidth4; |
|
3806 } |
|
3807 break; |
|
3808 } |
|
3809 |
|
3810 case EBusWidth4: |
|
3811 { |
|
3812 if(iPSLBuf[0] == 0xA5) |
|
3813 { |
|
3814 // 4-Bit Bus Supported |
|
3815 iBusWidthAndClock = is52MHzSupported ? E4Bits52Mhz : E4Bits26Mhz; |
|
3816 } |
|
3817 else |
|
3818 { |
|
3819 // 4-Bit bus not supported - retry with 1-Bit |
|
3820 retry = ETrue; |
|
3821 iSpare[0] = EBusWidth1; |
|
3822 } |
|
3823 break; |
|
3824 } |
|
3825 |
|
3826 case EBusWidth1: |
|
3827 { |
|
3828 if((iPSLBuf[0] & 0xC0) == 0x80) |
|
3829 { |
|
3830 // 1-Bit Bus Supported |
|
3831 iBusWidthAndClock = E1Bit20Mhz; |
|
3832 } |
|
3833 |
|
3834 // Failed to perform BUSTEST with 1-Bit bus. |
|
3835 // - We can't recover from this, but let's continue using low-speed 1-Bit mode |
|
3836 iBusWidthAndClock = E1Bit20Mhz; |
|
3837 break; |
|
3838 } |
|
3839 |
|
3840 default: |
|
3841 DMMCSocket::Panic(DMMCSocket::EMMCBadBusWidth); |
|
3842 break; |
|
3843 } |
|
3844 |
|
3845 if(retry) |
|
3846 { |
|
3847 __KTRACE_OPT(KPBUS1, Kern::Printf("...BUSTEST Failed : Retry")); |
|
3848 SMF_GOTOS(EstSendBusTest_W); |
|
3849 } |
|
3850 |
|
3851 switch(iBusWidthAndClock) |
|
3852 { |
|
3853 case E1Bit20Mhz: |
|
3854 iCardArray->CardP(iSelectedCardIndex)->SetBusWidth(1); |
|
3855 break; |
|
3856 |
|
3857 case E4Bits26Mhz: |
|
3858 case E4Bits52Mhz: |
|
3859 iCardArray->CardP(iSelectedCardIndex)->SetBusWidth(4); |
|
3860 break; |
|
3861 |
|
3862 case E8Bits26Mhz: |
|
3863 case E8Bits52Mhz: |
|
3864 iCardArray->CardP(iSelectedCardIndex)->SetBusWidth(8); |
|
3865 break; |
|
3866 } |
|
3867 |
|
3868 __KTRACE_OPT(KPBUS1, Kern::Printf("...BUSTEST OK")); |
|
3869 |
|
3870 DoSetBusWidth(EBusWidth1); |
|
3871 |
|
3872 SMF_STATE(EStExit) |
|
3873 iConfig.SetMode( KMMCModeCardControlled ); |
|
3874 |
|
3875 SMF_END |
|
3876 } |
|
3877 |
|
3878 |
|
3879 /** |
|
3880 * PSL-supplied method to retrieve an interface. |
|
3881 * The caller should set aInterfacePtr to NULL before calling |
|
3882 * the PSL should only modify aInterfacePtr if it supports the interface |
|
3883 * The default implementation here does nothing |
|
3884 * Replaces Dummy2() |
|
3885 */ |
|
3886 EXPORT_C void DMMCStack::GetInterface(TInterfaceId aInterfaceId, MInterface*& aInterfacePtr) |
|
3887 { |
|
3888 if (aInterfaceId == KInterfaceCancelSession) |
|
3889 { |
|
3890 DMMCSession* session = (DMMCSession*&) aInterfacePtr; |
|
3891 Abort(session); |
|
3892 UnlockStack(session); |
|
3893 } |
|
3894 |
|
3895 } |
|
3896 |
|
3897 |
|
3898 TMMCErr DMMCStack::GoIdleSM() |
|
3899 /** |
|
3900 * Issues GO_IDLE_STATE twice with a RetryGap between them. |
|
3901 * After that the bus context ought to be considered as "known". |
|
3902 * @return MMC error code |
|
3903 */ |
|
3904 { |
|
3905 enum states |
|
3906 { |
|
3907 EStBegin=0, |
|
3908 EStIdleLoop, |
|
3909 EStIdleEndCheck, |
|
3910 EStEnd |
|
3911 }; |
|
3912 |
|
3913 DMMCSession& s=Session(); |
|
3914 |
|
3915 SMF_BEGIN |
|
3916 |
|
3917 s.FillCommandDesc( ECmdGoIdleState, 0 ); |
|
3918 iCxPollRetryCount = KMMCIdleCommandsAtRestart; |
|
3919 |
|
3920 SMF_STATE(EStIdleLoop) |
|
3921 SMF_INVOKES( ExecCommandSMST, EStIdleEndCheck ) |
|
3922 |
|
3923 SMF_STATE(EStIdleEndCheck) |
|
3924 |
|
3925 if( --iCxPollRetryCount > 0 ) |
|
3926 SMF_INVOKES( RetryGapTimerSMST, EStIdleLoop ) |
|
3927 |
|
3928 iStackState &= ~(KMMCStackStateDoDeselect|KMMCStackStateBusInconsistent); |
|
3929 iSelectedCard = 0; |
|
3930 |
|
3931 // According to the spec, the default bus width after power up or GO_IDLE is 1 bit bus width |
|
3932 DoSetBusWidth(EBusWidth1); |
|
3933 |
|
3934 SMF_END |
|
3935 } |
|
3936 |
|
3937 EXPORT_C TMMCErr DMMCStack::AcquireStackSM() |
|
3938 /** |
|
3939 * This macro acquires new cards in an MMC - bus topology stack. |
|
3940 * It starts with the Controller reading the operating conditions of the |
|
3941 * cards in the stack (SEND_OP_COND - CMD1). Then, any new cards in the stack |
|
3942 * are identified (ALL_SEND_CID - CMD2) and each one is assigned a relative |
|
3943 * card address (SET_RCA - CMD3). This is done by systematically broadcasting |
|
3944 * CMD2 to all cards on the bus until all uninitialized cards have responded. |
|
3945 * Finally the card specific data (SEND_CSD - CMD9) is read from each card. |
|
3946 * @return MMC error code |
|
3947 */ |
|
3948 { |
|
3949 enum states |
|
3950 { |
|
3951 EStBegin=0, |
|
3952 EStIdle, |
|
3953 EStFullRangeDone, |
|
3954 EStSetRangeLoop, |
|
3955 EStSetRangeBusyCheck, |
|
3956 EStCIDLoop, |
|
3957 EStSendCIDIssued, |
|
3958 EStCIDsDone, |
|
3959 EStCSDLoop, |
|
3960 EStSendCSDDone, |
|
3961 EStMergeCards, |
|
3962 EStReMergeCards, |
|
3963 EStEnd |
|
3964 }; |
|
3965 |
|
3966 DMMCSession& s=Session(); |
|
3967 DMMCPsu* psu=(DMMCPsu*)iSocket->iVcc; |
|
3968 |
|
3969 SMF_BEGIN |
|
3970 |
|
3971 iRCAPool.ReleaseUnlocked(); |
|
3972 iCxPollRetryCount = 0; // Reset max number of poll attempts on card busy |
|
3973 |
|
3974 SMF_INVOKES( GoIdleSMST, EStIdle ) |
|
3975 |
|
3976 SMF_STATE(EStIdle) |
|
3977 |
|
3978 // If this platform doesn't support an adjustable voltage PSU then there is |
|
3979 // no point in interogating the card(s) present for their supported range |
|
3980 if ( !(psu->VoltageSupported()&KMMCAdjustableOpVoltage) ) |
|
3981 { |
|
3982 // if the PSU isn't adjustable then it can't support low voltage mode |
|
3983 iCurrentOpRange&= ~KMMCOCRLowVoltage; |
|
3984 s.FillCommandDesc(ECmdSendOpCond, (iCurrentOpRange | KMMCOCRAccessModeHCS | KMMCOCRBusy)); // Range supported + Busy bit (iArgument==KBit31) |
|
3985 SMF_GOTOS( EStSetRangeLoop ) |
|
3986 } |
|
3987 |
|
3988 // Interrogate card(s) present - issue CMD1 with omitted voltage range |
|
3989 s.FillCommandDesc( ECmdSendOpCond, KMMCOCRAccessModeHCS | KMMCOCRBusy); // Full range + Sector Access + Busy bit (iArgument==KBit31) |
|
3990 m.SetTraps( KMMCErrResponseTimeOut ); |
|
3991 |
|
3992 SMF_INVOKES( ExecCommandSMST, EStFullRangeDone ) |
|
3993 |
|
3994 SMF_STATE(EStFullRangeDone) |
|
3995 |
|
3996 if( err ) // If timeout |
|
3997 { |
|
3998 iConfig.RemoveMode( KMMCModeEnableTimeOutRetry ); // There is no point to do it second time |
|
3999 } |
|
4000 else |
|
4001 { |
|
4002 // Cards responded with Op range - evaluate the common subset with the current setting |
|
4003 // Dont worry aboout the busy bit for now, we'll check that when we repeat the command |
|
4004 TUint32 newrange = (TMMC::BigEndian32(s.ResponseP()) & ~KMMCOCRBusy); |
|
4005 newrange &= iCurrentOpRange; |
|
4006 |
|
4007 if (newrange==0) |
|
4008 { |
|
4009 // One or more card is incompatible with our h/w |
|
4010 if (iMaxCardsInStack<=1) |
|
4011 return( KMMCErrNotSupported ); // There can only be one card - we don't support it. |
|
4012 else |
|
4013 // Force the default range |
|
4014 iCurrentOpRange=(psu->VoltageSupported() & ~KMMCAdjustableOpVoltage); |
|
4015 } |
|
4016 else |
|
4017 iCurrentOpRange=newrange; // OK, new cards are compatible |
|
4018 } |
|
4019 |
|
4020 // If platform and the card both support low voltage mode (1.65 - 1.95v), switch |
|
4021 if (iCurrentOpRange & KMMCOCRLowVoltage) |
|
4022 { |
|
4023 iCurrentOpRange = KMMCOCRLowVoltage; |
|
4024 SMF_INVOKES( SwitchToLowVoltageSMST, EStSetRangeLoop ) |
|
4025 } |
|
4026 |
|
4027 SMF_STATE(EStSetRangeLoop) |
|
4028 |
|
4029 // Repeat CMD1 this time setting Current Op Range |
|
4030 s.Command().iArgument = iCurrentOpRange | KMMCOCRAccessModeHCS | KMMCOCRBusy; |
|
4031 |
|
4032 m.SetTraps( KMMCErrResponseTimeOut ); |
|
4033 |
|
4034 SMF_INVOKES( ExecCommandSMST, EStSetRangeBusyCheck ) |
|
4035 |
|
4036 SMF_STATE(EStSetRangeBusyCheck) |
|
4037 |
|
4038 if( !err ) |
|
4039 { |
|
4040 // Bit31 of the OCR response is low if the cards are still powering up. |
|
4041 const TUint32 ocrResponse = TMMC::BigEndian32(s.ResponseP()); |
|
4042 |
|
4043 const TBool isBusy = ((ocrResponse & KMMCOCRBusy) == 0); |
|
4044 __KTRACE_OPT(KPBUS1,Kern::Printf("-mmc:upd:bsy%d", isBusy)); |
|
4045 |
|
4046 if (isBusy) |
|
4047 { |
|
4048 // Some cards are still busy powering up. Check if we should timeout |
|
4049 if ( ++iCxPollRetryCount > iConfig.OpCondBusyTimeout() ) |
|
4050 return( KMMCErrBusTimeOut ); |
|
4051 m.ResetTraps(); |
|
4052 SMF_INVOKES( RetryGapTimerSMST, EStSetRangeLoop ) |
|
4053 } |
|
4054 |
|
4055 iSpare[0] = 0; |
|
4056 |
|
4057 if((ocrResponse & KMMCOCRAccessModeMask) == KMMCOCRAccessModeHCS) |
|
4058 { |
|
4059 __KTRACE_OPT(KPBUS1, Kern::Printf("Found large MMC card.")); |
|
4060 iSpare[0] = KMMCardIsHighCapacity; |
|
4061 } |
|
4062 } |
|
4063 |
|
4064 iConfig.SetMode( EffectiveModes(s.iConfig) & KMMCModeEnableTimeOutRetry ); // Restore original setting |
|
4065 |
|
4066 // All cards are now ready and notified of the voltage range - ask ASSP to set it up |
|
4067 psu->SetVoltage(iCurrentOpRange); |
|
4068 if (psu->SetState(EPsuOnFull) != KErrNone) |
|
4069 return(KMMCErrHardware); |
|
4070 |
|
4071 iCardArray->InitNewCardScan(); // Collect new cards, one by one |
|
4072 |
|
4073 SMF_STATE(EStCIDLoop) |
|
4074 |
|
4075 if ( iCardArray->NewCardCount() >= iMaxCardsInStack ) |
|
4076 SMF_GOTOS( EStCIDsDone ) |
|
4077 |
|
4078 s.FillCommandDesc( ECmdAllSendCID, 0 ); |
|
4079 m.SetTraps( KMMCErrResponseTimeOut ); |
|
4080 |
|
4081 SMF_INVOKES( ExecCommandSMST, EStSendCIDIssued ) |
|
4082 |
|
4083 SMF_STATE(EStSendCIDIssued) |
|
4084 |
|
4085 if( !err ) |
|
4086 { |
|
4087 // A card responded with a CID. Create a new card entry in the card array |
|
4088 // and initialise this entry with the CID. The card array allocates it an |
|
4089 // RCA, either the old RCA if we have seen this card before, or a new one. |
|
4090 TRCA rca; |
|
4091 iCardArray->AddNewCard(s.ResponseP(),&rca); // Response is CID |
|
4092 |
|
4093 // Now assign the new RCA to the card |
|
4094 s.FillCommandDesc( ECmdSetRelativeAddr, TMMCArgument(rca) ); |
|
4095 m.ResetTraps(); |
|
4096 SMF_INVOKES( ExecCommandSMST, EStCIDLoop ) |
|
4097 } |
|
4098 |
|
4099 SMF_STATE(EStCIDsDone) |
|
4100 |
|
4101 // All cards are initialised; get all their CSDs |
|
4102 m.ResetTraps(); // We are no longer processing any errors |
|
4103 |
|
4104 if( iCardArray->NewCardCount()==0 ) |
|
4105 SMF_EXIT // No new cards acquired |
|
4106 |
|
4107 iCxCardCount=0; // New cards index |
|
4108 s.FillCommandDesc( ECmdSendCSD ); |
|
4109 |
|
4110 SMF_STATE(EStCSDLoop) |
|
4111 |
|
4112 s.Command().iArgument = TMMCArgument(iCardArray->NewCardP(iCxCardCount)->iRCA); |
|
4113 SMF_INVOKES( ExecCommandSMST, EStSendCSDDone ) |
|
4114 |
|
4115 SMF_STATE(EStSendCSDDone) |
|
4116 |
|
4117 // Store the CSD in the new card entry |
|
4118 TMMCard* cardP = iCardArray->NewCardP(iCxCardCount); |
|
4119 cardP->iCSD = s.ResponseP(); |
|
4120 |
|
4121 // Perform MMC Specific parsing of the CSD structure |
|
4122 TUint specVers = cardP->CSD().SpecVers(); // 1 => 1.4, 2 => 2.0 - 2.2, 3 => 3.1 |
|
4123 |
|
4124 if ((specVers >= 2) && (cardP->CSD().CCC() & KMMCCmdClassLockCard)) |
|
4125 { |
|
4126 cardP->iFlags |= KMMCardIsLockable; |
|
4127 } |
|
4128 |
|
4129 if(iSpare[0] == KMMCardIsHighCapacity) |
|
4130 { |
|
4131 cardP->iFlags |= KMMCardIsHighCapacity; |
|
4132 } |
|
4133 |
|
4134 if( ++iCxCardCount < (TInt)iCardArray->NewCardCount() ) |
|
4135 SMF_GOTOS( EStCSDLoop ) |
|
4136 |
|
4137 SMF_NEXTS(EStMergeCards) |
|
4138 |
|
4139 SMF_STATE(EStMergeCards) |
|
4140 |
|
4141 // Merging the old card info with newly acquired cards (we will ask each card for status |
|
4142 // to determine whether it's really present later). |
|
4143 if( SchedGetOnDFC() ) |
|
4144 SMF_WAIT |
|
4145 if ( iCardArray->MergeCards(ETrue)==KErrNone ) |
|
4146 SMF_EXIT // Completed successfully |
|
4147 |
|
4148 SMF_INVOKES( CheckStackSMST, EStReMergeCards ) // No space so check if any cards have gone |
|
4149 |
|
4150 SMF_STATE(EStReMergeCards) |
|
4151 |
|
4152 if( SchedGetOnDFC() ) |
|
4153 SMF_WAIT |
|
4154 if ( iCardArray->MergeCards(EFalse)!=KErrNone ) // There are more cards in the stack than we can handle |
|
4155 return(KMMCErrTooManyCards); |
|
4156 |
|
4157 SMF_END |
|
4158 } |
|
4159 |
|
4160 |
|
4161 /** |
|
4162 * Power down the bus and power it up again in low-voltage mode |
|
4163 * |
|
4164 * @return MMC error code. |
|
4165 */ |
|
4166 TMMCErr DMMCStack::SwitchToLowVoltageSM() |
|
4167 { |
|
4168 enum states |
|
4169 { |
|
4170 EStBegin=0, |
|
4171 EStPoweredUp, |
|
4172 EStClockOn, |
|
4173 EStEnd |
|
4174 }; |
|
4175 |
|
4176 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:SwLowVolt")); |
|
4177 |
|
4178 DMMCPsu* psu=(DMMCPsu*)iSocket->iVcc; |
|
4179 |
|
4180 SMF_BEGIN |
|
4181 // turn power off |
|
4182 DoPowerDown(); |
|
4183 psu->SetState(EPsuOff); |
|
4184 |
|
4185 // turn power back on in low voltage mode |
|
4186 psu->SetVoltage(iCurrentOpRange); |
|
4187 if (psu->SetState(EPsuOnFull) != KErrNone) |
|
4188 return(KMMCErrHardware); |
|
4189 |
|
4190 SMF_INVOKES( DoPowerUpSMST, EStPoweredUp ) |
|
4191 |
|
4192 SMF_STATE(EStPoweredUp) |
|
4193 // turn the clock back on |
|
4194 SMF_INVOKES( InitClockOnSMST, EStClockOn ) // Feed init clock to the bus |
|
4195 |
|
4196 SMF_STATE(EStClockOn) |
|
4197 // wait for 1ms and then 74 clock cycles |
|
4198 // 74 clock cylces @ 400 Khz = 74 / 400,000 = 0.000185 secs = 0.185 ms |
|
4199 // so total wait = 1.185 ms |
|
4200 SMF_INVOKES(LowVoltagePowerupTimerSMST, EStEnd); |
|
4201 |
|
4202 SMF_END |
|
4203 |
|
4204 } |
|
4205 |
|
4206 inline TMMCErr DMMCStack::CIMCheckStackSM() |
|
4207 /** |
|
4208 * Performs the CIM_CHECK_STACK macro (with pre-emption disabled). |
|
4209 * @return MMC error code |
|
4210 */ |
|
4211 { |
|
4212 enum states |
|
4213 { |
|
4214 EStBegin=0, |
|
4215 EStFinish, |
|
4216 EStEnd |
|
4217 }; |
|
4218 |
|
4219 DMMCSession& s=Session(); |
|
4220 |
|
4221 SMF_BEGIN |
|
4222 |
|
4223 // This macro works naked and must not be preempted |
|
4224 iConfig.RemoveMode( KMMCModeEnablePreemption | KMMCModeCardControlled ); |
|
4225 s.iState |= KMMCSessStateInProgress; |
|
4226 |
|
4227 SMF_INVOKES( CheckStackSMST, EStFinish ) |
|
4228 |
|
4229 SMF_STATE(EStFinish) |
|
4230 |
|
4231 s.iState &= ~KMMCSessStateInProgress; |
|
4232 |
|
4233 SMF_END |
|
4234 } |
|
4235 |
|
4236 inline TMMCErr DMMCStack::CheckStackSM() |
|
4237 /** |
|
4238 * For each card in iCards[], sends CMD13 to see if still there. |
|
4239 * If not, calls DeclareCardAsGone(). Frees up space for new cards. |
|
4240 * @return MMC error code |
|
4241 */ |
|
4242 { |
|
4243 enum states |
|
4244 { |
|
4245 EStBegin=0, |
|
4246 EStLoop, |
|
4247 EStCardSelectedGotStatus, |
|
4248 EStCardDeselected, |
|
4249 EStEnd |
|
4250 }; |
|
4251 |
|
4252 DMMCSession& s=Session(); |
|
4253 |
|
4254 SMF_BEGIN |
|
4255 |
|
4256 iCxCardCount=-1; |
|
4257 m.SetTraps( KMMCErrResponseTimeOut ); |
|
4258 |
|
4259 SMF_STATE(EStLoop) |
|
4260 |
|
4261 if ( ++iCxCardCount == (TInt)iMaxCardsInStack ) |
|
4262 SMF_EXIT |
|
4263 |
|
4264 if ( !iCardArray->CardP(iCxCardCount)->IsPresent() ) |
|
4265 SMF_GOTOS( EStLoop ) // card's not present |
|
4266 |
|
4267 TUint32 arg = TUint32(iCardArray->CardP(iCxCardCount)->RCA()) << 16; |
|
4268 s.FillCommandDesc(ECmdSelectCard, arg); |
|
4269 SMF_INVOKES(ExecCommandSMST, EStCardSelectedGotStatus) |
|
4270 |
|
4271 SMF_STATE(EStCardSelectedGotStatus) |
|
4272 |
|
4273 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:cssm:err%08x", err)); |
|
4274 |
|
4275 if(err) |
|
4276 { |
|
4277 // Timeout - the card is no longer present so remove from the card array |
|
4278 iCardArray->DeclareCardAsGone(iCxCardCount); |
|
4279 SMF_GOTOS( EStLoop ) |
|
4280 } |
|
4281 |
|
4282 TMMCard& card=*(iCardArray->CardP(iCxCardCount)); |
|
4283 card.iStatus=s.ResponseP(); |
|
4284 |
|
4285 // This function is only called as part of the power up sequence, so |
|
4286 // take the opportunity to record if it has a password |
|
4287 if((card.iStatus & KMMCStatCardIsLocked) != 0) |
|
4288 { |
|
4289 card.iFlags|=KMMCardHasPassword; |
|
4290 } |
|
4291 |
|
4292 s.FillCommandDesc(ECmdSelectCard, 0); |
|
4293 SMF_INVOKES(ExecCommandSMST, EStCardDeselected) |
|
4294 |
|
4295 SMF_STATE(EStCardDeselected) |
|
4296 SMF_GOTOS( EStLoop ) |
|
4297 |
|
4298 SMF_END |
|
4299 } |
|
4300 |
|
4301 inline TMMCErr DMMCStack::CheckLockStatusSM() |
|
4302 /* |
|
4303 * Called as part of the power-up sequence, this determined if the card is locked or has a password. |
|
4304 * |
|
4305 * If the card reports itself as being unlocked and there is a mapping in the password store, |
|
4306 * then the stored password is used to attempt to lock the card. The overall aim of this is |
|
4307 * to ensure that if a card always powers up in the locked state if it contains a known password. |
|
4308 * |
|
4309 * This ensures that cards that are still unlocked after a power down/power up sequence do not |
|
4310 * end up having their passwords removed from the store, which can happen in environments where |
|
4311 * the PSU voltage level is not monitored - in such systems, we cannot guarantee that a card will |
|
4312 * be fully reset and power up locked, hence the need to attempt to lock the card. |
|
4313 * |
|
4314 * @return MMC error code |
|
4315 */ |
|
4316 { |
|
4317 enum states |
|
4318 { |
|
4319 EStBegin=0, |
|
4320 EStLoop, |
|
4321 EStCardSelectedGotStatus, |
|
4322 EStCheckLockStatus, |
|
4323 EStCardDeselected, |
|
4324 EStEnd |
|
4325 }; |
|
4326 |
|
4327 DMMCSession& s=Session(); |
|
4328 |
|
4329 SMF_BEGIN |
|
4330 |
|
4331 iCxCardCount=-1; |
|
4332 m.SetTraps( KMMCErrResponseTimeOut ); |
|
4333 iMinorBufLen = KMinMinorBufSize; |
|
4334 |
|
4335 SMF_STATE(EStLoop) |
|
4336 |
|
4337 if ( ++iCxCardCount == (TInt)iMaxCardsInStack ) |
|
4338 SMF_EXIT |
|
4339 |
|
4340 if ( !iCardArray->CardP(iCxCardCount)->IsPresent() ) |
|
4341 SMF_GOTOS( EStLoop ) // card's not present |
|
4342 |
|
4343 TUint32 arg = TUint32(iCardArray->CardP(iCxCardCount)->RCA()) << 16; |
|
4344 s.FillCommandDesc(ECmdSelectCard, arg); |
|
4345 SMF_INVOKES(ExecCommandSMST, EStCardSelectedGotStatus) |
|
4346 |
|
4347 SMF_STATE(EStCardSelectedGotStatus) |
|
4348 |
|
4349 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:cssm:err%08x", err)); |
|
4350 if ( !err ) |
|
4351 { |
|
4352 TMMCard& card=*(iCardArray->CardP(iCxCardCount)); |
|
4353 card.iStatus=s.ResponseP(); // Got the response |
|
4354 |
|
4355 iMinorBufLen = Max(iMinorBufLen, 1 << card.MaxReadBlLen()); |
|
4356 |
|
4357 // this function is only called as part of the power up sequence, so |
|
4358 // take the opportunity to record if it has a password |
|
4359 if((card.iStatus & KMMCStatCardIsLocked) != 0) |
|
4360 { |
|
4361 card.iFlags |= KMMCardHasPassword; |
|
4362 } |
|
4363 |
|
4364 // If the status suggests that the card is unlocked, we test |
|
4365 // for the presence of a password by attempting to lock the card |
|
4366 // (if we have a password in the store). This handles conditions |
|
4367 // where a card has not been fully powered down before reapplying power. |
|
4368 if(!(card.iFlags & KMMCardHasPassword)) |
|
4369 { |
|
4370 TMapping* pmp = iSocket->iPasswordStore->FindMappingInStore(card.CID()); |
|
4371 if(pmp) |
|
4372 { |
|
4373 if(pmp->iState == TMapping::EStValid) |
|
4374 { |
|
4375 const TInt kPWD_LEN = pmp->iPWD.Length(); |
|
4376 iPSLBuf[0] = KMMCLockUnlockLockUnlock; // LOCK_UNLOCK = 1, SET_PWD = 0, CLR_PWD = 0 |
|
4377 iPSLBuf[1] = static_cast<TUint8>(kPWD_LEN); |
|
4378 TPtr8 pwd(&iPSLBuf[2], kPWD_LEN); |
|
4379 pwd.Copy(pmp->iPWD); |
|
4380 |
|
4381 const TInt kBlockLen = 1 + 1 + kPWD_LEN; |
|
4382 |
|
4383 // Need to use CIMReadWriteBlocksSMST to ensure that the |
|
4384 // card is connected and the block length is set correctly |
|
4385 s.SetCard(iCardArray->CardP(iCxCardCount)); |
|
4386 m.SetTraps(KMMCErrStatus | KMMCErrUpdPswd); |
|
4387 s.FillCommandDesc(ECmdLockUnlock); |
|
4388 s.FillCommandArgs(0, kBlockLen, iPSLBuf, kBlockLen); |
|
4389 |
|
4390 TMMCCommandDesc& cmd = s.Command(); |
|
4391 cmd.iUnlockRetries = 0; |
|
4392 |
|
4393 SMF_INVOKES(CIMReadWriteBlocksSMST,EStCheckLockStatus) |
|
4394 } |
|
4395 } |
|
4396 } |
|
4397 } |
|
4398 |
|
4399 SMF_GOTOS( EStLoop ) |
|
4400 |
|
4401 SMF_STATE(EStCheckLockStatus) |
|
4402 |
|
4403 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:cssm:err%08x", err)); |
|
4404 |
|
4405 if ((err & KMMCErrUpdPswd) || |
|
4406 ((err & KMMCErrStatus) && (s.LastStatus().Error() == KMMCStatErrLockUnlock))) |
|
4407 { |
|
4408 // ECMDLockUnlock (with LockUnlockLockUnlock param) succeeded. |
|
4409 // (either locked successfully, or we have attempted to lock a locked card) |
|
4410 // Now determine if the card really is locked by checking the lock status. |
|
4411 TMMCard& card=*(iCardArray->CardP(iCxCardCount)); |
|
4412 card.iStatus=s.LastStatus(); |
|
4413 if((card.iStatus & KMMCStatCardIsLocked) != 0) |
|
4414 { |
|
4415 card.iFlags |= KMMCardHasPassword; |
|
4416 } |
|
4417 } |
|
4418 |
|
4419 s.FillCommandDesc(ECmdSelectCard, 0); |
|
4420 SMF_INVOKES(ExecCommandSMST, EStCardDeselected) |
|
4421 |
|
4422 SMF_STATE(EStCardDeselected) |
|
4423 SMF_GOTOS( EStLoop ) |
|
4424 |
|
4425 SMF_END |
|
4426 } |
|
4427 |
|
4428 EXPORT_C TMMCErr DMMCStack::ModifyCardCapabilitySM() |
|
4429 // |
|
4430 // This function provides a chance to modify the capability of paticular cards. |
|
4431 // Licensee may overide this function to modify certain card's capability as needed. |
|
4432 // A state machine is needed in derived function and function of base class should be |
|
4433 // called in order to act more generic behaviour. |
|
4434 // |
|
4435 { |
|
4436 enum states |
|
4437 { |
|
4438 EStBegin=0, |
|
4439 EStEnd |
|
4440 }; |
|
4441 |
|
4442 SMF_BEGIN |
|
4443 |
|
4444 SMF_END |
|
4445 } |
|
4446 |
|
4447 // |
|
4448 // Timers |
|
4449 // |
|
4450 |
|
4451 inline TMMCErr DMMCStack::PollGapTimerSM() |
|
4452 /** |
|
4453 * Starts the poll timer. |
|
4454 * |
|
4455 * This may be used when executing CIM_UPDATE_ACQ when handling cards which are |
|
4456 * slow to power-up/reset and return busy following the issuing of CMD1. |
|
4457 * |
|
4458 * @return MMC error code. |
|
4459 */ |
|
4460 { |
|
4461 enum states |
|
4462 { |
|
4463 EStBegin=0, |
|
4464 EStEnd |
|
4465 }; |
|
4466 #ifdef __EPOC32__ |
|
4467 DMMCSession& s=Session(); |
|
4468 #endif |
|
4469 |
|
4470 SMF_BEGIN |
|
4471 |
|
4472 #ifdef __EPOC32__ |
|
4473 s.SynchBlock( KMMCBlockOnPollTimer ); |
|
4474 s.iPollTimer.OneShot(KMMCPollGapInMilliseconds,EFalse); |
|
4475 |
|
4476 SMF_EXITWAIT |
|
4477 #endif |
|
4478 |
|
4479 SMF_END |
|
4480 } |
|
4481 |
|
4482 inline TMMCErr DMMCStack::RetryGapTimerSM() |
|
4483 /** |
|
4484 * Starts the retry timer. |
|
4485 * |
|
4486 * This may be used when executing CIM_UPDATE_ACQ. When initialising the stack, |
|
4487 * CMD0 is issued twice to get the bus in a known state and this timer is used |
|
4488 * to time the gap between issuing the two CMD0 commands. |
|
4489 * |
|
4490 * @return MMC error code. |
|
4491 */ |
|
4492 { |
|
4493 enum states |
|
4494 { |
|
4495 EStBegin=0, |
|
4496 EStEnd |
|
4497 }; |
|
4498 #ifdef __EPOC32__ |
|
4499 DMMCSession& s=Session(); |
|
4500 #endif |
|
4501 |
|
4502 SMF_BEGIN |
|
4503 |
|
4504 #ifdef __EPOC32__ |
|
4505 s.SynchBlock( KMMCBlockOnRetryTimer ); |
|
4506 s.iRetryTimer.OneShot(KMMCRetryGapInMilliseconds,EFalse); |
|
4507 |
|
4508 SMF_EXITWAIT |
|
4509 #endif |
|
4510 |
|
4511 SMF_END |
|
4512 } |
|
4513 |
|
4514 inline TMMCErr DMMCStack::ProgramTimerSM() |
|
4515 /** |
|
4516 * Starts the program timer. |
|
4517 * |
|
4518 * This is used during write operartions to a card to sleep for an PSL-dependent period |
|
4519 * between issuing send status commands (CMD13). This is required in order to check when |
|
4520 * the card has finished writing its data to payload memory. |
|
4521 * |
|
4522 * @return MMC error code. |
|
4523 */ |
|
4524 { |
|
4525 enum states |
|
4526 { |
|
4527 EStBegin = 0, |
|
4528 EStEnd |
|
4529 }; |
|
4530 |
|
4531 #ifdef __EPOC32__ |
|
4532 DMMCSession &s = Session(); |
|
4533 #endif |
|
4534 |
|
4535 SMF_BEGIN |
|
4536 #ifdef __EPOC32__ |
|
4537 s.SynchBlock(KMMCBlockOnPgmTimer); |
|
4538 s.iProgramTimer.Cancel(); |
|
4539 s.iProgramTimer.OneShot(ProgramPeriodInMilliSeconds(),EFalse); |
|
4540 |
|
4541 SMF_EXITWAIT |
|
4542 #endif |
|
4543 SMF_END |
|
4544 } |
|
4545 |
|
4546 TMMCErr DMMCStack::LowVoltagePowerupTimerSM() |
|
4547 /** |
|
4548 * Starts the low voltage power-up timer |
|
4549 * NB Re-uses the retry gap timer. |
|
4550 * |
|
4551 * This is used after powering the bus off and then on after discovering that |
|
4552 * both the controller and card support low voltage operation. |
|
4553 * |
|
4554 * @return MMC error code. |
|
4555 */ |
|
4556 { |
|
4557 enum states |
|
4558 { |
|
4559 EStBegin = 0, |
|
4560 EStEnd |
|
4561 }; |
|
4562 |
|
4563 #ifdef __EPOC32__ |
|
4564 DMMCSession &s = Session(); |
|
4565 #endif |
|
4566 |
|
4567 SMF_BEGIN |
|
4568 #ifdef __EPOC32__ |
|
4569 s.SynchBlock(KMMCBlockOnRetryTimer); |
|
4570 s.iRetryTimer.OneShot(KMMCLowVoltagePowerUpTimeoutInMilliseconds,EFalse); |
|
4571 |
|
4572 SMF_EXITWAIT |
|
4573 #endif |
|
4574 SMF_END |
|
4575 } |
|
4576 |
|
4577 |
|
4578 inline TMMCErr DMMCStack::ExecCommandSM() |
|
4579 /** |
|
4580 * The main command executor. |
|
4581 * Depending on the main command being issued, this macro may result in the issuing of whole sequence |
|
4582 * of commands as it prepares the bus for the command in question. |
|
4583 * |
|
4584 * In certain circumstances, this first issues one or more de-select commands (CMD7 + reserved RCA) |
|
4585 * to get the bus in a known state. It then analyses the main command and if necessary, selects the |
|
4586 * card in question (CMD7 + target RCA). |
|
4587 * |
|
4588 * For block transfer commands, it will set the block length on the card concerned (CMD16) if this has |
|
4589 * not been done already. Likewise, for SD Cards, if the bus width has not yet been set-up, it will issue |
|
4590 * the appropriate bus width command (ACMD6). |
|
4591 * |
|
4592 * Finally it issues the main command requested before performing any error recovery that may be necessary |
|
4593 * following this. |
|
4594 * |
|
4595 * In all cases it calls the generic layer child function IssueCommandCheckResponseSM() to execute each command. |
|
4596 * |
|
4597 * @return MMC error code |
|
4598 */ |
|
4599 { |
|
4600 enum states |
|
4601 { |
|
4602 EStBegin=0, |
|
4603 EStExecCmd, |
|
4604 EStRetry, |
|
4605 EStDeselectLoop, |
|
4606 EStDeselectEndCheck, |
|
4607 EStAnalyseCommand, |
|
4608 EStSelectDone, |
|
4609 EStBlockCountCmdIssued, |
|
4610 EStTestAppCommand, |
|
4611 EStIssueAppCommandDone, |
|
4612 EStIssueCommand, |
|
4613 EStCommandIssued, |
|
4614 EStErrRecover, |
|
4615 EStEnd |
|
4616 }; |
|
4617 |
|
4618 DMMCSession& s=Session(); |
|
4619 |
|
4620 SMF_BEGIN |
|
4621 |
|
4622 if ( ( s.CardRCA() != 0 ) && ( (s.CardP()->iStatus.State()) == ECardStateSlp) ) |
|
4623 { |
|
4624 // Currently selected media is asleep, so it must be awoken |
|
4625 SMF_INVOKES(ExecAwakeCommandSMST,EStExecCmd) |
|
4626 } |
|
4627 |
|
4628 SMF_STATE(EStExecCmd) |
|
4629 |
|
4630 TMMCCommandDesc& cmd = s.Command(); |
|
4631 // clearup some internally used flags |
|
4632 cmd.iFlags &= ~(KMMCCmdFlagExecTopBusy|KMMCCmdFlagExecSelBusy); |
|
4633 cmd.iPollAttempts = cmd.iTimeOutRetries = cmd.iCRCRetries = 0; |
|
4634 |
|
4635 SMF_STATE(EStRetry) |
|
4636 |
|
4637 TMMCCommandDesc& cmd = s.Command(); |
|
4638 m.SetTraps( KMMCErrBasic & ~Command().iExecNotHandle); // Processing all trappable errors |
|
4639 |
|
4640 if (iMultiplexedBus) |
|
4641 { |
|
4642 if(cmd.iCommand == ECmdSelectCard) |
|
4643 { |
|
4644 DeselectsToIssue(1); |
|
4645 } |
|
4646 |
|
4647 if (iConfig.iModes & KMMCModeCardControlled) |
|
4648 { |
|
4649 DoSetBusWidth(BusWidthEncoding(s.CardP()->BusWidth())); |
|
4650 DoSetClock(MaxTranSpeedInKilohertz(*s.CardP())); |
|
4651 |
|
4652 // Check if this card is already in the appropriate selected/deselected |
|
4653 // state for the forthcoming command. |
|
4654 if (s.CardRCA() != iSelectedCard) |
|
4655 { |
|
4656 DeselectsToIssue(1); |
|
4657 } |
|
4658 } |
|
4659 } |
|
4660 |
|
4661 // If bus context is unknown, issue DESELECT a few times with a RetryGap between them. |
|
4662 if ( (iStackState & KMMCStackStateDoDeselect) == 0 ) |
|
4663 SMF_GOTOS( EStAnalyseCommand ) |
|
4664 |
|
4665 // Save the top-level command while we issue de-selects |
|
4666 s.PushCommandStack(); |
|
4667 s.FillCommandDesc( ECmdSelectCard, 0 ); // Deselect - RCA=0 |
|
4668 iCxDeselectCount=iDeselectsToIssue; |
|
4669 |
|
4670 SMF_STATE(EStDeselectLoop) |
|
4671 |
|
4672 SMF_INVOKES(IssueCommandCheckResponseSMST,EStDeselectEndCheck) |
|
4673 |
|
4674 SMF_STATE(EStDeselectEndCheck) |
|
4675 |
|
4676 // If we got an error and this is the last de-select then give up |
|
4677 if (err && iCxDeselectCount == 1) |
|
4678 { |
|
4679 s.PopCommandStack(); |
|
4680 SMF_RETURN(err) |
|
4681 } |
|
4682 |
|
4683 if (--iCxDeselectCount > 0) |
|
4684 SMF_INVOKES(RetryGapTimerSMST,EStDeselectLoop) |
|
4685 |
|
4686 s.PopCommandStack(); |
|
4687 iStackState &= ~KMMCStackStateDoDeselect; |
|
4688 |
|
4689 SMF_STATE(EStAnalyseCommand) |
|
4690 |
|
4691 TMMCCommandDesc& cmd = s.Command(); |
|
4692 // Check if its un-important whether the card is in transfer state (i.e |
|
4693 // selected) or not for the command we are about to execute. Such |
|
4694 // commands are CMD0, CMD7 and CMD13. |
|
4695 |
|
4696 // This state machine should never send CMD55 |
|
4697 if (cmd.iCommand == ECmdAppCmd) |
|
4698 SMF_RETURN (KMMCErrNotSupported) |
|
4699 |
|
4700 SMF_NEXTS( EStTestAppCommand ) |
|
4701 if (cmd.iCommand == ECmdGoIdleState || cmd.iCommand == ECmdSelectCard || cmd.iCommand == ECmdSendStatus) |
|
4702 { |
|
4703 SMF_GOTONEXTS |
|
4704 } |
|
4705 |
|
4706 // See if we need to select (or deselect) this card |
|
4707 TRCA targetRCA=0; |
|
4708 switch( cmd.iSpec.iCommandType ) |
|
4709 { |
|
4710 case ECmdTypeBC: case ECmdTypeBCR: case ECmdTypeAC: |
|
4711 // Command which don't require the card to be selected |
|
4712 break; |
|
4713 case ECmdTypeACS: case ECmdTypeADTCS: case ECmdTypeADC: |
|
4714 // Commands which do require the card to be selected |
|
4715 { |
|
4716 if ( (iConfig.iModes & KMMCModeCardControlled) == 0 ) |
|
4717 SMF_GOTONEXTS |
|
4718 // Get the RCA of the card |
|
4719 if ( (targetRCA = s.CardRCA()) == 0 ) |
|
4720 SMF_RETURN( KMMCErrNoCard ) |
|
4721 break; |
|
4722 } |
|
4723 default: |
|
4724 SMF_GOTONEXTS |
|
4725 } |
|
4726 |
|
4727 // Check if this card is already in the appropriate selected/deselected |
|
4728 // state for the forthcoming command. |
|
4729 if (targetRCA==iSelectedCard) |
|
4730 SMF_GOTONEXTS |
|
4731 |
|
4732 // Need to select (or deselect by using RCA(0)) the card so push the |
|
4733 // top-level command onto the command stack while we issue the select command. |
|
4734 s.PushCommandStack(); |
|
4735 s.FillCommandDesc(ECmdSelectCard,targetRCA); |
|
4736 SMF_INVOKES(IssueCommandCheckResponseSMST,EStSelectDone) |
|
4737 |
|
4738 SMF_STATE(EStSelectDone) |
|
4739 |
|
4740 TMMCCommandDesc& cmd = s.Command(); |
|
4741 |
|
4742 if ( err ) |
|
4743 { |
|
4744 cmd.iFlags &= ~(KMMCCmdFlagASSPFlags|KMMCCmdFlagExecSelBusy); |
|
4745 |
|
4746 if (err == KMMCErrBusyTimeOut) |
|
4747 cmd.iFlags |= KMMCCmdFlagExecSelBusy; |
|
4748 |
|
4749 s.PopCommandStack(); |
|
4750 SMF_NEXTS(EStErrRecover) |
|
4751 return(err); // re-enter the next state with that error |
|
4752 } |
|
4753 |
|
4754 // Are we trying to recover from a top-level command returning busy (by de-selecting and re-selecting) |
|
4755 if ( cmd.iFlags & KMMCCmdFlagExecTopBusy ) |
|
4756 { |
|
4757 cmd.iFlags &= ~KMMCCmdFlagExecTopBusy; |
|
4758 |
|
4759 TUint32 blockLength = cmd.BlockLength(); |
|
4760 |
|
4761 if ( !(cmd.iSpec.iMultipleBlocks) || cmd.iTotalLength <= blockLength ) |
|
4762 SMF_EXIT // operation is completed |
|
4763 |
|
4764 cmd.iTotalLength -= blockLength; |
|
4765 cmd.iArgument = TUint(cmd.iArgument) + blockLength; |
|
4766 cmd.iDataMemoryP += blockLength; |
|
4767 s.iBytesTransferred += blockLength; |
|
4768 cmd.iPollAttempts = 0; |
|
4769 } |
|
4770 |
|
4771 s.PopCommandStack(); |
|
4772 |
|
4773 cmd = s.Command(); |
|
4774 if (!cmd.iSpec.iUseStopTransmission && cmd.iSpec.iMultipleBlocks) |
|
4775 { |
|
4776 // Multi-block command using SET_BLOCK_COUNT |
|
4777 // This is a re-try of the data transfer, normally select (CMD7) is performed along with the issuing of CMD23, |
|
4778 // therefore need to re-issue SET_BLOCK_COUNT..... |
|
4779 const TUint blocks = cmd.NumBlocks(); |
|
4780 |
|
4781 s.PushCommandStack(); |
|
4782 s.FillCommandDesc( ECmdSetBlockCount, blocks ); |
|
4783 SMF_INVOKES( IssueCommandCheckResponseSMST, EStBlockCountCmdIssued ) |
|
4784 } |
|
4785 else |
|
4786 { |
|
4787 SMF_GOTOS( EStTestAppCommand ) |
|
4788 } |
|
4789 |
|
4790 SMF_STATE(EStBlockCountCmdIssued) |
|
4791 |
|
4792 const TMMCStatus status(s.ResponseP()); |
|
4793 s.PopCommandStack(); |
|
4794 if (status.Error()) |
|
4795 SMF_RETURN(KMMCErrStatus) |
|
4796 |
|
4797 if(err & KMMCErrNotSupported) |
|
4798 { |
|
4799 // Not supported by the PSL, so use standard Stop Transmission mode |
|
4800 s.Command().iSpec.iUseStopTransmission = ETrue; |
|
4801 } |
|
4802 // Fall through... |
|
4803 |
|
4804 SMF_STATE(EStTestAppCommand) |
|
4805 TMMCCommandDesc& cmd = s.Command(); |
|
4806 if (cmd.iSpec.iCommandClass != KMMCCmdClassApplication) |
|
4807 SMF_GOTOS( EStIssueCommand ) |
|
4808 |
|
4809 s.PushCommandStack(); |
|
4810 s.FillCommandDesc(ECmdAppCmd, s.CardRCA()); // Send APP_CMD (CMD55) |
|
4811 SMF_INVOKES(IssueCommandCheckResponseSMST,EStIssueAppCommandDone) |
|
4812 |
|
4813 SMF_STATE(EStIssueAppCommandDone) |
|
4814 s.PopCommandStack(); |
|
4815 if ( err ) |
|
4816 { |
|
4817 SMF_NEXTS(EStErrRecover) |
|
4818 return(err); // re-enter the next state with that error |
|
4819 } |
|
4820 |
|
4821 |
|
4822 SMF_STATE(EStIssueCommand) |
|
4823 |
|
4824 TMMCCommandDesc& cmd = s.Command(); |
|
4825 // If its an addressed command (rather than a selected command), then |
|
4826 // setup the argument with the RCA. (Commands requiring card to be |
|
4827 // selected - ACS don't matter since selected cards don't need an RCA now). |
|
4828 if ((iConfig.iModes & KMMCModeCardControlled) && cmd.iSpec.iCommandType==ECmdTypeAC ) |
|
4829 { |
|
4830 const TRCA targetRCA = s.CardRCA(); |
|
4831 if ( targetRCA == 0 ) |
|
4832 SMF_RETURN( KMMCErrNoCard ) |
|
4833 cmd.iArgument.SetRCA(targetRCA); |
|
4834 } |
|
4835 |
|
4836 // Issue the top-level command |
|
4837 SMF_INVOKES(IssueCommandCheckResponseSMST,EStCommandIssued) |
|
4838 |
|
4839 SMF_STATE(EStCommandIssued) |
|
4840 |
|
4841 // If command was succesful then we've finished. |
|
4842 if (!err) |
|
4843 SMF_EXIT |
|
4844 |
|
4845 SMF_STATE(EStErrRecover) |
|
4846 |
|
4847 TMMCCommandDesc& cmd = s.Command(); |
|
4848 const TUint32 modes=iConfig.iModes; |
|
4849 SMF_NEXTS(EStRetry) |
|
4850 |
|
4851 m.ResetTraps(); // no more re-entries via return() |
|
4852 |
|
4853 if (cmd.iCommand == ECmdSelectCard) |
|
4854 DeselectsToIssue( 1 ); // in case stby/tran synch is lost |
|
4855 |
|
4856 if (cmd.iSpec.iMultipleBlocks && (cmd.iFlags & KMMCCmdFlagBytesValid)) |
|
4857 { |
|
4858 cmd.iTotalLength -= cmd.iBytesDone; |
|
4859 cmd.iArgument = TUint(cmd.iArgument) + cmd.iBytesDone; |
|
4860 cmd.iDataMemoryP += cmd.iBytesDone; |
|
4861 s.iBytesTransferred += cmd.iBytesDone; |
|
4862 |
|
4863 if (cmd.iTotalLength < cmd.BlockLength()) |
|
4864 { |
|
4865 DeselectsToIssue(1); |
|
4866 return(err); |
|
4867 } |
|
4868 } |
|
4869 |
|
4870 if ((modes & KMMCModeEnableRetries) == 0) |
|
4871 return( err ); |
|
4872 |
|
4873 const TUint32 toMask = (KMMCErrResponseTimeOut|KMMCErrDataTimeOut); |
|
4874 const TUint32 crcMask = (KMMCErrResponseCRC|KMMCErrDataCRC|KMMCErrCommandCRC); |
|
4875 |
|
4876 if( (err & ~(toMask|crcMask)) == KMMCErrNone ) // time-outs/CRC errors |
|
4877 { |
|
4878 if( cmd.iSpec.iCommandType == ECmdTypeADTCS ) // for data transfer commands |
|
4879 { |
|
4880 DeselectsToIssue( 1 ); // enforce deselect before any retries |
|
4881 |
|
4882 if( (modes & KMMCModeCardControlled) == 0 ) |
|
4883 return( err ); // we wouldn't know what to select - no retries |
|
4884 } |
|
4885 |
|
4886 TUint32 gapEnabled = 0; |
|
4887 |
|
4888 if( err & toMask ) |
|
4889 { |
|
4890 cmd.iTimeOutRetries++; |
|
4891 gapEnabled |= KMMCModeTimeOutRetryGap; |
|
4892 |
|
4893 if( (modes & KMMCModeEnableTimeOutRetry) == 0 || |
|
4894 cmd.iTimeOutRetries > iConfig.iTimeOutRetries ) |
|
4895 return( err ); |
|
4896 } |
|
4897 |
|
4898 if( err & crcMask ) |
|
4899 { |
|
4900 cmd.iCRCRetries++; |
|
4901 gapEnabled |= KMMCModeCRCRetryGap; |
|
4902 |
|
4903 if( (modes & KMMCModeEnableCRCRetry) == 0 || |
|
4904 cmd.iCRCRetries > iConfig.iCRCRetries || |
|
4905 ((err & KMMCErrDataCRC) != 0 && (modes & KMMCModeDataCRCRetry) == 0) ) |
|
4906 return( err ); |
|
4907 } |
|
4908 |
|
4909 if( (modes & gapEnabled) == gapEnabled ) |
|
4910 { |
|
4911 if( modes & KMMCModeCardControlled ) |
|
4912 s.iState |= KMMCSessStateSafeInGaps; // preemption allowed |
|
4913 |
|
4914 SMF_CALL( RetryGapTimerSMST ) |
|
4915 } |
|
4916 |
|
4917 if( (modes & (KMMCModeEnablePreemption|KMMCModePreemptOnRetry|KMMCModeCardControlled)) == |
|
4918 (KMMCModeEnablePreemption|KMMCModePreemptOnRetry|KMMCModeCardControlled) ) |
|
4919 { |
|
4920 s.SwapMe(); |
|
4921 SMF_WAIT // let the others take over the bus before retry |
|
4922 } |
|
4923 |
|
4924 // No preemption, just repeat the command |
|
4925 SMF_GOTONEXTS |
|
4926 } |
|
4927 |
|
4928 if( err & KMMCErrBusInconsistent ) |
|
4929 { |
|
4930 // ASSP layer reported that we must re-initialise the stack to recover. |
|
4931 // Here we'll allow stack initialiser to take over. The control will then be transferred |
|
4932 // to whoever processes KMMCErrInitContext (must be a top-level SM function) |
|
4933 |
|
4934 // ReportInconsistentBusState(); // ASSP layer should have it done |
|
4935 s.iGlobalRetries++; |
|
4936 |
|
4937 if( s.iGlobalRetries > KMMCMaxGlobalRetries ) |
|
4938 return( err ); |
|
4939 |
|
4940 s.SwapMe(); // To prevent the initialiser from aborting this session |
|
4941 SMF_WAIT // Initialiser will take over here |
|
4942 } |
|
4943 |
|
4944 if( err == KMMCErrBusyTimeOut ) |
|
4945 { |
|
4946 if ((cmd.iFlags & KMMCCmdFlagExecSelBusy) == 0) // check if that was a response |
|
4947 cmd.iFlags |= KMMCCmdFlagExecTopBusy; // to a top level command |
|
4948 |
|
4949 DeselectsToIssue( 1 ); // force deselect as the next bus operation |
|
4950 cmd.iPollAttempts++; |
|
4951 |
|
4952 if( (modes & KMMCModeEnableBusyPoll) == 0 || |
|
4953 ((modes & KMMCModeCardControlled) == 0 && cmd.iCommand != ECmdSelectCard) || |
|
4954 cmd.iPollAttempts > iConfig.iPollAttempts ) |
|
4955 return( err ); |
|
4956 |
|
4957 if( modes & KMMCModeBusyPollGap ) |
|
4958 { |
|
4959 s.iState |= KMMCSessStateSafeInGaps; // preemption allowed |
|
4960 SMF_CALL( PollGapTimerSMST ) |
|
4961 } |
|
4962 |
|
4963 if( (modes & (KMMCModeEnablePreemption|KMMCModePreemptOnBusy)) == |
|
4964 (KMMCModeEnablePreemption|KMMCModePreemptOnBusy) ) |
|
4965 { |
|
4966 s.SwapMe(); |
|
4967 SMF_WAIT // let the others take over the bus before retry |
|
4968 } |
|
4969 |
|
4970 // No preemption, just repeat the Deselect/Select sequence |
|
4971 SMF_GOTONEXTS |
|
4972 } |
|
4973 |
|
4974 return( err ); |
|
4975 |
|
4976 SMF_END |
|
4977 } |
|
4978 |
|
4979 TMMCErr DMMCStack::IssueCommandCheckResponseSM() |
|
4980 /** |
|
4981 * Issue a single command to the card and check the response |
|
4982 * |
|
4983 * This generic layer child function in turn calls IssueMMCCommandSM(). |
|
4984 * |
|
4985 * @return MMC error code. |
|
4986 */ |
|
4987 { |
|
4988 enum states |
|
4989 { |
|
4990 EStBegin=0, |
|
4991 EStIssueCommand, |
|
4992 EStCommandIssued, |
|
4993 EStEnd |
|
4994 }; |
|
4995 |
|
4996 DMMCSession& s=Session(); |
|
4997 TMMCCommandDesc& cmd = Command(); |
|
4998 |
|
4999 SMF_BEGIN |
|
5000 |
|
5001 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Issue %d %x",TUint(cmd.iCommand),TUint(cmd.iArgument))); |
|
5002 |
|
5003 // Stop the Controller from powering down the card due to bus inactivity |
|
5004 iSocket->ResetInactivity(0); |
|
5005 |
|
5006 SMF_STATE(EStIssueCommand) |
|
5007 |
|
5008 // If command is directed at a specific card then save this command in card object |
|
5009 if (iConfig.iModes & KMMCModeCardControlled) |
|
5010 { |
|
5011 s.iCardP->iLastCommand = cmd.iCommand; |
|
5012 |
|
5013 if(iMultiplexedBus) |
|
5014 { |
|
5015 DoSetBusWidth(BusWidthEncoding(s.CardP()->BusWidth())); |
|
5016 DoSetClock(MaxTranSpeedInKilohertz(*s.CardP())); |
|
5017 } |
|
5018 } |
|
5019 |
|
5020 if (cmd.iCommand==ECmdSelectCard) |
|
5021 iSelectedCard = TUint16(~0); |
|
5022 |
|
5023 // Pass the command to ASSP layer |
|
5024 cmd.iFlags &= ~(KMMCCmdFlagASSPFlags|KMMCCmdFlagExecSelBusy); |
|
5025 cmd.iBytesDone=0; |
|
5026 m.SetTraps(KMMCErrAll); |
|
5027 SMF_INVOKES(IssueMMCCommandSMST,EStCommandIssued) |
|
5028 |
|
5029 SMF_STATE(EStCommandIssued) |
|
5030 |
|
5031 #ifdef ENABLE_DETAILED_SD_COMMAND_TRACE |
|
5032 cmd.Dump(s.ResponseP(), err); |
|
5033 #endif |
|
5034 |
|
5035 TMMCErr exitCode=err; |
|
5036 // If we have just de-selected all cards in the stack, RCA(0) then ignore response timeout. |
|
5037 if ( cmd.iCommand==ECmdSelectCard && TRCA(cmd.iArgument)==0 ) |
|
5038 exitCode &= ~KMMCErrResponseTimeOut; |
|
5039 else |
|
5040 { |
|
5041 // If commands returns card status and there we no command errors |
|
5042 // (or the status contains errors) then save the status info. |
|
5043 if ( (cmd.iFlags & KMMCCmdFlagStatusReceived) || |
|
5044 ((exitCode==KMMCErrNone || (exitCode & KMMCErrStatus)) && |
|
5045 (cmd.iSpec.iResponseType==ERespTypeR1 || cmd.iSpec.iResponseType==ERespTypeR1B)) ) |
|
5046 { |
|
5047 TMMCStatus status=s.ResponseP(); |
|
5048 s.iLastStatus=status; |
|
5049 __KTRACE_OPT(KPBUS1, Kern::Printf("mmc:ec:st=%08x", TUint32(status))); |
|
5050 |
|
5051 if (iConfig.iModes & KMMCModeCardControlled) |
|
5052 s.iCardP->iStatus=status; |
|
5053 |
|
5054 // Update exit code if card status is reporting an error (in case not done already) |
|
5055 if (status.Error() != 0) |
|
5056 exitCode |= KMMCErrStatus; |
|
5057 } |
|
5058 } |
|
5059 |
|
5060 // If we've just selected a card and the command was succesful then |
|
5061 // remember which one so we don't need to do it twice. |
|
5062 if (cmd.iCommand==ECmdSelectCard && exitCode==KMMCErrNone) |
|
5063 iSelectedCard=TRCA(cmd.iArgument); |
|
5064 |
|
5065 SMF_RETURN(exitCode) |
|
5066 |
|
5067 SMF_END |
|
5068 } |
|
5069 |
|
5070 // |
|
5071 // General Client Service CIMs/Sessions; top level functions |
|
5072 // |
|
5073 inline TMMCErr DMMCStack::NakedSessionSM() |
|
5074 /** |
|
5075 * Executes an individual MMC command (as opposed to a macro). |
|
5076 * |
|
5077 * If the command is 'card controlled' it first invokes AttachCardSM() |
|
5078 * before calling ExecCommandSM() to excecute the requested command. |
|
5079 * |
|
5080 * @return MMC error code. |
|
5081 */ |
|
5082 { |
|
5083 enum states |
|
5084 { |
|
5085 EStBegin=0, |
|
5086 EStAttached, |
|
5087 EStFinish, |
|
5088 EStEnd |
|
5089 }; |
|
5090 |
|
5091 DMMCSession& s=Session(); |
|
5092 |
|
5093 SMF_BEGIN |
|
5094 s.iState |= KMMCSessStateInProgress; |
|
5095 |
|
5096 if( (iConfig.iModes & KMMCModeCardControlled) != 0 ) |
|
5097 SMF_INVOKES( AttachCardSMST, EStAttached ) |
|
5098 |
|
5099 SMF_BPOINT(EStAttached) |
|
5100 |
|
5101 SMF_INVOKES( ExecCommandSMST, EStFinish ) |
|
5102 |
|
5103 SMF_STATE(EStFinish) |
|
5104 |
|
5105 s.iState &= ~KMMCSessStateInProgress; |
|
5106 SMF_END |
|
5107 } |
|
5108 |
|
5109 inline TMMCErr DMMCStack::CIMSetupCardSM() |
|
5110 /** |
|
5111 * Executes the CIM_SETUP_CARD macro. |
|
5112 * |
|
5113 * @return MMC error code. |
|
5114 */ |
|
5115 { |
|
5116 enum states |
|
5117 { |
|
5118 EStBegin=0, |
|
5119 EStAttached, |
|
5120 EStSelected, |
|
5121 EStGotCSD, |
|
5122 EStEnd |
|
5123 }; |
|
5124 |
|
5125 DMMCSession& s=Session(); |
|
5126 |
|
5127 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:SetupCardSM %x",TUint(s.iLastStatus))); |
|
5128 |
|
5129 SMF_BEGIN |
|
5130 s.iState |= KMMCSessStateInProgress; |
|
5131 |
|
5132 SMF_INVOKES( AttachCardSMST, EStAttached ) // attachment is mandatory here |
|
5133 |
|
5134 SMF_BPOINT(EStAttached) |
|
5135 |
|
5136 s.FillCommandDesc( ECmdSelectCard, 0 ); |
|
5137 SMF_INVOKES( ExecCommandSMST, EStSelected ) |
|
5138 |
|
5139 SMF_STATE(EStSelected) |
|
5140 |
|
5141 if( !s.iCardP->IsReady() ) |
|
5142 return( KMMCErrNoCard ); |
|
5143 |
|
5144 s.FillCommandDesc( ECmdSendCSD, Command().iArgument ); // NB: the card will be deselected to execute this command |
|
5145 SMF_INVOKES( ExecCommandSMST, EStGotCSD ) |
|
5146 |
|
5147 SMF_STATE(EStGotCSD) |
|
5148 |
|
5149 s.iCardP->iCSD = s.ResponseP(); |
|
5150 |
|
5151 s.iState &= ~KMMCSessStateInProgress; |
|
5152 SMF_END |
|
5153 } |
|
5154 |
|
5155 EXPORT_C TMMCErr DMMCStack::CIMReadWriteBlocksSM() |
|
5156 /** |
|
5157 * This macro performs single/multiple block reads and writes. |
|
5158 * |
|
5159 * For normal read/write block operations, this function determines the appropriate |
|
5160 * MMC command to send and fills the command descriptor accordingly based on |
|
5161 * the value of the session ID set. However, it is necessary to have set the |
|
5162 * command arguments (with DMMCSession::FillCommandArgs()) before this function |
|
5163 * is called. |
|
5164 * |
|
5165 * For special block read/write operations, e.g. lock/unlock, it is required to |
|
5166 * have already filled the command descriptor (with DMMCSession::FillCommandDesc()) |
|
5167 * for the special command required - in addition to have setup the command arguments. |
|
5168 * |
|
5169 * @return MMC error code. |
|
5170 */ |
|
5171 { |
|
5172 enum states |
|
5173 { |
|
5174 EStBegin=0, |
|
5175 EStRestart, |
|
5176 EStAttached, |
|
5177 EStLength1, |
|
5178 EStLengthSet, |
|
5179 EStIssueBlockCount, |
|
5180 EStAppCmdIssued, |
|
5181 EStBlockCountCmdIssued, |
|
5182 EStBpoint1, |
|
5183 EStIssued, |
|
5184 EStWaitFinish, |
|
5185 EStWaitFinish1, |
|
5186 EStRWFinish, |
|
5187 EStEnd |
|
5188 }; |
|
5189 |
|
5190 DMMCSession& s=Session(); |
|
5191 |
|
5192 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:RWBlocksSM %x",TUint(s.iLastStatus))); |
|
5193 |
|
5194 SMF_BEGIN |
|
5195 |
|
5196 if(s.iSessionID == ECIMWriteBlock || s.iSessionID == ECIMWriteMBlock) |
|
5197 { |
|
5198 // Check that the card supports class 4 (Write) commands |
|
5199 const TUint ccc = s.iCardP->CSD().CCC(); |
|
5200 if(!(ccc & KMMCCmdClassBlockWrite)) |
|
5201 return( KMMCErrNotSupported ); |
|
5202 } |
|
5203 |
|
5204 s.iState |= KMMCSessStateInProgress; |
|
5205 m.SetTraps(KMMCErrInitContext); |
|
5206 |
|
5207 SMF_STATE(EStRestart) // NB: ErrBypass is not processed here |
|
5208 |
|
5209 SMF_CALLMEWR(EStRestart) // Create a recursive call entry to recover from the errors trapped |
|
5210 m.SetTraps(KMMCErrStatus); |
|
5211 if (s.Command().iSpec.iCommandClass!=KMMCCmdClassApplication || s.Command().iCommand==ECmdAppCmd) |
|
5212 { |
|
5213 s.ResetCommandStack(); |
|
5214 SMF_INVOKES( AttachCardSMST, EStAttached ) // attachment is mandatory here |
|
5215 } |
|
5216 |
|
5217 SMF_BPOINT(EStAttached) |
|
5218 |
|
5219 TMMCCommandDesc& cmd = s.Command(); |
|
5220 |
|
5221 const TUint32 blockLength = cmd.BlockLength(); |
|
5222 if(blockLength == 0) |
|
5223 return KMMCErrArgument; |
|
5224 |
|
5225 if(s.iSessionID == ECIMReadBlock || |
|
5226 s.iSessionID == ECIMWriteBlock || |
|
5227 s.iSessionID == ECIMReadMBlock || |
|
5228 s.iSessionID == ECIMWriteMBlock) |
|
5229 { |
|
5230 // read/write operation |
|
5231 if(!cmd.AdjustForBlockOrByteAccess(s)) |
|
5232 { |
|
5233 // unable to convert command arguments to suit the underlying block/byte access mode |
|
5234 return KMMCErrArgument; |
|
5235 } |
|
5236 } |
|
5237 |
|
5238 // Set the block length if it has changed. Always set for ECIMLockUnlock. |
|
5239 if ((blockLength == s.iCardP->iSetBlockLen) && (s.iSessionID != ECIMLockUnlock)) |
|
5240 { |
|
5241 SMF_GOTOS( EStLengthSet ) |
|
5242 } |
|
5243 |
|
5244 s.iCardP->iSetBlockLen = 0; |
|
5245 s.PushCommandStack(); |
|
5246 s.FillCommandDesc( ECmdSetBlockLen, blockLength ); |
|
5247 SMF_INVOKES( ExecCommandSMST, EStLength1 ) |
|
5248 |
|
5249 SMF_STATE(EStLength1) |
|
5250 |
|
5251 const TMMCStatus status(s.ResponseP()); |
|
5252 s.PopCommandStack(); |
|
5253 if (status.Error()) |
|
5254 SMF_RETURN(KMMCErrStatus) |
|
5255 s.iCardP->iSetBlockLen = s.Command().BlockLength(); |
|
5256 |
|
5257 SMF_STATE(EStLengthSet) |
|
5258 |
|
5259 TMMCCommandDesc& cmd = s.Command(); |
|
5260 TUint opType = 0; |
|
5261 const TUint kTypeWrite = KBit0; |
|
5262 const TUint kTypeMultiple = KBit1; |
|
5263 const TUint kTypeSpecial = KBit2; |
|
5264 static const TMMCCommandEnum cmdCodes[4] = |
|
5265 {ECmdReadSingleBlock, ECmdWriteBlock, ECmdReadMultipleBlock, ECmdWriteMultipleBlock}; |
|
5266 |
|
5267 switch( s.iSessionID ) |
|
5268 { |
|
5269 case ECIMReadBlock: |
|
5270 break; |
|
5271 case ECIMWriteBlock: |
|
5272 opType=kTypeWrite; |
|
5273 break; |
|
5274 case ECIMReadMBlock: |
|
5275 opType=kTypeMultiple; |
|
5276 break; |
|
5277 case ECIMWriteMBlock: |
|
5278 opType=kTypeWrite|kTypeMultiple; |
|
5279 break; |
|
5280 case ECIMLockUnlock: |
|
5281 default: |
|
5282 opType=kTypeSpecial; |
|
5283 break; |
|
5284 } |
|
5285 |
|
5286 const TUint blocks = cmd.NumBlocks(); |
|
5287 |
|
5288 SMF_NEXTS(EStBpoint1) |
|
5289 if ( !(opType & kTypeSpecial) ) // A special session has already set its command descriptor |
|
5290 { |
|
5291 |
|
5292 if (cmd.iFlags & KMMCCmdFlagReliableWrite) |
|
5293 //ensure multiple block commands are used for reliable writing |
|
5294 opType |= kTypeMultiple; |
|
5295 |
|
5296 if ( (blocks==1) && !(cmd.iFlags & KMMCCmdFlagReliableWrite) ) |
|
5297 { |
|
5298 // Reliable Write requires that Multi-Block command is used. |
|
5299 opType &= ~kTypeMultiple; |
|
5300 } |
|
5301 |
|
5302 TUint32 oldFlags = cmd.iFlags; // Maintain old flags which would be overwritten by FillCommandDesc |
|
5303 cmd.iCommand = cmdCodes[opType]; |
|
5304 s.FillCommandDesc(); |
|
5305 cmd.iFlags = oldFlags; // ...and restore |
|
5306 |
|
5307 if((opType & kTypeMultiple) == kTypeMultiple) |
|
5308 { |
|
5309 // |
|
5310 // This is a Multiple-Block DT command. Check the version of the card, as |
|
5311 // MMC Version 3.1 onwards supports the SET_BLOCK_COUNT mode for DT. |
|
5312 // |
|
5313 // Note that if the PSL does not support pre-determined block count of |
|
5314 // data transmission, then it may return KMMCErrNotSupported to force |
|
5315 // the 'Stop Transmission' mode to be used instead. |
|
5316 // |
|
5317 if(s.iCardP->CSD().SpecVers() >= 3) |
|
5318 { |
|
5319 cmd.iSpec.iUseStopTransmission = EFalse; |
|
5320 SMF_NEXTS(EStIssueBlockCount) |
|
5321 } |
|
5322 else |
|
5323 { |
|
5324 cmd.iSpec.iUseStopTransmission = ETrue; |
|
5325 } |
|
5326 } |
|
5327 } |
|
5328 |
|
5329 SMF_GOTONEXTS |
|
5330 |
|
5331 SMF_STATE(EStIssueBlockCount) |
|
5332 // |
|
5333 // Issues SET_BLOCK_COUNT (CMD23) for MB R/W data transfers. |
|
5334 // This is only issued if MMC version >= 3.1 and the PSL |
|
5335 // supports this mode of operation. |
|
5336 // |
|
5337 TMMCCommandDesc& cmd = s.Command(); |
|
5338 TUint32 args = (TUint16)cmd.NumBlocks(); |
|
5339 |
|
5340 m.SetTraps(KMMCErrStatus | KMMCErrNotSupported); |
|
5341 |
|
5342 if (cmd.iFlags & KMMCCmdFlagReliableWrite) |
|
5343 { |
|
5344 // Command marked as Reliable Write |
|
5345 // set Bit31 in CMD23 argument |
|
5346 args |= KMMCCmdReliableWrite; |
|
5347 } |
|
5348 |
|
5349 s.PushCommandStack(); |
|
5350 s.FillCommandDesc( ECmdSetBlockCount, args ); |
|
5351 SMF_INVOKES( ExecCommandSMST, EStBlockCountCmdIssued ) |
|
5352 |
|
5353 SMF_STATE(EStBlockCountCmdIssued) |
|
5354 |
|
5355 const TMMCStatus status(s.ResponseP()); |
|
5356 s.PopCommandStack(); |
|
5357 if (status.Error()) |
|
5358 SMF_RETURN(KMMCErrStatus) |
|
5359 |
|
5360 if(err & KMMCErrNotSupported) |
|
5361 { |
|
5362 // Not supported by the PSL, so use standard Stop Transmission mode |
|
5363 s.Command().iSpec.iUseStopTransmission = ETrue; |
|
5364 } |
|
5365 |
|
5366 SMF_GOTOS(EStBpoint1) |
|
5367 |
|
5368 SMF_STATE(EStAppCmdIssued) |
|
5369 |
|
5370 const TMMCStatus status(s.ResponseP()); |
|
5371 s.PopCommandStack(); |
|
5372 if (status.Error()) |
|
5373 SMF_RETURN(KMMCErrStatus) |
|
5374 |
|
5375 SMF_BPOINT(EStBpoint1) |
|
5376 |
|
5377 // NB We need to trap KMMCErrStatus errors, because if one occurs, |
|
5378 // we still need to wait to exit PRG/RCV/DATA state |
|
5379 m.SetTraps(KMMCErrStatus); |
|
5380 |
|
5381 SMF_INVOKES( ExecCommandSMST, EStIssued ) |
|
5382 |
|
5383 SMF_STATE(EStIssued) |
|
5384 |
|
5385 // check state of card after data transfer with CMD13. |
|
5386 |
|
5387 if (s.Command().Direction() != 0) |
|
5388 { |
|
5389 SMF_GOTOS(EStWaitFinish) |
|
5390 } |
|
5391 |
|
5392 SMF_GOTOS(EStRWFinish); |
|
5393 |
|
5394 SMF_STATE(EStWaitFinish) |
|
5395 // Save the status and examine it after issuing CMD13... |
|
5396 // NB We don't know where in the command stack the last response is stored (e.g. there may |
|
5397 // have bee a Deselect/Select issued), but we do know last response is stored in iLastStatus |
|
5398 TMMC::BigEndian4Bytes(s.ResponseP(), s.iLastStatus); |
|
5399 |
|
5400 s.PushCommandStack(); |
|
5401 s.FillCommandDesc(ECmdSendStatus, 0); |
|
5402 SMF_INVOKES(ExecCommandSMST, EStWaitFinish1) |
|
5403 |
|
5404 SMF_STATE(EStWaitFinish1) |
|
5405 const TMMCStatus status(s.ResponseP()); |
|
5406 s.PopCommandStack(); |
|
5407 |
|
5408 #ifdef __WINS__ |
|
5409 SMF_GOTOS(EStRWFinish); |
|
5410 #else |
|
5411 const TMMCardStateEnum st1 = status.State(); |
|
5412 if (st1 == ECardStatePrg || st1 == ECardStateRcv || st1 == ECardStateData) |
|
5413 { |
|
5414 SMF_INVOKES(ProgramTimerSMST, EStWaitFinish); |
|
5415 } |
|
5416 if (status.Error()) |
|
5417 SMF_RETURN(KMMCErrStatus) |
|
5418 #endif |
|
5419 |
|
5420 // Fall through if CURRENT_STATE is not PGM or DATA |
|
5421 SMF_STATE(EStRWFinish) |
|
5422 |
|
5423 if (TMMCStatus(s.ResponseP()).Error() != 0) |
|
5424 SMF_RETURN(KMMCErrStatus); |
|
5425 |
|
5426 s.iState &= ~KMMCSessStateInProgress; |
|
5427 |
|
5428 // skip over recursive entry or throw error and catch in CIMLockUnlockSM() |
|
5429 return (s.Command().iCommand == ECmdLockUnlock) ? KMMCErrUpdPswd : KMMCErrBypass; |
|
5430 |
|
5431 SMF_END |
|
5432 } |
|
5433 |
|
5434 inline TMMCErr DMMCStack::CIMEraseSM() |
|
5435 /** |
|
5436 * This macro performs sector/group erase of a continuous area |
|
5437 * |
|
5438 * @return MMC error code. |
|
5439 */ |
|
5440 { |
|
5441 enum states |
|
5442 { |
|
5443 EStBegin=0, |
|
5444 EStRestart, |
|
5445 EStAttached, |
|
5446 EStStartTagged, |
|
5447 EStEndTagged, |
|
5448 EStErased, |
|
5449 EStWaitFinish, |
|
5450 EStWaitFinish1, |
|
5451 EStEraseFinish, |
|
5452 EStEnd |
|
5453 }; |
|
5454 |
|
5455 DMMCSession& s=Session(); |
|
5456 |
|
5457 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:EraseSM %x",TUint(s.iLastStatus))); |
|
5458 |
|
5459 SMF_BEGIN |
|
5460 |
|
5461 // Check that the card supports class 4 (Write) commands |
|
5462 const TUint ccc = s.iCardP->CSD().CCC(); |
|
5463 if(!(ccc & KMMCCmdClassErase)) |
|
5464 return( KMMCErrNotSupported ); |
|
5465 |
|
5466 s.iState |= KMMCSessStateInProgress; |
|
5467 m.SetTraps( KMMCErrInitContext ); |
|
5468 |
|
5469 SMF_STATE(EStRestart) |
|
5470 |
|
5471 SMF_CALLMEWR(EStRestart) // Create a recursive call entry to recover from Init |
|
5472 |
|
5473 s.ResetCommandStack(); |
|
5474 SMF_INVOKES( AttachCardSMST, EStAttached ) // attachment is mandatory |
|
5475 |
|
5476 SMF_BPOINT(EStAttached) |
|
5477 |
|
5478 TMMCCommandDesc& cmd = s.Command(); |
|
5479 |
|
5480 if(cmd.iTotalLength == 0) |
|
5481 return( KMMCErrArgument ); |
|
5482 |
|
5483 switch( s.iSessionID ) |
|
5484 { |
|
5485 case ECIMEraseSector: |
|
5486 TMMCEraseInfo eraseInfo; |
|
5487 s.iCardP->GetEraseInfo(eraseInfo); |
|
5488 cmd.iBlockLength = eraseInfo.iMinEraseSectorSize; |
|
5489 cmd.iCommand = ECmdTagSectorStart; |
|
5490 break; |
|
5491 case ECIMEraseGroup: |
|
5492 cmd.iBlockLength = s.iCardP->iCSD.EraseGroupSize(); |
|
5493 if(cmd.iBlockLength == 0 || cmd.iTotalLength % cmd.iBlockLength != 0) |
|
5494 return KMMCErrArgument; |
|
5495 cmd.iCommand = ECmdTagEraseGroupStart; |
|
5496 break; |
|
5497 default: |
|
5498 DMMCSocket::Panic(DMMCSocket::EMMCEraseSessionID); |
|
5499 } |
|
5500 |
|
5501 if(!cmd.AdjustForBlockOrByteAccess(s)) |
|
5502 return KMMCErrArgument; |
|
5503 |
|
5504 iConfig.RemoveMode( KMMCModeEnablePreemption ); // erase sequence must not be pre-empted |
|
5505 |
|
5506 const TUint flags = cmd.iFlags; |
|
5507 s.FillCommandDesc(); |
|
5508 cmd.iFlags = flags; |
|
5509 SMF_INVOKES( ExecCommandSMST, EStStartTagged ) |
|
5510 |
|
5511 SMF_STATE(EStStartTagged) |
|
5512 |
|
5513 const TMMCCommandDesc& cmd = s.Command(); |
|
5514 |
|
5515 TMMCCommandEnum command; |
|
5516 TUint endAddr = cmd.iArgument; |
|
5517 if(s.iSessionID == ECIMEraseSector) |
|
5518 { |
|
5519 endAddr += cmd.IsBlockCmd() ? (cmd.iTotalLength / cmd.iBlockLength - 1) : (cmd.iTotalLength - cmd.iBlockLength); |
|
5520 command = ECmdTagSectorEnd; |
|
5521 } |
|
5522 else |
|
5523 { |
|
5524 if(cmd.IsBlockCmd()) |
|
5525 { |
|
5526 endAddr += (cmd.iTotalLength - cmd.iBlockLength) >> KMMCardHighCapBlockSizeLog2; |
|
5527 } |
|
5528 else |
|
5529 { |
|
5530 endAddr += cmd.iTotalLength - cmd.iBlockLength; |
|
5531 } |
|
5532 |
|
5533 command = ECmdTagEraseGroupEnd; |
|
5534 } |
|
5535 |
|
5536 s.PushCommandStack(); |
|
5537 s.FillCommandDesc( command, endAddr ); |
|
5538 SMF_INVOKES( ExecCommandSMST, EStEndTagged ) |
|
5539 |
|
5540 SMF_STATE(EStEndTagged) |
|
5541 |
|
5542 // Increase the inactivity timeout as an erase operation can potentially take a long time |
|
5543 // At the moment this is a somewhat arbitrary 30 seconds. This could be calculated more accurately |
|
5544 // using TAAC,NSAC, R2W_FACTOR etc. but that seems to yield very large values (?) |
|
5545 const TInt KMaxEraseTimeoutInSeconds = 30; |
|
5546 iBody->SetInactivityTimeout(KMaxEraseTimeoutInSeconds); |
|
5547 m.SetTraps(KMMCErrAll); |
|
5548 s.FillCommandDesc( ECmdErase, 0 ); |
|
5549 SMF_INVOKES( ExecCommandSMST, EStErased ) |
|
5550 |
|
5551 SMF_STATE(EStErased) |
|
5552 m.SetTraps( KMMCErrInitContext ); |
|
5553 iBody->RestoreInactivityTimeout(); |
|
5554 if (err != KMMCErrNone) |
|
5555 SMF_RETURN(err); |
|
5556 |
|
5557 |
|
5558 SMF_STATE(EStWaitFinish) |
|
5559 |
|
5560 s.PushCommandStack(); |
|
5561 s.FillCommandDesc(ECmdSendStatus, 0); |
|
5562 SMF_INVOKES(ExecCommandSMST, EStWaitFinish1) |
|
5563 |
|
5564 SMF_STATE(EStWaitFinish1) |
|
5565 |
|
5566 const TMMCStatus st(s.ResponseP()); |
|
5567 s.PopCommandStack(); |
|
5568 |
|
5569 #ifdef __WINS__ |
|
5570 SMF_GOTOS(EStEraseFinish); |
|
5571 #else |
|
5572 const TMMCardStateEnum st1 = st.State(); |
|
5573 if (st1 == ECardStatePrg || st1 == ECardStateRcv || st1 == ECardStateData) |
|
5574 { |
|
5575 SMF_INVOKES(ProgramTimerSMST, EStWaitFinish); |
|
5576 } |
|
5577 #endif |
|
5578 |
|
5579 // Fall through if CURRENT_STATE is not PGM or DATA |
|
5580 SMF_STATE(EStEraseFinish) |
|
5581 |
|
5582 s.iState &= ~KMMCSessStateInProgress; |
|
5583 return( KMMCErrBypass ); // to skip over the recursive entry |
|
5584 |
|
5585 SMF_END |
|
5586 } |
|
5587 |
|
5588 inline TMMCErr DMMCStack::CIMReadWriteIOSM() |
|
5589 /** |
|
5590 * This macro reads/writes a stream of bytes from/to an I/O register. |
|
5591 * This is a generalised form of FAST_IO (CMD39) MMC command. |
|
5592 * |
|
5593 * @return MMC error code. |
|
5594 */ |
|
5595 { |
|
5596 enum states |
|
5597 { |
|
5598 EStBegin=0, |
|
5599 EStReadIO, |
|
5600 EStIOLoop, |
|
5601 EStEnd |
|
5602 }; |
|
5603 |
|
5604 DMMCSession& s=Session(); |
|
5605 TMMCCommandDesc& cmd = s.Command(); |
|
5606 |
|
5607 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:IOSM %x",TUint(s.iLastStatus))); |
|
5608 |
|
5609 SMF_BEGIN |
|
5610 s.iState |= KMMCSessStateInProgress; |
|
5611 TUint argument = (TUint(cmd.iArgument)&0x7F) << 8; // shift reg addr into a proper position |
|
5612 |
|
5613 switch( s.iSessionID ) |
|
5614 { |
|
5615 case ECIMReadIO: |
|
5616 break; |
|
5617 case ECIMWriteIO: |
|
5618 argument |= 0x8000; |
|
5619 break; |
|
5620 default: |
|
5621 DMMCSocket::Panic(DMMCSocket::EMMCIOSessionID); |
|
5622 } |
|
5623 |
|
5624 s.FillCommandDesc( ECmdFastIO, argument ); |
|
5625 s.iBytesTransferred = ~0UL; |
|
5626 |
|
5627 SMF_INVOKES( AttachCardSMST, EStIOLoop ) // attachment's mandatory |
|
5628 |
|
5629 SMF_STATE(EStReadIO) |
|
5630 |
|
5631 *(cmd.iDataMemoryP)++ = s.ResponseP()[3]; |
|
5632 cmd.iTotalLength--; |
|
5633 |
|
5634 SMF_BPOINT(EStIOLoop) |
|
5635 |
|
5636 s.iBytesTransferred++; |
|
5637 |
|
5638 if( cmd.iTotalLength == 0 ) |
|
5639 { |
|
5640 s.iState &= ~KMMCSessStateInProgress; |
|
5641 SMF_EXIT |
|
5642 } |
|
5643 |
|
5644 if( s.iSessionID == ECIMWriteIO ) |
|
5645 { |
|
5646 TUint8 byte = *(cmd.iDataMemoryP)++; |
|
5647 cmd.iTotalLength--; |
|
5648 cmd.iArgument = (TUint(cmd.iArgument)&0xFF00) | byte; |
|
5649 } |
|
5650 else |
|
5651 SMF_NEXTS(EStReadIO) |
|
5652 |
|
5653 iConfig.RemoveMode( KMMCModeEnableRetries ); // no retries on I/O registers! |
|
5654 |
|
5655 SMF_CALL( ExecCommandSMST ) |
|
5656 |
|
5657 SMF_END |
|
5658 } |
|
5659 |
|
5660 inline TMMCErr DMMCStack::CIMLockUnlockSM() |
|
5661 /** |
|
5662 * Locking and unlocking a card involves writing a data block to the card. |
|
5663 * CIMReadWriteBlocksSM() could be used directly, but, in practive, a card must |
|
5664 * sometimes be sent the data block twice. |
|
5665 * |
|
5666 * @return MMC error code. |
|
5667 */ |
|
5668 { |
|
5669 enum states |
|
5670 { |
|
5671 EStBegin, |
|
5672 EStRetry, |
|
5673 EStTestR1, |
|
5674 EStEnd |
|
5675 }; |
|
5676 |
|
5677 DMMCSession& s=Session(); |
|
5678 TMMCCommandDesc& cmd = Command(); |
|
5679 |
|
5680 __KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm")); |
|
5681 |
|
5682 SMF_BEGIN |
|
5683 m.SetTraps(KMMCErrStatus | KMMCErrUpdPswd); |
|
5684 cmd.iUnlockRetries = 0; // attempt counter |
|
5685 iCMD42CmdByte = cmd.iDataMemoryP[0]; |
|
5686 |
|
5687 SMF_STATE(EStRetry) |
|
5688 __KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:%x/%x", cmd.iUnlockRetries, (iSessionP == &iAutoUnlockSession) ? KMMCMaxAutoUnlockRetries : iConfig.iUnlockRetries)); |
|
5689 |
|
5690 if (iCMD42CmdByte == KMMCLockUnlockErase) |
|
5691 { |
|
5692 // Section 4.6.2 of version 4.2 of the the MMC specification states that |
|
5693 // the maximum time for a force erase operation should be 3 minutes |
|
5694 const TInt KMaxForceEraseTimeoutInSeconds = 3 * 60; |
|
5695 iBody->SetInactivityTimeout(KMaxForceEraseTimeoutInSeconds); |
|
5696 m.SetTraps(KMMCErrAll); |
|
5697 } |
|
5698 |
|
5699 |
|
5700 SMF_INVOKES(CIMReadWriteBlocksSMST, EStTestR1); |
|
5701 |
|
5702 SMF_STATE(EStTestR1) |
|
5703 if (iCMD42CmdByte == KMMCLockUnlockErase) |
|
5704 { |
|
5705 m.SetTraps(KMMCErrStatus | KMMCErrUpdPswd); |
|
5706 iBody->RestoreInactivityTimeout(); |
|
5707 } |
|
5708 |
|
5709 if (err & KMMCErrStatus) |
|
5710 { |
|
5711 const TMMCStatus st = s.LastStatus(); // set in ExecCommandSM() / EStCommandIssued |
|
5712 TMMCCommandDesc& cmd0 = Command(); |
|
5713 |
|
5714 __KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:EStTestR1 [err: %08x, st:%08x] : RETRY [%d]", |
|
5715 err, (TInt)s.LastStatus(), cmd0.iUnlockRetries)); |
|
5716 |
|
5717 const TInt KMaxRetries = (iSessionP == &iAutoUnlockSession) ? KMMCMaxAutoUnlockRetries : iConfig.iUnlockRetries; |
|
5718 |
|
5719 // retry if LOCK_UNLOCK_FAIL only error bit |
|
5720 if (!( iCMD42CmdByte == 0 // LOCK_UNLOCK = 0; SET_PWD = 0 |
|
5721 && err == KMMCErrStatus && st.Error() == KMMCStatErrLockUnlock |
|
5722 && (iConfig.iModes & KMMCModeEnableUnlockRetry) != 0 |
|
5723 && ++cmd0.iUnlockRetries < KMaxRetries )) |
|
5724 { |
|
5725 __KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:abt")); |
|
5726 |
|
5727 SMF_RETURN(err); |
|
5728 } |
|
5729 |
|
5730 __KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:retry")); |
|
5731 |
|
5732 #ifdef __EPOC32__ |
|
5733 s.SynchBlock(KMMCBlockOnRetryTimer); |
|
5734 s.iRetryTimer.OneShot(KMMCUnlockRetryGapInMilliseconds,EFalse); |
|
5735 SMF_WAITS(EStRetry) |
|
5736 #else |
|
5737 SMF_GOTOS(EStRetry); |
|
5738 #endif |
|
5739 } |
|
5740 else if (err & KMMCErrUpdPswd) |
|
5741 { |
|
5742 // CMD42 executed successfully, so update 'Has Password' flag |
|
5743 if ((iCMD42CmdByte & (KMMCLockUnlockClrPwd | KMMCLockUnlockErase)) != 0) |
|
5744 { |
|
5745 s.CardP()->iFlags&=(~KMMCardHasPassword); |
|
5746 } |
|
5747 else if ((iCMD42CmdByte & KMMCLockUnlockSetPwd) != 0) |
|
5748 { |
|
5749 s.CardP()->iFlags|=KMMCardHasPassword; |
|
5750 } |
|
5751 |
|
5752 SMF_EXIT; |
|
5753 } |
|
5754 else if (err != KMMCErrNone) |
|
5755 { |
|
5756 SMF_RETURN(err); |
|
5757 } |
|
5758 |
|
5759 |
|
5760 SMF_END |
|
5761 } |
|
5762 |
|
5763 inline TMMCErr DMMCStack::CIMAutoUnlockSM() |
|
5764 /** |
|
5765 * Performs auto-unlocking of the card stack |
|
5766 * |
|
5767 * @return MMC error code |
|
5768 */ |
|
5769 { |
|
5770 enum states |
|
5771 { |
|
5772 EStBegin=0, |
|
5773 EStNextIndex, |
|
5774 EStInitStackAfterUnlock, |
|
5775 EStIssuedLockUnlock, |
|
5776 EStDone, |
|
5777 EStEnd |
|
5778 }; |
|
5779 |
|
5780 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:CIMAutoUnlockSM")); |
|
5781 |
|
5782 DMMCSession& s=Session(); |
|
5783 |
|
5784 SMF_BEGIN |
|
5785 |
|
5786 iAutoUnlockIndex = -1; |
|
5787 |
|
5788 m.SetTraps(KMMCErrAll); // Trap (and ignore) all errors during auto-unlock |
|
5789 |
|
5790 SMF_STATE(EStNextIndex) |
|
5791 |
|
5792 if(err) |
|
5793 { |
|
5794 iSocket->PasswordControlEnd(&Session(), err); |
|
5795 } |
|
5796 |
|
5797 // the cycle is finished when iAutoUnlockIndex == KMaxMultiMediaCardsPerStack |
|
5798 |
|
5799 if(iAutoUnlockIndex >= TInt(KMaxMMCardsPerStack)) |
|
5800 { |
|
5801 SMF_GOTOS(EStInitStackAfterUnlock); |
|
5802 } |
|
5803 |
|
5804 // find next available card with password in the controller store |
|
5805 |
|
5806 const TMapping *mp = NULL; |
|
5807 |
|
5808 TBool useIndex = EFalse; |
|
5809 for (++iAutoUnlockIndex; iAutoUnlockIndex < TInt(KMaxMMCardsPerStack); ++iAutoUnlockIndex) |
|
5810 { |
|
5811 useIndex = // card must be present with valid mapping |
|
5812 iCardArray->CardP(iAutoUnlockIndex)->IsPresent() |
|
5813 && (mp = iSocket->iPasswordStore->FindMappingInStore(iCardArray->CardP(iAutoUnlockIndex)->CID())) != NULL |
|
5814 && mp->iState == TMapping::EStValid; |
|
5815 |
|
5816 // don't increment iAutoUnlockIndex in continuation loop |
|
5817 if (useIndex) |
|
5818 break; |
|
5819 } |
|
5820 |
|
5821 if (! useIndex) |
|
5822 { |
|
5823 // if no usable index, complete with no error code |
|
5824 SMF_GOTOS(EStInitStackAfterUnlock); |
|
5825 } |
|
5826 |
|
5827 // |
|
5828 // We've found a locked card with a password in the password store, |
|
5829 // so attempt to unlock using the CIMLockUnlockSMST state machine. |
|
5830 // |
|
5831 // Upon completion, test the next card before performing further initialisation. |
|
5832 // |
|
5833 |
|
5834 TMMCard &cd = *(iCardArray->CardP(iAutoUnlockIndex++)); |
|
5835 s.SetCard(&cd); |
|
5836 |
|
5837 const TInt kPWD_LEN = mp->iPWD.Length(); |
|
5838 iPSLBuf[0] = 0; // LOCK_UNLOCK = 0; unlock |
|
5839 iPSLBuf[1] = static_cast<TUint8>(kPWD_LEN); |
|
5840 TPtr8 pwd(&iPSLBuf[2], kPWD_LEN); |
|
5841 pwd.Copy(mp->iPWD); |
|
5842 |
|
5843 const TInt kBlockLen = 1 + 1 + kPWD_LEN; |
|
5844 |
|
5845 s.FillCommandDesc(ECmdLockUnlock); |
|
5846 s.FillCommandArgs(0, kBlockLen, iPSLBuf, kBlockLen); |
|
5847 |
|
5848 SMF_INVOKES( CIMLockUnlockSMST, EStNextIndex ) |
|
5849 |
|
5850 SMF_STATE(EStInitStackAfterUnlock) |
|
5851 |
|
5852 // |
|
5853 // We've attempted to unlock all cards (successfully or not) |
|
5854 // - Now perform second-stage initialisation that can only be performed |
|
5855 // on unlocked cards (such as setting bus width, high speed etc..) |
|
5856 // |
|
5857 |
|
5858 m.ResetTraps(); |
|
5859 |
|
5860 SMF_INVOKES( InitStackAfterUnlockSMST, EStDone ) |
|
5861 |
|
5862 SMF_STATE(EStDone) |
|
5863 |
|
5864 SMF_END |
|
5865 } |
|
5866 |
|
5867 inline TMMCErr DMMCStack::NoSessionSM() |
|
5868 /** |
|
5869 * A null state machine function. Just returns KMMCErrNotSupported. |
|
5870 * |
|
5871 * @return KMMCErrNotSupported |
|
5872 */ |
|
5873 { |
|
5874 enum states |
|
5875 { |
|
5876 EStBegin=0, |
|
5877 EStEnd |
|
5878 }; |
|
5879 |
|
5880 SMF_BEGIN |
|
5881 |
|
5882 return( KMMCErrNotSupported ); |
|
5883 |
|
5884 SMF_END |
|
5885 } |
|
5886 |
|
5887 // |
|
5888 // Static adapter functions to top-level state machines. |
|
5889 // |
|
5890 |
|
5891 TMMCErr DMMCStack::NakedSessionSMST(TAny* aStackP) // ECIMNakedSession |
|
5892 { return( static_cast<DMMCStack *>(aStackP)->NakedSessionSM() ); } |
|
5893 |
|
5894 TMMCErr DMMCStack::CIMUpdateAcqSMST( TAny* aStackP ) // ECIMUpdateAcq |
|
5895 { return( static_cast<DMMCStack *>(aStackP)->CIMUpdateAcqSM() ); } |
|
5896 |
|
5897 TMMCErr DMMCStack::CIMInitStackSMST( TAny* aStackP ) // ECIMInitStack |
|
5898 { return( static_cast<DMMCStack *>(aStackP)->CIMInitStackSM() ); } |
|
5899 |
|
5900 TMMCErr DMMCStack::CIMCheckStackSMST( TAny* aStackP ) // ECIMCheckStack |
|
5901 { return( static_cast<DMMCStack *>(aStackP)->CIMCheckStackSM() ); } |
|
5902 |
|
5903 TMMCErr DMMCStack::CIMSetupCardSMST(TAny* aStackP) // ECIMSetupCard |
|
5904 { return( static_cast<DMMCStack *>(aStackP)->CIMSetupCardSM() ); } |
|
5905 |
|
5906 EXPORT_C TMMCErr DMMCStack::CIMReadWriteBlocksSMST(TAny* aStackP) // ECIMReadBlock, ECIMWriteBlock, ECIMReadMBlock, ECIMWriteMBlock |
|
5907 { return( static_cast<DMMCStack *>(aStackP)->CIMReadWriteBlocksSM() ); } |
|
5908 |
|
5909 TMMCErr DMMCStack::CIMEraseSMST(TAny* aStackP) // ECIMEraseSector, ECIMEraseGroup |
|
5910 { return( static_cast<DMMCStack *>(aStackP)->CIMEraseSM() ); } |
|
5911 |
|
5912 TMMCErr DMMCStack::CIMReadWriteIOSMST(TAny* aStackP) // ECIMReadIO, ECIMWriteIO |
|
5913 { return( static_cast<DMMCStack *>(aStackP)->CIMReadWriteIOSM() ); } |
|
5914 |
|
5915 TMMCErr DMMCStack::CIMLockUnlockSMST(TAny* aStackP) // ECIMLockUnlock |
|
5916 { return( static_cast<DMMCStack *>(aStackP)->CIMLockUnlockSM() ); } |
|
5917 |
|
5918 TMMCErr DMMCStack::NoSessionSMST(TAny* aStackP) // ECIMLockStack |
|
5919 { return( static_cast<DMMCStack *>(aStackP)->NoSessionSM() ); } |
|
5920 |
|
5921 TMMCErr DMMCStack::InitStackAfterUnlockSMST( TAny* aStackP ) |
|
5922 { return( static_cast<DMMCStack *>(aStackP)->InitStackAfterUnlockSM() ); } |
|
5923 |
|
5924 TMMCErr DMMCStack::AcquireStackSMST( TAny* aStackP ) |
|
5925 { return( static_cast<DMMCStack *>(aStackP)->AcquireStackSM() ); } |
|
5926 |
|
5927 TMMCErr DMMCStack::CheckStackSMST( TAny* aStackP ) |
|
5928 { return( static_cast<DMMCStack *>(aStackP)->CheckStackSM() ); } |
|
5929 |
|
5930 TMMCErr DMMCStack::ModifyCardCapabilitySMST( TAny* aStackP ) |
|
5931 { return( static_cast<DMMCStack *>(aStackP)->ModifyCardCapabilitySM() ); } |
|
5932 |
|
5933 TMMCErr DMMCStack::AttachCardSMST( TAny* aStackP ) |
|
5934 { return( static_cast<DMMCStack *>(aStackP)->AttachCardSM() ); } |
|
5935 |
|
5936 TMMCErr DMMCStack::ExecCommandSMST( TAny* aStackP ) |
|
5937 { return( static_cast<DMMCStack *>(aStackP)->ExecCommandSM() ); } |
|
5938 |
|
5939 TMMCErr DMMCStack::IssueCommandCheckResponseSMST(TAny* aStackP) |
|
5940 { return( static_cast<DMMCStack *>(aStackP)->IssueCommandCheckResponseSM() ); } |
|
5941 |
|
5942 TMMCErr DMMCStack::PollGapTimerSMST( TAny* aStackP ) |
|
5943 { return( static_cast<DMMCStack *>(aStackP)->PollGapTimerSM() ); } |
|
5944 |
|
5945 TMMCErr DMMCStack::RetryGapTimerSMST( TAny* aStackP ) |
|
5946 { return( static_cast<DMMCStack *>(aStackP)->RetryGapTimerSM() ); } |
|
5947 |
|
5948 TMMCErr DMMCStack::ProgramTimerSMST(TAny *aStackP) |
|
5949 { return static_cast<DMMCStack *>(aStackP)->ProgramTimerSM(); } |
|
5950 |
|
5951 TMMCErr DMMCStack::GoIdleSMST( TAny* aStackP ) |
|
5952 { return( static_cast<DMMCStack *>(aStackP)->GoIdleSM() ); } |
|
5953 |
|
5954 TMMCErr DMMCStack::CheckLockStatusSMST( TAny* aStackP ) |
|
5955 { return( static_cast<DMMCStack *>(aStackP)->CheckLockStatusSM() ); } |
|
5956 |
|
5957 TMMCErr DMMCStack::CIMAutoUnlockSMST( TAny* aStackP ) |
|
5958 { return( static_cast<DMMCStack *>(aStackP)->CIMAutoUnlockSM() ); } |
|
5959 |
|
5960 TMMCErr DMMCStack::ExecSleepCommandSMST( TAny* aStackP ) |
|
5961 { return( static_cast<DMMCStack *>(aStackP)->ExecSleepCommandSM() ); } |
|
5962 |
|
5963 TMMCErr DMMCStack::ExecAwakeCommandSMST( TAny* aStackP ) |
|
5964 { return( static_cast<DMMCStack *>(aStackP)->ExecAwakeCommandSM() ); } |
|
5965 // |
|
5966 // Static interfaces to ASSP layer SM functions |
|
5967 // |
|
5968 TMMCErr DMMCStack::DoPowerUpSMST( TAny* aStackP ) |
|
5969 { return( static_cast<DMMCStack *>(aStackP)->DoPowerUpSM() ); } |
|
5970 |
|
5971 TMMCErr DMMCStack::InitClockOnSMST( TAny* aStackP ) |
|
5972 { return( static_cast<DMMCStack *>(aStackP)->InitClockOnSM() ); } |
|
5973 |
|
5974 EXPORT_C TMMCErr DMMCStack::IssueMMCCommandSMST( TAny* aStackP ) |
|
5975 { return( static_cast<DMMCStack *>(aStackP)->IssueMMCCommandSM() ); } |
|
5976 |
|
5977 TMMCErr DMMCStack::DetermineBusWidthAndClockSMST( TAny* aStackP ) |
|
5978 { return( static_cast<DMMCStack *>(aStackP)->DetermineBusWidthAndClockSM() ); } |
|
5979 |
|
5980 TMMCErr DMMCStack::ConfigureHighSpeedSMST( TAny* aStackP ) |
|
5981 { return( static_cast<DMMCStack *>(aStackP)->ConfigureHighSpeedSM() ); } |
|
5982 |
|
5983 TMMCErr DMMCStack::ExecSwitchCommandST( TAny* aStackP ) |
|
5984 { return( static_cast<DMMCStack *>(aStackP)->ExecSwitchCommand() ); } |
|
5985 |
|
5986 TMMCErr DMMCStack::SwitchToLowVoltageSMST( TAny* aStackP ) |
|
5987 { return( static_cast<DMMCStack *>(aStackP)->SwitchToLowVoltageSM() ); } |
|
5988 |
|
5989 TMMCErr DMMCStack::LowVoltagePowerupTimerSMST(TAny *aStackP) |
|
5990 { return static_cast<DMMCStack *>(aStackP)->LowVoltagePowerupTimerSM(); } |
|
5991 |
|
5992 TMMCErr DMMCStack::ExecBusTestSMST( TAny* aStackP ) |
|
5993 { return( static_cast<DMMCStack *>(aStackP)->ExecBusTestSM() ); } |
|
5994 |
|
5995 TMMCErr DMMCStack::DoWakeUpSMST( TAny* aStackP ) |
|
5996 { |
|
5997 MDoWakeUp* dowakeup = NULL; |
|
5998 static_cast<DMMCStack *>(aStackP)->GetInterface(KInterfaceDoWakeUpSM, (MInterface*&) dowakeup); |
|
5999 if (dowakeup) |
|
6000 { |
|
6001 return dowakeup->DoWakeUpSM(); |
|
6002 } |
|
6003 else |
|
6004 { |
|
6005 // Interface not supported at PSL Level |
|
6006 return KMMCErrNotSupported; |
|
6007 } |
|
6008 } |
|
6009 |
|
6010 |
|
6011 EXPORT_C DMMCSession* DMMCStack::AllocSession(const TMMCCallBack& aCallBack) const |
|
6012 /** |
|
6013 * Factory function to create DMMCSession derived object. Non-generic MMC |
|
6014 * controllers can override this to generate more specific objects. |
|
6015 * @param aCallBack Callback function to notify the client that a session has completed |
|
6016 * @return A pointer to the new session |
|
6017 */ |
|
6018 { |
|
6019 return new DMMCSession(aCallBack); |
|
6020 } |
|
6021 |
|
6022 EXPORT_C void DMMCStack::Dummy1() {} |
|
6023 |
|
6024 /** |
|
6025 * Calls the PSL-implemented function SetBusWidth() if the bus width has changed |
|
6026 * |
|
6027 */ |
|
6028 void DMMCStack::DoSetBusWidth(TUint32 aBusWidth) |
|
6029 { |
|
6030 if (iBody->iCurrentSelectedBusWidth != aBusWidth) |
|
6031 { |
|
6032 iBody->iCurrentSelectedBusWidth = aBusWidth; |
|
6033 SetBusWidth(aBusWidth); |
|
6034 } |
|
6035 } |
|
6036 |
|
6037 /** |
|
6038 * Sets iConfig.iBusConfig.iBusClock - which the PSL SHOULD use to set the clock before every command. |
|
6039 * |
|
6040 * Some PSLs, however, may only change the clock when SetBusConfigDefaults() is called, |
|
6041 * so if the clock has changed, SetBusConfigDefaults() is called |
|
6042 * |
|
6043 * @param aClock The requested clock frequency in Kilohertz |
|
6044 */ |
|
6045 void DMMCStack::DoSetClock(TUint32 aClock) |
|
6046 { |
|
6047 iConfig.iBusConfig.iBusClock = aClock; |
|
6048 |
|
6049 if (iPoweredUp&&(iBody->iCurrentSelectedClock != aClock)) |
|
6050 { |
|
6051 iBody->iCurrentSelectedClock = aClock; |
|
6052 SetBusConfigDefaults(iConfig.iBusConfig, aClock); |
|
6053 } |
|
6054 } |
|
6055 |
|
6056 |
|
6057 TUint DMMCStack::MaxTranSpeedInKilohertz(const TMMCard& aCard) const |
|
6058 { |
|
6059 TUint32 highSpeedClock = aCard.HighSpeedClock(); |
|
6060 return highSpeedClock ? highSpeedClock : aCard.MaxTranSpeedInKilohertz(); |
|
6061 } |
|
6062 |
|
6063 |
|
6064 |
|
6065 EXPORT_C void DMMCStack::SetBusWidth(TUint32 /*aBusWidth*/) |
|
6066 { |
|
6067 } |
|
6068 |
|
6069 EXPORT_C void DMMCStack::MachineInfo(TDes8& /*aMachineInfo*/) |
|
6070 { |
|
6071 } |
|
6072 |
|
6073 TBusWidth DMMCStack::BusWidthEncoding(TInt aBusWidth) const |
|
6074 /** |
|
6075 * Returns the bus width as a TBusWidth given a card's bus width |
|
6076 * expressed as an integer (1,4 or 8) |
|
6077 * @return the bus width encoded as a TBusWidth |
|
6078 */ |
|
6079 { |
|
6080 TBusWidth busWidth = EBusWidth1; |
|
6081 |
|
6082 switch(aBusWidth) |
|
6083 { |
|
6084 case 8: |
|
6085 busWidth = EBusWidth8; |
|
6086 break; |
|
6087 case 4: |
|
6088 busWidth = EBusWidth4; |
|
6089 break; |
|
6090 case 1: |
|
6091 case 0: |
|
6092 busWidth = EBusWidth1; |
|
6093 break; |
|
6094 default: |
|
6095 DMMCSocket::Panic(DMMCSocket::EMMCBadBusWidth); |
|
6096 |
|
6097 } |
|
6098 return busWidth; |
|
6099 } |
|
6100 |
|
6101 /** |
|
6102 * class DMMCSocket |
|
6103 */ |
|
6104 EXPORT_C DMMCSocket::DMMCSocket(TInt aSocketNumber, TMMCPasswordStore* aPasswordStore) |
|
6105 /** |
|
6106 * Constructs a DMMCSocket class |
|
6107 * @param aSocketNumber the socket ID |
|
6108 * @param aPasswordStore pointer to the password store |
|
6109 */ |
|
6110 :DPBusSocket(aSocketNumber), |
|
6111 iPasswordStore(aPasswordStore) |
|
6112 { |
|
6113 } |
|
6114 |
|
6115 TInt DMMCSocket::TotalSupportedCards() |
|
6116 /** |
|
6117 * Returns the total number of MMC slots supported by the socket. |
|
6118 * @return The number of MMC slots supported by the socket |
|
6119 */ |
|
6120 { |
|
6121 return iMachineInfo.iTotalSockets; |
|
6122 } |
|
6123 |
|
6124 |
|
6125 // -------- Password store management -------- |
|
6126 |
|
6127 // |
|
6128 // The persistent file is a contiguous sequence of entries. |
|
6129 // An entry format is [CID@16 | PWD_LEN@4 | PWD@PWD_LEN]. |
|
6130 // CID and PWD_LEN are both stored in big endian format. |
|
6131 // |
|
6132 |
|
6133 TInt DMMCSocket::PrepareStore(TInt aBus, TInt aFunc, TLocalDrivePasswordData &aData) |
|
6134 /** |
|
6135 * Called from media driver before CMD42 session engaged, in kernel server context |
|
6136 * so that mappings can be allocated or deallocated. |
|
6137 * |
|
6138 * Using zero-length passwords for MMC operations is disallowed by this function. |
|
6139 * Locking with and clearing a null password is failed with KErrAccessDenied. |
|
6140 * If the drive is already mounted, then TBusLocalDrive::Unlock() will fail with |
|
6141 * KErrAlreadyExists. Otherwise, this function will fail with KErrLocked, which |
|
6142 * is translated to KErrAccessDenied in Unlock(), in the same way as unlocking |
|
6143 * a locked card with the wrong password |
|
6144 * |
|
6145 * @param aBus The card to be unlocked. |
|
6146 * @param aFunc The operation to perform (EPasswordLock, EPasswordUnlock, EPasswordClear). |
|
6147 * @param aData TLocalDrivePasswordData reference containing the password |
|
6148 * @return KErrAccessDenied An attempt to lock or clear was made with a NULL password. |
|
6149 * @return KErrLocked An an attempt to unlock was made with a NULL password. |
|
6150 * @return KErrNone on success |
|
6151 */ |
|
6152 { |
|
6153 TInt r = 0; |
|
6154 |
|
6155 TMMCard *card=iStack->CardP(aBus); |
|
6156 __ASSERT_ALWAYS(card, Panic(EMMCSessionNoPswdCard)); |
|
6157 const TCID &cid = card->CID(); |
|
6158 |
|
6159 switch (aFunc) |
|
6160 { |
|
6161 case DLocalDrive::EPasswordLock: |
|
6162 { |
|
6163 TMediaPassword newPwd = *aData.iNewPasswd; |
|
6164 |
|
6165 if (newPwd.Length() == 0) |
|
6166 r = KErrAccessDenied; |
|
6167 else |
|
6168 r = PasswordControlStart(cid, aData.iStorePasswd ? &newPwd : NULL); |
|
6169 } |
|
6170 break; |
|
6171 |
|
6172 case DLocalDrive::EPasswordUnlock: |
|
6173 { |
|
6174 TMediaPassword curPwd = *aData.iOldPasswd; |
|
6175 |
|
6176 if (curPwd.Length() == 0) |
|
6177 r = KErrLocked; |
|
6178 else |
|
6179 r = PasswordControlStart(cid, aData.iStorePasswd ? &curPwd : NULL); |
|
6180 } |
|
6181 break; |
|
6182 |
|
6183 case DLocalDrive::EPasswordClear: |
|
6184 { |
|
6185 TMediaPassword curPwd = *aData.iOldPasswd; |
|
6186 |
|
6187 if (curPwd.Length() == 0) |
|
6188 r = KErrAccessDenied; |
|
6189 else |
|
6190 r = PasswordControlStart(cid, aData.iStorePasswd ? &curPwd : NULL); |
|
6191 } |
|
6192 break; |
|
6193 |
|
6194 default: |
|
6195 Panic(EMMCSessionPswdCmd); |
|
6196 break; |
|
6197 } |
|
6198 |
|
6199 return r; |
|
6200 } |
|
6201 |
|
6202 |
|
6203 TInt DMMCSocket::PasswordControlStart(const TCID &aCID, const TMediaPassword *aPWD) |
|
6204 /** |
|
6205 * Remove any non-validated mappings from the store, and allocate a binding for |
|
6206 * the card's CID if necessary. |
|
6207 * |
|
6208 * s = source (current) password stored; t = target (new) password should be stored |
|
6209 * f = failure |
|
6210 * |
|
6211 * t is equivalent to iMPTgt.Length() > 0, which is used by PasswordControlEnd(). |
|
6212 * |
|
6213 * The target password is not stored in the store at this point, but in the stack. |
|
6214 * This leaves any existing mapping which can be used for recovery if the operation |
|
6215 * fails. This means the user does not have to re-enter the right password after |
|
6216 * trying to unlock a card with the wrong password. |
|
6217 * |
|
6218 * See PasswordControlEnd() for recovery policy. |
|
6219 */ |
|
6220 { |
|
6221 TInt r = KErrNone; // error code |
|
6222 |
|
6223 TBool changed = EFalse; // compress store if changed |
|
6224 |
|
6225 TBuf8<KMMCCIDLength> cid; // convert to TBuf8<> for comparison |
|
6226 cid.SetLength(KMMCCIDLength); |
|
6227 aCID.Copy(&cid[0]); |
|
6228 |
|
6229 TBool s = EFalse; // source password (current mapping) |
|
6230 TBool t = aPWD != NULL; // target pasword (new value for mapping) |
|
6231 |
|
6232 // remove any bindings which were not validated. This is all non EStValid |
|
6233 // bindings - the previous operation could have failed before CMD42 was sent, |
|
6234 // in which case its state would be EStPending, not EStInvalid. |
|
6235 |
|
6236 // an inefficiency exists where an invalid binding for the target CID exists. |
|
6237 // This could be reused instead of being deallocated and reallocated. This |
|
6238 // situation would occur if the user inserted a card whose password was not |
|
6239 // known to the machine, unlocked it with the wrong password and tried again. |
|
6240 // The case is rare and the cost is run-time speed, which is not noticeable, |
|
6241 // The run-time memory usage is equivalent, so it is probably not worth the |
|
6242 // extra rom bytes and logic. |
|
6243 |
|
6244 for (TInt i = 0; i < iPasswordStore->iStore->Count(); ) |
|
6245 { |
|
6246 if ((*iPasswordStore->iStore)[i].iState != TMapping::EStValid) |
|
6247 { |
|
6248 iPasswordStore->iStore->Remove(i); // i becomes index for next item |
|
6249 changed = ETrue; |
|
6250 } |
|
6251 else |
|
6252 { |
|
6253 if ((*iPasswordStore->iStore)[i].iCID == cid) |
|
6254 s = ETrue; |
|
6255 ++i; |
|
6256 } |
|
6257 } |
|
6258 |
|
6259 if (! t) |
|
6260 iStack->iMPTgt.Zero(); |
|
6261 else |
|
6262 { |
|
6263 iStack->iMPTgt = *aPWD; |
|
6264 |
|
6265 if (!s) |
|
6266 { |
|
6267 TMediaPassword mp; // empty, to indicate !s |
|
6268 if ((r = iPasswordStore->InsertMapping(aCID, mp, TMapping::EStPending)) != KErrNone) |
|
6269 return r; |
|
6270 |
|
6271 changed = ETrue; |
|
6272 } |
|
6273 } |
|
6274 |
|
6275 if (changed) |
|
6276 iPasswordStore->iStore->Compress(); |
|
6277 |
|
6278 return r; |
|
6279 } |
|
6280 |
|
6281 |
|
6282 |
|
6283 void DMMCSocket::PasswordControlEnd(DMMCSession *aSessP, TInt aResult) |
|
6284 /** |
|
6285 * called by DMMCStack::SchedCompletionPass() after CMD42 has completed to |
|
6286 * update internal store. This function does not run in ks context and so |
|
6287 * can only invalidate bindings for later removal in PasswordControlStart(). |
|
6288 * |
|
6289 * s = source (current) password stored; t = target (new) password should be stored |
|
6290 * f = failure |
|
6291 * |
|
6292 * If the operation fails, then a recovery policy is used so the user does |
|
6293 * not lose the good current binding and have to re-enter the password. |
|
6294 * ' |
|
6295 * f = 0 f = 1 |
|
6296 * T T |
|
6297 * 0 1 0 1 |
|
6298 * S 0 N V S 0 N I |
|
6299 * 1 W V 1 R R |
|
6300 * |
|
6301 * N nothing V validate W wipe |
|
6302 * I invalidate R restore |
|
6303 * ' |
|
6304 * See PasswordControlStart() for details of how store set up. |
|
6305 */ |
|
6306 { |
|
6307 // autounlock is a special case because the PasswordControlStart() will |
|
6308 // not have been called (the CID is not known in ks context.) The mapping |
|
6309 // for this specific card is removed on failure, because it is the current |
|
6310 // mapping that is definitely wrong. |
|
6311 |
|
6312 TBuf8<KMMCCIDLength> cid; // convert to TBuf8<> for comparison |
|
6313 cid.SetLength(KMMCCIDLength); |
|
6314 aSessP->CardP()->CID().Copy(&cid[0]); |
|
6315 |
|
6316 if (aSessP == &iStack->iAutoUnlockSession) |
|
6317 { |
|
6318 TBool changed = EFalse; // compress store if changed |
|
6319 |
|
6320 for (TInt j = 0; j < iPasswordStore->iStore->Count(); ) |
|
6321 { |
|
6322 TMapping &mp = (*iPasswordStore->iStore)[j]; |
|
6323 if (mp.iCID == cid) |
|
6324 { |
|
6325 mp.iState = (aResult == KErrNone ? TMapping::EStValid : TMapping::EStInvalid); |
|
6326 if(mp.iState == TMapping::EStInvalid) |
|
6327 { |
|
6328 iPasswordStore->iStore->Remove(j); |
|
6329 changed = ETrue; |
|
6330 } |
|
6331 else |
|
6332 { |
|
6333 j++; |
|
6334 } |
|
6335 } |
|
6336 else |
|
6337 { |
|
6338 j++; |
|
6339 } |
|
6340 } |
|
6341 |
|
6342 if (changed) |
|
6343 iPasswordStore->iStore->Compress(); |
|
6344 } |
|
6345 else |
|
6346 { |
|
6347 const TMediaPassword &mpTgt = iStack->iMPTgt; |
|
6348 TBool s = EFalse; // default value in case no mapping |
|
6349 TBool t = mpTgt.Length() > 0; |
|
6350 TBool f = (aResult != KErrNone); |
|
6351 |
|
6352 TMapping mp, *pmp; // get mapping to mutate |
|
6353 mp.iCID = cid; |
|
6354 TInt psn = iPasswordStore->iStore->Find(mp, iPasswordStore->iIdentityRelation); |
|
6355 if (psn == KErrNotFound) |
|
6356 { |
|
6357 return; |
|
6358 } |
|
6359 else |
|
6360 { |
|
6361 pmp = &(*iPasswordStore->iStore)[psn]; |
|
6362 s = pmp->iPWD.Length() > 0; |
|
6363 } |
|
6364 |
|
6365 if (f) |
|
6366 { |
|
6367 if (s) // s & ~f |
|
6368 pmp->iState = TMapping::EStValid; // restore |
|
6369 else |
|
6370 { |
|
6371 if (t) // ~s & t & f |
|
6372 pmp->iState = TMapping::EStInvalid; // invalidate |
|
6373 } |
|
6374 } |
|
6375 else |
|
6376 { |
|
6377 if (t) // t & ~f |
|
6378 { |
|
6379 pmp->iState = TMapping::EStValid; // validate |
|
6380 pmp->iPWD = mpTgt; |
|
6381 } |
|
6382 else |
|
6383 { |
|
6384 if (s) // s & ~t & ~f |
|
6385 pmp->iState = TMapping::EStInvalid; // wipe |
|
6386 } |
|
6387 } // else (f) |
|
6388 } // else if (aSessP == &iStack->iAutoUnlockSession) |
|
6389 } |
|
6390 |
|
6391 |
|
6392 TMMCPasswordStore::TMMCPasswordStore() |
|
6393 /** |
|
6394 * Contructor |
|
6395 */ |
|
6396 : iIdentityRelation(TMMCPasswordStore::CompareCID) |
|
6397 { |
|
6398 } |
|
6399 |
|
6400 TInt TMMCPasswordStore::Init() |
|
6401 /** |
|
6402 * Initialises the password store and allocates resources. |
|
6403 * @return KErrNone if successful, standard error code otherwise. |
|
6404 */ |
|
6405 { |
|
6406 // We don't have a destructor yet as this object lasts forever |
|
6407 iStore = new RArray<TMapping>(4, _FOFF(TMapping, iCID)); |
|
6408 if(!iStore) |
|
6409 return KErrNoMemory; |
|
6410 return KErrNone; |
|
6411 } |
|
6412 |
|
6413 EXPORT_C TBool TMMCPasswordStore::IsMappingIncorrect(const TCID& aCID, const TMediaPassword& aPWD) |
|
6414 /** |
|
6415 * Returns true if the password is definitely incorrect, i.e. if a valid entry with a |
|
6416 * different password exists. Returns false if correct (because the mapping matches,) |
|
6417 * or if cannot tell (because no valid mapping.) |
|
6418 */ |
|
6419 { |
|
6420 TMapping* pmp = FindMappingInStore(aCID); |
|
6421 return (pmp != 0 && pmp->iState == TMapping::EStValid && pmp->iPWD.Compare(aPWD) != 0); |
|
6422 } |
|
6423 |
|
6424 TMapping *TMMCPasswordStore::FindMappingInStore(const TCID &aCID) |
|
6425 /** |
|
6426 * return pointer to aCID mapping in store or NULL if not found |
|
6427 */ |
|
6428 { |
|
6429 TMapping *pmp = NULL; |
|
6430 TMapping mp; // 8 + 16 + 8 + 16 + 4 bytes |
|
6431 mp.iCID.SetLength(KMMCCIDLength); |
|
6432 aCID.Copy(&mp.iCID[0]); |
|
6433 |
|
6434 TInt psn=iStore->Find(mp, iIdentityRelation); |
|
6435 if(psn!=KErrNotFound) |
|
6436 { |
|
6437 pmp = &(*iStore)[psn]; |
|
6438 } |
|
6439 return pmp; |
|
6440 } |
|
6441 |
|
6442 TInt TMMCPasswordStore::InsertMapping(const TCID &aCID, const TMediaPassword &aPWD, TMapping::TState aState) |
|
6443 /** |
|
6444 * Ensures that a mapping from aCID to aPWD exists in the store. If an |
|
6445 * existing entry does not exist to mutate, then insert a new one. This |
|
6446 * may cause an allocation, depending on the granularity and count. |
|
6447 * |
|
6448 * If the CID is already bound to something in the store, then this operation |
|
6449 * is a binary search, otherwise it may involve kernel heap allocation. |
|
6450 */ |
|
6451 { |
|
6452 TInt r = KErrNone; |
|
6453 TMapping mpN; |
|
6454 mpN.iCID.SetLength(KMMCCIDLength); |
|
6455 aCID.Copy(&mpN.iCID[0]); // copies from aCID into buffer. |
|
6456 |
|
6457 TInt psn = iStore->Find(mpN, iIdentityRelation); |
|
6458 if(psn == KErrNotFound) |
|
6459 { |
|
6460 mpN.iPWD.Copy(aPWD); |
|
6461 mpN.iState = aState; |
|
6462 r=iStore->Insert(mpN, iStore->Count()); |
|
6463 } |
|
6464 else |
|
6465 { |
|
6466 TMapping &mpE = (*iStore)[psn]; |
|
6467 mpE.iPWD.Copy(aPWD); |
|
6468 mpE.iState = aState; |
|
6469 r = KErrNone; |
|
6470 } |
|
6471 |
|
6472 return r; |
|
6473 } |
|
6474 |
|
6475 TInt TMMCPasswordStore::PasswordStoreLengthInBytes() |
|
6476 /** |
|
6477 * virtual from DPeriphBusController, kern exec |
|
6478 * return number of bytes needed for persistent file representation |
|
6479 * of the password store |
|
6480 */ |
|
6481 { |
|
6482 TInt sz = 0; |
|
6483 |
|
6484 for (TInt i = 0; i < iStore->Count(); ++i) |
|
6485 { |
|
6486 const TMapping &mp = (*iStore)[i]; |
|
6487 if (mp.iState == TMapping::EStValid) |
|
6488 sz += KMMCCIDLength + sizeof(TInt32) + mp.iPWD.Length(); |
|
6489 } |
|
6490 |
|
6491 return sz; |
|
6492 } |
|
6493 |
|
6494 TBool TMMCPasswordStore::ReadPasswordData(TDes8 &aBuf) |
|
6495 /** |
|
6496 * virtual from DPeriphBusController, kern exec |
|
6497 * fills descriptor with persistent representation of password store |
|
6498 * data. aBuf is resized to contain exactly the password data from |
|
6499 * the store. If its maximum length is not enough then KErrOverflow |
|
6500 * is returned and aBuf is not mutated. |
|
6501 */ |
|
6502 { |
|
6503 TInt r=KErrNone; // error code |
|
6504 |
|
6505 if (PasswordStoreLengthInBytes() > aBuf.MaxLength()) |
|
6506 r = KErrOverflow; |
|
6507 else |
|
6508 { |
|
6509 aBuf.Zero(); |
|
6510 for (TInt i = 0; i < iStore->Count(); ++i) |
|
6511 { |
|
6512 const TMapping &mp = (*iStore)[i]; |
|
6513 |
|
6514 if (mp.iState == TMapping::EStValid) |
|
6515 { |
|
6516 aBuf.Append(mp.iCID); |
|
6517 |
|
6518 TUint8 lenBuf[sizeof(TInt32)]; // length, big-endian |
|
6519 TMMC::BigEndian4Bytes(lenBuf, TInt32(mp.iPWD.Length())); |
|
6520 aBuf.Append(&lenBuf[0], sizeof(TInt32)); |
|
6521 |
|
6522 aBuf.Append(mp.iPWD); |
|
6523 } |
|
6524 } |
|
6525 |
|
6526 r = KErrNone; |
|
6527 } |
|
6528 |
|
6529 return r; |
|
6530 } |
|
6531 |
|
6532 |
|
6533 TInt TMMCPasswordStore::WritePasswordData(TDesC8 &aBuf) |
|
6534 /** |
|
6535 * virtual from DPeriphBusController, kern server |
|
6536 * replace current store with data from persistent representation in aBuf. |
|
6537 */ |
|
6538 { |
|
6539 // should only be called at boot up, but remove chance of duplicate entries |
|
6540 iStore->Reset(); |
|
6541 |
|
6542 TInt iBIdx; // buffer index |
|
6543 |
|
6544 // check buffer integrity |
|
6545 |
|
6546 TBool corrupt = EFalse; // abort flag |
|
6547 for (iBIdx = 0; iBIdx < aBuf.Length(); ) |
|
6548 { |
|
6549 // enough raw data for CID, PWD_LEN and 1 byte of PWD |
|
6550 corrupt = TUint(aBuf.Length() - iBIdx) < KMMCCIDLength + sizeof(TUint32) + 1; |
|
6551 if (corrupt) |
|
6552 break; |
|
6553 |
|
6554 // PWD_LEN is valid and enough raw data left for PWD |
|
6555 iBIdx += KMMCCIDLength; |
|
6556 const TInt32 pwd_len(TMMC::BigEndian32(&aBuf[iBIdx])); |
|
6557 corrupt = !( |
|
6558 (pwd_len <= TInt32(KMaxMediaPassword)) |
|
6559 && aBuf.Length() - iBIdx >= TInt(sizeof(TUint32)) + pwd_len ); |
|
6560 if (corrupt) |
|
6561 break; |
|
6562 |
|
6563 // skip over PWD_LEN and PWD to next entry |
|
6564 iBIdx += sizeof(TInt32) + pwd_len; |
|
6565 } |
|
6566 |
|
6567 if (corrupt) |
|
6568 return KErrCorrupt; |
|
6569 |
|
6570 // Build the store from the entries in the buffer. |
|
6571 TInt r = KErrNone; // error code |
|
6572 for (iBIdx = 0; r == KErrNone && iBIdx < aBuf.Length(); ) |
|
6573 { |
|
6574 TPtrC8 pCID(&aBuf[iBIdx], KMMCCIDLength); // CID |
|
6575 const TCID cid(pCID.Ptr()); |
|
6576 |
|
6577 const TInt32 pwd_len(TMMC::BigEndian32(&aBuf[iBIdx + KMMCCIDLength])); |
|
6578 TMediaPassword pwd; |
|
6579 pwd.Copy(&aBuf[iBIdx + KMMCCIDLength + sizeof(TInt32)], pwd_len); |
|
6580 |
|
6581 iBIdx += KMMCCIDLength + sizeof(TInt32) + pwd_len; |
|
6582 r = InsertMapping(cid, pwd, TMapping::EStValid); |
|
6583 } |
|
6584 |
|
6585 // it may be acceptable to use a partially created store, providing the |
|
6586 // sections that do exist are valid. Alternatively, the operation should |
|
6587 // atomic from the startup thread's point of view. |
|
6588 |
|
6589 if (r != KErrNone) |
|
6590 iStore->Reset(); |
|
6591 |
|
6592 return r; |
|
6593 } |
|
6594 |
|
6595 TInt TMMCPasswordStore::CompareCID(const TMapping& aLeft, const TMapping& aRight) |
|
6596 /** |
|
6597 * CID Comparason Functions for RArray::Find |
|
6598 */ |
|
6599 { |
|
6600 return(aLeft.iCID == aRight.iCID); |
|
6601 } |
|
6602 |
|
6603 void DMMCSocket::InitiatePowerUpSequence() |
|
6604 /** |
|
6605 * Initiates a power up sequence on the stack |
|
6606 */ |
|
6607 { |
|
6608 iStack->PowerUpStack(); |
|
6609 } |
|
6610 |
|
6611 TBool DMMCSocket::CardIsPresent() |
|
6612 /** |
|
6613 * Indicates the presence of a card. |
|
6614 * @return ETrue if a card is present, EFalse otherwise |
|
6615 */ |
|
6616 { |
|
6617 return(iStack->HasCardsPresent()); |
|
6618 } |
|
6619 |
|
6620 void DMMCSocket::AdjustPartialRead(const TMMCard* aCard, TUint32 aStart, TUint32 aEnd, TUint32* aPhysStart, TUint32* aPhysEnd) const |
|
6621 /** |
|
6622 * Calculates the minimum range that must be read off a card, an optimisation that takes advantage |
|
6623 * of the partial read feature found on some cards. It takes the logical range that the media driver |
|
6624 * wants to read from the card, and increases it to take into account factors such as FIFO width and |
|
6625 * minimum DMA transfer size. |
|
6626 * @param aCard A pointer to the MMC Card |
|
6627 * @param aStart The required start position |
|
6628 * @param aEnd The required end position |
|
6629 * @param aPhysStart The adjusted start position |
|
6630 * @param aPhysEnd The adjusted end position |
|
6631 */ |
|
6632 { |
|
6633 iStack->AdjustPartialRead(aCard, aStart, aEnd, aPhysStart, aPhysEnd); |
|
6634 } |
|
6635 |
|
6636 void DMMCSocket::GetBufferInfo(TUint8** aMDBuf, TInt* aMDBufLen) |
|
6637 /** |
|
6638 * Returns the details of the buffer allocated by the socket for data transfer operations. The buffer |
|
6639 * is allocated and configured at the variant layer to allow , for example, contiguous pages to be |
|
6640 * allocated for DMA transfers. |
|
6641 * @param aMDBuf A pointer to the allocated buffer |
|
6642 * @param aMDBufLen The length of the allocated buffer |
|
6643 */ |
|
6644 { |
|
6645 iStack->GetBufferInfo(aMDBuf, aMDBufLen); |
|
6646 } |
|
6647 |
|
6648 void DMMCSocket::Reset1() |
|
6649 /** |
|
6650 * Resets the socket by powering down the stack. |
|
6651 * If there are operations in progress (inCritical), this call will be deferred |
|
6652 * until the operation is complete. In the case of an emergency power down, |
|
6653 * this will occur immediately. |
|
6654 */ |
|
6655 { |
|
6656 if (iState == EPBusCardAbsent) |
|
6657 { |
|
6658 // Reset is result of card eject! |
|
6659 iStack->iStackState |= KMMCStackStateCardRemoved; |
|
6660 } |
|
6661 |
|
6662 |
|
6663 iStack->PowerDownStack(); |
|
6664 } |
|
6665 |
|
6666 void DMMCSocket::Reset2() |
|
6667 /** |
|
6668 * Resets the socket in response to a PSU fault or media change. |
|
6669 * Called after Reset1, gives the opportunity to free upp allocated resources |
|
6670 */ |
|
6671 { |
|
6672 // No need to do anything here, as the only thing to do is power down the |
|
6673 // stack, which is performed in ::Reset1 |
|
6674 } |
|
6675 |
|
6676 TInt DMMCSocket::Init() |
|
6677 /** |
|
6678 * Allocates resources and initialises the MMC socket and associated stack object. |
|
6679 * @return KErrNotReady if no stack has been allocated, standard error code otherwise |
|
6680 */ |
|
6681 { |
|
6682 __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Init")); |
|
6683 |
|
6684 GetMachineInfo(); |
|
6685 |
|
6686 // We need to make sure the stack is initialised, |
|
6687 // as DPBusSocket::Init() will initiate a power up sequence |
|
6688 if(iStack == NULL) |
|
6689 return KErrNotReady; |
|
6690 |
|
6691 TInt r = iStack->Init(); |
|
6692 if (r!=KErrNone) |
|
6693 return r; |
|
6694 |
|
6695 r = DPBusSocket::Init(); |
|
6696 if (r!=KErrNone) |
|
6697 return r; |
|
6698 |
|
6699 return KErrNone; |
|
6700 } |
|
6701 |
|
6702 void DMMCSocket::GetMachineInfo() |
|
6703 /** |
|
6704 * Gets the platform specific configuration information. |
|
6705 * @see TMMCMachineInfo |
|
6706 */ |
|
6707 { |
|
6708 // Get machine info from the stack |
|
6709 iStack->MachineInfo(iMachineInfo); |
|
6710 |
|
6711 __KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iTotalSockets %u", iMachineInfo.iTotalSockets)); |
|
6712 __KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iTotalMediaChanges %u", iMachineInfo.iTotalMediaChanges)); |
|
6713 __KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iTotalPrimarySupplies %u", iMachineInfo.iTotalPrimarySupplies)); |
|
6714 __KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iSPIMode %u", iMachineInfo.iSPIMode)); |
|
6715 __KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iBaseBusNumber %u", iMachineInfo.iBaseBusNumber)); |
|
6716 |
|
6717 } |
|
6718 |
|
6719 |
|
6720 // MMC specific functions |
|
6721 |
|
6722 EXPORT_C void DMMCSocket::Panic(TMMCPanic aPanic) |
|
6723 /** |
|
6724 * Panic the MMC Controller |
|
6725 * @param aPanic The panic code |
|
6726 */ |
|
6727 { |
|
6728 |
|
6729 _LIT(KPncNm,"PBUS-MMC"); |
|
6730 Kern::PanicCurrentThread(KPncNm,aPanic); |
|
6731 } |
|
6732 |
|
6733 EXPORT_C DMMCPsu::DMMCPsu(TInt aPsuNum, TInt aMediaChangedNum) |
|
6734 /** |
|
6735 * Constructor for a DMMCPsu object |
|
6736 * @param aPsuNum The power supply number |
|
6737 * @param aMediaChangedNum The associated media change number |
|
6738 */ |
|
6739 : DPBusPsuBase(aPsuNum, aMediaChangedNum) |
|
6740 { |
|
6741 |
|
6742 iVoltageSetting=0x00ffc000; // Default voltage range - 2.6V to 3.6V (OCR reg. format). |
|
6743 } |
|
6744 |
|
6745 EXPORT_C TInt DMMCPsu::DoCreate() |
|
6746 /** |
|
6747 * Create a DMMCPsu object. |
|
6748 * This should be overridden at the variant layer to allow interrupts and |
|
6749 * other variant-specific parameters to be initialised. The default |
|
6750 * implementation does nothing. |
|
6751 * @return Standard Symbian OS error code. |
|
6752 */ |
|
6753 { |
|
6754 return KErrNone; |
|
6755 } |
|
6756 |
|
6757 |
|
6758 void DMMCPsu::SleepCheck(TAny* aPtr) |
|
6759 /** |
|
6760 * Checks if media can be placed in Sleep state |
|
6761 * and therefore if VccQ supply can be turned off. |
|
6762 * |
|
6763 * @Param aPtr reference to DMMCPsu Object to be acted upon. |
|
6764 */ |
|
6765 { |
|
6766 DMMCPsu& self = *static_cast<DMMCPsu*>(aPtr); |
|
6767 |
|
6768 if ( |
|
6769 (self.iNotLockedTimeout&&!self.IsLocked()&&++self.iNotLockedCount>self.iNotLockedTimeout) || |
|
6770 (self.iInactivityTimeout&&++self.iInactivityCount>self.iInactivityTimeout) |
|
6771 ) |
|
6772 { |
|
6773 DMMCSocket* socket = static_cast<DMMCSocket*>(self.iSocket); |
|
6774 socket->iStack->QSleepStack(); |
|
6775 } |
|
6776 } |
|
6777 |
|
6778 EXPORT_C DMMCMediaChange::DMMCMediaChange(TInt aMediaChangeNum) |
|
6779 /** |
|
6780 * Constructor for a DMMCMediaChange object |
|
6781 * @param aMediaChangeNum The media change number |
|
6782 */ |
|
6783 : DMediaChangeBase(aMediaChangeNum) |
|
6784 {} |
|
6785 |
|
6786 EXPORT_C TInt DMMCMediaChange::Create() |
|
6787 /** |
|
6788 * Create a DMMCMediaChange object. |
|
6789 * This should be overridden at the variant layer to allow interrupts and |
|
6790 * other variant-specific parameters to be initialised. The base class implementation |
|
6791 * should be called prior to any variant-specific initialisation. |
|
6792 * @return Standard Symbian OS error code. |
|
6793 */ |
|
6794 { |
|
6795 return DMediaChangeBase::Create(); |
|
6796 } |
|
6797 |