SdDisks.Mod 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. MODULE SdDisks;
  2. (**
  3. AUTHOR Timothée Martiel
  4. PURPOSE Disk driver for SD cards.
  5. *)
  6. IMPORT
  7. SYSTEM,
  8. Objects, Kernel, Plugins, Disks, Strings,
  9. Sd, SdEnvironment, KernelLog;
  10. CONST
  11. NameBase = "SD";
  12. BlockSize = 512; (** Size of a SD block *)
  13. CacheSize = 32; (** Number of entries in cache *)
  14. WBSize = 150; (** Number of entries in write buffer *)
  15. (*FlushPeriod = 5 * 1000;*)
  16. EnableCache* = FALSE;
  17. TYPE
  18. CacheEntry = RECORD
  19. buffer: POINTER TO ARRAY OF CHAR;
  20. ru: LONGINT;
  21. active: BOOLEAN;
  22. END;
  23. WriteBufferEntry = RECORD
  24. buffer: POINTER TO ARRAY OF CHAR;
  25. block, len: LONGINT;
  26. END;
  27. (**
  28. SD Card Disk Device
  29. *)
  30. Device * = OBJECT (Disks.Device)
  31. VAR
  32. card: Sd.Card;
  33. next: Device;
  34. timer: Kernel.Timer;
  35. cache: POINTER TO ARRAY OF CacheEntry;
  36. wbuffer: POINTER TO ARRAY OF WriteBufferEntry;
  37. bufferSize: LONGINT;
  38. head, size: LONGINT; (** Write buffer parameters *)
  39. stop: BOOLEAN;
  40. PROCEDURE & InitSdDevice (card: Sd.Card);
  41. VAR i: LONGINT;
  42. BEGIN
  43. SELF.card := card;
  44. blockSize := BlockSize;
  45. CASE card.sdStatus.speedClass OF
  46. 2, 4:
  47. IF card.csd.capacity > 1024*1024*1024 THEN
  48. bufferSize := 32 * 1024 DIV BlockSize
  49. ELSE
  50. bufferSize := 16 * 1024 DIV BlockSize
  51. END
  52. |6: bufferSize := 64 * 1024 DIV BlockSize
  53. |10: bufferSize := 512 * 1024 DIV BlockSize
  54. END;
  55. INCL(flags, Disks.Removable);
  56. IF EnableCache THEN
  57. NEW(cache, CacheSize);
  58. FOR i := 0 TO CacheSize - 1 DO NEW(cache[i].buffer, bufferSize * BlockSize) END;
  59. NEW(timer);
  60. NEW(wbuffer, WBSize);
  61. FOR i := 0 TO WBSize - 1 DO NEW(wbuffer[i].buffer, bufferSize * BlockSize) END;
  62. END;
  63. END InitSdDevice;
  64. PROCEDURE Transfer * (op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: LONGINT);
  65. VAR
  66. size: LONGINT;
  67. PROCEDURE TransferCached();
  68. VAR
  69. s, ru, ruIdx, ruOfs: LONGINT;
  70. entry: POINTER {UNSAFE} TO CacheEntry;
  71. wbentry: POINTER {UNSAFE} TO WriteBufferEntry;
  72. BEGIN
  73. (* Caching *)
  74. WHILE size > 0 DO
  75. ru := block DIV bufferSize;
  76. ruOfs := block MOD bufferSize;
  77. s := MIN(size, (bufferSize - ruOfs) * BlockSize);
  78. ruIdx := ru MOD CacheSize;
  79. IF ~UpdateCacheEntry(ru, ruIdx, res) THEN RETURN END;
  80. entry := ADDRESSOF(cache[ruIdx]);
  81. IF op = Disks.Write THEN
  82. SYSTEM.MOVE(ADDRESSOF(data[ofs]), ADDRESSOF(entry.buffer[ruOfs * BlockSize]), s);
  83. (*IF ru = wbuffer[(head + SELF.size - 1) MOD WBSize].block DIV bufferSize THEN
  84. wbentry := ADDRESSOF(wbuffer[(head + SELF.size - 1) MOD WBSize]);
  85. SYSTEM.MOVE(ADDRESSOF(data[ofs]), ADDRESSOF(wbentry.buffer[ruOfs * BlockSize]), s);
  86. INC(wbentry.len, s);
  87. ELSE*)
  88. AWAIT(SELF.size < WBSize);
  89. wbentry := ADDRESSOF(wbuffer[(head + SELF.size) MOD WBSize]);
  90. SYSTEM.MOVE(ADDRESSOF(data[ofs]), ADDRESSOF(wbentry.buffer[0]), s);
  91. wbentry.block := block;
  92. wbentry.len := s;
  93. INC(SELF.size)
  94. (*END*)
  95. ; INC(NbufferQueueSize, SELF.size); INC(NbufferQueueSamples);
  96. ELSE
  97. SYSTEM.MOVE(ADDRESSOF(entry.buffer[ruOfs * BlockSize]), ADDRESSOF(data[ofs]), s);
  98. END;
  99. DEC(size, s);
  100. INC(block, s DIV BlockSize);
  101. INC(ofs, s)
  102. END;
  103. END TransferCached;
  104. PROCEDURE TransferUncached();
  105. VAR
  106. ignore: BOOLEAN;
  107. BEGIN
  108. size := num * blockSize;
  109. CASE op OF
  110. Disks.Write:
  111. ignore := Sd.Write(card, block, size, data, ofs, res)
  112. |Disks.Read:
  113. ignore := Sd.Read(card, block, size, data, ofs, res)
  114. END
  115. END TransferUncached;
  116. BEGIN {EXCLUSIVE}
  117. size := num * blockSize;
  118. IF EnableCache THEN
  119. TransferCached;
  120. ELSE
  121. TransferUncached;
  122. END;
  123. END Transfer;
  124. PROCEDURE GetSize * (VAR size, res: LONGINT);
  125. BEGIN
  126. size := LONGINT(card.csd.capacity);
  127. IF size < 0 THEN size := MAX(LONGINT) END;
  128. res := Disks.Ok
  129. END GetSize;
  130. PROCEDURE Handle * (VAR msg: Disks.Message; VAR res: LONGINT);
  131. BEGIN
  132. res := 0;
  133. IF msg IS Disks.LockMsg THEN
  134. Sd.SetLedState(card.hc, TRUE)
  135. ELSIF msg IS Disks.UnlockMsg THEN
  136. Sd.SetLedState(card.hc, FALSE);
  137. IF EnableCache THEN Sync; END;
  138. ELSIF (msg IS Disks.EjectMsg) OR (msg IS Disks.SyncMsg) THEN
  139. IF EnableCache THEN Sync; END;
  140. END
  141. END Handle;
  142. (** Make sure that the RU ru is in cache entry idx *)
  143. PROCEDURE UpdateCacheEntry (ru, idx: LONGINT; VAR res: LONGINT): BOOLEAN;
  144. VAR entry: POINTER {UNSAFE} TO CacheEntry;
  145. BEGIN
  146. entry := ADDRESSOF(cache[idx]);
  147. IF ~entry.active OR (entry.ru # ru) THEN
  148. INC(NcacheMiss);
  149. IF entry.active THEN INC(NcacheEvict) END;
  150. (*IF entry.active & entry.mod THEN
  151. INC(NcacheWriteback);
  152. IF ~Sd.Write(card, entry.ru * bufferSize, bufferSize * BlockSize, entry.buffer^, 0, res) THEN RETURN FALSE END
  153. END;*)
  154. entry.active := TRUE;
  155. (*entry.mod := FALSE;*)
  156. entry.ru := ru;
  157. IF ~Sd.Read(card, ru * bufferSize, bufferSize * BlockSize, entry.buffer^, 0, res) THEN RETURN FALSE END
  158. ELSE
  159. INC(NcacheHits)
  160. END;
  161. RETURN TRUE
  162. END UpdateCacheEntry;
  163. (** Write back all modified cache entries to disk and invalidate all entries *)
  164. PROCEDURE Sync;
  165. VAR
  166. i, len, res, ofs: LONGINT;
  167. wbentry, wbnext: POINTER {UNSAFE} TO WriteBufferEntry;
  168. ignore: BOOLEAN;
  169. BEGIN {EXCLUSIVE}
  170. wbentry := ADDRESSOF(wbuffer[head MOD WBSize]);
  171. i := 1;
  172. len := wbentry.len;
  173. LOOP
  174. IF i = size THEN EXIT END;
  175. wbnext := ADDRESSOF(wbuffer[(head + i) MOD WBSize]);
  176. IF wbentry.block + len DIV BlockSize # wbnext.block THEN EXIT END;
  177. ofs := wbnext.block MOD bufferSize;
  178. SYSTEM.MOVE(ADDRESSOF(wbnext.buffer[ofs]), ADDRESSOF(wbentry.buffer[ofs]), wbnext.len);
  179. INC(len, wbnext.len);
  180. INC(i)
  181. END;
  182. ignore := Sd.Write(card, wbentry.block, len, wbentry.buffer^, 0, res);
  183. INC(head, i);
  184. DEC(size, i);
  185. INC(NbufferWrites);
  186. INC(NbufferSize, len)
  187. END Sync;
  188. PROCEDURE Stop;
  189. BEGIN {EXCLUSIVE}
  190. stop := TRUE
  191. END Stop;
  192. BEGIN {ACTIVE, PRIORITY(Objects.Normal)}
  193. LOOP
  194. BEGIN {EXCLUSIVE}
  195. AWAIT(stop OR (size > 0));
  196. IF stop THEN EXIT END;
  197. END;
  198. IF EnableCache THEN Sync; END;
  199. END;
  200. IF EnableCache THEN Sync; END;
  201. END Device;
  202. (** Handle SD Controller Events: create & register a new disk on card insertion, remove disk on card removal *)
  203. PROCEDURE HandleSdEvent * (card: Sd.Card; event: LONGINT);
  204. VAR
  205. disk, prev: Device;
  206. name, id: ARRAY 32 OF CHAR;
  207. result: LONGINT;
  208. BEGIN
  209. CASE event OF
  210. Sd.OnInitialization:
  211. NEW(disk, card);
  212. Strings.IntToStr(diskId, id);
  213. name := NameBase;
  214. Strings.Append(name, id);
  215. disk.SetName(name);
  216. disk.desc := "SD";
  217. CASE card.scr.security OF
  218. Sd.TypeNone, Sd.TypeSDSC:
  219. |Sd.TypeSDHC: Strings.Append(disk.desc, "HC")
  220. |Sd.TypeSDXC: Strings.Append(disk.desc, "XC")
  221. ELSE
  222. Strings.Append(disk.desc, "??");
  223. KernelLog.String("[SD] unknown card type: "); KernelLog.Int(card.scr.security, 0); KernelLog.Ln;
  224. END;
  225. Strings.Append(disk.desc, " card, v");
  226. CASE card.scr.version OF
  227. Sd.Version1: Strings.Append(disk.desc, "1")
  228. |Sd.Version1p1: Strings.Append(disk.desc, "1.10")
  229. |Sd.Version2: Strings.Append(disk.desc, "2")
  230. |Sd.Version3: Strings.Append(disk.desc, "3")
  231. |Sd.Version4: Strings.Append(disk.desc, "4")
  232. |Sd.Version5: Strings.Append(disk.desc, "5")
  233. |Sd.Version6: Strings.Append(disk.desc, "6")
  234. ELSE
  235. Strings.Append(disk.desc, "?");
  236. KernelLog.String("[SD] unknown card version: "); KernelLog.Int(card.scr.version, 0); KernelLog.Ln;
  237. END;
  238. Disks.registry.Add(disk, result);
  239. IF result # Plugins.Ok THEN
  240. SdEnvironment.String("Error registering disk");
  241. SdEnvironment.Ln
  242. ELSE
  243. INC(diskId);
  244. disk.next := devices;
  245. devices := disk;
  246. SdEnvironment.String("Disk ");
  247. SdEnvironment.String(name);
  248. SdEnvironment.String(" is now available");
  249. SdEnvironment.Ln
  250. END
  251. |Sd.OnRemoval:
  252. ASSERT(devices # NIL);
  253. IF devices.card = card THEN
  254. devices.Stop;
  255. SdEnvironment.String("Removed disk ");
  256. SdEnvironment.String(devices.name);
  257. SdEnvironment.Ln;
  258. devices := devices.next
  259. ELSE
  260. disk := devices;
  261. WHILE (disk # NIL) & (disk.card # card) DO
  262. prev := disk;
  263. disk := disk.next
  264. END;
  265. ASSERT(disk # NIL);
  266. disk.Stop;
  267. SdEnvironment.String("Removed disk ");
  268. SdEnvironment.String(disk.name);
  269. SdEnvironment.Ln;
  270. prev.next := disk.next
  271. END;
  272. END
  273. END HandleSdEvent;
  274. VAR
  275. devices: Device;
  276. diskId: LONGINT;
  277. (* Statistics *)
  278. NcacheHits *, NcacheMiss *, NcacheEvict *, NbufferWrites *, NbufferSize *, NbufferQueueSize *, NbufferQueueSamples *: LONGINT;
  279. PROCEDURE ResetStats *;
  280. BEGIN
  281. NcacheHits := 0;
  282. NcacheMiss := 0;
  283. NcacheEvict := 0;
  284. NbufferWrites := 0;
  285. NbufferSize := 0
  286. END ResetStats;
  287. END SdDisks.