Disable the FMC fifo until we have code that can detect when it's empty.
[SCSI2SD-V6.git] / src / firmware / config.c
1 //    Copyright (C) 2014 Michael McMaster <michael@codesrc.com>\r
2 //\r
3 //    This file is part of SCSI2SD.\r
4 //\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
9 //\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
14 //\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
17 \r
18 #include "config.h"\r
19 #include "led.h"\r
20 #include "bsp.h"\r
21 #include "scsi.h"\r
22 #include "scsiPhy.h"\r
23 #include "sd.h"\r
24 #include "disk.h"\r
25 #include "bootloader.h"\r
26 #include "spinlock.h"\r
27 \r
28 #include "../../include/scsi2sd.h"\r
29 #include "../../include/hidpacket.h"\r
30 \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
35 \r
36 \r
37 #include <string.h>\r
38 \r
39 static const uint16_t FIRMWARE_VERSION = 0x0647;\r
40 \r
41 // Optional static config\r
42 extern uint8_t* __fixed_config;\r
43 \r
44 extern SD_HandleTypeDef hsd;\r
45 \r
46 #ifdef S2S_USB_HS\r
47 #define configUsbDev hUsbDeviceHS\r
48 #else\r
49 #define configUsbDev hUsbDeviceFS\r
50 #endif\r
51 \r
52 // 1 flash row\r
53 static const uint8_t DEFAULT_CONFIG[128] =\r
54 {\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
61 };\r
62 \r
63 \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
66 \r
67 \r
68 enum USB_STATE\r
69 {\r
70     USB_IDLE,\r
71     USB_DATA_SENT\r
72 };\r
73 \r
74 \r
75 static int usbInEpState;\r
76 \r
77 static void s2s_debugTimer();\r
78 \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
84 \r
85 void TIM7_IRQHandler()\r
86 {\r
87     HAL_TIM_IRQHandler(&htim7);\r
88 }\r
89 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)\r
90 {\r
91     if (s2s_spin_trylock(&usbDevLock)) {\r
92         s2s_debugTimer();\r
93         s2s_spin_unlock(&usbDevLock);\r
94     }\r
95 }\r
96 \r
97 void s2s_configInit(S2S_BoardCfg* config)\r
98 {\r
99     usbInEpState = USB_IDLE;\r
100 \r
101     if (memcmp(__fixed_config, "BCFG", 4) == 0)\r
102     {\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
106     }\r
107 \r
108     else if ((blockDev.state & DISK_PRESENT) && sdDev.capacity)\r
109     {\r
110         int cfgSectors = (S2S_CFG_SIZE + 511) / 512;\r
111         BSP_SD_ReadBlocks_DMA(\r
112             &s2s_cfg[0],\r
113             sdDev.capacity - cfgSectors,\r
114             cfgSectors);\r
115 \r
116         memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));\r
117 \r
118         if (memcmp(config->magic, "BCFG", 4))\r
119         {\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
126 \r
127             memcpy(\r
128                 &s2s_cfg[0] + sizeof(S2S_BoardCfg),\r
129                 DEFAULT_CONFIG,\r
130                 sizeof(S2S_TargetCfg));\r
131         }\r
132     }\r
133     else\r
134     {\r
135         // No SD card, use existing config if valid\r
136         if (memcmp(config->magic, "BCFG", 4))\r
137         {\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
143         }\r
144     }\r
145 }\r
146 \r
147 static void debugInit(void)\r
148 {\r
149     if (debugTimerStarted == 1) return;\r
150 \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
155 #ifdef STM32F2xx\r
156     htim7.Init.Prescaler = 10800 - 1; // 16bit. 108MHz down to 10KHz\r
157 #else\r
158     htim7.Init.Prescaler = 18000 - 1; // 16bit. 180MHz down to 10KHz\r
159 #endif\r
160 \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
166 \r
167     HAL_NVIC_SetPriority(TIM7_IRQn, 10, 0);\r
168     HAL_NVIC_EnableIRQ(TIM7_IRQn);\r
169 }\r
170 \r
171 \r
172 static void\r
173 pingCommand()\r
174 {\r
175     uint8_t response[] =\r
176     {\r
177         S2S_CFG_STATUS_GOOD\r
178     };\r
179     hidPacket_send(response, sizeof(response));\r
180 }\r
181 \r
182 static void\r
183 sdInfoCommand()\r
184 {\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
188 \r
189     hidPacket_send(response, sizeof(response));\r
190 }\r
191 \r
192 \r
193 static void\r
194 scsiTestCommand()\r
195 {\r
196     int resultCode = scsiSelfTest();\r
197     uint8_t response[] =\r
198     {\r
199         resultCode == 0 ? S2S_CFG_STATUS_GOOD : S2S_CFG_STATUS_ERR,\r
200         resultCode\r
201     };\r
202     hidPacket_send(response, sizeof(response));\r
203 }\r
204 \r
205 static void\r
206 scsiDevInfoCommand()\r
207 {\r
208     uint8_t response[] =\r
209     {\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
215         sdDev.capacity,\r
216         1 // useSdConfig, always true for V6.\r
217     };\r
218     hidPacket_send(response, sizeof(response));\r
219 }\r
220 \r
221 static void\r
222 debugCommand()\r
223 {\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
247 }\r
248 \r
249 static void\r
250 sdWriteCommand(const uint8_t* cmd, size_t cmdSize)\r
251 {\r
252     if (cmdSize < 517)\r
253     {\r
254         return; // ignore.\r
255     }\r
256     uint32_t lba =\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
261 \r
262     memcpy(configDmaBuf, &cmd[5], 512);\r
263     BSP_SD_WriteBlocks_DMA(configDmaBuf, lba, 1);\r
264 \r
265     uint8_t response[] =\r
266     {\r
267         S2S_CFG_STATUS_GOOD\r
268     };\r
269     hidPacket_send(response, sizeof(response));\r
270 }\r
271 \r
272 static void\r
273 sdReadCommand(const uint8_t* cmd, size_t cmdSize)\r
274 {\r
275     if (cmdSize < 5)\r
276     {\r
277         return; // ignore.\r
278     }\r
279     uint32_t lba =\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
284 \r
285     BSP_SD_ReadBlocks_DMA(configDmaBuf, lba, 1);\r
286     hidPacket_send(configDmaBuf, 512);\r
287 }\r
288 \r
289 static void\r
290 processCommand(const uint8_t* cmd, size_t cmdSize)\r
291 {\r
292     switch (cmd[0])\r
293     {\r
294     case S2S_CMD_PING:\r
295         pingCommand();\r
296         break;\r
297 \r
298     case S2S_CMD_REBOOT:\r
299         s2s_enterBootloader();\r
300         break;\r
301 \r
302     case S2S_CMD_SDINFO:\r
303         sdInfoCommand();\r
304         break;\r
305 \r
306     case S2S_CMD_SCSITEST:\r
307         scsiTestCommand();\r
308         break;\r
309 \r
310     case S2S_CMD_DEVINFO:\r
311         scsiDevInfoCommand();\r
312         break;\r
313 \r
314     case S2S_CMD_SD_WRITE:\r
315         sdWriteCommand(cmd, cmdSize);\r
316         break;\r
317 \r
318     case S2S_CMD_SD_READ:\r
319         sdReadCommand(cmd, cmdSize);\r
320         break;\r
321 \r
322     case S2S_CMD_DEBUG:\r
323         if (debugTimerStarted == 0) {\r
324             debugInit();\r
325             debugCommand();\r
326         }\r
327         // Always triggered from timer for consistent behaviour\r
328 \r
329         break;\r
330 \r
331     case S2S_CMD_NONE: // invalid\r
332     default:\r
333         break;\r
334     }\r
335 }\r
336 \r
337 void s2s_configPoll()\r
338 {\r
339     s2s_spin_lock(&usbDevLock);\r
340 \r
341     if (!USBD_Composite_IsConfigured(&configUsbDev))\r
342     {\r
343         usbInEpState = USB_IDLE;\r
344         hidPacket_reset();\r
345         goto out;\r
346     }\r
347 \r
348     if (USBD_HID_IsReportReady(&configUsbDev))\r
349     {\r
350         // Check if we have a previous command outstanding\r
351         // before accepting another\r
352         size_t cmdSize;\r
353         if (hidPacket_peekPacket(&cmdSize) == NULL)\r
354         {\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
358         }\r
359     }\r
360 \r
361     if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send\r
362     {\r
363         size_t cmdSize;\r
364         const uint8_t* cmd = hidPacket_peekPacket(&cmdSize);\r
365         if (cmd && (cmdSize > 0))\r
366         {\r
367             if (!debugTimerStarted || (cmd[0] != S2S_CMD_DEBUG))\r
368             {\r
369                 hidPacket_getPacket(&cmdSize);\r
370                 s2s_ledOn();\r
371                 processCommand(cmd, cmdSize);\r
372                 s2s_ledOff();\r
373             }\r
374         }\r
375     }\r
376 \r
377     switch (usbInEpState)\r
378     {\r
379     case USB_IDLE:\r
380         {\r
381             uint8_t hidBuffer[USBHID_LEN];\r
382             const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
383 \r
384             if (nextChunk)\r
385             {\r
386                 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));\r
387                 usbInEpState = USB_DATA_SENT;\r
388             }\r
389         }\r
390         break;\r
391 \r
392     case USB_DATA_SENT:\r
393         if (!USBD_HID_IsBusy(&configUsbDev))\r
394         {\r
395             // Data accepted.\r
396             usbInEpState = USB_IDLE;\r
397         }\r
398         break;\r
399     }\r
400 \r
401 out:\r
402     s2s_spin_unlock(&usbDevLock);\r
403 }\r
404 \r
405 void s2s_debugTimer()\r
406 {\r
407     if (!USBD_Composite_IsConfigured(&configUsbDev))\r
408     {\r
409         usbInEpState = USB_IDLE;\r
410         hidPacket_reset();\r
411         return;\r
412     }\r
413 \r
414     if (USBD_HID_IsReportReady(&configUsbDev))\r
415     {\r
416         // Check if we have a previous command outstanding\r
417         // before accepting another\r
418         size_t cmdSize;\r
419         if (hidPacket_peekPacket(&cmdSize) == NULL)\r
420         {\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
424         }\r
425     }\r
426 \r
427     if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send\r
428     {\r
429         size_t cmdSize;\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
433         {\r
434             if (cmd[0] == S2S_CMD_DEBUG)\r
435             {\r
436                 hidPacket_getPacket(&cmdSize);\r
437                 debugCommand();\r
438             }\r
439             else if (cmd[0] == S2S_CMD_PING)\r
440             {\r
441                 hidPacket_getPacket(&cmdSize);\r
442                 pingCommand();\r
443             }\r
444 \r
445         }\r
446     }\r
447 \r
448     switch (usbInEpState)\r
449     {\r
450         case USB_IDLE:\r
451         {\r
452             uint8_t hidBuffer[USBHID_LEN];\r
453             const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
454 \r
455             if (nextChunk)\r
456             {\r
457                 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));\r
458                 usbInEpState = USB_DATA_SENT;\r
459             }\r
460         }\r
461         break;\r
462 \r
463         case USB_DATA_SENT:\r
464             if (!USBD_HID_IsBusy(&configUsbDev))\r
465             {\r
466                 // Data accepted.\r
467                 usbInEpState = USB_IDLE;\r
468             }\r
469             break;\r
470     }\r
471 }\r
472 \r
473 // Public method for storing MODE SELECT results.\r
474 void s2s_configSave(int scsiId, uint16_t bytesPerSector)\r
475 {\r
476     S2S_TargetCfg* cfg = (S2S_TargetCfg*) s2s_getConfigById(scsiId);\r
477     cfg->bytesPerSector = bytesPerSector;\r
478 \r
479     BSP_SD_WriteBlocks_DMA(\r
480         &s2s_cfg[0],\r
481         sdDev.capacity - S2S_CFG_SIZE,\r
482         (S2S_CFG_SIZE + 511) / 512);\r
483 }\r
484 \r
485 const S2S_TargetCfg* s2s_getConfigByIndex(int i)\r
486 {\r
487     return (const S2S_TargetCfg*)\r
488         (s2s_cfg + sizeof(S2S_BoardCfg) + (i * sizeof(S2S_TargetCfg)));\r
489 }\r
490 \r
491 const S2S_TargetCfg* s2s_getConfigById(int scsiId)\r
492 {\r
493     int i;\r
494     for (i = 0; i < S2S_MAX_TARGETS; ++i)\r
495     {\r
496         const S2S_TargetCfg* tgt = s2s_getConfigByIndex(i);\r
497         if ((tgt->scsiId & S2S_CFG_TARGET_ID_BITS) == scsiId)\r
498         {\r
499             return tgt;\r
500         }\r
501     }\r
502     return NULL;\r
503 }\r
504 \r