Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
5.2KB

  1. #include <stdio.h>
  2. #include "sdc.h"
  3. // Add data to crc with polynomial 0x1021
  4. // https://stackoverflow.com/a/23726131
  5. static uint16_t crc16(uint16_t crc, uint8_t data)
  6. {
  7. uint8_t x = crc >> 8 ^ data;
  8. x ^= x>>4;
  9. crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x);
  10. return crc;
  11. }
  12. void sdc_init(SDC *sdc)
  13. {
  14. sdc->selected = false;
  15. sdc->initstat = 0;
  16. sdc->recvidx = 0;
  17. sdc->sendidx = -1;
  18. sdc->resp = 0xff;
  19. sdc->fp = NULL;
  20. sdc->cmd17bytes = -1;
  21. sdc->cmd24bytes = -2;
  22. }
  23. // TODO: for now, any nonzero value enables the SDC. To allow
  24. // emulation of systems with multi-devices SPI relay, change
  25. // this.
  26. void sdc_ctl_wr(SDC *sdc, uint8_t val)
  27. {
  28. sdc->selected = val;
  29. }
  30. void sdc_spi_wr(SDC *sdc, uint8_t val)
  31. {
  32. if (!sdc->selected) {
  33. return;
  34. }
  35. sdc->resp = 0xff;
  36. if (sdc->initstat < 8) {
  37. // not woken yet.
  38. sdc->initstat++;
  39. return;
  40. }
  41. if (sdc->sendidx >= 0) {
  42. sdc->resp = sdc->sendbuf[sdc->sendidx++];
  43. if (sdc->sendidx == 5) {
  44. sdc->sendidx = -1;
  45. }
  46. return;
  47. }
  48. if (sdc->cmd17bytes >= 0) {
  49. if (sdc->fp) {
  50. sdc->resp = getc(sdc->fp);
  51. }
  52. sdc->crc16 = crc16(sdc->crc16, sdc->resp);
  53. sdc->cmd17bytes++;
  54. if (sdc->cmd17bytes == 512) {
  55. sdc->sendbuf[3] = sdc->crc16 >> 8;
  56. sdc->sendbuf[4] = sdc->crc16 & 0xff;
  57. sdc->sendidx = 3;
  58. sdc->cmd17bytes = -1;
  59. }
  60. return;
  61. }
  62. if (sdc->cmd24bytes == -1) {
  63. if (val == 0xff) {
  64. // it's ok to receive idle bytes before the data token.
  65. return;
  66. }
  67. if (val == 0xfe) {
  68. // data token, good
  69. sdc->cmd24bytes = 0;
  70. } else {
  71. // something is wrong, cancel cmd24
  72. sdc->cmd24bytes = -2;
  73. }
  74. return;
  75. }
  76. if (sdc->cmd24bytes >= 0) {
  77. if (sdc->cmd24bytes < 512) {
  78. if (sdc->fp) {
  79. putc(val, sdc->fp);
  80. }
  81. sdc->crc16 = crc16(sdc->crc16, val);
  82. } else if (sdc->cmd24bytes == 512) {
  83. // CRC MSB
  84. if (val == (sdc->crc16>>8)) {
  85. fprintf(stderr, "Good CRC16 MSB\n");
  86. } else {
  87. fprintf(stderr, "Bad CRC16 MSB\n");
  88. }
  89. } else {
  90. if (val == (sdc->crc16&0xff)) {
  91. fprintf(stderr, "Good CRC16 LSB\n");
  92. } else {
  93. fprintf(stderr, "Bad CRC16 LSB\n");
  94. }
  95. // valid response for CMD24
  96. sdc->sendbuf[4] = 0x05;
  97. sdc->sendidx = 4;
  98. sdc->cmd24bytes = -3;
  99. }
  100. sdc->cmd24bytes++;
  101. return;
  102. }
  103. if ((sdc->recvidx == 0) && ((val > 0x7f) || (val < 0x40))) {
  104. // not a command
  105. return;
  106. }
  107. sdc->recvbuf[sdc->recvidx++] = val;
  108. if (sdc->recvidx < 6) {
  109. // incomplete command
  110. return;
  111. }
  112. // Command complete
  113. val &= 0x3f;
  114. sdc->recvidx = 0;
  115. uint8_t *b = sdc->recvbuf;
  116. uint8_t cmd = b[0] & 0x3f;
  117. uint16_t arg1 = (b[1] << 8) | b[2];
  118. uint16_t arg2 = (b[3] << 8) | b[4];
  119. // printf("cmd %02x %04x %04x\n", cmd, arg1, arg2);
  120. if (sdc->initstat == 8) {
  121. // At this stage, we're expecting CMD0
  122. if (cmd == 0) {
  123. sdc->initstat++;
  124. sdc->sendbuf[4] = 0x01;
  125. sdc->sendidx = 4;
  126. }
  127. return;
  128. }
  129. if (sdc->initstat == 9) {
  130. // At this stage, we're expecting CMD8 with 0x1aa arg2
  131. if ((cmd == 8) && (arg2 == 0x01aa)) {
  132. sdc->initstat++;
  133. sdc->sendbuf[0] = 0x01;
  134. sdc->sendbuf[1] = 0;
  135. sdc->sendbuf[2] = 0;
  136. sdc->sendbuf[3] = 0x01;
  137. sdc->sendbuf[4] = 0xaa;
  138. sdc->sendidx = 0;
  139. } else {
  140. sdc-> initstat = 8;
  141. }
  142. return;
  143. }
  144. if (sdc->initstat == 10) {
  145. // At this stage, we're expecting CMD55
  146. if (cmd == 55) {
  147. sdc->initstat++;
  148. sdc->sendbuf[4] = 0x01;
  149. sdc->sendidx = 4;
  150. } else {
  151. sdc->initstat = 8;
  152. }
  153. return;
  154. }
  155. if (sdc->initstat == 11) {
  156. // At this stage, we're expecting CMD41
  157. if ((cmd == 41) && (arg1 == 0x4000)) {
  158. sdc->initstat++;
  159. sdc->sendbuf[4] = 0x00;
  160. sdc->sendidx = 4;
  161. } else {
  162. sdc->initstat = 8;
  163. }
  164. return;
  165. }
  166. // We have a fully initialized card.
  167. if (cmd == 17) {
  168. if (sdc->fp) {
  169. fseek(sdc->fp, arg2*512, SEEK_SET);
  170. }
  171. sdc->sendbuf[3] = 0x00;
  172. // data token
  173. sdc->sendbuf[4] = 0xfe;
  174. sdc->sendidx = 3;
  175. sdc->cmd17bytes = 0;
  176. sdc->crc16 = 0;
  177. return;
  178. }
  179. if (cmd == 24) {
  180. if (sdc->fp) {
  181. fseek(sdc->fp, arg2*512, SEEK_SET);
  182. }
  183. sdc->sendbuf[4] = 0x00;
  184. sdc->sendidx = 4;
  185. sdc->cmd24bytes = -1;
  186. sdc->crc16 = 0;
  187. return;
  188. }
  189. // Simulate success for any unknown command.
  190. sdc->sendbuf[4] = 0x00;
  191. sdc->sendidx = 4;
  192. }
  193. uint8_t sdc_spi_rd(SDC *sdc)
  194. {
  195. if (!sdc->selected) {
  196. return 0xff;
  197. }
  198. return sdc->resp;
  199. }