1 // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
\r
3 // This file is part of SCSI2SD.
\r
5 // SCSI2SD is free software: you can redistribute it and/or modify
\r
6 // it under the terms of the GNU General Public License as published by
\r
7 // the Free Software Foundation, either version 3 of the License, or
\r
8 // (at your option) any later version.
\r
10 // SCSI2SD is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
\r
22 #include "scsiPhy.h"
\r
25 #include "bootloader.h"
\r
26 #include "spinlock.h"
\r
28 #include "../../include/scsi2sd.h"
\r
29 #include "../../include/hidpacket.h"
\r
31 #include "usb_device/usb_device.h"
\r
32 #include "usb_device/usbd_hid.h"
\r
33 #include "usb_device/usbd_composite.h"
\r
34 #include "bsp_driver_sd.h"
\r
39 static const uint16_t FIRMWARE_VERSION = 0x064B;
\r
41 // Optional static config
\r
42 extern uint8_t* __fixed_config;
\r
44 extern SD_HandleTypeDef hsd;
\r
47 #define configUsbDev hUsbDeviceHS
\r
49 #define configUsbDev hUsbDeviceFS
\r
53 static const uint8_t DEFAULT_CONFIG[128] =
\r
55 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00,
\r
56 0x00, 0x02, 0x3F, 0x00, 0xFF, 0x00, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x73,
\r
57 0x72, 0x63, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53,
\r
58 0x43, 0x53, 0x49, 0x32, 0x53, 0x44, 0x20, 0x31, 0x2E, 0x30, 0x31, 0x32,
\r
59 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
\r
60 0x37, 0x38, 0x00, 0x00
\r
64 static uint8_t s2s_cfg[S2S_CFG_SIZE] S2S_DMA_ALIGN;
\r
65 static uint8_t configDmaBuf[512] S2S_DMA_ALIGN; // For SD card writes.
\r
75 static int usbInEpState;
\r
77 static void s2s_debugTimer();
\r
79 // Debug timer to log via USB.
\r
80 // Timer 6 & 7 is a simple counter with no external IO supported.
\r
81 static s2s_lock_t usbDevLock = s2s_lock_init;
\r
82 TIM_HandleTypeDef htim7;
\r
83 static int debugTimerStarted = 0;
\r
85 void TIM7_IRQHandler()
\r
87 HAL_TIM_IRQHandler(&htim7);
\r
89 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
\r
91 if (s2s_spin_trylock(&usbDevLock)) {
\r
93 s2s_spin_unlock(&usbDevLock);
\r
97 void s2s_configInit(S2S_BoardCfg* config)
\r
99 usbInEpState = USB_IDLE;
\r
101 if (memcmp(__fixed_config, "BCFG", 4) == 0)
\r
103 // Use hardcoded config
\r
104 memcpy(s2s_cfg, __fixed_config, S2S_CFG_SIZE);
\r
105 memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));
\r
108 else if ((blockDev.state & DISK_PRESENT) && sdDev.capacity)
\r
110 int cfgSectors = (S2S_CFG_SIZE + 511) / 512;
\r
111 BSP_SD_ReadBlocks_DMA(
\r
113 sdDev.capacity - cfgSectors,
\r
116 memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));
\r
118 if (memcmp(config->magic, "BCFG", 4))
\r
120 // Invalid SD card config, use default.
\r
121 memset(&s2s_cfg[0], 0, S2S_CFG_SIZE);
\r
122 memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));
\r
123 memcpy(config->magic, "BCFG", 4);
\r
124 config->selectionDelay = 255; // auto
\r
125 config->flags6 = S2S_CFG_ENABLE_TERMINATOR;
\r
128 &s2s_cfg[0] + sizeof(S2S_BoardCfg),
\r
130 sizeof(S2S_TargetCfg));
\r
135 // No SD card, use existing config if valid
\r
136 if (memcmp(config->magic, "BCFG", 4))
\r
138 // Not valid, use empty config with no disks.
\r
139 memset(&s2s_cfg[0], 0, S2S_CFG_SIZE);
\r
140 memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));
\r
141 config->selectionDelay = 255; // auto
\r
142 config->flags6 = S2S_CFG_ENABLE_TERMINATOR;
\r
147 static void debugInit(void)
\r
149 if (debugTimerStarted == 1) return;
\r
151 debugTimerStarted = 1;
\r
152 // 10ms debug timer to capture logs over USB
\r
153 __TIM7_CLK_ENABLE();
\r
154 htim7.Instance = TIM7;
\r
156 htim7.Init.Prescaler = 10800 - 1; // 16bit. 108MHz down to 10KHz
\r
158 htim7.Init.Prescaler = 18000 - 1; // 16bit. 180MHz down to 10KHz
\r
161 htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
\r
162 htim7.Init.Period = 100 - 1; // 16bit. 10KHz down to 10ms.
\r
163 htim7.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
\r
164 HAL_TIM_Base_Init(&htim7);
\r
165 HAL_TIM_Base_Start_IT(&htim7);
\r
167 HAL_NVIC_SetPriority(TIM7_IRQn, 10, 0);
\r
168 HAL_NVIC_EnableIRQ(TIM7_IRQn);
\r
175 uint8_t response[] =
\r
177 S2S_CFG_STATUS_GOOD
\r
179 hidPacket_send(response, sizeof(response));
\r
185 uint8_t response[sizeof(sdDev.csd) + sizeof(sdDev.cid)];
\r
186 memcpy(response, sdDev.csd, sizeof(sdDev.csd));
\r
187 memcpy(response + sizeof(sdDev.csd), sdDev.cid, sizeof(sdDev.cid));
\r
189 hidPacket_send(response, sizeof(response));
\r
196 int resultCode = scsiSelfTest();
\r
197 uint8_t response[] =
\r
199 resultCode == 0 ? S2S_CFG_STATUS_GOOD : S2S_CFG_STATUS_ERR,
\r
202 hidPacket_send(response, sizeof(response));
\r
206 scsiDevInfoCommand()
\r
208 uint8_t response[] =
\r
210 FIRMWARE_VERSION >> 8,
\r
211 FIRMWARE_VERSION & 0xff,
\r
212 sdDev.capacity >> 24,
\r
213 sdDev.capacity >> 16,
\r
214 sdDev.capacity >> 8,
\r
216 1 // useSdConfig, always true for V6.
\r
218 hidPacket_send(response, sizeof(response));
\r
224 uint8_t response[32];
\r
225 memcpy(&response, &scsiDev.cdb, 12);
\r
226 response[12] = scsiDev.msgIn;
\r
227 response[13] = scsiDev.msgOut;
\r
228 response[14] = scsiDev.lastStatus;
\r
229 response[15] = scsiDev.lastSense;
\r
230 response[16] = scsiDev.phase;
\r
231 response[17] = *SCSI_STS_SCSI;
\r
232 response[18] = scsiDev.target != NULL ? scsiDev.target->syncOffset : 0;
\r
233 response[19] = scsiDev.target != NULL ? scsiDev.target->syncPeriod : 0;
\r
234 response[20] = scsiDev.minSyncPeriod;
\r
235 response[21] = scsiDev.rstCount;
\r
236 response[22] = scsiDev.selCount;
\r
237 response[23] = scsiDev.msgCount;
\r
238 response[24] = scsiDev.cmdCount;
\r
239 response[25] = scsiDev.watchdogTick;
\r
240 response[26] = (hsd.State << 4) | blockDev.state;
\r
241 response[27] = scsiDev.lastSenseASC >> 8;
\r
242 response[28] = scsiDev.lastSenseASC;
\r
243 response[29] = *SCSI_STS_DBX & 0xff; // What we've read
\r
244 response[30] = *SCSI_STS_SELECTED;
\r
245 response[31] = *SCSI_STS_DBX >> 8; // What we're writing
\r
246 hidPacket_send(response, sizeof(response));
\r
250 sdWriteCommand(const uint8_t* cmd, size_t cmdSize)
\r
257 (((uint32_t)cmd[1]) << 24) |
\r
258 (((uint32_t)cmd[2]) << 16) |
\r
259 (((uint32_t)cmd[3]) << 8) |
\r
260 ((uint32_t)cmd[4]);
\r
262 memcpy(configDmaBuf, &cmd[5], 512);
\r
263 BSP_SD_WriteBlocks_DMA(configDmaBuf, lba, 1);
\r
265 uint8_t response[] =
\r
267 S2S_CFG_STATUS_GOOD
\r
269 hidPacket_send(response, sizeof(response));
\r
273 sdReadCommand(const uint8_t* cmd, size_t cmdSize)
\r
280 (((uint32_t)cmd[1]) << 24) |
\r
281 (((uint32_t)cmd[2]) << 16) |
\r
282 (((uint32_t)cmd[3]) << 8) |
\r
283 ((uint32_t)cmd[4]);
\r
285 BSP_SD_ReadBlocks_DMA(configDmaBuf, lba, 1);
\r
286 hidPacket_send(configDmaBuf, 512);
\r
290 processCommand(const uint8_t* cmd, size_t cmdSize)
\r
298 case S2S_CMD_REBOOT:
\r
299 s2s_enterBootloader();
\r
302 case S2S_CMD_SDINFO:
\r
306 case S2S_CMD_SCSITEST:
\r
310 case S2S_CMD_DEVINFO:
\r
311 scsiDevInfoCommand();
\r
314 case S2S_CMD_SD_WRITE:
\r
315 sdWriteCommand(cmd, cmdSize);
\r
318 case S2S_CMD_SD_READ:
\r
319 sdReadCommand(cmd, cmdSize);
\r
322 case S2S_CMD_DEBUG:
\r
323 if (debugTimerStarted == 0) {
\r
327 // Always triggered from timer for consistent behaviour
\r
331 case S2S_CMD_NONE: // invalid
\r
337 void s2s_configPoll()
\r
339 s2s_spin_lock(&usbDevLock);
\r
341 if (!USBD_Composite_IsConfigured(&configUsbDev))
\r
343 usbInEpState = USB_IDLE;
\r
348 if (USBD_HID_IsReportReady(&configUsbDev))
\r
350 // Check if we have a previous command outstanding
\r
351 // before accepting another
\r
353 if (hidPacket_peekPacket(&cmdSize) == NULL)
\r
355 uint8_t hidBuffer[USBHID_LEN];
\r
356 int byteCount = USBD_HID_GetReport(&configUsbDev, hidBuffer, sizeof(hidBuffer));
\r
357 hidPacket_recv(hidBuffer, byteCount);
\r
361 if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send
\r
364 const uint8_t* cmd = hidPacket_peekPacket(&cmdSize);
\r
365 if (cmd && (cmdSize > 0))
\r
367 if (!debugTimerStarted || (cmd[0] != S2S_CMD_DEBUG))
\r
369 hidPacket_getPacket(&cmdSize);
\r
371 processCommand(cmd, cmdSize);
\r
377 switch (usbInEpState)
\r
381 uint8_t hidBuffer[USBHID_LEN];
\r
382 const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);
\r
386 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));
\r
387 usbInEpState = USB_DATA_SENT;
\r
392 case USB_DATA_SENT:
\r
393 if (!USBD_HID_IsBusy(&configUsbDev))
\r
396 usbInEpState = USB_IDLE;
\r
402 s2s_spin_unlock(&usbDevLock);
\r
405 void s2s_debugTimer()
\r
407 if (!USBD_Composite_IsConfigured(&configUsbDev))
\r
409 usbInEpState = USB_IDLE;
\r
414 if (USBD_HID_IsReportReady(&configUsbDev))
\r
416 // Check if we have a previous command outstanding
\r
417 // before accepting another
\r
419 if (hidPacket_peekPacket(&cmdSize) == NULL)
\r
421 uint8_t hidBuffer[USBHID_LEN];
\r
422 int byteCount = USBD_HID_GetReport(&configUsbDev, hidBuffer, sizeof(hidBuffer));
\r
423 hidPacket_recv(hidBuffer, byteCount);
\r
427 if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send
\r
430 const uint8_t* cmd = hidPacket_peekPacket(&cmdSize);
\r
431 // This is called from an ISR, only process simple commands.
\r
432 if (cmd && (cmdSize > 0))
\r
434 if (cmd[0] == S2S_CMD_DEBUG)
\r
436 hidPacket_getPacket(&cmdSize);
\r
439 else if (cmd[0] == S2S_CMD_PING)
\r
441 hidPacket_getPacket(&cmdSize);
\r
448 switch (usbInEpState)
\r
452 uint8_t hidBuffer[USBHID_LEN];
\r
453 const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);
\r
457 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));
\r
458 usbInEpState = USB_DATA_SENT;
\r
463 case USB_DATA_SENT:
\r
464 if (!USBD_HID_IsBusy(&configUsbDev))
\r
467 usbInEpState = USB_IDLE;
\r
473 // Public method for storing MODE SELECT results.
\r
474 void s2s_configSave(int scsiId, uint16_t bytesPerSector)
\r
476 S2S_TargetCfg* cfg = (S2S_TargetCfg*) s2s_getConfigById(scsiId);
\r
477 cfg->bytesPerSector = bytesPerSector;
\r
479 BSP_SD_WriteBlocks_DMA(
\r
481 sdDev.capacity - S2S_CFG_SIZE,
\r
482 (S2S_CFG_SIZE + 511) / 512);
\r
485 const S2S_TargetCfg* s2s_getConfigByIndex(int i)
\r
487 return (const S2S_TargetCfg*)
\r
488 (s2s_cfg + sizeof(S2S_BoardCfg) + (i * sizeof(S2S_TargetCfg)));
\r
491 const S2S_TargetCfg* s2s_getConfigById(int scsiId)
\r
494 for (i = 0; i < S2S_MAX_TARGETS; ++i)
\r
496 const S2S_TargetCfg* tgt = s2s_getConfigByIndex(i);
\r
497 if ((tgt->scsiId & S2S_CFG_TARGET_ID_BITS) == scsiId)
\r