Files
2026-03-24 22:22:37 +00:00

175 lines
7.0 KiB
C++
Vendored

/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: FSPI.h
// Created: Jan 2025
// Version: v1.1
// Author(s): Philip Smart
// Description: Class definition to encapsulate the Espressif Fast SPI Interface.
// v1.1: Binary IPC frame protocol support. Fixed max_transfer_sz (was 32, now
// IPCF_MAX_FRAME_SIZE = 8260). DMA-capable buffers for command/response
// frames. receiveBinaryCmd() / sendBinaryResp() added.
// Credits:
// Copyright: (c) 2019-2026 Philip Smart <philip.smart@net2net.org>
//
// History: v1.00 Jan 2025 - Initial write.
// v1.10 Mar 2026 - Binary IPC protocol, fix max_transfer_sz, burst support.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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 source file 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef FSPI_H
#define FSPI_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <map>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/spi_slave.h"
#include "esp_system.h"
#include "esp_log.h"
#include "sdmmc_cmd.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "esp_vfs_fat.h"
#include <string>
#include <functional>
#include "ipc_protocol.h"
// Define the SPI host and protocol constants.
class FSPI
{
#define FSPI_CMD_SIZE 4
#define FSPI_RESP_SIZE 4
#define FSPI_PKTCTRL_SIZE 4
#define FSPI_DATABLOCK_SIZE 512
#define FSPI_FILENAME_LENGTH 28
#define FSPI_PARAM_LENGTH 128
#define FSPI_DATABLOCK_OFFSET FSPI_RESP_SIZE + FSPI_PKTCTRL_SIZE - 1
#define FSPI_CHKSUM_OFFSET FSPI_RESP_SIZE + FSPI_PKTCTRL_SIZE + FSPI_DATABLOCK_SIZE
#define FSPI_CMDMSG_SIZE FSPI_CMD_SIZE + FSPI_FILENAME_LENGTH
#define FSPI_DATAMSG_SIZE FSPI_RESP_SIZE + FSPI_PKTCTRL_SIZE + FSPI_DATABLOCK_SIZE
#define FSPI_HOST SPI2_HOST
#define FSPI_RESP_ACK 0xE0
#define FSPI_RESP_NAK 0xEE
#define FSPI_RESP_ABORT 0xEF
#define FSPI_RESULT_CODE_SUCCESS 0xF0
#define FSPI_RESULT_CODE_NEXTBLK 0xF1
#define FSPI_RESULT_CODE_LASTBLK 0xF2
#define FSPI_RESULT_CODE_CHKSUM 0xFC
#define FSPI_RESULT_CODE_BADFILE 0xFD
#define FSPI_RESULT_CODE_TIMEOUT 0xFE
#define FSPI_RESULT_CODE_ERROR 0xFF
#define FSPI_SPI_TICK_TIMEOUT 1000
#define FSPI_SPI_XACT_TIMEOUT portMAX_DELAY
// Macros.
#define NUMELEM(a) (sizeof(a) / sizeof(a[0]))
// Constants.
#define FSPI_VERSION 1.10
public:
// Prototypes.
FSPI(void);
virtual ~FSPI(void) {};
bool init();
// Legacy SPI transactions (kept for backward compatibility with INF data path).
bool receiveCommand(char *cmd, size_t timeout);
esp_err_t sendData(const uint8_t *data, size_t length, size_t timeout);
esp_err_t receiveData(uint8_t *data, size_t length, size_t timeout);
esp_err_t sendResponse(uint8_t resultCode, size_t length, size_t timeout);
bool decodeParam(const char *paramStr, std::vector<std::string> *paramVec);
uint8_t getResponse(size_t timeout);
esp_err_t sendACK(size_t timeout);
esp_err_t sendNAK(size_t timeout);
esp_err_t sendABORT(size_t timeout);
uint8_t calculateChecksum(uint8_t *data, size_t length);
// ---------------------------------------------------------------------------
// Binary IPC frame protocol (v1.1)
// ---------------------------------------------------------------------------
// Receive a 64-byte command header from the RP2350 master.
// Blocks until a transaction completes or the timeout expires.
// timeout: FreeRTOS ticks (use portMAX_DELAY to wait forever).
// Returns ESP_OK on success.
esp_err_t receiveBinaryCmd(uint8_t *cmdBuf, TickType_t timeout = portMAX_DELAY);
// Send a binary response frame: 64-byte header + payload + CRC32.
// hdr - Pre-filled response header (frameType, command, status, payloadLen).
// payload - Pointer to payload data (may be NULL if payloadLen == 0).
// payloadLen - Bytes of payload to include.
// respSize - Total frame size to transmit (header + payload + CRC32); the SPI
// slave always sends exactly this many bytes so the master can pre-size
// its receive buffer. Must not exceed IPCF_MAX_FRAME_SIZE.
// timeout - FreeRTOS ticks.
esp_err_t sendBinaryResp(const t_IpcFrameHdr *hdr, const uint8_t *payload, uint32_t payloadLen, uint32_t respSize, TickType_t timeout = portMAX_DELAY);
// CRC32 — standard IEEE 802.3, matches FSPI_crc32() on RP2350.
// Uses esp_rom_crc32_le() for hardware acceleration.
static uint32_t crc32(const uint8_t *data, size_t len);
// Public buffers (DMA-capable, allocated in init()).
// ipcCmdBuf: receives command frames from RP2350 master.
// ipcRespBuf: transmits response frames to RP2350 master.
// Also used as dataBuf alias for the legacy single-block path.
uint8_t *ipcCmdBuf; // IPCF_MAX_FRAME_SIZE bytes
uint8_t *ipcRespBuf; // IPCF_MAX_FRAME_SIZE bytes
// Legacy public data buffer alias (first 512 bytes of ipcCmdBuf).
uint8_t dataBuf[FSPI_DATABLOCK_SIZE];
// Helper method to identify the sub class.
const char *getClassName(const std::string &prettyFunction)
{
size_t colons = prettyFunction.find("::");
if (colons == std::string::npos)
return "::";
size_t begin = prettyFunction.substr(0, colons).rfind(" ") + 1;
size_t end = colons - begin;
return (prettyFunction.substr(begin, end).c_str());
}
void replaceExt(std::string &fileName, const std::string &newExt)
{
std::string::size_type extPos = fileName.rfind('.', fileName.length());
if (extPos != std::string::npos)
fileName.replace(extPos + 1, newExt.length(), newExt);
}
template <typename E> constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept
{
return static_cast<typename std::underlying_type<E>::type>(e);
}
virtual float version(void)
{
return (FSPI_VERSION);
}
protected:
private:
// No per-instance private state beyond the public DMA buffers.
};
#endif // FSPI_H