e983e71eb03e55517f4922d1727b143aeafe3dfb
[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 = 0x0640;\r
40 \r
41 // Optional static config\r
42 extern uint8_t* __fixed_config;\r
43 \r
44 #ifdef S2S_USB_HS\r
45 #define configUsbDev hUsbDeviceHS\r
46 #else\r
47 #define configUsbDev hUsbDeviceFS\r
48 #endif\r
49 \r
50 // 1 flash row\r
51 static const uint8_t DEFAULT_CONFIG[128] =\r
52 {\r
53         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00,\r
54         0x00, 0x02, 0x3F, 0x00, 0xFF, 0x00, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x73,\r
55         0x72, 0x63, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53,\r
56         0x43, 0x53, 0x49, 0x32, 0x53, 0x44, 0x20, 0x31, 0x2E, 0x30, 0x31, 0x32,\r
57         0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,\r
58         0x37, 0x38, 0x00, 0x00\r
59 };\r
60 \r
61 \r
62 static uint8_t s2s_cfg[S2S_CFG_SIZE] S2S_DMA_ALIGN;\r
63 static uint8_t configDmaBuf[512] S2S_DMA_ALIGN; // For SD card writes.\r
64 \r
65 \r
66 enum USB_STATE\r
67 {\r
68         USB_IDLE,\r
69         USB_DATA_SENT\r
70 };\r
71 \r
72 \r
73 static int usbInEpState;\r
74 \r
75 static void s2s_debugTimer();\r
76 \r
77 // Debug timer to log via USB.\r
78 // Timer 6 & 7 is a simple counter with no external IO supported.\r
79 static s2s_lock_t usbDevLock = s2s_lock_init;\r
80 TIM_HandleTypeDef htim7;\r
81 static int debugTimerStarted = 0;\r
82 void TIM7_IRQHandler()\r
83 {\r
84         HAL_TIM_IRQHandler(&htim7);\r
85 }\r
86 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)\r
87 {\r
88         if (s2s_spin_trylock(&usbDevLock)) {\r
89                 s2s_debugTimer();\r
90                 s2s_spin_unlock(&usbDevLock);\r
91         }\r
92 }\r
93 \r
94 void s2s_configInit(S2S_BoardCfg* config)\r
95 {\r
96         usbInEpState = USB_IDLE;\r
97 \r
98         if (memcmp(__fixed_config, "BCFG", 4) == 0)\r
99         {\r
100                 // Use hardcoded config\r
101                 memcpy(s2s_cfg, __fixed_config, S2S_CFG_SIZE);\r
102                 memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));\r
103         }\r
104 \r
105         else if ((blockDev.state & DISK_PRESENT) && sdDev.capacity)\r
106         {\r
107                 int cfgSectors = (S2S_CFG_SIZE + 511) / 512;\r
108                 BSP_SD_ReadBlocks_DMA(\r
109                         &s2s_cfg[0],\r
110                         sdDev.capacity - cfgSectors,\r
111                         cfgSectors);\r
112 \r
113                 memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));\r
114 \r
115                 if (memcmp(config->magic, "BCFG", 4))\r
116                 {\r
117                         // Invalid SD card config, use default.\r
118                         memset(&s2s_cfg[0], 0, S2S_CFG_SIZE);\r
119                         memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));\r
120                         memcpy(config->magic, "BCFG", 4);\r
121                         config->selectionDelay = 255; // auto\r
122                         config->flags6 = S2S_CFG_ENABLE_TERMINATOR;\r
123 \r
124                         memcpy(\r
125                                 &s2s_cfg[0] + sizeof(S2S_BoardCfg),\r
126                                 DEFAULT_CONFIG,\r
127                                 sizeof(S2S_TargetCfg));\r
128                 }\r
129         }\r
130         else\r
131         {\r
132                 // No SD card, use existing config if valid\r
133                 if (memcmp(config->magic, "BCFG", 4))\r
134                 {\r
135                         // Not valid, use empty config with no disks.\r
136                         memset(&s2s_cfg[0], 0, S2S_CFG_SIZE);\r
137                         memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg));\r
138                         config->selectionDelay = 255; // auto\r
139                         config->flags6 = S2S_CFG_ENABLE_TERMINATOR;\r
140                 }\r
141         }\r
142 }\r
143 \r
144 static void debugInit(void)\r
145 {\r
146         if (debugTimerStarted == 1) return;\r
147 \r
148         debugTimerStarted = 1;\r
149         // 10ms debug timer to capture logs over USB\r
150         __TIM7_CLK_ENABLE();\r
151         htim7.Instance = TIM7;\r
152         htim7.Init.Prescaler = 10800 - 1; // 16bit. 108MHz down to 10KHz\r
153         htim7.Init.CounterMode = TIM_COUNTERMODE_UP;\r
154         htim7.Init.Period = 100 - 1; // 16bit. 10KHz down to 10ms.\r
155         htim7.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;\r
156         HAL_TIM_Base_Init(&htim7);\r
157         HAL_TIM_Base_Start_IT(&htim7);\r
158 \r
159         HAL_NVIC_SetPriority(TIM7_IRQn, 10, 0);\r
160         HAL_NVIC_EnableIRQ(TIM7_IRQn);\r
161 }\r
162 \r
163 \r
164 static void\r
165 pingCommand()\r
166 {\r
167         uint8_t response[] =\r
168         {\r
169                 S2S_CFG_STATUS_GOOD\r
170         };\r
171         hidPacket_send(response, sizeof(response));\r
172 }\r
173 \r
174 static void\r
175 sdInfoCommand()\r
176 {\r
177         uint8_t response[sizeof(sdDev.csd) + sizeof(sdDev.cid)];\r
178         memcpy(response, sdDev.csd, sizeof(sdDev.csd));\r
179         memcpy(response + sizeof(sdDev.csd), sdDev.cid, sizeof(sdDev.cid));\r
180 \r
181         hidPacket_send(response, sizeof(response));\r
182 }\r
183 \r
184 \r
185 static void\r
186 scsiTestCommand()\r
187 {\r
188         int resultCode = scsiSelfTest();\r
189         uint8_t response[] =\r
190         {\r
191                 resultCode == 0 ? S2S_CFG_STATUS_GOOD : S2S_CFG_STATUS_ERR,\r
192                 resultCode\r
193         };\r
194         hidPacket_send(response, sizeof(response));\r
195 }\r
196 \r
197 static void\r
198 scsiDevInfoCommand()\r
199 {\r
200         uint8_t response[] =\r
201         {\r
202                 FIRMWARE_VERSION >> 8,\r
203                 FIRMWARE_VERSION & 0xff,\r
204                 sdDev.capacity >> 24,\r
205                 sdDev.capacity >> 16,\r
206                 sdDev.capacity >> 8,\r
207                 sdDev.capacity,\r
208                 1 // useSdConfig, always true for V6.\r
209         };\r
210         hidPacket_send(response, sizeof(response));\r
211 }\r
212 \r
213 static void\r
214 debugCommand()\r
215 {\r
216         uint8_t response[32];\r
217         memcpy(&response, &scsiDev.cdb, 12);\r
218         response[12] = scsiDev.msgIn;\r
219         response[13] = scsiDev.msgOut;\r
220         response[14] = scsiDev.lastStatus;\r
221         response[15] = scsiDev.lastSense;\r
222         response[16] = scsiDev.phase;\r
223         response[17] = *SCSI_STS_SCSI;\r
224         response[18] = scsiDev.target != NULL ? scsiDev.target->syncOffset : 0;\r
225         response[19] = scsiDev.target != NULL ? scsiDev.target->syncPeriod : 0;\r
226         response[20] = scsiDev.minSyncPeriod;\r
227         response[21] = scsiDev.rstCount;\r
228         response[22] = scsiDev.selCount;\r
229         response[23] = scsiDev.msgCount;\r
230         response[24] = scsiDev.cmdCount;\r
231         response[25] = scsiDev.watchdogTick;\r
232         response[26] = blockDev.state;\r
233         response[27] = scsiDev.lastSenseASC >> 8;\r
234         response[28] = scsiDev.lastSenseASC;\r
235         response[29] = *SCSI_STS_DBX & 0xff; // What we've read\r
236         response[30] = *SCSI_STS_SELECTED;\r
237         response[31] = *SCSI_STS_DBX >> 8; // What we're writing\r
238         hidPacket_send(response, sizeof(response));\r
239 }\r
240 \r
241 static void\r
242 sdWriteCommand(const uint8_t* cmd, size_t cmdSize)\r
243 {\r
244         if (cmdSize < 517)\r
245         {\r
246                 return; // ignore.\r
247         }\r
248         uint32_t lba =\r
249                 (((uint32_t)cmd[1]) << 24) |\r
250                 (((uint32_t)cmd[2]) << 16) |\r
251                 (((uint32_t)cmd[3]) << 8) |\r
252                 ((uint32_t)cmd[4]);\r
253 \r
254         memcpy(configDmaBuf, &cmd[5], 512);\r
255         BSP_SD_WriteBlocks_DMA(configDmaBuf, lba, 1);\r
256 \r
257         uint8_t response[] =\r
258         {\r
259                 S2S_CFG_STATUS_GOOD\r
260         };\r
261         hidPacket_send(response, sizeof(response));\r
262 }\r
263 \r
264 static void\r
265 sdReadCommand(const uint8_t* cmd, size_t cmdSize)\r
266 {\r
267         if (cmdSize < 5)\r
268         {\r
269                 return; // ignore.\r
270         }\r
271         uint32_t lba =\r
272                 (((uint32_t)cmd[1]) << 24) |\r
273                 (((uint32_t)cmd[2]) << 16) |\r
274                 (((uint32_t)cmd[3]) << 8) |\r
275                 ((uint32_t)cmd[4]);\r
276 \r
277         BSP_SD_ReadBlocks_DMA(configDmaBuf, lba, 1);\r
278         hidPacket_send(configDmaBuf, 512);\r
279 }\r
280 \r
281 static void\r
282 processCommand(const uint8_t* cmd, size_t cmdSize)\r
283 {\r
284         switch (cmd[0])\r
285         {\r
286         case S2S_CMD_PING:\r
287                 pingCommand();\r
288                 break;\r
289 \r
290         case S2S_CMD_REBOOT:\r
291                 s2s_enterBootloader();\r
292                 break;\r
293 \r
294         case S2S_CMD_SDINFO:\r
295                 sdInfoCommand();\r
296                 break;\r
297 \r
298         case S2S_CMD_SCSITEST:\r
299                 scsiTestCommand();\r
300                 break;\r
301 \r
302         case S2S_CMD_DEVINFO:\r
303                 scsiDevInfoCommand();\r
304                 break;\r
305 \r
306         case S2S_CMD_SD_WRITE:\r
307                 sdWriteCommand(cmd, cmdSize);\r
308                 break;\r
309 \r
310         case S2S_CMD_SD_READ:\r
311                 sdReadCommand(cmd, cmdSize);\r
312                 break;\r
313 \r
314         case S2S_CMD_DEBUG:\r
315                 if (debugTimerStarted == 0) {\r
316                         debugInit();\r
317                 }\r
318                 debugCommand();\r
319                 break;\r
320 \r
321         case S2S_CMD_NONE: // invalid\r
322         default:\r
323                 break;\r
324         }\r
325 }\r
326 \r
327 void s2s_configPoll()\r
328 {\r
329         s2s_spin_lock(&usbDevLock);\r
330 \r
331         if (!USBD_Composite_IsConfigured(&configUsbDev))\r
332         {\r
333                 usbInEpState = USB_IDLE;\r
334                 hidPacket_reset();\r
335                 goto out;\r
336         }\r
337 \r
338         if (USBD_HID_IsReportReady(&configUsbDev))\r
339         {\r
340                 // Check if we have a previous command outstanding\r
341                 // before accepting another\r
342                 size_t cmdSize;\r
343                 if (hidPacket_peekPacket(&cmdSize) == NULL)\r
344                 {\r
345                         uint8_t hidBuffer[USBHID_LEN];\r
346                         int byteCount = USBD_HID_GetReport(&configUsbDev, hidBuffer, sizeof(hidBuffer));\r
347                         hidPacket_recv(hidBuffer, byteCount);\r
348                 }\r
349         }\r
350 \r
351         if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send\r
352         {\r
353                 size_t cmdSize;\r
354                 const uint8_t* cmd = hidPacket_getPacket(&cmdSize);\r
355                 if (cmd && (cmdSize > 0))\r
356                 {\r
357                         s2s_ledOn();\r
358                         processCommand(cmd, cmdSize);\r
359                         s2s_ledOff();\r
360                 }\r
361         }\r
362 \r
363         switch (usbInEpState)\r
364         {\r
365         case USB_IDLE:\r
366                 {\r
367                         uint8_t hidBuffer[USBHID_LEN];\r
368                         const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
369 \r
370                         if (nextChunk)\r
371                         {\r
372                                 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));\r
373                                 usbInEpState = USB_DATA_SENT;\r
374                         }\r
375                 }\r
376                 break;\r
377 \r
378         case USB_DATA_SENT:\r
379                 if (!USBD_HID_IsBusy(&configUsbDev))\r
380                 {\r
381                         // Data accepted.\r
382                         usbInEpState = USB_IDLE;\r
383                 }\r
384                 break;\r
385         }\r
386 \r
387 out:\r
388         s2s_spin_unlock(&usbDevLock);\r
389 }\r
390 \r
391 void s2s_debugTimer()\r
392 {\r
393         if (!USBD_Composite_IsConfigured(&configUsbDev))\r
394         {\r
395                 usbInEpState = USB_IDLE;\r
396                 return;\r
397         }\r
398 \r
399         if (USBD_HID_IsReportReady(&configUsbDev))\r
400         {\r
401                 // Check if we have a previous command outstanding\r
402                 // before accepting another\r
403                 size_t cmdSize;\r
404                 if (hidPacket_peekPacket(&cmdSize) == NULL)\r
405                 {\r
406                         uint8_t hidBuffer[USBHID_LEN];\r
407                         int byteCount = USBD_HID_GetReport(&configUsbDev, hidBuffer, sizeof(hidBuffer));\r
408                         hidPacket_recv(hidBuffer, byteCount);\r
409                 }\r
410         }\r
411 \r
412         if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send\r
413         {\r
414                 size_t cmdSize;\r
415                 const uint8_t* cmd = hidPacket_peekPacket(&cmdSize);\r
416                 // This is called from an ISR, only process simple commands.\r
417                 if (cmd && (cmdSize > 0))\r
418                 {\r
419                         if (cmd[0] == S2S_CMD_DEBUG)\r
420                         {\r
421                                 hidPacket_getPacket(&cmdSize);\r
422                                 debugCommand();\r
423                         }\r
424                         else if (cmd[0] == S2S_CMD_PING)\r
425                         {\r
426                                 hidPacket_getPacket(&cmdSize);\r
427                                 pingCommand();\r
428                         }\r
429                 }\r
430         }\r
431 \r
432         switch (usbInEpState)\r
433         {\r
434                 case USB_IDLE:\r
435                 {\r
436                         uint8_t hidBuffer[USBHID_LEN];\r
437                         const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
438 \r
439                         if (nextChunk)\r
440                         {\r
441                                 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));\r
442                                 usbInEpState = USB_DATA_SENT;\r
443                         }\r
444                 }\r
445                 break;\r
446 \r
447                 case USB_DATA_SENT:\r
448                         if (!USBD_HID_IsBusy(&configUsbDev))\r
449                         {\r
450                                 // Data accepted.\r
451                                 usbInEpState = USB_IDLE;\r
452                         }\r
453                         break;\r
454         }\r
455 }\r
456 \r
457 \r
458 \r
459 // Public method for storing MODE SELECT results.\r
460 void s2s_configSave(int scsiId, uint16_t bytesPerSector)\r
461 {\r
462         S2S_TargetCfg* cfg = (S2S_TargetCfg*) s2s_getConfigById(scsiId);\r
463         cfg->bytesPerSector = bytesPerSector;\r
464 \r
465         BSP_SD_WriteBlocks_DMA(\r
466                 &s2s_cfg[0],\r
467                 sdDev.capacity - S2S_CFG_SIZE,\r
468                 (S2S_CFG_SIZE + 511) / 512);\r
469 }\r
470 \r
471 \r
472 const S2S_TargetCfg* s2s_getConfigByIndex(int i)\r
473 {\r
474         return (const S2S_TargetCfg*)\r
475                 (s2s_cfg + sizeof(S2S_BoardCfg) + (i * sizeof(S2S_TargetCfg)));\r
476 }\r
477 \r
478 const S2S_TargetCfg* s2s_getConfigById(int scsiId)\r
479 {\r
480         int i;\r
481         for (i = 0; i < S2S_MAX_TARGETS; ++i)\r
482         {\r
483                 const S2S_TargetCfg* tgt = s2s_getConfigByIndex(i);\r
484                 if ((tgt->scsiId & S2S_CFG_TARGET_ID_BITS) == scsiId)\r
485                 {\r
486                         return tgt;\r
487                 }\r
488         }\r
489         return NULL;\r
490 \r
491 }\r
492 \r