//#define USB_SERIAL_PRIVATE_INCLUDE //#include "usb_mass_storage_debug.h" //#include "pauls_ugly_debug.h" #include "core_pins.h" // for delayMicroseconds inline uint32_t media_size(void); void media_init(void); uint8_t media_lock(void); void media_unlock(void); void media_poll(void); static void media_send_begin(uint32_t lba); static void media_send_chunk(uint32_t lba, uint8_t chunk); static void media_send_end(void); static void media_receive_begin(uint32_t lba); static void media_receive_chunk(uint32_t lba, uint8_t chunk); static void media_receive_end(void); /************************************************************************** * * Storage Media Access Functions * **************************************************************************/ #define SPI_PORT PORTB #define SPI_DDR DDRB #define SPI_SS_PIN 0 #define SPI_SCLK_PIN 1 #define SPI_MOSI_PIN 2 #define SPI_MISO_PIN 3 #define SD_CMD_GO_IDLE_STATE 0x4095 #define SD_CMD_SEND_OP_COND 0x41FF #define SD_CMD_SEND_IF_COND 0x4887 #define SD_CMD_SEND_CSD 0x49FF #define SD_CMD_STOP_TRANSMISSION 0x4CFF #define SD_CMD_READ_SINGLE_BLOCK 0x51FF #define SD_CMD_READ_MULTIPLE_BLOCK 0x52FF #define SD_CMD_WRITE_SINGLE_BLOCK 0x58FF #define SD_CMD_WRITE_MULTIPLE_BLOCK 0x59FF #define SD_ACMD_SEND_OP_COND 0x69FF #define SD_CMD_APP_CMD 0x77FF #define SD_CMD_READ_OCR 0x7AFF inline void spi_write(uint8_t val) __attribute__((always_inline)); inline void spi_write(uint8_t val) { SPDR = val; while ((SPSR & (1<> 8); phex(in.byte.b4); phex(in.byte.b3); phex(in.byte.b2); phex(in.byte.b1); phex(cmd); print("\n"); spi_select(); spi_write(cmd >> 8); spi_write(in.byte.b4); spi_write(in.byte.b3); spi_write(in.byte.b2); spi_write(in.byte.b1); SPI_PORT |= (1<> 6)) + 1; c1 = spi_xfer(0xFF); // C_SIZE_MULT[2:1] c2 = spi_xfer(0xFF); // C_SIZE_MULT[0] //phex(c1); //phex(c2); //print("\n"); c1 &= 0x03; c2 &= 0x80; c1 = (c1 << 1) | (c2 >> 7); mult = mult + c1 - 7; spi_ignore_bytes(8); sd_deselect(); return ((uint32_t)c_size << mult); } else if (r == 0x40) { // Version 2.0 (high capacity, more than 2 gigbytes) uint8_t c1, c2, c3; uint32_t size; spi_ignore_bytes(6); c1 = spi_xfer(0xFF); // C_SIZE c2 = spi_xfer(0xFF); // C_SIZE c3 = spi_xfer(0xFF); // C_SIZE spi_ignore_bytes(9); //phex(c1); //phex(c2); //phex(c3); //print("\n"); c1 &= 0x3F; size = (((uint32_t)c1 << 16) | ((uint16_t)c2 << 8) | c3) + 1; size <<= 10; sd_deselect(); return size; } else { goto error; // unknown CSD_STRUCTURE } error: sd_deselect(); media_state = MEDIA_STATE_NOCARD; return 1; } uint8_t media_read_sector(uint32_t lba, uint8_t *buffer) { uint8_t r, i, ret=0; while (!media_lock()) /*wait*/ ; if (media_type != MEDIA_TYPE_SDHC) lba = (lba << 9); r = sd_command(SD_CMD_READ_SINGLE_BLOCK, lba); if (r) { // TODO: check for errors... print("User Read Error, r="); phex(r); print("\n"); goto done; } print("User Read OK, lba="); phex32(lba >> 9); print("\n"); if (sd_wait_data() != 0xFE) { print("User Read Error, bad token\n"); goto done; } for (i=0; i<64; i++) { *buffer++ = spi_xfer(0xFF); *buffer++ = spi_xfer(0xFF); *buffer++ = spi_xfer(0xFF); *buffer++ = spi_xfer(0xFF); *buffer++ = spi_xfer(0xFF); *buffer++ = spi_xfer(0xFF); *buffer++ = spi_xfer(0xFF); *buffer++ = spi_xfer(0xFF); } ret = 1; spi_write(0xFF); // ignore CRC spi_write(0xFF); done: sd_deselect(); media_unlock(); return ret; } static void media_send_begin(uint32_t lba) { uint8_t r; if (media_type != MEDIA_TYPE_SDHC) lba = (lba << 9); r = sd_command(SD_CMD_READ_MULTIPLE_BLOCK, lba); if (r) { // TODO: check for errors... print("Read Error, r="); phex(r); print("\n"); } else { print("Read OK\n"); } } static void media_send_chunk(uint32_t lba, uint8_t chunk) { uint8_t i; if (chunk == 0) { i = sd_wait_data(); //phex(i); if (i != 0xFE) { print("Read error, token="); phex(i); print("\n"); } } for (i=0; i<8; i++) { // TODO: asm optimization UEDATX = spi_xfer(0xFF); UEDATX = spi_xfer(0xFF); UEDATX = spi_xfer(0xFF); UEDATX = spi_xfer(0xFF); UEDATX = spi_xfer(0xFF); UEDATX = spi_xfer(0xFF); UEDATX = spi_xfer(0xFF); UEDATX = spi_xfer(0xFF); } UEINTX = 0x3A; if (chunk == 7) { spi_write(0xFF); spi_write(0xFF); } //print("."); } static void media_send_end(void) { uint8_t r; r = sd_command(SD_CMD_STOP_TRANSMISSION, 0); // TODO: proper handling of stop transaction..... // but what is the proper way? Older cards stop instantly, // but newer ones spew 1 or 2 bytes of garbage, then maybe // 0xFF's until the ok (0) response. What a mess! spi_write(0xFF); spi_write(0xFF); spi_write(0xFF); spi_write(0xFF); spi_write(0xFF); spi_write(0xFF); spi_write(0xFF); spi_write(0xFF); //print("\rr="); //phex(r); #if 0 if (r) { print("Stop Transmission Error, r="); phex(r); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); print("\n"); // handle errors } else { print("Stop Transmission OK, r="); phex(r); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); phex(spi_xfer(0xFF)); print("\n"); } do { r = spi_xfer(0xFF); //phex(r); } while (r == 0); #endif sd_deselect(); //print("\n"); } uint8_t media_write_sector(uint32_t lba, const uint8_t *buffer) { uint8_t r, i, ret=0; while (!media_lock()) /*wait*/ ; if (media_type != MEDIA_TYPE_SDHC) lba = (lba << 9); r = sd_command(SD_CMD_WRITE_SINGLE_BLOCK, lba); if (r) { // TODO: check for errors... print("User Write Error, r="); phex(r); print("\n"); goto done; } print("User Write OK, lba="); phex32(lba >> 9); print("\n"); spi_write(0xFE); // start block token for (i=0; i<64; i++) { spi_write(*buffer++); spi_write(*buffer++); spi_write(*buffer++); spi_write(*buffer++); spi_write(*buffer++); spi_write(*buffer++); spi_write(*buffer++); spi_write(*buffer++); } spi_write(0xFF); // CRC spi_write(0xFF); do { r = spi_xfer(0xFF); // wait for busy //phex(r); } while (r != 0xFF); ret = 1; done: sd_deselect(); media_unlock(); return ret; } static void media_receive_begin(uint32_t lba) { uint8_t r; // TODO: check media_rdonly, return error is read only mode if (media_type != MEDIA_TYPE_SDHC) lba = (lba << 9); sd_command(SD_CMD_WRITE_MULTIPLE_BLOCK, lba); if (r) { // TODO: check for errors... } } static void media_receive_chunk(uint32_t lba, uint8_t chunk) { uint8_t i, r; if (chunk == 0) { spi_write(0xFC); } for (i=0; i<8; i++) { // TODO: asm optimization spi_write(UEDATX); spi_write(UEDATX); spi_write(UEDATX); spi_write(UEDATX); spi_write(UEDATX); spi_write(UEDATX); spi_write(UEDATX); spi_write(UEDATX); } UEINTX = 0x6B; print("."); if (chunk == 7) { spi_write(0xFF); spi_write(0xFF); do { r = spi_xfer(0xFF); phex(r); } while (r != 0xFF); } } static void media_receive_end(void) { uint8_t r; print("stop"); spi_write(0xFD); spi_write(0xFF); do { // TODO: this can wait for a long time... would be // much better to create a busy media state and // return from the ISR, but then need to deal with // it everywhere else! r = spi_xfer(0xFF); phex(r); } while (r != 0xFF); sd_deselect(); print("\n"); }