1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 //  This file is licensed under the MIT License.
5 //  Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Linq;
9 using Antmicro.Renode.Debugging;
10 using Antmicro.Renode.Utilities;
11 
12 namespace Antmicro.Renode.Peripherals.SD
13 {
14     public static class SDHelpers
15     {
TypeFromCapacity(ulong capacity)16         public static CardType TypeFromCapacity(ulong capacity)
17         {
18             if(capacity <= 2.GB())
19             {
20                 return CardType.StandardCapacity_SC;
21             }
22             else if(capacity <= 32.GB())
23             {
24                 return CardType.HighCapacity_HC;
25             }
26             else if(capacity <= 2.TB())
27             {
28                 return CardType.ExtendedCapacity_XC;
29             }
30             else
31             {
32                 throw new ArgumentException($"Unexpected capacity: {capacity}");
33             }
34         }
35 
BlockLengthInBytes(BlockLength blockSize)36         public static uint BlockLengthInBytes(BlockLength blockSize)
37         {
38             return blockSize == BlockLength.Undefined
39                ? 0
40                : (uint)(1 << (int)blockSize);
41         }
42 
SeekForCapacityParameters(long capacity, BlockLength blockSize = BlockLength.Undefined)43         public static SDCapacityParameters SeekForCapacityParameters(long capacity, BlockLength blockSize = BlockLength.Undefined)
44         {
45             DebugHelper.Assert((blockSize == BlockLength.Undefined) || (capacity % BlockLengthInBytes(blockSize) == 0));
46 
47             if(blockSize != BlockLength.Undefined)
48             {
49                 if(TryFindParameters(capacity, blockSize, out var result))
50                 {
51                     return result;
52                 }
53             }
54             else
55             {
56                 foreach(BlockLength possibleBlockSize in Enum.GetValues(typeof(BlockLength)))
57                 {
58                     if(possibleBlockSize == BlockLength.Undefined)
59                     {
60                         continue;
61                     }
62 
63                     if(TryFindParameters(capacity, possibleBlockSize, out var result))
64                     {
65                         return result;
66                     }
67                 }
68             }
69 
70             // We should never reach here.
71             throw new InvalidOperationException($"Could not calculate capacity parameters for given arguments: capacity={capacity}, blockSize={blockSize}");
72         }
73 
TryFindParameters(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)74         private static bool TryFindParameters(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)
75         {
76             switch(TypeFromCapacity((ulong)capacity))
77             {
78                 case CardType.StandardCapacity_SC:
79                     return TryFindParametersStandardCapacity(capacity, blockSize, out parameters);
80 
81                 case CardType.HighCapacity_HC:
82                 case CardType.ExtendedCapacity_XC:
83                     return TryFindParametersHighCapacity(capacity, blockSize, out parameters);
84 
85                 default:
86                     // unsupported type
87                     parameters = default(SDCapacityParameters);
88                     return false;
89             }
90         }
91 
TryFindParametersStandardCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)92         private static bool TryFindParametersStandardCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)
93         {
94             // cSize has only 12 bits, hence the limit
95             const int MaxCSizeValue = (1 << 12) - 1;
96 
97             var numberOfBlocks = (int)(capacity >> (int)blockSize);
98 
99             foreach(var multiplierEncoded in Enum.GetValues(typeof(SizeMultiplier))
100                     .Cast<SizeMultiplier>()
101                     .Where(x => x != SizeMultiplier.Undefined)
102                     .OrderByDescending(x => (int)x))
103             {
104                 var multiplierValue = 1 << ((int)multiplierEncoded + 2);
105                 if(multiplierValue > numberOfBlocks)
106                 {
107                     // we are looking for the highest possible multiplier that is lower-or-equal than numberOfBlocks
108                     continue;
109                 }
110 
111                 var cSize = (long)(numberOfBlocks / multiplierValue) - 1;
112                 if(cSize > MaxCSizeValue)
113                 {
114                     // if for the highest possible multiplier cSize is still too high,
115                     // checking smaller multipliers won't help; we can back-off immediately
116                     break;
117                 }
118 
119                 parameters = new SDCapacityParameters(blockSize, multiplierEncoded, cSize);
120                 return true;
121             }
122 
123             parameters = default(SDCapacityParameters);
124             return false;
125         }
126 
TryFindParametersHighCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)127         private static bool TryFindParametersHighCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)
128         {
129             // block sizes other than 512 are not allowed in the HC/XC modes
130             if(blockSize != BlockLength.Block512)
131             {
132                 parameters = default(SDCapacityParameters);
133                 return false;
134             }
135 
136             var cSize = (capacity / 1024 / 512) - 1;
137 
138             // for HC/XC cards multiplier is not used
139             parameters = new SDCapacityParameters(BlockLength.Block512, SizeMultiplier.Undefined, cSize);
140             return true;
141         }
142     }
143 
144     public enum SizeMultiplier
145     {
146         Multiplier4 = 0,
147         Multiplier8 = 1,
148         Multiplier16 = 2,
149         Multiplier32 = 3,
150         Multiplier64 = 4,
151         Multiplier128 = 5,
152         Multiplier256 = 6,
153         Multiplier512 = 7,
154         // other values are reserved
155         Undefined = int.MaxValue
156     }
157 
158     public enum CardType
159     {
160         StandardCapacity_SC,
161         HighCapacity_HC,
162         ExtendedCapacity_XC
163     }
164 
165     public enum BlockLength
166     {
167         Block512 = 9,
168         Block1024 = 10,
169         Block2048 = 11,
170         // other values are reserved
171         Undefined = int.MaxValue
172     }
173 
174     public struct SDCapacityParameters
175     {
SDCapacityParametersAntmicro.Renode.Peripherals.SD.SDCapacityParameters176         public SDCapacityParameters(BlockLength blockSize, SizeMultiplier multiplier, long deviceSize)
177         {
178             this.BlockSize = blockSize;
179             this.Multiplier = multiplier;
180             this.DeviceSize = deviceSize;
181         }
182 
ToStringAntmicro.Renode.Peripherals.SD.SDCapacityParameters183         public override string ToString()
184         {
185             return $"[BlockSize={BlockSize}, Multiplier={Multiplier}, DeviceSize={DeviceSize}, MemoryCapacity={MemoryCapacity}]";
186         }
187 
188         public BlockLength BlockSize { get; }
189         public SizeMultiplier Multiplier { get; }
190         public long DeviceSize { get; }
191 
192         public long MemoryCapacity
193         {
194             get
195             {
196                 // HC/XC SD cards do not use Multiplier to describe size
197                 // but calculate the it by simply multiplying `DeviceSize + 1` by 512Kb;
198                 // we internally mark this by setting Multipler to Undefined
199                 if(Multiplier == SizeMultiplier.Undefined)
200                 {
201                     DebugHelper.Assert(BlockSize == BlockLength.Block512);
202                     return (DeviceSize + 1) * 512.KB();
203                 }
204                 else
205                 {
206                     return (DeviceSize + 1) * (1L << ((int)Multiplier + (int)BlockSize + 2));
207                 }
208             }
209         }
210 
211 
212     }
213 }
214