Add LULZBOT_TOUCH_UI (#14967)

This commit is contained in:
Marcio Teixeira
2019-08-16 17:34:13 -06:00
committed by Scott Lahteine
parent 179d6c4ed1
commit 274934ad81
356 changed files with 22195 additions and 58 deletions

View File

@@ -0,0 +1,560 @@
/*********************
* flash_storage.cpp *
*********************/
/****************************************************************************
* Written By Mark Pelletier 2017 - Aleph Objects, Inc. *
* Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <http://www.gnu.org/licenses/>. *
****************************************************************************/
#include "../compat.h"
#if ENABLED(LULZBOT_TOUCH_UI)
#include "../ftdi_eve_lib/ftdi_eve_lib.h"
#include "media_file_reader.h"
#include "flash_storage.h"
// The following must be changed whenever the layout of the flash
// data is changed in a manner that would render the data invalid.
constexpr uint32_t flash_eeprom_version = 1;
/* SPI Flash Memory Map:
*
* The following offsets and sizes are specified in 4k erase units:
*
* Page Size Description
* 0 16 DATA STORAGE AREA
* 16 1 VERSIONING DATA
* 17 inf MEDIA STORAGE AREA
*
*/
#define DATA_STORAGE_SIZE_64K
using namespace FTDI::SPI;
using namespace FTDI::SPI::most_significant_byte_first;
bool UIFlashStorage::is_present = false;
#ifdef SPI_FLASH_SS
/************************** SPI Flash Chip Interface **************************/
void SPIFlash::wait_while_busy() {
uint8_t status;
safe_delay(1);
do {
spi_flash_select();
spi_write_8(READ_STATUS_1);
status = spi_read_8();
spi_flash_deselect();
safe_delay(1);
} while (status & 1);
}
void SPIFlash::erase_sector_4k(uint32_t addr) {
spi_flash_select();
spi_write_8(WRITE_ENABLE);
spi_flash_deselect();
spi_flash_select();
spi_write_8(ERASE_4K);
spi_write_24(addr);
spi_flash_deselect();
wait_while_busy();
}
void SPIFlash::erase_sector_64k(uint32_t addr) {
spi_flash_select();
spi_write_8(WRITE_ENABLE);
spi_flash_deselect();
spi_flash_select();
spi_write_8(ERASE_64K);
spi_write_24(addr);
spi_flash_deselect();
wait_while_busy();
}
void SPIFlash::spi_write_begin(uint32_t addr) {
spi_flash_select();
spi_write_8(WRITE_ENABLE);
spi_flash_deselect();
spi_flash_select();
spi_write_8(PAGE_PROGRAM);
spi_write_24(addr);
}
void SPIFlash::spi_write_end() {
spi_flash_deselect();
wait_while_busy();
}
void SPIFlash::spi_read_begin(uint32_t addr) {
spi_flash_select();
spi_write_8(READ_DATA);
spi_write_24(addr);
}
void SPIFlash::spi_read_end() {
spi_flash_deselect();
}
void SPIFlash::erase_chip() {
spi_flash_select();
spi_write_8(WRITE_ENABLE);
spi_flash_deselect();
spi_flash_select();
spi_write_8(ERASE_CHIP);
spi_flash_deselect();
wait_while_busy();
}
void SPIFlash::read_jedec_id(uint8_t &manufacturer_id, uint8_t &device_type, uint8_t &capacity) {
spi_flash_select();
spi_write_8(READ_JEDEC_ID);
manufacturer_id = spi_recv();
device_type = spi_recv();
capacity = spi_recv();
spi_flash_deselect ();
}
/* This function writes "size" bytes from "data" starting at addr, while properly
* taking into account the special case of writing across a 256 byte page boundary.
* Returns the addr directly after the write.
*/
uint32_t SPIFlash::write(uint32_t addr, const void *_data, size_t size) {
const uint8_t *data = (const uint8_t*) _data;
while (size) {
const uint32_t page_start = addr & 0xFFFF00ul;
const uint32_t page_end = page_start + 256;
const uint32_t write_size = min(page_end - addr, size);
spi_write_begin(addr);
spi_write_bulk<ram_write>(data, write_size);
spi_write_end();
addr += write_size;
size -= write_size;
data += write_size;
}
return addr;
}
uint32_t SPIFlash::read(uint32_t addr, void *data, size_t size) {
spi_read_begin(addr);
spi_read_bulk(data, size);
spi_read_end();
return addr + size;
}
/********************************** UTILITY ROUTINES *********************************/
bool UIFlashStorage::check_known_device() {
uint8_t manufacturer_id, device_type, capacity;
read_jedec_id(manufacturer_id, device_type, capacity);
const bool is_known =
((manufacturer_id == 0xEF) && (device_type == 0x40) && (capacity == 0x15)) || // unknown
((manufacturer_id == 0x01) && (device_type == 0x40) && (capacity == 0x15)) || // Cypress S25FL116K
((manufacturer_id == 0xEF) && (device_type == 0x14) && (capacity == 0x15)) || // Winbond W25Q16JV
((manufacturer_id == 0x1F) && (device_type == 0x86) && (capacity == 0x01)) ; // Adesto AT255F161
if (!is_known) {
SERIAL_ECHO_START(); SERIAL_ECHOLNPGM("Unable to locate supported SPI Flash Memory.");
SERIAL_ECHO_START(); SERIAL_ECHOLNPAIR(" Manufacturer ID, got: ", manufacturer_id);
SERIAL_ECHO_START(); SERIAL_ECHOLNPAIR(" Device Type , got: ", device_type);
SERIAL_ECHO_START(); SERIAL_ECHOLNPAIR(" Capacity , got: ", capacity);
}
return is_known;
}
void UIFlashStorage::initialize() {
for(uint8_t i = 0; i < 10; i++) {
if (check_known_device()) {
is_present = true;
break;
}
safe_delay(1000);
}
}
/**************************** DATA STORAGE AREA (first 4K or 64k) ********************/
#ifdef DATA_STORAGE_SIZE_64K
constexpr uint32_t data_storage_area_size = 64 * 1024; // Large erase unit
#else
constexpr uint32_t data_storage_area_size = 4 * 1024; // Small erase unit
#endif
/* In order to provide some degree of wear leveling, each data write to the
* SPI Flash chip is appended to data that was already written before, until
* the data storage area is completely filled. New data is written preceeded
* with a 32-bit delimiter 'LULZ', so that we can distinguish written and
* unwritten data:
*
* 'LULZ' <--- 1st record delimiter
* <data_byte>
* <data_byte>
* <data_byte>
* 'LULZ' <--- 2nd record delimiter
* <data_byte>
* <data_byte>
* <data_byte>
* ...
* 'LULZ' <--- Last record delimiter
* <data_byte>
* <data_byte>
* <data_byte>
* 0xFF <--- Start of free space
* 0xFF
* ...
*
* This function walks down the data storage area, verifying that the
* delimiters are either 'LULZ' or 0xFFFFFFFF. In the case that an invalid
* delimiter is found, this function returns -1, indicating that the Flash
* data is invalid (this will happen if the block_size changed with respect
* to earlier firmware). Otherwise, it returns the offset of the last
* valid delimiter 'LULZ', indicating the most recently written data.
*/
int32_t UIFlashStorage::get_config_read_offset(uint32_t block_size) {
uint16_t stride = 4 + block_size;
int32_t read_offset = -1;
for(uint32_t offset = 0; offset < (data_storage_area_size - stride); offset += stride) {
uint32_t delim;
spi_read_begin(offset);
spi_read_bulk (&delim, sizeof(delim));
spi_read_end();
switch (delim) {
case 0xFFFFFFFFul: return read_offset;
case delimiter: read_offset = offset; break;
default:
SERIAL_ECHO_START(); SERIAL_ECHOLNPAIR("Invalid delimiter in Flash: ", delim);
return -1;
}
}
SERIAL_ECHO_START(); SERIAL_ECHOLNPGM("No LULZ delimiter found.");
return -1;
}
/* This function returns the offset at which new data should be
* appended, or -1 if the Flash needs to be erased */
int32_t UIFlashStorage::get_config_write_offset(uint32_t block_size) {
int32_t read_offset = get_config_read_offset(block_size);
if (read_offset == -1) return -1; // The SPI flash is invalid
int32_t write_offset = read_offset + 4 + block_size;
if ((write_offset + 4 + block_size) > data_storage_area_size) {
SERIAL_ECHO_START(); SERIAL_ECHOLNPGM("Not enough free space in Flash.");
return -1; // Not enough free space
}
return write_offset;
}
bool UIFlashStorage::verify_config_data(const void *data, size_t size) {
if (!is_present) return false;
int32_t read_addr = get_config_read_offset(size);
if (read_addr == -1) return false;
uint32_t delim;
spi_read_begin(read_addr);
spi_read_bulk (&delim, sizeof(delim));
bool ok = spi_verify_bulk(data,size);
spi_read_end();
return ok && delim == delimiter;
}
bool UIFlashStorage::read_config_data(void *data, size_t size) {
if (!is_present) return false;
int32_t read_addr = get_config_read_offset(size);
if (read_addr == -1) return false;
uint32_t delim;
spi_read_begin(read_addr);
spi_read_bulk (&delim, sizeof(delim));
spi_read_bulk (data, size);
spi_read_end();
return delim == delimiter;
}
void UIFlashStorage::write_config_data(const void *data, size_t size) {
if (!is_present) {
SERIAL_ECHO_START(); SERIAL_ECHOLNPGM("SPI Flash chip not present. Not saving UI settings.");
return;
}
// Since Flash storage has a limited number of write cycles,
// make sure that the data is different before rewriting.
if (verify_config_data(data, size)) {
SERIAL_ECHO_START(); SERIAL_ECHOLNPGM("UI settings already written, skipping write.");
return;
}
int16_t write_addr = get_config_write_offset(size);
if (write_addr == -1) {
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Erasing UI settings from SPI Flash... ");
#ifdef DATA_STORAGE_SIZE_64K
erase_sector_64k(0);
#else
erase_sector_4k(0);
#endif
write_addr = 0;
SERIAL_ECHOLNPGM("DONE");
}
SERIAL_ECHO_START();
SERIAL_ECHOPAIR("Writing UI settings to SPI Flash (offset ", write_addr);
SERIAL_ECHOPGM(")...");
const uint32_t delim = delimiter;
write_addr = write(write_addr, &delim, sizeof(delim));
write_addr = write(write_addr, data, size);
SERIAL_ECHOLNPGM("DONE");
}
/************************** VERSIONING INFO AREA ************************/
/* The version info area follows the data storage area. If the version
* is incorrect, the data on the chip is invalid and format_flash should
* be called.
*/
typedef struct {
uint32_t magic;
uint32_t version;
} flash_version_info;
constexpr uint32_t version_info_addr = data_storage_area_size;
constexpr uint32_t version_info_size = 4 * 1024; // Small erase unit
bool UIFlashStorage::is_valid() {
flash_version_info info;
spi_read_begin(version_info_addr);
spi_read_bulk (&info, sizeof(flash_version_info));
spi_read_end();
return info.magic == delimiter && info.version == flash_eeprom_version;
}
void UIFlashStorage::write_version_info() {
flash_version_info info;
info.magic = delimiter;
info.version = flash_eeprom_version;
spi_write_begin(version_info_addr);
spi_write_bulk<ram_write>(&info, sizeof(flash_version_info));
spi_write_end();
}
/**************************** MEDIA STORAGE AREA *****************************/
/* The media storage area follows the versioning info area. It consists
* of a file index followed by the data for one or more media files.
*
* The file index consists of an array of 32-bit file sizes. If a file
* is not present, the file's size will be set to 0xFFFFFFFF
*/
constexpr uint32_t media_storage_addr = version_info_addr + version_info_size;
constexpr uint8_t media_storage_slots = 4;
void UIFlashStorage::format_flash() {
SERIAL_ECHO_START(); SERIAL_ECHOPGM("Erasing SPI Flash...");
SPIFlash::erase_chip();
SERIAL_ECHOLNPGM("DONE");
write_version_info();
}
uint32_t UIFlashStorage::get_media_file_start(uint8_t slot) {
uint32_t addr = media_storage_addr + sizeof(uint32_t) * media_storage_slots;
spi_read_begin(media_storage_addr);
for(uint8_t i = 0; i < slot; i++) {
addr += spi_read_32();
}
spi_read_end();
return addr;
}
void UIFlashStorage::set_media_file_size(uint8_t slot, uint32_t size) {
spi_write_begin(media_storage_addr + sizeof(uint32_t) * slot);
spi_write_32(size);
spi_write_end();
}
uint32_t UIFlashStorage::get_media_file_size(uint8_t slot) {
spi_read_begin(media_storage_addr + sizeof(uint32_t) * slot);
uint32_t size = spi_read_32();
spi_read_end();
return size;
}
/* Writes a media file from the SD card/USB flash drive into a slot on the SPI Flash. Media
* files must be written sequentially following by a chip erase and it is not possible to
* overwrite files. */
UIFlashStorage::error_t UIFlashStorage::write_media_file(progmem_str filename, uint8_t slot) {
#if ENABLED(SDSUPPORT)
uint32_t addr;
uint8_t buff[write_page_size];
strcpy_P( (char*) buff, (const char*) filename);
MediaFileReader reader;
if (!reader.open((char*) buff)) {
SERIAL_ECHO_START(); SERIAL_ECHOLNPGM("Unable to find media file");
return FILE_NOT_FOUND;
}
if (get_media_file_size(slot) != 0xFFFFFFFFUL) {
SERIAL_ECHO_START(); SERIAL_ECHOLNPGM("Media file already exists");
return WOULD_OVERWRITE;
}
SERIAL_ECHO_START(); SERIAL_ECHOPGM("Writing SPI Flash...");
set_media_file_size(slot, reader.size());
addr = get_media_file_start(slot);
// Write out the file itself
for(;;) {
const int16_t nBytes = reader.read(buff, write_page_size);
if (nBytes == -1) {
SERIAL_ECHOLNPGM("Failed to read from file");
return READ_ERROR;
}
addr = write(addr, buff, nBytes);
if (nBytes != write_page_size)
break;
#if ENABLED(EXTENSIBLE_UI)
ExtUI::yield();
#endif
}
SERIAL_ECHOLNPGM("DONE");
SERIAL_ECHO_START(); SERIAL_ECHOPGM("Verifying SPI Flash...");
bool verifyOk = true;
// Verify the file index
if (get_media_file_start(slot+1) != (get_media_file_start(slot) + reader.size())) {
SERIAL_ECHOLNPGM("File index verification failed. ");
verifyOk = false;
}
// Verify the file itself
addr = get_media_file_start(slot);
reader.rewind();
while (verifyOk) {
const int16_t nBytes = reader.read(buff, write_page_size);
if (nBytes == -1) {
SERIAL_ECHOPGM("Failed to read from file");
verifyOk = false;
break;
}
spi_read_begin(addr);
if (!spi_verify_bulk(buff, nBytes)) {
verifyOk = false;
spi_read_end();
break;
}
spi_read_end();
addr += nBytes;
if (nBytes != write_page_size) break;
#if ENABLED(EXTENSIBLE_UI)
ExtUI::yield();
#endif
};
if (verifyOk) {
SERIAL_ECHOLNPGM("DONE");
return SUCCESS;
} else {
SERIAL_ECHOLNPGM("FAIL");
return VERIFY_ERROR;
}
#else
return VERIFY_ERROR;
#endif // ENABLED(SDSUPPORT)
}
bool UIFlashStorage::BootMediaReader::isAvailable(uint32_t slot) {
if (!is_present) return false;
bytes_remaining = get_media_file_size(slot);
if (bytes_remaining != 0xFFFFFFFFUL) {
SERIAL_ECHO_START(); SERIAL_ECHOLNPAIR("Boot media file size:", bytes_remaining);
addr = get_media_file_start(slot);
return true;
} else {
return false;
}
}
int16_t UIFlashStorage::BootMediaReader::read(void *data, const size_t size) {
if (bytes_remaining == 0xFFFFFFFFUL) return -1;
if (size > bytes_remaining)
return read(data, bytes_remaining);
if (size > 0) {
spi_read_begin(addr);
spi_read_bulk(data, size);
spi_read_end();
addr += size;
bytes_remaining -= size;
}
return size;
}
int16_t UIFlashStorage::BootMediaReader::read(void *obj, void *data, const size_t size) {
return reinterpret_cast<UIFlashStorage::BootMediaReader*>(obj)->read(data, size);
}
#else
void UIFlashStorage::initialize() {}
bool UIFlashStorage::is_valid() {return true;}
void UIFlashStorage::write_config_data(const void *, size_t) {}
bool UIFlashStorage::verify_config_data(const void *, size_t) {return false;}
bool UIFlashStorage::read_config_data(void *, size_t ) {return false;}
UIFlashStorage::error_t UIFlashStorage::write_media_file(progmem_str, uint8_t) {return FILE_NOT_FOUND;}
void UIFlashStorage::format_flash() {}
bool UIFlashStorage::BootMediaReader::isAvailable(uint32_t) {return false;}
int16_t UIFlashStorage::BootMediaReader::read(void *, const size_t) {return -1;}
int16_t UIFlashStorage::BootMediaReader::read(void *, void *, const size_t) {return -1;}
#endif // SPI_FLASH_SS
#endif // LULZBOT_TOUCH_UI

View File

@@ -0,0 +1,110 @@
/*******************
* flash_storage.h *
*******************/
/****************************************************************************
* Written By Mark Pelletier 2017 - Aleph Objects, Inc. *
* Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <http://www.gnu.org/licenses/>. *
****************************************************************************/
#ifdef LULZBOT_TOUCH_UI
class SPIFlash {
public:
static constexpr uint32_t erase_unit_size = 4 * 1024; // Minimum erase unit
static constexpr uint32_t write_page_size = 256; // Minimum page write unit
enum {
READ_STATUS_1 = 0x05,
READ_STATUS_2 = 0x35,
READ_STATUS_3 = 0x33,
WRITE_ENABLE = 0x06,
WRITE_DISABLE = 0x04,
READ_ID = 0x90,
READ_JEDEC_ID = 0x9F,
READ_DATA = 0x03,
PAGE_PROGRAM = 0x02,
ERASE_4K = 0x20,
ERASE_64K = 0xD8,
ERASE_CHIP = 0xC7
};
static void wait_while_busy();
static void erase_sector_4k(uint32_t addr);
static void erase_sector_64k(uint32_t addr);
static void erase_chip ();
static void read_jedec_id(uint8_t &manufacturer_id, uint8_t &device_type, uint8_t &capacity);
static void spi_read_begin(uint32_t addr);
static void spi_read_end();
static void spi_write_begin(uint32_t addr);
static void spi_write_end();
static uint32_t write(uint32_t addr, const void *data, size_t size);
static uint32_t read(uint32_t addr, void *data, size_t size);
};
class UIFlashStorage : private SPIFlash {
private:
static bool is_present;
static int32_t get_config_read_offset(uint32_t block_size);
static int32_t get_config_write_offset(uint32_t block_size);
static uint32_t get_media_file_start(uint8_t slot);
static void set_media_file_size(uint8_t slot, uint32_t size);
static uint32_t get_media_file_size(uint8_t slot);
static constexpr uint32_t delimiter = 0x4C554C5A; // 'LULZ'
public:
enum error_t {
SUCCESS,
FILE_NOT_FOUND,
READ_ERROR,
VERIFY_ERROR,
WOULD_OVERWRITE
};
static void initialize ();
static void format_flash ();
static bool check_known_device();
static bool is_valid ();
static void write_version_info();
static void write_config_data (const void *data, size_t size);
static bool verify_config_data (const void *data, size_t size);
static bool read_config_data (void *data, size_t size);
static error_t write_media_file (progmem_str filename, uint8_t slot = 0);
class BootMediaReader;
};
class UIFlashStorage::BootMediaReader {
private:
uint32_t addr;
uint32_t bytes_remaining;
public:
bool isAvailable(uint32_t slot = 0);
int16_t read(void *buffer, size_t const size);
static int16_t read(void *obj, void *buffer, const size_t size);
};
#endif // LULZBOT_TOUCH_UI

View File

@@ -0,0 +1,63 @@
/************************
* media_filereader.cpp *
************************/
/****************************************************************************
* Written By Mark Pelletier 2017 - Aleph Objects, Inc. *
* Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <http://www.gnu.org/licenses/>. *
****************************************************************************/
#include "../compat.h"
#if ENABLED(LULZBOT_TOUCH_UI)
#include "media_file_reader.h"
#if ENABLED(SDSUPPORT)
bool MediaFileReader::open(const char* filename) {
card.init(SPI_SPEED, SDSS);
volume.init(&card);
root.openRoot(&volume);
return file.open(&root, filename, O_READ);
}
int16_t MediaFileReader::read(void *buff, size_t bytes) {
return file.read(buff, bytes);
}
void MediaFileReader::close() {
file.close();
}
uint32_t MediaFileReader::size() {
return file.fileSize();
}
void MediaFileReader::rewind() {
file.rewind();
}
int16_t MediaFileReader::read(void *obj, void *buff, size_t bytes) {
return reinterpret_cast<MediaFileReader*>(obj)->read(buff, bytes);
}
#else
bool MediaFileReader::open(const char*) {return -1;}
int16_t MediaFileReader::read(void *, size_t) {return 0;}
void MediaFileReader::close() {}
uint32_t MediaFileReader::size() {return 0;}
void MediaFileReader::rewind() {}
int16_t MediaFileReader::read(void *, void *, size_t) {return 0;}
#endif
#endif // LULZBOT_TOUCH_UI

View File

@@ -0,0 +1,44 @@
/**********************
* media_filereader.h *
**********************/
/****************************************************************************
* Written By Mark Pelletier 2017 - Aleph Objects, Inc. *
* Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <http://www.gnu.org/licenses/>. *
****************************************************************************/
#pragma once
#include "../../../../../sd/SdFile.h"
#include "../../../../../sd/cardreader.h"
class MediaFileReader {
private:
#if ENABLED(SDSUPPORT)
Sd2Card card;
SdVolume volume;
SdFile root, file;
#endif
public:
bool open(const char* filename);
int16_t read(void *buff, size_t bytes);
uint32_t size();
void rewind();
void close();
static int16_t read(void *obj, void *buff, size_t bytes);
};