SCSI2SD/software/SCSI2SD/src/config.c

570 lines
13 KiB
C
Executable File

// Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
//
// This file is part of SCSI2SD.
//
// SCSI2SD 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.
//
// SCSI2SD 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 SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
#include "device.h"
#include "config.h"
#include "debug.h"
#include "USBFS.h"
#include "led.h"
#include "scsi.h"
#include "scsiPhy.h"
#include "disk.h"
#include "trace.h"
#include "../../include/scsi2sd.h"
#include "../../include/hidpacket.h"
#include <string.h>
static const uint16_t FIRMWARE_VERSION = 0x0485;
// 1 flash row
static const uint8_t DEFAULT_CONFIG[256] =
{
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00,
0x00, 0x02, 0x3F, 0x00, 0xFF, 0x00, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x73,
0x72, 0x63, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53,
0x43, 0x53, 0x49, 0x32, 0x53, 0x44, 0x20, 0x31, 0x2E, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x00, 0x00
};
enum USB_ENDPOINTS
{
USB_EP_OUT = 1,
USB_EP_IN = 2,
USB_EP_COMMAND = 3,
USB_EP_DEBUG = 4
};
enum USB_STATE
{
USB_IDLE,
USB_DATA_SENT
};
static uint8_t hidBuffer[USBHID_LEN];
static uint8_t dbgHidBuffer[USBHID_LEN];
static int usbInEpState;
static int usbDebugEpState;
static int usbReady;
static void initBoardConfig(S2S_BoardConfig* config) {
memcpy(
config,
(const void*)(
CY_FLASH_BASE +
(CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +
(CY_FLASH_SIZEOF_ROW * SCSI_CONFIG_BOARD_ROW)
),
sizeof(S2S_BoardConfig));
if (memcmp(config->magic, "BCFG", 4)) {
// Set a default from the deprecated flags, or 0 if
// there is no initial config.
config->flags = getConfigByIndex(0)->flagsDEPRECATED;
config->selectionDelay = 255; // auto
config->flags6 = S2S_CFG_ENABLE_TERMINATOR;
}
}
void configInit(S2S_BoardConfig* config)
{
// The USB block will be powered by an internal 3.3V regulator.
// The PSoC must be operating between 4.6V and 5V for the regulator
// to work.
USBFS_Start(0, USBFS_5V_OPERATION);
usbInEpState = usbDebugEpState = USB_IDLE;
usbReady = 0; // We don't know if host is connected yet.
int invalid = 1;
uint8_t* rawConfig = (uint8_t*)getConfigByIndex(0);
int i;
for (i = 0; i < 64; ++i)
{
if (rawConfig[i])
{
invalid = 0;
break;
}
}
if (invalid)
{
// Save a default config.
CySetTemp();
CyWriteRowData(SCSI_CONFIG_ARRAY, SCSI_CONFIG_0_ROW, DEFAULT_CONFIG);
}
initBoardConfig(config);
}
static void
readFlashCommand(const uint8_t* cmd, size_t cmdSize)
{
if (cmdSize < 3)
{
return; // ignore.
}
uint8_t flashArray = cmd[1];
uint8_t flashRow = cmd[2];
uint8_t* flash =
CY_FLASH_BASE +
(CY_FLASH_SIZEOF_ARRAY * (size_t) flashArray) +
(CY_FLASH_SIZEOF_ROW * (size_t) flashRow);
hidPacket_send(flash, SCSI_CONFIG_ROW_SIZE);
}
static void
writeFlashCommand(const uint8_t* cmd, size_t cmdSize)
{
if (cmdSize < 259)
{
return; // ignore.
}
uint8_t flashArray = cmd[257];
uint8_t flashRow = cmd[258];
// Be very careful not to overwrite the bootloader or other\r
// code. Bootloader updates no longer supported. Use v5.1 board
// instead.
if ((flashArray != SCSI_CONFIG_ARRAY) ||
(flashRow < SCSI_CONFIG_4_ROW) ||
(flashRow >= SCSI_CONFIG_3_ROW + SCSI_CONFIG_ROWS))
{
uint8_t response[] = { CONFIG_STATUS_ERR };
hidPacket_send(response, sizeof(response));
}
CySetTemp();
int status = CyWriteRowData(flashArray, flashRow, cmd + 1);
uint8_t response[] =
{
status == CYRET_SUCCESS ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR
};
hidPacket_send(response, sizeof(response));
}
static void
pingCommand()
{
uint8_t response[] =
{
CONFIG_STATUS_GOOD
};
hidPacket_send(response, sizeof(response));
}
static void
sdInfoCommand()
{
uint8_t response[sizeof(sdCard.csd) + sizeof(sdCard.cid)];
memcpy(response, sdCard.csd, sizeof(sdCard.csd));
memcpy(response + sizeof(sdCard.csd), sdCard.cid, sizeof(sdCard.cid));
hidPacket_send(response, sizeof(response));
}
static void
scsiTestCommand()
{
int resultCode = scsiSelfTest();
uint8_t response[] =
{
resultCode == 0 ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR,
resultCode
};
hidPacket_send(response, sizeof(response));
}
static void
deviceListCommand()
{
int deviceCount;
S2S_Device** devices = s2s_GetDevices(&deviceCount);
uint8_t response[16] = // Make larger if there can be more than 2 devices
{
deviceCount
};
int pos = 1;
for (int i = 0; i < deviceCount; ++i)
{
response[pos++] = devices[i]->deviceType;
uint32_t capacity = devices[i]->getCapacity(devices[i]);
response[pos++] = capacity >> 24;
response[pos++] = capacity >> 16;
response[pos++] = capacity >> 8;
response[pos++] = capacity;
}
hidPacket_send(response, pos);
}
static void
deviceEraseCommand(const uint8_t* cmd)
{
int deviceCount;
S2S_Device** devices = s2s_GetDevices(&deviceCount);
uint32_t sectorNum =
((uint32_t)cmd[2]) << 24 |
((uint32_t)cmd[3]) << 16 |
((uint32_t)cmd[4]) << 8 |
((uint32_t)cmd[5]);
uint32_t count =
((uint32_t)cmd[6]) << 24 |
((uint32_t)cmd[7]) << 16 |
((uint32_t)cmd[8]) << 8 |
((uint32_t)cmd[9]);
devices[cmd[1]]->erase(devices[cmd[1]], sectorNum, count);
uint8_t response[] =
{
CONFIG_STATUS_GOOD
};
hidPacket_send(response, sizeof(response));
}
static void
deviceWriteCommand(const uint8_t* cmd)
{
int deviceCount;
S2S_Device** devices = s2s_GetDevices(&deviceCount);
uint32_t sectorNum =
((uint32_t)cmd[2]) << 24 |
((uint32_t)cmd[3]) << 16 |
((uint32_t)cmd[4]) << 8 |
((uint32_t)cmd[5]);
devices[cmd[1]]->write(devices[cmd[1]], sectorNum, 1, &cmd[6]);
uint8_t response[] =
{
CONFIG_STATUS_GOOD
};
hidPacket_send(response, sizeof(response));
}
static void
deviceReadCommand(const uint8_t* cmd)
{
int deviceCount;
S2S_Device** devices = s2s_GetDevices(&deviceCount);
uint32_t sectorNum =
((uint32_t)cmd[2]) << 24 |
((uint32_t)cmd[3]) << 16 |
((uint32_t)cmd[4]) << 8 |
((uint32_t)cmd[5]);
uint8_t response[512];
devices[cmd[1]]->read(devices[cmd[1]], sectorNum, 1, &response[0]);
hidPacket_send(&response[0], 512);
}
static void
processCommand(const uint8_t* cmd, size_t cmdSize)
{
switch (cmd[0])
{
case CONFIG_PING:
pingCommand();
break;
case CONFIG_READFLASH:
readFlashCommand(cmd, cmdSize);
break;
case CONFIG_WRITEFLASH:
writeFlashCommand(cmd, cmdSize);
break;
case CONFIG_REBOOT:
Bootloadable_1_Load();
break;
case CONFIG_SDINFO:
sdInfoCommand();
break;
case CONFIG_SCSITEST:
scsiTestCommand();
break;
case S2S_CMD_DEV_LIST:
deviceListCommand();
break;
case S2S_CMD_DEV_ERASE:
deviceEraseCommand(cmd);
break;
case S2S_CMD_DEV_WRITE:
deviceWriteCommand(cmd);
break;
case S2S_CMD_DEV_READ:
deviceReadCommand(cmd);
break;
case CONFIG_NONE: // invalid
default:
break;
}
}
void configPoll()
{
int reset = 0;
if (!usbReady || USBFS_IsConfigurationChanged())
{
reset = 1;
}
usbReady = USBFS_bGetConfiguration();
if (!usbReady)
{
return;
}
if (reset)
{
hidPacket_reset();
USBFS_EnableOutEP(USB_EP_OUT);
USBFS_EnableOutEP(USB_EP_COMMAND);
usbInEpState = usbDebugEpState = USB_IDLE;
}
if(USBFS_GetEPState(USB_EP_OUT) == USBFS_OUT_BUFFER_FULL)
{
// The host sent us some data!
int byteCount = USBFS_GetEPCount(USB_EP_OUT);
USBFS_ReadOutEP(USB_EP_OUT, hidBuffer, sizeof(hidBuffer));
hidPacket_recv(hidBuffer, byteCount);
size_t cmdSize;
if (hidPacket_peekPacket(&cmdSize) == NULL)
{
// Allow the host to send us another updated config.
USBFS_EnableOutEP(USB_EP_OUT);
}
}
if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send
{
size_t cmdSize;
const uint8_t* cmd = hidPacket_getPacket(&cmdSize);
if (cmd && (cmdSize > 0))
{
ledOn();
processCommand(cmd, cmdSize);
ledOff();
// Allow the host to send us another updated config.
USBFS_EnableOutEP(USB_EP_OUT);
}
}
switch (usbInEpState)
{
case USB_IDLE:
{
const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);
if (nextChunk)
{
USBFS_LoadInEP(USB_EP_IN, nextChunk, sizeof(hidBuffer));
usbInEpState = USB_DATA_SENT;
}
}
break;
case USB_DATA_SENT:
if (USBFS_bGetEPAckState(USB_EP_IN))
{
// Data accepted.
usbInEpState = USB_IDLE;
}
break;
}
}
void debugPoll()
{
if (!usbReady)
{
return;
}
if(USBFS_GetEPState(USB_EP_COMMAND) == USBFS_OUT_BUFFER_FULL)
{
// The host sent us some data!
int byteCount = USBFS_GetEPCount(USB_EP_COMMAND);
USBFS_ReadOutEP(USB_EP_COMMAND, (uint8 *)&dbgHidBuffer, byteCount);
if (byteCount >= 1 &&
dbgHidBuffer[0] == 0x01)
{
// Reboot command.
Bootloadable_1_Load();
}
// Allow the host to send us another command.
// (assuming we didn't reboot outselves)
USBFS_EnableOutEP(USB_EP_COMMAND);
}
switch (usbDebugEpState)
{
case USB_IDLE:
memcpy(&dbgHidBuffer, &scsiDev.cdb, 12);
dbgHidBuffer[12] = scsiDev.msgIn;
dbgHidBuffer[13] = scsiDev.msgOut;
dbgHidBuffer[14] = scsiDev.lastStatus;
dbgHidBuffer[15] = scsiDev.lastSense;
dbgHidBuffer[16] = scsiDev.phase;
dbgHidBuffer[17] = SCSI_ReadFilt(SCSI_Filt_BSY);
dbgHidBuffer[18] = SCSI_ReadFilt(SCSI_Filt_SEL);
dbgHidBuffer[19] = SCSI_ReadFilt(SCSI_Filt_ATN);
dbgHidBuffer[20] = SCSI_ReadFilt(SCSI_Filt_RST);
dbgHidBuffer[21] = scsiDev.rstCount;
dbgHidBuffer[22] = scsiDev.selCount;
dbgHidBuffer[23] = scsiDev.msgCount;
dbgHidBuffer[24] = scsiDev.cmdCount;
dbgHidBuffer[25] = scsiDev.watchdogTick;
dbgHidBuffer[26] = 0; // OBSOLETE. Previously media state
dbgHidBuffer[27] = scsiDev.lastSenseASC >> 8;
dbgHidBuffer[28] = scsiDev.lastSenseASC;
dbgHidBuffer[29] = scsiReadDBxPins();
dbgHidBuffer[30] = LastTrace;
dbgHidBuffer[58] = sdCard.capacity >> 24;
dbgHidBuffer[59] = sdCard.capacity >> 16;
dbgHidBuffer[60] = sdCard.capacity >> 8;
dbgHidBuffer[61] = sdCard.capacity;
dbgHidBuffer[62] = FIRMWARE_VERSION >> 8;
dbgHidBuffer[63] = FIRMWARE_VERSION & 0xFF;
USBFS_LoadInEP(USB_EP_DEBUG, (uint8 *)&dbgHidBuffer, sizeof(dbgHidBuffer));
usbDebugEpState = USB_DATA_SENT;
break;
case USB_DATA_SENT:
if (USBFS_bGetEPAckState(USB_EP_DEBUG))
{
// Data accepted.
usbDebugEpState = USB_IDLE;
}
break;
}
}
CY_ISR(debugTimerISR)
{
Debug_Timer_ReadStatusRegister();
Debug_Timer_Interrupt_ClearPending();
uint8 savedIntrStatus = CyEnterCriticalSection();
debugPoll();
CyExitCriticalSection(savedIntrStatus);
}
void debugInit()
{
Debug_Timer_Interrupt_StartEx(debugTimerISR);
Debug_Timer_Start();
}
void debugPause()
{
Debug_Timer_Stop();
}
void debugResume()
{
Debug_Timer_Start();
}
int isDebugEnabled()
{
return usbReady;
}
// Public method for storing MODE SELECT results.
void configSave(int scsiId, uint16_t bytesPerSector)
{
int cfgIdx;
for (cfgIdx = 0; cfgIdx < MAX_SCSI_TARGETS; ++cfgIdx)
{
const S2S_TargetCfg* tgt = getConfigByIndex(cfgIdx);
if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)
{
// Save row to flash
// We only save the first row of the configuration
// this contains the parameters changeable by a MODE SELECT command
uint8_t rowData[CYDEV_FLS_ROW_SIZE];
S2S_TargetCfg* rowCfgData = (S2S_TargetCfg*)&rowData;
memcpy(rowCfgData, tgt, sizeof(rowData));
rowCfgData->bytesPerSector = bytesPerSector;
CySetTemp();
CyWriteRowData(
SCSI_CONFIG_ARRAY,
SCSI_CONFIG_0_ROW + (cfgIdx * SCSI_CONFIG_ROWS),
(uint8_t*)rowCfgData);
return;
}
}
}
const S2S_TargetCfg* getConfigByIndex(int i)
{
if (i <= 3)
{
size_t row = SCSI_CONFIG_0_ROW + (i * SCSI_CONFIG_ROWS);
return (const S2S_TargetCfg*)
(
CY_FLASH_BASE +
(CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +
(CY_FLASH_SIZEOF_ROW * row)
);
} else {
size_t row = SCSI_CONFIG_4_ROW + ((i-4) * SCSI_CONFIG_ROWS);
return (const S2S_TargetCfg*)
(
CY_FLASH_BASE +
(CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +
(CY_FLASH_SIZEOF_ROW * row)
);
}
}