|
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 // |
|
15 |
|
16 #include <drivers/sdcard.h> |
|
17 |
|
18 |
|
19 // ======== TSDCard ======== |
|
20 |
|
21 TSDCard::TSDCard() |
|
22 : iProtectedAreaSize(0), iPARootDirEnd(KPARootDirEndUnknown) |
|
23 { |
|
24 // empty |
|
25 } |
|
26 |
|
27 TInt64 TSDCard::DeviceSize64() const |
|
28 // |
|
29 // returns the SD device size |
|
30 // |
|
31 { |
|
32 if(iFlags & KSDCardIsSDCard) |
|
33 { |
|
34 return (IsHighCapacity()) ? 512 * 1024 * (TInt64)(1 + CSD().CSDField(69, 48)) : TMMCard::DeviceSize64(); |
|
35 } |
|
36 |
|
37 return(TMMCard::DeviceSize64()); |
|
38 } |
|
39 |
|
40 TUint32 TSDCard::PreferredWriteGroupLength() const |
|
41 // |
|
42 // return SD erase sector size, (SECTOR_SIZE + 1) * 2 ** WRITE_BLK_LEN |
|
43 // |
|
44 { |
|
45 if(iFlags & KSDCardIsSDCard) |
|
46 { |
|
47 TSDCSD sdcsd(CSD()); |
|
48 return (sdcsd.SDSectorSize() + 1) * (1 << sdcsd.WriteBlLen()); |
|
49 } |
|
50 |
|
51 return(TMMCard::PreferredWriteGroupLength()); |
|
52 } |
|
53 |
|
54 TInt TSDCard::GetFormatInfo(TLDFormatInfo& /*aFormatInfo*/) const |
|
55 { |
|
56 return KErrNotSupported; |
|
57 } |
|
58 |
|
59 TUint32 TSDCard::MinEraseSectorSize() const |
|
60 { |
|
61 if(iFlags&KSDCardIsSDCard) |
|
62 { |
|
63 TSDCSD sdcsd(CSD()); |
|
64 if (sdcsd.SDEraseBlkEn()) |
|
65 return sdcsd.WriteBlockLength(); // raised logarithm |
|
66 else |
|
67 return (sdcsd.SDSectorSize() + 1) * sdcsd.WriteBlockLength(); |
|
68 } |
|
69 |
|
70 return TMMCard::MinEraseSectorSize(); |
|
71 } |
|
72 |
|
73 |
|
74 const TUint32 KEraseSectorSizeShift = 8; // KEraseSectorSizeShift determines the multiple of the sector size |
|
75 // that can be erased in one operation |
|
76 TUint32 TSDCard::EraseSectorSize() const |
|
77 { |
|
78 if(iFlags&KSDCardIsSDCard) |
|
79 { |
|
80 TSDCSD sdcsd(CSD()); |
|
81 return ((sdcsd.SDSectorSize() + 1) * sdcsd.WriteBlockLength()) << KEraseSectorSizeShift; |
|
82 } |
|
83 |
|
84 return TMMCard::EraseSectorSize(); |
|
85 } |
|
86 |
|
87 const TInt KDefaultBlockLen = 9; // 2^9 = 512 bytes |
|
88 const TInt KDefaultBlockLenInBytes = 1 << KDefaultBlockLen; // 2^9 = 512 bytes |
|
89 const TInt KTwoGbyteSDBlockLen = 10; // 2^10 = 1024 bytes |
|
90 const TInt KFourGbyteSDBlockLen = 11; // 2^11 = 2048 bytes |
|
91 |
|
92 TInt TSDCard::GetEraseInfo(TMMCEraseInfo& aEraseInfo) const |
|
93 // |
|
94 // Return info. on erase services for this card |
|
95 // |
|
96 { |
|
97 |
|
98 // SD Controllers support MMC cards too. Check if we are really dealing with an SD card |
|
99 if(!(iFlags&KSDCardIsSDCard)) |
|
100 return(TMMCard::GetEraseInfo(aEraseInfo)); |
|
101 |
|
102 if (CSD().CCC() & KMMCCmdClassErase) |
|
103 { |
|
104 // This card supports erase cmds. However, SD cards don't support Erase Group commands (i.e. CMD35, CMD36). |
|
105 aEraseInfo.iEraseFlags=KMMCEraseClassCmdsSupported; |
|
106 |
|
107 // Return the preferred size to be used as the unit for erase operations. |
|
108 TSDCSD sdcsd(CSD()); |
|
109 TUint32 prefSize=((sdcsd.SDSectorSize() + 1) * sdcsd.WriteBlockLength()); |
|
110 prefSize<<=KEraseSectorSizeShift; // Use multiples of the sector size for each erase operation |
|
111 aEraseInfo.iPreferredEraseUnitSize=prefSize; |
|
112 |
|
113 // Return the smallest size that can be used as the unit for erase operations |
|
114 if (sdcsd.SDEraseBlkEn()) |
|
115 { |
|
116 aEraseInfo.iMinEraseSectorSize = KDefaultBlockLenInBytes; |
|
117 } |
|
118 else |
|
119 { |
|
120 aEraseInfo.iMinEraseSectorSize=(sdcsd.SDSectorSize() + 1) * sdcsd.WriteBlockLength(); |
|
121 } |
|
122 } |
|
123 else |
|
124 aEraseInfo.iEraseFlags=0; |
|
125 |
|
126 return(KErrNone); |
|
127 } |
|
128 |
|
129 TInt TSDCard::MaxReadBlLen() const |
|
130 /** |
|
131 * Returns the maximum read block length supported by the card encoded as a logarithm |
|
132 * Normally this is the same as the READ_BL_LEN field in the CSD register, |
|
133 * but for high capacity cards (> 2GB) this is set to a maximum of 512 bytes, |
|
134 * if possible, to try to avoid compatibility issues. |
|
135 */ |
|
136 { |
|
137 if (IsSDCard()) |
|
138 { |
|
139 TInt blkLenLog2 = CSD().ReadBlLen(); |
|
140 if (blkLenLog2 == KTwoGbyteSDBlockLen || blkLenLog2 == KFourGbyteSDBlockLen) |
|
141 { |
|
142 // The SD card spec. makes a special case for 2GByte cards, |
|
143 // ...and some manufacturers apply the same method to support 4G cards |
|
144 __KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl > 2GB SD")); |
|
145 blkLenLog2 = KDefaultBlockLen; |
|
146 } |
|
147 return blkLenLog2; |
|
148 } |
|
149 else // MMC card |
|
150 { |
|
151 return (TMMCard::MaxReadBlLen()); |
|
152 } |
|
153 } |
|
154 |
|
155 TInt TSDCard::MaxWriteBlLen() const |
|
156 /** |
|
157 * Returns the maximum write block length supported by the card encoded as a logarithm |
|
158 * Normally this is the same as the WRITE_BL_LEN field in the CSD register, |
|
159 * but for high capacity cards (> 2GB) this is set to a maximum of 512 bytes, |
|
160 * if possible, to try to avoid compatibility issues. |
|
161 */ |
|
162 { |
|
163 if (IsSDCard()) |
|
164 { |
|
165 TInt blkLenLog2 = CSD().WriteBlLen(); |
|
166 if (blkLenLog2 == KTwoGbyteSDBlockLen || blkLenLog2 == KFourGbyteSDBlockLen) |
|
167 { |
|
168 // The SD card spec. makes a special case for 2GByte cards, |
|
169 // ...and some manufacturers apply the same method to support 4G cards |
|
170 __KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mwbl > 2GB SD")); |
|
171 blkLenLog2 = KDefaultBlockLen; |
|
172 } |
|
173 return blkLenLog2; |
|
174 } |
|
175 else // MMC card |
|
176 { |
|
177 return (TMMCard::MaxWriteBlLen()); |
|
178 } |
|
179 } |
|
180 |
|
181 TUint TSDCard::MaxTranSpeedInKilohertz() const |
|
182 /** |
|
183 * Returns the maximum supported clock rate for the card, in Kilohertz. |
|
184 * @return Speed, in Kilohertz |
|
185 */ |
|
186 { |
|
187 TUint maxClk = TMMCard::MaxTranSpeedInKilohertz(); |
|
188 |
|
189 if (IsSDCard()) |
|
190 { |
|
191 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >TSDCard(%d): MaxTranSpeedInKilohertz: %d",(iIndex-1),maxClk)); |
|
192 |
|
193 #ifdef _DEBUG |
|
194 //MaxClk for SD should only be either 25000KHz or 50000KHz |
|
195 if ( (maxClk != KSDDTClk25MHz) && (maxClk != KSDDTClk50MHz) ) |
|
196 { |
|
197 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >DSDStack: Non-Compliant DT Clock")); |
|
198 } |
|
199 #endif |
|
200 if (maxClk > KSDDTClk50MHz) |
|
201 { |
|
202 //Clock rate exceeds SD possible max clock rate |
|
203 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >DSDStack: Tuning DT Clock down to 50MHz")); |
|
204 maxClk = KSDDTClk50MHz; |
|
205 } |
|
206 } |
|
207 |
|
208 return maxClk; |
|
209 } |
|
210 |
|
211 // ======== TSDCardArray ======== |
|
212 |
|
213 EXPORT_C TInt TSDCardArray::AllocCards() |
|
214 // |
|
215 // allocate TSDCard objects for iCards and iNewCardsArray. This function |
|
216 // is called at bootup as part of stack allocation so there is no cleanup |
|
217 // if it fails. |
|
218 // |
|
219 { |
|
220 for (TInt i = 0; i < (TInt) KMaxMMCardsPerStack; ++i) |
|
221 { |
|
222 // zeroing the card data used to be implicit because embedded in |
|
223 // CBase-derived DMMCStack. |
|
224 if ((iCards[i] = new TSDCard) == 0) |
|
225 return KErrNoMemory; |
|
226 iCards[i]->iUsingSessionP = 0; |
|
227 if ((iNewCards[i] = new TSDCard) == 0) |
|
228 return KErrNoMemory; |
|
229 } |
|
230 |
|
231 return KErrNone; |
|
232 } |
|
233 |
|
234 void TSDCardArray::AddCardSDMode(TUint aCardNumber,const TUint8* aCID,TRCA* aNewRCA) |
|
235 // |
|
236 // Add an MMC card straight to the main card array in slot 'aCardNumber'. Save |
|
237 // the CID value in the slot. Return a RCA for the card. |
|
238 // |
|
239 { |
|
240 |
|
241 TRCA rca=0; |
|
242 |
|
243 // First, lets check if the same card was here before. If it was, keep the same RCA |
|
244 if (Card(aCardNumber).IsPresent() && Card(aCardNumber).iCID==aCID) |
|
245 rca=Card(aCardNumber).iRCA; |
|
246 else |
|
247 { |
|
248 // Allocate and new RCA and store the CID in the slot selected |
|
249 __ASSERT_ALWAYS( (rca=iOwningStack->iRCAPool.GetFreeRCA())!=0,DMMCSocket::Panic(DMMCSocket::EMMCNoFreeRCA) ); |
|
250 Card(aCardNumber).iCID=aCID; |
|
251 if ( Card(aCardNumber).iRCA != 0 ) |
|
252 iOwningStack->iRCAPool.UnlockRCA(Card(aCardNumber).iRCA); |
|
253 Card(aCardNumber).iRCA=rca; |
|
254 iOwningStack->iRCAPool.LockRCA(Card(aCardNumber).iRCA); |
|
255 } |
|
256 |
|
257 Card(aCardNumber).iIndex=(aCardNumber+1); // Mark card as being present |
|
258 *aNewRCA=rca; |
|
259 } |
|
260 |
|
261 TInt TSDCardArray::StoreRCAIfUnique(TUint aCardNumber,TRCA& anRCA) |
|
262 // |
|
263 // Check that no other array element has the same RCA value 'anRCA'. If no |
|
264 // no duplication then store in slot 'aCardNumber'. |
|
265 // |
|
266 { |
|
267 |
|
268 if (anRCA==0) |
|
269 return(KErrGeneral); |
|
270 Card(aCardNumber).iRCA=0; |
|
271 |
|
272 // Now let's look if we've seen this card before |
|
273 for ( TUint i=0 ; i<iOwningStack->iMaxCardsInStack ; i++ ) |
|
274 { |
|
275 if ( Card(i).IsPresent() && Card(i).iRCA==anRCA ) |
|
276 return(KErrInUse); |
|
277 } |
|
278 Card(aCardNumber).iRCA=anRCA; |
|
279 Card(aCardNumber).iIndex=(aCardNumber+1); // Mark card as being present |
|
280 return(KErrNone); |
|
281 } |
|
282 |
|
283 EXPORT_C void TSDCardArray::DeclareCardAsGone(TUint aCardNumber) |
|
284 // |
|
285 // reset SD specific fields to initial values and then reset generic MultiMediaCard |
|
286 // |
|
287 { |
|
288 Card(aCardNumber).SetBusWidth(1); |
|
289 TMMCardArray::DeclareCardAsGone(aCardNumber); |
|
290 } |
|
291 |
|
292 // ======== DSDSession ======== |
|
293 |
|
294 void DSDSession::FillAppCommandDesc(TMMCCommandDesc& aDesc, TSDAppCmd aCmd) |
|
295 { |
|
296 aDesc.iCommand = (TMMCCommandEnum) aCmd; |
|
297 aDesc.iArgument = 0; // set stuff bits to zero |
|
298 FillAppCommandDesc(aDesc); |
|
299 } |
|
300 |
|
301 void DSDSession::FillAppCommandDesc(TMMCCommandDesc& aDesc, TSDAppCmd aCmd, TMMCArgument aArg) |
|
302 { |
|
303 aDesc.iCommand = (TMMCCommandEnum) aCmd; |
|
304 aDesc.iArgument = aArg; |
|
305 FillAppCommandDesc(aDesc); |
|
306 } |
|
307 |
|
308 const TUint32 CCA = KMMCCmdClassApplication; |
|
309 const TMMCIdxCommandSpec AppCmdSpecTable[] = |
|
310 { // Class Type Dir MBlk StopT Rsp Type Len |
|
311 {ESDACmdSetBusWidth, {CCA,ECmdTypeACS, EDirNone, EFalse, EFalse, ERespTypeR1, 4}}, //ACMD6 |
|
312 {ESDACmdSDStatus, {CCA,ECmdTypeADTCS, EDirRead, EFalse, EFalse, ERespTypeR1, 4}}, //ACMD13 |
|
313 {ESDACmdSendNumWrBlocks, {CCA,ECmdTypeADTCS, EDirRead, EFalse, EFalse, ERespTypeR1, 4}}, //ACMD22 |
|
314 {ESDACmdSetWrBlkEraseCount, {CCA,ECmdTypeACS, EDirNone, EFalse, EFalse, ERespTypeR1, 4}}, //ACMD23 |
|
315 {ESDACmdSDAppOpCond, {CCA,ECmdTypeBCR, EDirNone, EFalse, EFalse, ERespTypeR3, 4}}, //ACMD41 |
|
316 {ESDACmdSetClrCardDetect, {CCA,ECmdTypeAC, EDirNone, EFalse, EFalse, ERespTypeR1, 4}}, //ACMD42 |
|
317 {ESDACmdSendSCR, {CCA,ECmdTypeADTCS, EDirRead, EFalse, EFalse, ERespTypeR1, 4}} //ACMD51 |
|
318 }; |
|
319 |
|
320 void DSDSession::FillAppCommandDesc(TMMCCommandDesc& aDesc) |
|
321 { |
|
322 aDesc.iSpec = FindCommandSpec(AppCmdSpecTable, aDesc.iCommand); |
|
323 aDesc.iFlags = 0; |
|
324 aDesc.iBytesDone = 0; |
|
325 } |
|
326 |
|
327 const TMMCIdxCommandSpec SdSpecificCmdSpecTable[] = |
|
328 /** |
|
329 * SD Specific Command Table |
|
330 * |
|
331 * - Some commands defined in the SD specification overload those defined in the MMC specification. |
|
332 * This table contains the SD specific versions of those commands. |
|
333 */ |
|
334 { |
|
335 // Class Type Dir MBlk StopT Rsp Type Len |
|
336 {ESDCmdSendRelativeAddress, {KMMCCmdClassBasic, ECmdTypeBCR, EDirNone, EFalse, EFalse, ERespTypeR6, 4}}, // CMD3 : SEND_RELATIVE_ADDRESS |
|
337 {ESDCmdSwitchFunction, {KMMCCmdClassSwitch,ECmdTypeADTCS, EDirRead, EFalse, EFalse, ERespTypeR1, 4}}, // CMD6 : SWITCH_FUNCTION |
|
338 {ESDCmdSendIfCond, {KMMCCmdClassBasic, ECmdTypeBCR, EDirNone, EFalse, EFalse, ERespTypeR7, 4}} // CMD8 : SEND_IF_COND |
|
339 }; |
|
340 |
|
341 void DSDSession::FillSdSpecificCommandDesc(TMMCCommandDesc& aDesc, TSDSpecificCmd aCmd, TMMCArgument aArg) |
|
342 { |
|
343 aDesc.iCommand = (TMMCCommandEnum) aCmd; |
|
344 aDesc.iArgument = aArg; |
|
345 FillSdSpecificCommandDesc(aDesc); |
|
346 } |
|
347 |
|
348 void DSDSession::FillSdSpecificCommandDesc(TMMCCommandDesc& aDesc, TSDSpecificCmd aCmd) |
|
349 { |
|
350 aDesc.iCommand = (TMMCCommandEnum) aCmd; |
|
351 aDesc.iArgument = 0; // set stuff bits to zero |
|
352 FillSdSpecificCommandDesc(aDesc); |
|
353 } |
|
354 |
|
355 void DSDSession::FillSdSpecificCommandDesc(TMMCCommandDesc& aDesc) |
|
356 { |
|
357 aDesc.iSpec = FindCommandSpec(SdSpecificCmdSpecTable, aDesc.iCommand); |
|
358 aDesc.iFlags = 0; |
|
359 aDesc.iBytesDone = 0; |
|
360 } |
|
361 |
|
362 |
|
363 // ======== DSDStack ======== |
|
364 |
|
365 EXPORT_C TInt DSDStack::Init() |
|
366 { |
|
367 return DMMCStack::Init(); |
|
368 } |
|
369 |
|
370 |
|
371 const TInt KMaxRCASendLoops=3; |
|
372 const TUint KSDMaxPollAttempts=25; |
|
373 EXPORT_C TMMCErr DSDStack::AcquireStackSM() |
|
374 // |
|
375 // This macro acquires new cards in an SD Card - star topology stack. |
|
376 // This means each card has its own CMD and DAT lines and can be addressed |
|
377 // individually by the Controller in turn. Commands can also be broadcast |
|
378 // simultaneously to the entire stack. |
|
379 // It starts with the Controller reading the operating conditions of each |
|
380 // card in the stack (SEND_OP_COND - ACMD41). Then, the following |
|
381 // initialisation sequence is performed to each card in turn:- |
|
382 // New cards in the stack are identified (ALL_SEND_CID - CMD2) and each one |
|
383 // is requested to publish a relative card address (SEND_RCA - CMD3). Finally, |
|
384 // the card specific data (SEND_CSD - CMD9) is read from each card. |
|
385 // Note that the initialization of MMC cards are supported by this function |
|
386 // if they are encountered. These require a slightly different init. procdure. |
|
387 // |
|
388 { |
|
389 enum states |
|
390 { |
|
391 EStBegin=0, |
|
392 EStNextFullRange, |
|
393 EStSendCIDIssued, |
|
394 EStIssueSendRCA, |
|
395 EStSendRCACheck, |
|
396 EStRCADone, |
|
397 EStMoreCardsCheck, |
|
398 EStEnd |
|
399 }; |
|
400 |
|
401 DMMCSession& s=Session(); |
|
402 |
|
403 SMF_BEGIN |
|
404 |
|
405 __KTRACE_OPT(KPBUS1, Kern::Printf(">DSDStack::AcquireStackSM()")); |
|
406 |
|
407 iRCAPool.ReleaseUnlocked(); |
|
408 iCxCardCount=0; // Reset current card number |
|
409 |
|
410 SMF_STATE(EStNextFullRange) |
|
411 |
|
412 iCxCardType = ESDCardTypeUnknown; |
|
413 |
|
414 AddressCard(iCxCardCount); // Address the next card |
|
415 |
|
416 // Before issueing commands, see if there's actually a card present |
|
417 if (!CardDetect(iCxCardCount)) |
|
418 SMF_GOTOS(EStMoreCardsCheck) |
|
419 |
|
420 m.SetTraps(KMMCErrResponseTimeOut); |
|
421 SMF_INVOKES(InitialiseMemoryCardSMST, EStSendCIDIssued) |
|
422 |
|
423 SMF_STATE(EStSendCIDIssued) |
|
424 |
|
425 if( !err ) |
|
426 { |
|
427 // The card responded with a CID. We need to initialise the |
|
428 // appropriate entry in the card array with the CID. |
|
429 if (iCxCardType==ESDCardTypeIsSD) |
|
430 { |
|
431 // Now prepare to recieve an RCA from to the card |
|
432 CardArray().CardP(iCxCardCount)->iCID=s.ResponseP(); |
|
433 DSDSession::FillSdSpecificCommandDesc(Command(), ESDCmdSendRelativeAddress,0); // SEND_RCA with argument just stuff bits |
|
434 |
|
435 m.ResetTraps(); |
|
436 iCxPollRetryCount=0; // Init count of send RCA attempts |
|
437 SMF_GOTOS(EStIssueSendRCA) |
|
438 } |
|
439 else |
|
440 { |
|
441 // The card array allocates an RCA, either the old RCA |
|
442 // if we have seen this card before, or a new one. |
|
443 TRCA rca; |
|
444 CardArray().AddCardSDMode(iCxCardCount,s.ResponseP(),&rca); |
|
445 |
|
446 // Now assign the new RCA to the card |
|
447 s.FillCommandDesc(ECmdSetRelativeAddr,TMMCArgument(rca)); |
|
448 m.ResetTraps(); |
|
449 SMF_INVOKES(ExecCommandSMST,EStRCADone) |
|
450 } |
|
451 } |
|
452 else |
|
453 { |
|
454 m.ResetTraps(); |
|
455 SMF_GOTOS(EStMoreCardsCheck) // Timed out, try the next card slot |
|
456 } |
|
457 |
|
458 SMF_STATE(EStIssueSendRCA) |
|
459 |
|
460 SMF_INVOKES(ExecCommandSMST,EStSendRCACheck) |
|
461 |
|
462 SMF_STATE(EStSendRCACheck) |
|
463 |
|
464 // We need to check that the RCA recieved from the card doesn't clash |
|
465 // with any others in this stack. RCA is first 2 bytes of response buffer (in big endian) |
|
466 TRCA rca=(TUint16)((s.ResponseP()[0]<<8) | s.ResponseP()[1]); |
|
467 if (CardArray().StoreRCAIfUnique(iCxCardCount,rca)!=KErrNone) |
|
468 SMF_GOTOS( ((++iCxPollRetryCount<KMaxRCASendLoops)?EStIssueSendRCA:EStMoreCardsCheck) ) |
|
469 |
|
470 SMF_STATE(EStRCADone) |
|
471 |
|
472 SMF_INVOKES(ConfigureMemoryCardSMST, EStMoreCardsCheck) |
|
473 |
|
474 SMF_STATE(EStMoreCardsCheck) |
|
475 |
|
476 if (++iCxCardCount < (TInt)iMaxCardsInStack) |
|
477 { |
|
478 __KTRACE_OPT(KPBUS1, Kern::Printf(">DSDStack::AcquireStackSM(): More Cards to check: %d",iCxCardCount)); |
|
479 SMF_GOTOS(EStNextFullRange) |
|
480 } |
|
481 else |
|
482 { |
|
483 AddressCard(KBroadcastToAllCards); // Set back to broadcast mode |
|
484 __KTRACE_OPT(KPBUS1, Kern::Printf("<DSDStack::AcquireStackSM()")); |
|
485 } |
|
486 |
|
487 SMF_END |
|
488 } |
|
489 |
|
490 |
|
491 TMMCErr DSDStack::InitialiseMemoryCardSMST(TAny* aStackP) |
|
492 { return static_cast<DSDStack*>(aStackP)->InitialiseMemoryCardSM(); } |
|
493 |
|
494 |
|
495 TMMCErr DSDStack::InitialiseMemoryCardSM() |
|
496 /** |
|
497 */ |
|
498 { |
|
499 enum states |
|
500 { |
|
501 EStBegin=0, |
|
502 EStSendInterfaceCondition, |
|
503 EStSentInterfaceCondition, |
|
504 EStSetFullRangeCmd, |
|
505 EStCheckForFullRangeCmd41Timeout, |
|
506 EStSentAppCommandBeforeCheckVoltage, |
|
507 EStCheckVoltage, |
|
508 EStFullRangeDone, |
|
509 EStSetRangeCmd, |
|
510 EStCheckForRangeCmd41Timeout, |
|
511 EStSetRangeBusyCheck, |
|
512 EStCIDCmd, |
|
513 EStSendCIDIssued, |
|
514 EStEnd |
|
515 }; |
|
516 |
|
517 DMMCSession& s=Session(); |
|
518 DMMCPsu* psu=(DMMCPsu*)MMCSocket()->iVcc; |
|
519 |
|
520 static const TUint32 KCmd8Param = 0x0100 | 0x00AA; // Voltage supplied : 2.7-3.6V, Check Pattern 10101010b |
|
521 static const TUint32 KCmd8CheckMask = 0x00000FFF; |
|
522 |
|
523 SMF_BEGIN |
|
524 |
|
525 iCxCardType = ESDCardTypeUnknown; |
|
526 s.iCardP = NULL; // This stops ExecCommandSM() from setting old RCA when sending CMD55 |
|
527 |
|
528 // Send CMD0 to initialise memory |
|
529 SMF_INVOKES(GoIdleSMST, EStSendInterfaceCondition); |
|
530 |
|
531 SMF_STATE(EStSendInterfaceCondition) |
|
532 |
|
533 iCxPollRetryCount=0; // Reset max number of poll attempts on card busy |
|
534 iConfig.SetPollAttempts(KSDMaxPollAttempts); // Increase card busy timeout to 1 Sec for SD Cards |
|
535 |
|
536 iConfig.RemoveMode( KMMCModeEnableTimeOutRetry ); // Temporarily disable timeout retries - since we use a timeout event to distinguish between MMC and SD |
|
537 |
|
538 DSDSession::FillSdSpecificCommandDesc(Command(), ESDCmdSendIfCond, KCmd8Param); |
|
539 |
|
540 // SD2.0 defines CMD8 as having a new response type - R7 |
|
541 // if the PSL doesn't indicate support for R7, use R1 instead |
|
542 if (!(MMCSocket()->MachineInfo().iFlags & TMMCMachineInfo::ESupportsR7)) |
|
543 { |
|
544 __KTRACE_OPT(KPBUS1, Kern::Printf("R7 not supported.")); |
|
545 Command().iSpec.iResponseType = ERespTypeR1; |
|
546 } |
|
547 |
|
548 |
|
549 m.SetTraps(KMMCErrAll); |
|
550 SMF_INVOKES(ExecCommandSMST, EStSentInterfaceCondition) |
|
551 |
|
552 SMF_STATE(EStSentInterfaceCondition) |
|
553 |
|
554 if (err == KMMCErrNone) |
|
555 { |
|
556 // Check the response for voltage and check pattern |
|
557 const TUint32 status = TMMC::BigEndian32(s.ResponseP()); |
|
558 if((status & KCmd8CheckMask) == KCmd8Param) |
|
559 { |
|
560 __KTRACE_OPT(KPBUS1, Kern::Printf("Found v2 card.")); |
|
561 iCurrentOpRange |= KMMCOCRAccessModeHCS; |
|
562 } |
|
563 else |
|
564 { |
|
565 // Pattern Mis-match, card does not support the specified voltage range |
|
566 return( KMMCErrNotSupported ); |
|
567 } |
|
568 |
|
569 SMF_GOTOS(EStCheckVoltage); |
|
570 } |
|
571 |
|
572 // Go idle again after CMD8 failure |
|
573 SMF_INVOKES(GoIdleSMST, EStCheckVoltage); |
|
574 |
|
575 |
|
576 SMF_STATE(EStCheckVoltage) |
|
577 |
|
578 |
|
579 // If platform doesn't support an adjustable voltage PSU then there's no |
|
580 // point in doing a full range for its supported range. To support range |
|
581 // checking on a multi-card stack would require a complete scan of all |
|
582 // cards before actually setting the range. This would over-complicate things |
|
583 // and make the more normal single card/none adjustable cases less efficient. |
|
584 if ( !(psu->VoltageSupported()&KMMCAdjustableOpVoltage) || iMaxCardsInStack>1) |
|
585 { |
|
586 // if the PSU isn't adjustable then it can't support low voltage mode |
|
587 iCurrentOpRange&= ~KMMCOCRLowVoltage; |
|
588 |
|
589 SMF_GOTOS(EStSetRangeCmd) |
|
590 } |
|
591 |
|
592 SMF_STATE(EStSetFullRangeCmd) |
|
593 |
|
594 // Issue ACMD41/CMD1 with omitted voltage range |
|
595 if (iCxCardType==ESDCardTypeIsMMC) |
|
596 { |
|
597 s.FillCommandDesc(ECmdSendOpCond, KMMCOCRAccessModeHCS | KMMCOCRBusy); // Full range + Sector Access + Busy bit (iArgument==KBit31) |
|
598 SMF_NEXTS(EStFullRangeDone) |
|
599 } |
|
600 else |
|
601 { |
|
602 DSDSession::FillAppCommandDesc(Command(), ESDACmdSDAppOpCond, TMMCArgument(0)); |
|
603 SMF_NEXTS(EStCheckForFullRangeCmd41Timeout) |
|
604 } |
|
605 |
|
606 m.SetTraps(KMMCErrResponseTimeOut); |
|
607 SMF_CALL(ExecCommandSMST) |
|
608 |
|
609 SMF_STATE(EStCheckForFullRangeCmd41Timeout) |
|
610 |
|
611 if (err==KMMCErrResponseTimeOut) |
|
612 { |
|
613 __KTRACE_OPT(KPBUS1, Kern::Printf("ACMD 41 not supported - Assuming MMC")); |
|
614 iCxCardType=ESDCardTypeIsMMC; |
|
615 |
|
616 // Send CMD0 to re-initialise the card - otherwise we may get |
|
617 // KMMCStatErrIllegalCommand returned for the next command |
|
618 // expecting an R1 response. NB The SD spec recommends ignoring the error |
|
619 // whereas the SDIO spec recommends this approach (ignoring the error |
|
620 // would be difficult to code anyway, since by then we're no longer |
|
621 // in this state machine). |
|
622 SMF_INVOKES(GoIdleSMST, EStSetFullRangeCmd); // Repeat - but using CMD1 |
|
623 } |
|
624 else |
|
625 { |
|
626 // No response timeout - so it must be an SD Card |
|
627 (CardArray().CardP(iCxCardCount)->iFlags)|=KSDCardIsSDCard; |
|
628 iCxCardType=ESDCardTypeIsSD; |
|
629 } |
|
630 |
|
631 SMF_STATE(EStFullRangeDone) |
|
632 |
|
633 if (!err) |
|
634 { |
|
635 // Card responded with Op range - evaluate the common subset with the current setting. |
|
636 // Dont worry about the busy bit for now, we'll check that when we repeat the command |
|
637 const TUint32 range = (iCurrentOpRange & ~KMMCOCRAccessModeHCS) & (TMMC::BigEndian32(s.ResponseP()) & ~KMMCOCRBusy); |
|
638 if(range == 0) |
|
639 { |
|
640 return( KMMCErrNotSupported ); // Card is incompatible with our h/w |
|
641 } |
|
642 iCurrentOpRange = range | (iCurrentOpRange & KMMCOCRAccessModeHCS); |
|
643 } |
|
644 |
|
645 // Repeat SEND_OP_COND this time setting Current Op Range |
|
646 if (iCxCardType==ESDCardTypeIsMMC) |
|
647 { |
|
648 // If platform and the card both support low voltage mode (1.65 - 1.95v), switch |
|
649 // NB If this fails then there is no recovery. |
|
650 if (iCurrentOpRange & KMMCOCRLowVoltage) |
|
651 { |
|
652 iCurrentOpRange = KMMCOCRLowVoltage; |
|
653 SMF_INVOKES( SwitchToLowVoltageSMST, EStSetRangeCmd ) |
|
654 } |
|
655 } |
|
656 |
|
657 SMF_STATE(EStSetRangeCmd) |
|
658 |
|
659 // Issue ACMD41/CMD1 with voltage range |
|
660 if (iCxCardType==ESDCardTypeIsMMC) |
|
661 { |
|
662 s.FillCommandDesc(ECmdSendOpCond,(iCurrentOpRange | KMMCOCRAccessModeHCS | KMMCOCRBusy)); // Range supported + Sector Access Busy bit (iArgument==KBit31) |
|
663 SMF_NEXTS(EStSetRangeBusyCheck) |
|
664 } |
|
665 else |
|
666 { |
|
667 TUint arg = (iCurrentOpRange & ~KMMCOCRAccessModeHCS); // Range supported |
|
668 if((iCurrentOpRange & KMMCOCRAccessModeHCS) != 0) |
|
669 { |
|
670 arg |= KMMCOCRAccessModeHCS; |
|
671 } |
|
672 DSDSession::FillAppCommandDesc(Command(), ESDACmdSDAppOpCond, arg); |
|
673 SMF_NEXTS((iCxCardType == ESDCardTypeUnknown)? EStCheckForRangeCmd41Timeout : EStSetRangeBusyCheck) |
|
674 } |
|
675 |
|
676 m.SetTraps(KMMCErrResponseTimeOut); |
|
677 SMF_CALL(ExecCommandSMST) |
|
678 |
|
679 SMF_STATE(EStCheckForRangeCmd41Timeout) |
|
680 |
|
681 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:ascs:crct:%d", err)); |
|
682 if (err==KMMCErrResponseTimeOut) |
|
683 { |
|
684 iCxCardType=ESDCardTypeIsMMC; |
|
685 // Send CMD0 to re-initialise the card - otherwise we may get |
|
686 // KMMCStatErrIllegalCommand returned for the next command |
|
687 // expecting an R1 response. NB The SD spec recommends ignoring the error |
|
688 // whereas the SDIO spec recommends this approach (ignoring the error |
|
689 // would be difficult to code anyway, since by then we're no longer |
|
690 // in this state machine). |
|
691 SMF_INVOKES(GoIdleSMST, EStSetRangeCmd); // Repeat - but using CMD1 |
|
692 } |
|
693 else |
|
694 { |
|
695 // No response timeout - so it must be an SD Card |
|
696 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:ascs:crct2:%x", iCardArray)); |
|
697 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:ascs:crct3:%x", iCxCardCount)); |
|
698 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:ascs:crct4:%x", CardArray().CardP(iCxCardCount))); |
|
699 |
|
700 (CardArray().CardP(iCxCardCount)->iFlags)|=KSDCardIsSDCard; |
|
701 iCxCardType=ESDCardTypeIsSD; |
|
702 } |
|
703 |
|
704 SMF_STATE(EStSetRangeBusyCheck) |
|
705 |
|
706 __KTRACE_OPT(KPBUS1, Kern::Printf("-mst:ascs:src:%d",iCxCardType)); // 1:MMC, 2:SD |
|
707 |
|
708 if ( !err ) |
|
709 { |
|
710 const TUint32 ocrResponse = TMMC::BigEndian32(s.ResponseP()); |
|
711 |
|
712 if ((ocrResponse & KMMCOCRBusy) == 0) |
|
713 { |
|
714 __KTRACE_OPT(KPBUS1,Kern::Printf("-sd:upd:bsy")); |
|
715 // Card is still busy powering up. Check if we should timeout |
|
716 if ( ++iCxPollRetryCount > iConfig.OpCondBusyTimeout() ) |
|
717 { |
|
718 __KTRACE_OPT2(KPBUS1, KPANIC, Kern::Printf("-sd:ocr busy timed out")); |
|
719 return( KMMCErrBusTimeOut ); |
|
720 } |
|
721 |
|
722 #ifdef _DEBUG |
|
723 if ( iCxPollRetryCount > KMMCSpecOpCondBusyTimeout ) |
|
724 { |
|
725 __KTRACE_OPT2(KPBUS1, KPANIC, Kern::Printf("-sd:ocr exceeded spec timeout!! (%d ms)", (iCxPollRetryCount*KMMCRetryGapInMilliseconds))); |
|
726 } |
|
727 #endif |
|
728 m.ResetTraps(); |
|
729 |
|
730 SMF_INVOKES(RetryGapTimerSMST,EStSetRangeCmd) |
|
731 } |
|
732 else |
|
733 { |
|
734 if(ocrResponse & KMMCOCRAccessModeHCS) |
|
735 { |
|
736 CardArray().CardP(iCxCardCount)->iFlags |= KMMCardIsHighCapacity; |
|
737 #ifdef _DEBUG |
|
738 if(iCxCardType == ESDCardTypeIsSD) |
|
739 { |
|
740 __KTRACE_OPT(KPBUS1, Kern::Printf("Found large SD card.")); |
|
741 } |
|
742 else if(iCxCardType == ESDCardTypeIsMMC) |
|
743 { |
|
744 __KTRACE_OPT(KPBUS1, Kern::Printf("Found large MMC card.")); |
|
745 } |
|
746 #endif |
|
747 } |
|
748 } |
|
749 } |
|
750 |
|
751 // Restore original settings |
|
752 iConfig.SetMode( EffectiveModes(s.iConfig) & KMMCModeEnableTimeOutRetry ); |
|
753 iConfig.SetPollAttempts(KMMCMaxPollAttempts); |
|
754 |
|
755 // All cards are now ready and notified of the voltage range - ask ASSP to set it up |
|
756 if (iCxCardType==ESDCardTypeIsMMC) |
|
757 { |
|
758 iCurrentOpRange &= ~KMMCOCRAccessModeMask; |
|
759 } |
|
760 else |
|
761 { |
|
762 iCurrentOpRange &= ~KMMCOCRAccessModeHCS; |
|
763 } |
|
764 |
|
765 psu->SetVoltage(iCurrentOpRange); |
|
766 if (psu->SetState(EPsuOnFull) != KErrNone) |
|
767 { |
|
768 return(KMMCErrHardware); |
|
769 } |
|
770 |
|
771 SMF_STATE(EStCIDCmd) |
|
772 |
|
773 s.FillCommandDesc(ECmdAllSendCID,0); |
|
774 m.ResetTraps(); |
|
775 SMF_INVOKES(ExecCommandSMST,EStSendCIDIssued) |
|
776 |
|
777 SMF_STATE(EStSendCIDIssued) |
|
778 |
|
779 |
|
780 // All done - Higher level state machine expects CID in s.ResponseP() |
|
781 |
|
782 SMF_END |
|
783 } |
|
784 |
|
785 TMMCErr DSDStack::ConfigureMemoryCardSMST(TAny* aStackP) |
|
786 { return static_cast<DSDStack*>(aStackP)->ConfigureMemoryCardSM(); } |
|
787 |
|
788 TMMCErr DSDStack::ConfigureMemoryCardSM() |
|
789 /** |
|
790 */ |
|
791 { |
|
792 enum states |
|
793 { |
|
794 EStBegin=0, |
|
795 EStSendCSDDone, |
|
796 EStEnd |
|
797 }; |
|
798 |
|
799 DMMCSession& s=Session(); |
|
800 |
|
801 //coverity[UNREACHABLE] |
|
802 //Part of state machine design. |
|
803 SMF_BEGIN |
|
804 |
|
805 // Cards is initialised so get its CSD |
|
806 |
|
807 s.FillCommandDesc(ECmdSendCSD, TUint32(CardArray().CardP(iCxCardCount)->iRCA) << 16); |
|
808 SMF_INVOKES(ExecCommandSMST, EStSendCSDDone) |
|
809 |
|
810 SMF_STATE(EStSendCSDDone) |
|
811 |
|
812 // Store the CSD in the new card entry |
|
813 TMMCard* cardP = CardArray().CardP(iCxCardCount); |
|
814 cardP->iCSD = s.ResponseP(); |
|
815 |
|
816 if(CardArray().Card(iCxCardCount).IsSDCard()) |
|
817 { |
|
818 // Perform SD Specific parsing of the CSD structure |
|
819 if(cardP->CSD().CCC() & KMMCCmdClassLockCard) |
|
820 { |
|
821 cardP->iFlags |= KMMCardIsLockable; |
|
822 } |
|
823 } |
|
824 else |
|
825 { |
|
826 // Perform MMC Specific parsing of the CSD structure |
|
827 TUint specVers = cardP->CSD().SpecVers(); // 1 => 1.4, 2 => 2.0 - 2.2, 3 => 3.1 |
|
828 if ((specVers >= 2) && (cardP->CSD().CCC() & KMMCCmdClassLockCard)) |
|
829 { |
|
830 cardP->iFlags |= KMMCardIsLockable; |
|
831 } |
|
832 } |
|
833 |
|
834 // Check the state of the mechanical write protect switch |
|
835 if (WriteProtected(iCxCardCount)) |
|
836 { |
|
837 cardP->iFlags |= KMMCardIsWriteProtected; |
|
838 } |
|
839 |
|
840 SMF_END |
|
841 } |
|
842 |
|
843 EXPORT_C TMMCErr DSDStack::InitStackAfterUnlockSM() |
|
844 // |
|
845 // Performs initialisation of the SD card after the card has been unlocked |
|
846 // |
|
847 { |
|
848 enum states |
|
849 { |
|
850 EStBegin=0, |
|
851 EStNextCard, |
|
852 EStSelectCard, |
|
853 EStSetBusWidth, |
|
854 EStSetBusWidth1, |
|
855 EStGetSDStatus, |
|
856 EStGetSDStatus1, |
|
857 EStDecodeSDStatus, |
|
858 EStDeselectCard, |
|
859 EStCardDeselectedReadCSD, |
|
860 EStCSDCmdSent, |
|
861 EStMoreCardsCheck, |
|
862 EStEnd |
|
863 }; |
|
864 |
|
865 DMMCSession& s=Session(); |
|
866 |
|
867 SMF_BEGIN |
|
868 |
|
869 __KTRACE_OPT(KPBUS1, Kern::Printf(">DSDStack::InitStackAfterUnlockSM()")); |
|
870 iRCAPool.ReleaseUnlocked(); |
|
871 iCxCardCount=0; // Reset current card number |
|
872 |
|
873 SMF_STATE(EStNextCard) |
|
874 AddressCard(iCxCardCount); // Address the next card |
|
875 |
|
876 if (!CardDetect(iCxCardCount)) |
|
877 SMF_GOTOS(EStMoreCardsCheck) |
|
878 |
|
879 s.SetCard(CardArray().CardP(iCxCardCount)); |
|
880 |
|
881 if (!CardArray().Card(iCxCardCount).IsSDCard()) |
|
882 { |
|
883 SMF_INVOKES( DMMCStack::InitCurrentCardAfterUnlockSMST, EStMoreCardsCheck ) |
|
884 } |
|
885 |
|
886 SMF_STATE(EStSelectCard) |
|
887 |
|
888 TRCA targetRCA = CardArray().Card(iCxCardCount).RCA(); |
|
889 if (targetRCA == SelectedCard()) |
|
890 { |
|
891 SMF_GOTOS(EStSetBusWidth) |
|
892 } |
|
893 |
|
894 s.FillCommandDesc(ECmdSelectCard, targetRCA); |
|
895 SMF_INVOKES(ExecCommandSMST,EStSetBusWidth) |
|
896 |
|
897 SMF_STATE(EStSetBusWidth) |
|
898 const TMMCStatus status = s.LastStatus(); |
|
899 if((status & KMMCStatCardIsLocked) != 0) |
|
900 SMF_GOTOS(EStDeselectCard) |
|
901 |
|
902 // set bus width with ACMD6 |
|
903 TUint32 arg = TUint32(CardArray().Card(iCxCardCount).RCA()) << 16; |
|
904 s.FillCommandDesc(ECmdAppCmd, arg); |
|
905 SMF_INVOKES(IssueCommandCheckResponseSMST,EStSetBusWidth1) |
|
906 |
|
907 SMF_STATE(EStSetBusWidth1) |
|
908 CardArray().Card(iCxCardCount).SetBusWidth(4); |
|
909 DSDSession::FillAppCommandDesc(Command(), ESDACmdSetBusWidth, KSDBusWidth4); |
|
910 SMF_INVOKES(IssueCommandCheckResponseSMST,EStGetSDStatus) |
|
911 |
|
912 SMF_STATE(EStGetSDStatus) |
|
913 // Now we have sent ACMD6, ask the controller to set the bus width to 4 |
|
914 DoSetBusWidth(EBusWidth4); |
|
915 |
|
916 // get protected area size with ACMD13 |
|
917 TUint32 arg = TUint32(CardArray().Card(iCxCardCount).RCA()) << 16; |
|
918 s.FillCommandDesc(ECmdAppCmd,arg); |
|
919 SMF_INVOKES(IssueCommandCheckResponseSMST,EStGetSDStatus1) |
|
920 |
|
921 SMF_STATE(EStGetSDStatus1) |
|
922 DSDSession::FillAppCommandDesc(Command(), ESDACmdSDStatus); |
|
923 s.FillCommandArgs(0, KSDStatusBlockLength, iPSLBuf, KSDStatusBlockLength); |
|
924 SMF_INVOKES(IssueCommandCheckResponseSMST,EStDecodeSDStatus); |
|
925 |
|
926 SMF_STATE(EStDecodeSDStatus) |
|
927 #ifdef _DEBUG |
|
928 for (TUint i = 0; i < KSDStatusBlockLength; ++i) |
|
929 { |
|
930 __KTRACE_OPT(KPBUS1, Kern::Printf("SD_STATUS[0x%x] = %x", i, iPSLBuf[i])); |
|
931 } |
|
932 #endif |
|
933 // bits 495:480 are SD_CARD_TYPE. Check this is 00xxh (x = don't care). |
|
934 |
|
935 if (iPSLBuf[2] != 0) |
|
936 return KMMCErrNotSupported; |
|
937 |
|
938 // bits 479:448 contain SIZE_OF_PROTECTED_AREA. |
|
939 // (This is bytes 4 to 7 in big-endian format.) |
|
940 |
|
941 TSDCard& sdc = CardArray().Card(iCxCardCount); |
|
942 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >DSDStack: Card %d", iCxCardCount)); |
|
943 TUint32 size_of_protected_area = TMMC::BigEndian32(&iPSLBuf[4]); |
|
944 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >DSDStack: SizeOfProtectedArea: %d", size_of_protected_area)); |
|
945 const TCSD& csd = sdc.CSD(); |
|
946 TUint32 pas = 0; |
|
947 |
|
948 if (sdc.IsHighCapacity()) |
|
949 { |
|
950 // High Capacity Card |
|
951 // Protected Area = SIZE_OF_PROTECTED_AREA |
|
952 pas = size_of_protected_area; |
|
953 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >DSDStack(SDHC): SetProtectedAreaSize: %d", pas)); |
|
954 } |
|
955 else |
|
956 { |
|
957 // Standard Capacity Card |
|
958 // Protected Area = SIZE_OF_PROTECTED_AREA * C_SIZE_MULT * BLOCK_LEN |
|
959 pas = size_of_protected_area * (1 << (csd.CSizeMult() + 2 + csd.ReadBlLen())); |
|
960 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >DSDStack(SDSC): SetProtectedAreaSize: %d", pas)); |
|
961 } |
|
962 |
|
963 sdc.SetProtectedAreaSize(pas); |
|
964 |
|
965 //bits 431:428 contain AU_SIZE |
|
966 //(This is higher order 4 bits of 10th byte in big endian format) |
|
967 TUint8 au = TUint8(iPSLBuf[10] >> 4); |
|
968 if(au == 0) //AU_SIZE field in SD status register is undefined. |
|
969 au = 6; //Defaulting to value corresponding to 512K |
|
970 sdc.SetAUSize(au); |
|
971 |
|
972 SMF_INVOKES(SwitchToHighSpeedModeSMST, EStDeselectCard) |
|
973 |
|
974 SMF_STATE(EStDeselectCard) |
|
975 s.FillCommandDesc(ECmdSelectCard, 0); |
|
976 SMF_INVOKES(ExecCommandSMST, EStCardDeselectedReadCSD) |
|
977 |
|
978 SMF_STATE(EStCardDeselectedReadCSD) |
|
979 // |
|
980 // Read the card's CSD register (again) |
|
981 // |
|
982 // - We re-read the CSD, as the TRAN_SPEED field may have changed due to a switch to HS Mode |
|
983 // |
|
984 TUint32 arg = TUint32(CardArray().Card(iCxCardCount).RCA()) << 16; |
|
985 s.FillCommandDesc( ECmdSendCSD, arg ); |
|
986 SMF_INVOKES(ExecCommandSMST, EStCSDCmdSent) |
|
987 |
|
988 SMF_STATE(EStCSDCmdSent) |
|
989 // |
|
990 // Store the CSD in the card entry |
|
991 // |
|
992 TMMCard* cardP = iCardArray->CardP(iCxCardCount); |
|
993 cardP->iCSD = s.ResponseP(); |
|
994 |
|
995 SMF_STATE(EStMoreCardsCheck) |
|
996 if (++iCxCardCount < (TInt)iMaxCardsInStack) |
|
997 { |
|
998 __KTRACE_OPT(KPBUS1, Kern::Printf("\t >DSDStack: Address Next card: %d",iCxCardCount)); |
|
999 SMF_GOTOS(EStNextCard) |
|
1000 } |
|
1001 else |
|
1002 { |
|
1003 AddressCard(KBroadcastToAllCards); |
|
1004 __KTRACE_OPT(KPBUS1, Kern::Printf("<DSDStack::InitStackAfterUnlockSM()")); |
|
1005 } |
|
1006 |
|
1007 SMF_END |
|
1008 |
|
1009 } |
|
1010 |
|
1011 TMMCErr DSDStack::CIMReadWriteMemoryBlocksSMST(TAny* aStackP) |
|
1012 { return( static_cast<DSDStack *>(aStackP)->DMMCStack::CIMReadWriteBlocksSM() ); } |
|
1013 |
|
1014 |
|
1015 EXPORT_C TMMCErr DSDStack::CIMReadWriteBlocksSM() |
|
1016 // |
|
1017 // This macro performs single/multiple block reads and writes |
|
1018 // For normal read/write block operations, this function determines the appropriate |
|
1019 // MMC command to send and fills the command descriptor accordingly based on |
|
1020 // the value of the session ID set. However, it is necessary to have set the |
|
1021 // command arguments (with DMMCSession::FillCommandArgs()) before this function |
|
1022 // is called. |
|
1023 // For special block read/write operations, e.g. lock/unlock, it is required to |
|
1024 // have already filled the command descriptor (with DMMCSession::FillCommandDesc()) |
|
1025 // for the special command required - in addition to have setup the command arguments. |
|
1026 // |
|
1027 { |
|
1028 enum states |
|
1029 { |
|
1030 EStBegin=0, |
|
1031 EStRestart, |
|
1032 EStAttached, |
|
1033 EStLength1, |
|
1034 EStLengthSet, |
|
1035 EStIssued, |
|
1036 EStWaitFinish, |
|
1037 EStWaitFinish1, |
|
1038 EStRWFinish, |
|
1039 EStDone, |
|
1040 EStEnd |
|
1041 }; |
|
1042 |
|
1043 DMMCSession& s = Session(); |
|
1044 |
|
1045 __KTRACE_OPT(KPBUS1,Kern::Printf(">SD:RWBlocksSM %x",TUint(s.iLastStatus))); |
|
1046 |
|
1047 SMF_BEGIN |
|
1048 |
|
1049 TSDCard& sdCard = *static_cast<TSDCard*>(s.iCardP); |
|
1050 AddressCard(sdCard.iIndex-1); |
|
1051 |
|
1052 if(sdCard.IsSDCard() == EFalse) |
|
1053 { |
|
1054 // |
|
1055 // If this is not an SD card, then use the more appropriate |
|
1056 // MMC state machine as this is optimised for MMC performance |
|
1057 // |
|
1058 SMF_INVOKES(CIMReadWriteMemoryBlocksSMST, EStDone); |
|
1059 } |
|
1060 |
|
1061 if(s.iSessionID == ECIMWriteBlock || s.iSessionID == ECIMWriteMBlock) |
|
1062 { |
|
1063 // Check that the card supports class 4 (Write) commands |
|
1064 const TUint ccc = s.iCardP->CSD().CCC(); |
|
1065 if(!(ccc & KMMCCmdClassBlockWrite)) |
|
1066 return( KMMCErrNotSupported ); |
|
1067 } |
|
1068 |
|
1069 Command().iCustomRetries = 0; // MBW retries |
|
1070 s.iState |= KMMCSessStateInProgress; |
|
1071 m.SetTraps(KMMCErrInitContext); |
|
1072 |
|
1073 SMF_STATE(EStRestart) // NB: ErrBypass is not processed here |
|
1074 |
|
1075 SMF_CALLMEWR(EStRestart) // Create a recursive call entry to recover from the errors trapped |
|
1076 m.SetTraps(KMMCErrStatus); |
|
1077 if (s.Command().iSpec.iCommandClass!=KMMCCmdClassApplication || s.Command().iCommand==ECmdAppCmd ) |
|
1078 { |
|
1079 s.ResetCommandStack(); |
|
1080 SMF_INVOKES( AttachCardSMST, EStAttached ) // attachment is mandatory here |
|
1081 } |
|
1082 |
|
1083 SMF_BPOINT(EStAttached) |
|
1084 |
|
1085 TMMCCommandDesc& cmd = s.Command(); |
|
1086 |
|
1087 const TUint32 blockLength = cmd.BlockLength(); |
|
1088 if((blockLength == 0) || (blockLength > (TUint)KDefaultBlockLenInBytes)) |
|
1089 { |
|
1090 __KTRACE_OPT(KPBUS1,Kern::Printf(">SD:RWBlocksSM err BlockLen:%d",blockLength)); |
|
1091 return KMMCErrArgument; |
|
1092 } |
|
1093 |
|
1094 if(s.iSessionID == ECIMReadBlock || |
|
1095 s.iSessionID == ECIMWriteBlock || |
|
1096 s.iSessionID == ECIMReadMBlock || |
|
1097 s.iSessionID == ECIMWriteMBlock) |
|
1098 { |
|
1099 // read/write operation |
|
1100 if(!cmd.AdjustForBlockOrByteAccess(s)) |
|
1101 { |
|
1102 // unable to convert command arguments to suit the underlying block/byte access mode |
|
1103 return KMMCErrArgument; |
|
1104 } |
|
1105 } |
|
1106 |
|
1107 // Set the block length if it has changed. Always set for ECIMLockUnlock. |
|
1108 if ((blockLength == s.iCardP->iSetBlockLen) && (s.iSessionID != ECIMLockUnlock)) |
|
1109 { |
|
1110 SMF_GOTOS( EStLengthSet ) |
|
1111 } |
|
1112 |
|
1113 s.iCardP->iSetBlockLen = 0; |
|
1114 s.PushCommandStack(); |
|
1115 s.FillCommandDesc( ECmdSetBlockLen, blockLength ); |
|
1116 SMF_INVOKES( ExecCommandSMST, EStLength1 ) |
|
1117 |
|
1118 SMF_STATE(EStLength1) |
|
1119 |
|
1120 const TMMCStatus status(s.ResponseP()); |
|
1121 s.PopCommandStack(); |
|
1122 if (status.Error()) |
|
1123 SMF_RETURN(KMMCErrStatus) |
|
1124 s.iCardP->iSetBlockLen = s.Command().BlockLength(); |
|
1125 |
|
1126 SMF_STATE(EStLengthSet) |
|
1127 |
|
1128 TMMCCommandDesc& cmd = s.Command(); |
|
1129 TUint opType = 0; |
|
1130 const TUint kTypeWrite = KBit0; |
|
1131 const TUint kTypeMultiple = KBit1; |
|
1132 const TUint kTypeSpecial = KBit2; |
|
1133 static const TMMCCommandEnum cmdCodes[4] = |
|
1134 {ECmdReadSingleBlock, ECmdWriteBlock, ECmdReadMultipleBlock, ECmdWriteMultipleBlock}; |
|
1135 |
|
1136 switch( s.iSessionID ) |
|
1137 { |
|
1138 case ECIMReadBlock: |
|
1139 break; |
|
1140 case ECIMWriteBlock: |
|
1141 opType=kTypeWrite; |
|
1142 break; |
|
1143 case ECIMReadMBlock: |
|
1144 opType=kTypeMultiple; |
|
1145 break; |
|
1146 case ECIMWriteMBlock: |
|
1147 opType=kTypeWrite|kTypeMultiple; |
|
1148 break; |
|
1149 case ECIMLockUnlock: |
|
1150 default: |
|
1151 opType=kTypeSpecial; |
|
1152 break; |
|
1153 } |
|
1154 |
|
1155 const TUint blocks = cmd.iTotalLength / cmd.BlockLength(); |
|
1156 if ( blocks * cmd.BlockLength() != cmd.iTotalLength ) |
|
1157 return( KMMCErrArgument ); |
|
1158 |
|
1159 if ( !(opType & kTypeSpecial) ) // A special session has already set its command descriptor |
|
1160 { |
|
1161 if (blocks==1) |
|
1162 opType &= ~kTypeMultiple; |
|
1163 |
|
1164 TUint32 oldFlags = cmd.iFlags; // Store the existing command flags, as they will be reset by FillCommandDesc() |
|
1165 cmd.iCommand = cmdCodes[opType]; |
|
1166 s.FillCommandDesc(); |
|
1167 cmd.iFlags = oldFlags; // ...and restore the old command flags |
|
1168 } |
|
1169 |
|
1170 // NB We need to trap KMMCErrStatus errors, because if one occurs, |
|
1171 // we still need to wait to exit PRG/RCV/DATA state |
|
1172 if (Command().iCommand == ECmdWriteMultipleBlock) |
|
1173 { |
|
1174 Command().iExecNotHandle = KMMCErrDataCRC | KMMCErrDataTimeOut; |
|
1175 m.SetTraps(KMMCErrStatus | KMMCErrDataCRC | KMMCErrDataTimeOut); |
|
1176 } |
|
1177 else |
|
1178 { |
|
1179 m.SetTraps(KMMCErrStatus); |
|
1180 } |
|
1181 |
|
1182 SMF_INVOKES( ExecCommandSMST, EStIssued ) |
|
1183 |
|
1184 SMF_STATE(EStIssued) |
|
1185 |
|
1186 // check state of card after data transfer with CMD13. |
|
1187 if (s.Command().Direction() != 0) |
|
1188 { |
|
1189 SMF_GOTOS(EStWaitFinish) |
|
1190 } |
|
1191 |
|
1192 SMF_GOTOS(EStRWFinish); |
|
1193 |
|
1194 SMF_STATE(EStWaitFinish) |
|
1195 // if MBW fail, then recover by rewriting ALL blocks... |
|
1196 // (used to recover using ACMD22, but this has been changed |
|
1197 // as is difficult to test for little gain in efficiency) |
|
1198 if (Command().iCommand == ECmdWriteMultipleBlock && err != 0) |
|
1199 { |
|
1200 if (Command().iCustomRetries++ >= (TInt) KSDMaxMBWRetries) |
|
1201 { |
|
1202 SMF_RETURN(err) |
|
1203 } |
|
1204 |
|
1205 m.Pop(); // remove recursive call to EStRestart |
|
1206 SMF_GOTOS(EStRestart) |
|
1207 } |
|
1208 |
|
1209 // Save the status and examine it after issuing CMD13... |
|
1210 // NB We don't know where in the command stack the last response is stored (e.g. there may |
|
1211 // have bee a Deselect/Select issued), but we do know last response is stored in iLastStatus |
|
1212 TMMC::BigEndian4Bytes(s.ResponseP(), s.iLastStatus); |
|
1213 |
|
1214 // ...else issue CMD13 to poll for the card finishing and check for errors |
|
1215 s.PushCommandStack(); |
|
1216 s.FillCommandDesc(ECmdSendStatus, 0); |
|
1217 SMF_INVOKES(ExecCommandSMST, EStWaitFinish1) |
|
1218 |
|
1219 SMF_STATE(EStWaitFinish1) |
|
1220 |
|
1221 const TMMCStatus status(s.ResponseP()); |
|
1222 s.PopCommandStack(); |
|
1223 |
|
1224 #ifdef __WINS__ |
|
1225 SMF_GOTOS(EStRWFinish); |
|
1226 #else |
|
1227 const TMMCardStateEnum st1 = status.State(); |
|
1228 |
|
1229 if (st1 == ECardStatePrg || st1 == ECardStateRcv || st1 == ECardStateData) |
|
1230 { |
|
1231 SMF_INVOKES(ProgramTimerSMST, EStWaitFinish); |
|
1232 } |
|
1233 |
|
1234 if (status.Error()) |
|
1235 SMF_RETURN(KMMCErrStatus) |
|
1236 #endif |
|
1237 |
|
1238 // Fall through if CURRENT_STATE is not PGM or DATA |
|
1239 SMF_STATE(EStRWFinish) |
|
1240 |
|
1241 if (TMMCStatus(s.ResponseP()).Error() != 0) |
|
1242 SMF_RETURN(KMMCErrStatus); |
|
1243 |
|
1244 s.iState &= ~KMMCSessStateInProgress; |
|
1245 |
|
1246 // skip over recursive entry or throw error and catch in CIMLockUnlockSM() |
|
1247 return (s.Command().iCommand == ECmdLockUnlock) ? KMMCErrUpdPswd : KMMCErrBypass; |
|
1248 |
|
1249 SMF_STATE(EStDone) |
|
1250 |
|
1251 __KTRACE_OPT(KPBUS1,Kern::Printf("<SD:RWBlocksSM()")); |
|
1252 |
|
1253 SMF_END |
|
1254 } |
|
1255 |
|
1256 EXPORT_C TMMCErr DSDStack::ModifyCardCapabilitySM() |
|
1257 // |
|
1258 // This function provides a chance to modify the capability of paticular cards. |
|
1259 // Licensee may overide this function to modify certain card's capability as needed. |
|
1260 // A state machine is needed in derived function and function of base class should be |
|
1261 // called in order to act more generic behaviour. |
|
1262 // |
|
1263 { |
|
1264 enum states |
|
1265 { |
|
1266 EStBegin=0, |
|
1267 EStDone, |
|
1268 EStEnd |
|
1269 }; |
|
1270 |
|
1271 //coverity[unreachable] |
|
1272 //Part of state machine design. |
|
1273 SMF_BEGIN |
|
1274 |
|
1275 SMF_INVOKES( DMMCStack::BaseModifyCardCapabilitySMST, EStDone ) |
|
1276 |
|
1277 SMF_STATE(EStDone) |
|
1278 |
|
1279 SMF_END |
|
1280 } |
|
1281 |
|
1282 inline TMMCErr DSDStack::SwitchToHighSpeedModeSMST( TAny* aStackP ) |
|
1283 { return( static_cast<DSDStack *>(aStackP)->DSDStack::SwitchToHighSpeedModeSM() ); } |
|
1284 |
|
1285 TMMCErr DSDStack::SwitchToHighSpeedModeSM() |
|
1286 { |
|
1287 enum states |
|
1288 { |
|
1289 EStBegin=0, |
|
1290 EstCheckController, |
|
1291 EStSendSCRCmd, |
|
1292 EStCheckSpecVer, |
|
1293 EStCheckFunction, |
|
1294 EStCheckFunctionSent, |
|
1295 EStSwitchFunctionSent, |
|
1296 EStDone, |
|
1297 EStEnd |
|
1298 }; |
|
1299 |
|
1300 __KTRACE_OPT(KPBUS1,Kern::Printf(">SD:SwitchToHighSpeedModeSM ")); |
|
1301 |
|
1302 DMMCSession& s = Session(); |
|
1303 |
|
1304 SMF_BEGIN |
|
1305 |
|
1306 SMF_STATE(EstCheckController) |
|
1307 // Get the clock speed supported by the controller |
|
1308 TMMCMachineInfoV4 machineInfo; |
|
1309 TMMCMachineInfoV4Pckg machineInfoPckg(machineInfo); |
|
1310 MachineInfo(machineInfoPckg); |
|
1311 |
|
1312 if (machineInfo.iVersion >= TMMCMachineInfoV4::EVersion4) |
|
1313 { |
|
1314 if (machineInfo.iMaxClockSpeedInMhz < (KSDDTClk50MHz/1000) ) |
|
1315 { |
|
1316 __KTRACE_OPT(KPBUS1, Kern::Printf("High speed mode not supported by controller")); |
|
1317 SMF_GOTOS(EStDone); |
|
1318 } |
|
1319 } |
|
1320 |
|
1321 SMF_STATE(EStSendSCRCmd) |
|
1322 // |
|
1323 // ACMD51 Read the SD Configuration Register |
|
1324 // |
|
1325 DSDSession::FillAppCommandDesc(Command(), ESDACmdSendSCR); |
|
1326 s.FillCommandArgs(0, KSDSCRLength, iPSLBuf, KSDSCRLength); |
|
1327 SMF_INVOKES(ExecCommandSMST, EStCheckSpecVer); |
|
1328 |
|
1329 SMF_STATE(EStCheckSpecVer) |
|
1330 // |
|
1331 // Check the SD version |
|
1332 // |
|
1333 // 0 : version 1.0-1.01 : SDHS Is NOT Supported |
|
1334 // 1 : version 1.10+ : SDHS Is Supported |
|
1335 // |
|
1336 __KTRACE_OPT(KPBUS1,Kern::Printf(" SD Configuration Register received")); |
|
1337 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...card_status=%x", TUint(s.iLastStatus))); |
|
1338 |
|
1339 #ifdef _DEBUG |
|
1340 for (TUint32 i = 0; i < KSDSCRLength; ++i) |
|
1341 { |
|
1342 __KTRACE_OPT(KPBUS1, Kern::Printf(" ...SCR_STATUS[0x%x] = %x", i, iPSLBuf[i])); |
|
1343 } |
|
1344 #endif |
|
1345 |
|
1346 if(iPSLBuf[0]==2) |
|
1347 { |
|
1348 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...SD Spec Version 2")); |
|
1349 SMF_GOTOS(EStCheckFunction); |
|
1350 } |
|
1351 |
|
1352 if(iPSLBuf[0]==1) |
|
1353 { |
|
1354 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...SD Spec Version 1.10")); |
|
1355 SMF_GOTOS(EStCheckFunction); |
|
1356 } |
|
1357 |
|
1358 if(iPSLBuf[0]==0) |
|
1359 { |
|
1360 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...SD Spec Version 1.01")); |
|
1361 SMF_GOTOS(EStDone); |
|
1362 } |
|
1363 |
|
1364 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...SD Spec Version > 2 !")); |
|
1365 |
|
1366 SMF_STATE(EStCheckFunction) |
|
1367 |
|
1368 m.SetTraps(KMMCErrResponseTimeOut | KMMCErrNotSupported); |
|
1369 |
|
1370 // |
|
1371 // SD1.1 uses CMD6 which is not defined by the MMCA |
|
1372 // - fill in command details using the SD Specific command description table |
|
1373 // |
|
1374 |
|
1375 DSDSession::FillSdSpecificCommandDesc(Command(), ESDCmdSwitchFunction); |
|
1376 s.FillCommandArgs(KSDCheckFunctionHighSpeed, KSDSwitchFuncLength, iPSLBuf, KSDSwitchFuncLength); |
|
1377 |
|
1378 SMF_INVOKES(IssueCommandCheckResponseSMST,EStCheckFunctionSent) |
|
1379 |
|
1380 SMF_STATE(EStCheckFunctionSent) |
|
1381 |
|
1382 __KTRACE_OPT(KPBUS1,Kern::Printf(" CheckFunctionSent %x",TUint(s.iLastStatus))); |
|
1383 |
|
1384 m.ResetTraps(); |
|
1385 |
|
1386 if(err == KMMCErrResponseTimeOut) |
|
1387 { |
|
1388 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...CMD6 [Read] Response Timeout")); |
|
1389 SMF_GOTOS(EStDone); |
|
1390 } |
|
1391 else if(err == KMMCErrNotSupported) |
|
1392 { |
|
1393 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...CMD6 [Read] Not Supported")); |
|
1394 SMF_GOTOS(EStDone); |
|
1395 } |
|
1396 |
|
1397 #ifdef _DEBUG |
|
1398 for (TUint32 i = 0; i < KSDSwitchFuncLength; ++i) |
|
1399 { |
|
1400 __KTRACE_OPT(KPBUS1, Kern::Printf(" ...SD Switch Func Status[0x%x] = %x", i, iPSLBuf[i])); |
|
1401 } |
|
1402 |
|
1403 m.SetTraps(KMMCErrResponseTimeOut); |
|
1404 #endif |
|
1405 |
|
1406 // |
|
1407 // SD1.1 uses CMD6 which is not defined by the MMCA |
|
1408 // - fill in command details using the SD Specific command description table |
|
1409 // |
|
1410 |
|
1411 DSDSession::FillSdSpecificCommandDesc(Command(), ESDCmdSwitchFunction); |
|
1412 s.FillCommandArgs(KSDSwitchFunctionHighSpeed, KSDSwitchFuncLength, iPSLBuf, KSDSwitchFuncLength); |
|
1413 |
|
1414 SMF_INVOKES(IssueCommandCheckResponseSMST,EStSwitchFunctionSent) |
|
1415 |
|
1416 SMF_STATE(EStSwitchFunctionSent) |
|
1417 |
|
1418 #ifdef _DEBUG |
|
1419 m.ResetTraps(); |
|
1420 |
|
1421 if(err == KMMCErrResponseTimeOut) |
|
1422 { |
|
1423 __KTRACE_OPT(KPBUS1,Kern::Printf(" ...CMD6 [Write] Response Timeout")); |
|
1424 } |
|
1425 |
|
1426 for (TUint32 i = 0; i < KSDSwitchFuncLength; ++i) |
|
1427 { |
|
1428 __KTRACE_OPT(KPBUS1, Kern::Printf(" ...SD Switch[0x%x] = %x", i, iPSLBuf[i])); |
|
1429 } |
|
1430 #endif |
|
1431 |
|
1432 SMF_STATE(EStDone) |
|
1433 |
|
1434 SMF_END |
|
1435 } |
|
1436 |
|
1437 |
|
1438 EXPORT_C DMMCSession* DSDStack::AllocSession(const TMMCCallBack& aCallBack) const |
|
1439 /** |
|
1440 * Factory function to create DMMCSession derived object. Non-generic MMC |
|
1441 * controllers can override this to generate more specific objects. |
|
1442 * @param aCallBack Callback function to notify the client that a session has completed |
|
1443 * @return A pointer to the new session |
|
1444 */ |
|
1445 { |
|
1446 return new DSDSession(aCallBack); |
|
1447 } |
|
1448 |
|
1449 EXPORT_C void DSDStack::Dummy1() {} |
|
1450 EXPORT_C void DSDStack::Dummy2() {} |
|
1451 EXPORT_C void DSDStack::Dummy3() {} |
|
1452 EXPORT_C void DSDStack::Dummy4() {} |