#include #include #include #include #include #include #include #include #include "spi.h" #include "cpu.h" #include "inih/INIReader.h" #ifndef SPIVERBOSE # define SPIVERBOSE 0 #endif namespace Spi { // ---- bus composition static Device *spi0 = 0; static Device *spi1 = 0; void clock(uint16_t b, uint16_t a) { if (spi0) spi0->clock(b, a); if (spi1) spi1->clock(b, a); } bool config(INIReader &reader, const std::string §ionString) { if (sectionString == "SD0") { spi0 = new SDCard(0); return spi0->config(reader, sectionString); } if (sectionString == "SD1") { spi1 = new SDCard(1); return spi1->config(reader, sectionString); } if (sectionString == "MCP1") { fprintf(stderr, "Spi::Device: MCP device is not yet supported\n"); } return false; } // ---- generic device Device::Device(int num) : cs((num >= 0 && num < 4) ? (0x4 << num) : 0), mask(0) { if (num < 0 || num > 3) fprintf(stderr, "Spi::Device: port number must be in range 0..3\n"); } uint8_t Device::spiselect(void) { // first byte returned after selecting the device return 0xff; } void Device::clock(uint16_t b, uint16_t a) { bool selected = (cs && !(a & cs)); // cs was just asserted if (selected && (b & cs)) { #if SPIVERBOSE fprintf(stderr, "sdi%d: selected\n", ffs(cs)-3); #endif mask = 0x80; miso_byte = spiselect(); Cpu::setXIN(miso_byte & mask ? 0xf : 0); } #if SPIVERBOSE if (!selected && !(b & cs)) fprintf(stderr, "sdi%d: deselected\n", ffs(cs)-3); #endif // clock change if (selected && ((a ^ b) & 1)) { if (a & 1) { // clock rising (latch) if (a & 0x8000) mosi_byte |= mask; else mosi_byte &= ~mask; } else { // clock falling (shift) if (! (mask >>= 1)) { mask = 0x80; #if SPIVERBOSE fprintf(stderr,"sdi%d: sent 0x%02x recv 0x%02x\n", ffs(cs)-3, miso_byte, mosi_byte); #endif miso_byte = spibyte(mosi_byte); } Cpu::setXIN((miso_byte & mask) ? 0xf : 0); } } } // ---- sdcard device static uint8_t crc7(const uint8_t *ptr, uint32_t count) { int i; uint8_t crc = 0; while (count--) { crc = crc ^ *ptr++; for (i=0; i<8; i++) { if (crc & 0x80) crc ^= 0x89; crc = (crc << 1); } } // byte { crc7, 1 } return crc | 1; } static uint16_t crc16(const uint8_t *ptr, uint32_t count) { int i; uint16_t crc = 0; while (count--) { crc = crc ^ (((uint16_t)(*ptr++)) << 8); for (i=0; i<8; i++) { if (crc & 0x8000) crc = (crc << 1) ^ 0x1021; else crc = (crc << 1); } } return crc; } SDCard::SDCard(int num) : Device(num), idle(1), state(0), count(0), len(0), fd(0) { buffer = new uint8_t[512]; } SDCard::~SDCard() { delete [] buffer; if (fd) fclose((FILE*)(fd)); } bool SDCard::config(INIReader &reader, const std::string §ion) { std::string result; std::map typemap; typemap["NONE"] = NONE; typemap["MMC"] = MMC; typemap["SDSC"] = SDSC; typemap["SDHC"] = SDHC; type = NONE; result = reader.Get(section, "Type", "NONE"); if (typemap.find(result) == typemap.end()) fprintf(stderr, "Spi::SDCard: card type must be one of NONE, MMC, SDSC, SDHC\n"); else type = typemap[result]; if (type != NONE) { filename = reader.Get(section, "Filename", std::string()); if (filename.empty()) { fprintf(stderr, "Spi:SDCard: no filename\n"); type = NONE; } } if (type != NONE) { fd = (File*)fopen(filename.c_str(), "rb+"); if (! fd) { fprintf(stderr, "Spi::SDCard: cannot open file: %s\n", filename.c_str()); type = NONE; } } if (type != NONE && fd) { filelen = 0; #ifdef _MSC_VER if (_fseeki64((FILE*)fd, 0, SEEK_END) >= 0) filelen = (long long)_ftelli64((FILE*)fd); #elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L if (fseeko((FILE*)fd, 0, SEEK_END) >= 0) filelen = (long long)ftello((FILE*)fd); #else if (fseek((FILE*)fd, 0, SEEK_END) >= 0) filelen = (long long)ftell((FILE*)fd); #endif if (filelen <= 0) { fprintf(stderr, "Spi::SDCard: cannot measure file size: %s\n", filename.c_str()); fclose((FILE*)fd); type = NONE; fd = 0; } else if (filelen % (256 * 1024)) { fprintf(stderr, "Spi::SDCard: file size should be a multiple of 256K: %s\n", filename.c_str()); fclose((FILE*)fd); type = NONE; fd = 0; } } return true; } uint8_t SDCard::spiselect(void) { // This function is called whenever the spi port is selected (/SSx // falling edge). Its return value is the byte transmitted to the // host during the next 8 clock cycles. // In the case of a SD card, it also resets the state to CMD // (listening for a command) except during a long action (during a // read) or when the state is INIT or APPCMD which are also // listening for a command (but while uninitialized or after a // CMD55.) Context ctx = context(); if (action() == BUSY) return 0x00; if (ctx != INIT && ctx != APPCMD) ctx = CMD; set_wait_state(ctx); return 0xff; } uint8_t SDCard::spibyte(uint8_t in) { // This function is called whenever a byte is exchanged. Its // argument is the byte received by the slave. Its return value // is the next byte to be sent by the slave. // In the case of a SD card, what happens depends on the // card state represented by the (context,action) pair. Context c = context(); Action a = action(); // consistent action behavior switch(a) { case WAIT: if (in == 0xff || type == NONE) // waiting for master data return 0xff; break; case RECV: buffer[count] = in; if (++count < len) return 0xff; break; case SEND: if (c == READM && in == 64 + 12) // received CMD12! break; if (count < len) return buffer[count++]; break; case BUSY: // busy state for len bytes if (count++ < len) return 0; break; } switch(c) { case INIT: case CMD: case APPCMD: { if (a == WAIT) // got first byte set_recv_state(c, 6, in); else if (a == SEND || a == BUSY) // just sent reply set_wait_state(c); else // got command sdcommand(c); return 0xff; } case REG: { assert(a == SEND); buffer[0] = 0xfe; buffer[16] = crc7(buffer+1, 15); uint16_t crc = crc16(buffer+1, 16); buffer[17] = (crc >> 8); buffer[18] = (crc & 0xff); set_send_state(CMD, 16+3); return 0xff; } case READ: { assert(a == SEND); if (! read_data()) set_send_r1_state(CMD, 9); // send error token else set_send_state(CMD, 512+3); return 0xff; } case READM: { assert(a == SEND); if (in == 64 + 12) set_recv_state(CMD, 6, in); else if (! read_data()) set_send_r1_state(CMD, 9); // send error token else set_send_state(READM, 512+3); offset += 512; return 0xff; } case WRITE: { assert (a == SEND); set_wait_state(WRITE1); return 0xff; } case WRITE1: { if (a == WAIT) { set_recv_state(WRITE1, 512+3, in); } else { set_busy_state(CMD, 4); if (buffer[0] == 0xfe && write_data()) return 0x5; else return 0xd; } return 0xff; } case WRITEM: { assert(a == SEND || a == BUSY); set_wait_state(WRITEM1); return 0xff; } case WRITEM1: { if (a == WAIT && in == 0xfd) { set_busy_state(CMD, 4); // stop tran } else if (a == WAIT) { set_recv_state(WRITEM1, 512+3, in); } else if (buffer[0] == 0xfc && write_data()) { offset += 512; set_busy_state(WRITEM, 4); return 0x5; } else { set_busy_state(CMD, 4); return 0xd; } return 0xff; } default: break; } set_send_r1_state(CMD, 4); return 0xff; } void SDCard::sdcommand(Context context) { int cmd = buffer[0] & 0x3f; if (context == INIT && memcmp(buffer, "\x40\0\0\0\0\x95", 6)) { // In the init state, the only accepted command is command 0 set_send_r1_state(INIT, 5); return; } if (buffer[0] != cmd + 64) { set_send_r1_state(CMD, 5); return; } if (context == APPCMD) { cmd += 128; } if (idle) { if (cmd != 0 && cmd != 1 && cmd != 8 && cmd != 128+41 && cmd != 55 && cmd != 58) cmd = 0xff; } switch(cmd) { case 128+41: // ACMD41: APP_SEND_OP_COND { if (type < SDSC) set_send_r1_state(CMD, 4 + idle); else { idle = 0; set_send_r1_state(CMD, 0); } break; } case 128+23: // ACMD22: SET_WR_BLOCK_ERASE_COUNT { // ignored but not illegal set_send_r1_state(CMD, 0); break; } case 0: // CMD0: GO_IDLE_STATE { idle = 1; set_send_r1_state(CMD, idle); break; } case 1: // CMD1: SEND_OP_COND { idle = 0; set_send_r1_state(CMD, 0); break; } case 8: // CMD8: SEND_IF_COND { buffer[0] = idle; if (type < SDSC) set_send_r1_state(CMD, 4 + idle); else set_send_state(CMD, 5); break; } case 9: // CMD9: SEND_CSD { long size = filelen / (512 * 1024); if (type >= SDHC) { //1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 memcpy(buffer, "\0" "\x40\x0e\x00\x32\x5b\x59\x00\x00\x00\x00\x7f\x80\x0a\x40\x40\xf1" "XX", 16 + 3); buffer[10] = (size & 0xff); buffer[9] = (size & 0xff00) >> 8; buffer[8] = (size & 0xcf0000) >> 16; } else { memcpy(buffer, "\0" "\x00\x0e\x00\x32\x5b\x59\x00\x00\x00\x00\x7f\x80\x0a\x40\x40\xf1" "XX", 16 + 3); buffer[7] |= (size & 0xc00) >> 10; buffer[8] |= (size & 0x3fc) >> 2; buffer[9] |= (size & 0x3) << 6; } set_send_state(REG, 1); break; } case 10: // CMD10: SEND_CID { memcpy(buffer, "\0" "\xbbSD00000\x11\0\0\0\0\0\1\xf1" "XX", 16+3); set_send_state(REG, 1); break; } case 12: // CMD12: STOP_TRANSMISSION { set_busy_state(CMD, 3); break; } case 16: // CMD16: SET_BLOCK_LENGTH { long bl = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]); if (bl != 512) set_send_r1_state(CMD, 64); else set_send_r1_state(CMD, 0); break; } case 17: // CMD17: READ_SINGLE_BLOCK { offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]); if (type >= SDHC) offset *= 512; if (offset > filelen - 512) set_send_r1_state(CMD, 64); else set_send_r1_state(READ, 0); break; } case 18: // CMD18: READ_MULTIPLE_BLOCK { offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]); if (type >= SDHC) offset *= 512; if (offset > filelen - 512) set_send_r1_state(CMD, 64); else set_send_r1_state(READM, 0); break; } case 23: // CMD23: SET_BLOCK_COUNT (MMC only) { set_send_r1_state(CMD, (type == MMC) ? 64 : 4); // unsupported break; } case 24: // CMD24: WRITE_SINGLE_BLOCK { offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]); if (type >= SDHC) offset *= 512; if (offset > filelen - 512) set_send_r1_state(CMD, 64); else set_send_r1_state(WRITE, 0); break; } case 25: // CMD18: WRITE_MULTIPLE_BLOCK { offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]); if (type >= SDHC) offset *= 512; if (offset > filelen - 512) set_send_r1_state(CMD, 64); else set_send_r1_state(WRITEM, 0); break; } case 55: // CMD55: APP_CMD { set_send_r1_state(APPCMD, idle); break; } case 58: // CMD58: READ_OCR { buffer[0] = 0; buffer[1] = (type >= SDHC) ? 0x40 : 0; buffer[2] = 0xff; buffer[3] = 0x80; buffer[4] = 0; set_send_state(CMD, 5); break; } default: { set_send_r1_state(CMD, 4 + idle); break; } } } bool SDCard::read_data() { uint16_t crc; #if SPIVERBOSE fprintf(stderr, "sdi0: reading block at offset 0x%llx\n", offset); #endif #ifdef _MSC_VER if (fd == 0 || offset != (long long)(__int64)offset || _fseeki64((FILE*)fd, (__int64)offset, SEEK_SET) < 0) return false; #elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L if (fd == 0 || offset != (long long)(off_t)offset || fseeko((FILE*)fd, (off_t)offset, SEEK_SET) < 0) return false; #else if (fd == 0 || offset != (long long)(long)offset || fseek((FILE*)fd, (long)offset, SEEK_SET) < 0) return false; #endif if (fread((void*)(buffer+1), 1, 512, (FILE*)fd) != 512) return false; crc = crc16(buffer+1, 512); buffer[0] = 0xfe; buffer[513] = (crc >> 8); buffer[514] = (crc & 0xff); return true; } bool SDCard::write_data() { #if SPIVERBOSE fprintf(stderr, "sdi0: writing block at offset 0x%llx\n", offset); #endif #ifdef _MSC_VER if (fd == 0 || offset != (long long)(__int64)offset || _fseeki64((FILE*)fd, (__int64)offset, SEEK_SET) < 0) return false; #elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L if (fd == 0 || offset != (long long)(off_t)offset || fseeko((FILE*)fd, (off_t)offset, SEEK_SET) < 0) return false; #else if (fd == 0 || offset != (long long)(long)offset || fseek((FILE*)fd, (long)offset, SEEK_SET) < 0) return false; #endif if (fwrite((void*)(buffer+1), 1, 512, (FILE*)fd) != 512) return false; return true; } } // namespace Spi