e75be816c4983835776a2fc7ac7ebfef7cdae28d
[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 = 0x0641;\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 #ifdef STM32F2xx\r
153         htim7.Init.Prescaler = 10800 - 1; // 16bit. 108MHz down to 10KHz\r
154 #else\r
155         htim7.Init.Prescaler = 18000 - 1; // 16bit. 180MHz down to 10KHz\r
156 #endif\r
157 \r
158         htim7.Init.CounterMode = TIM_COUNTERMODE_UP;\r
159         htim7.Init.Period = 100 - 1; // 16bit. 10KHz down to 10ms.\r
160         htim7.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;\r
161         HAL_TIM_Base_Init(&htim7);\r
162         HAL_TIM_Base_Start_IT(&htim7);\r
163 \r
164         HAL_NVIC_SetPriority(TIM7_IRQn, 10, 0);\r
165         HAL_NVIC_EnableIRQ(TIM7_IRQn);\r
166 }\r
167 \r
168 \r
169 static void\r
170 pingCommand()\r
171 {\r
172         uint8_t response[] =\r
173         {\r
174                 S2S_CFG_STATUS_GOOD\r
175         };\r
176         hidPacket_send(response, sizeof(response));\r
177 }\r
178 \r
179 static void\r
180 sdInfoCommand()\r
181 {\r
182         uint8_t response[sizeof(sdDev.csd) + sizeof(sdDev.cid)];\r
183         memcpy(response, sdDev.csd, sizeof(sdDev.csd));\r
184         memcpy(response + sizeof(sdDev.csd), sdDev.cid, sizeof(sdDev.cid));\r
185 \r
186         hidPacket_send(response, sizeof(response));\r
187 }\r
188 \r
189 \r
190 static void\r
191 scsiTestCommand()\r
192 {\r
193         int resultCode = scsiSelfTest();\r
194         uint8_t response[] =\r
195         {\r
196                 resultCode == 0 ? S2S_CFG_STATUS_GOOD : S2S_CFG_STATUS_ERR,\r
197                 resultCode\r
198         };\r
199         hidPacket_send(response, sizeof(response));\r
200 }\r
201 \r
202 static void\r
203 scsiDevInfoCommand()\r
204 {\r
205         uint8_t response[] =\r
206         {\r
207                 FIRMWARE_VERSION >> 8,\r
208                 FIRMWARE_VERSION & 0xff,\r
209                 sdDev.capacity >> 24,\r
210                 sdDev.capacity >> 16,\r
211                 sdDev.capacity >> 8,\r
212                 sdDev.capacity,\r
213                 1 // useSdConfig, always true for V6.\r
214         };\r
215         hidPacket_send(response, sizeof(response));\r
216 }\r
217 \r
218 static void\r
219 debugCommand()\r
220 {\r
221         uint8_t response[32];\r
222         memcpy(&response, &scsiDev.cdb, 12);\r
223         response[12] = scsiDev.msgIn;\r
224         response[13] = scsiDev.msgOut;\r
225         response[14] = scsiDev.lastStatus;\r
226         response[15] = scsiDev.lastSense;\r
227         response[16] = scsiDev.phase;\r
228         response[17] = *SCSI_STS_SCSI;\r
229         response[18] = scsiDev.target != NULL ? scsiDev.target->syncOffset : 0;\r
230         response[19] = scsiDev.target != NULL ? scsiDev.target->syncPeriod : 0;\r
231         response[20] = scsiDev.minSyncPeriod;\r
232         response[21] = scsiDev.rstCount;\r
233         response[22] = scsiDev.selCount;\r
234         response[23] = scsiDev.msgCount;\r
235         response[24] = scsiDev.cmdCount;\r
236         response[25] = scsiDev.watchdogTick;\r
237         response[26] = blockDev.state;\r
238         response[27] = scsiDev.lastSenseASC >> 8;\r
239         response[28] = scsiDev.lastSenseASC;\r
240         response[29] = *SCSI_STS_DBX & 0xff; // What we've read\r
241         response[30] = *SCSI_STS_SELECTED;\r
242         response[31] = *SCSI_STS_DBX >> 8; // What we're writing\r
243         hidPacket_send(response, sizeof(response));\r
244 }\r
245 \r
246 static void\r
247 sdWriteCommand(const uint8_t* cmd, size_t cmdSize)\r
248 {\r
249         if (cmdSize < 517)\r
250         {\r
251                 return; // ignore.\r
252         }\r
253         uint32_t lba =\r
254                 (((uint32_t)cmd[1]) << 24) |\r
255                 (((uint32_t)cmd[2]) << 16) |\r
256                 (((uint32_t)cmd[3]) << 8) |\r
257                 ((uint32_t)cmd[4]);\r
258 \r
259         memcpy(configDmaBuf, &cmd[5], 512);\r
260         BSP_SD_WriteBlocks_DMA(configDmaBuf, lba, 1);\r
261 \r
262         uint8_t response[] =\r
263         {\r
264                 S2S_CFG_STATUS_GOOD\r
265         };\r
266         hidPacket_send(response, sizeof(response));\r
267 }\r
268 \r
269 static void\r
270 sdReadCommand(const uint8_t* cmd, size_t cmdSize)\r
271 {\r
272         if (cmdSize < 5)\r
273         {\r
274                 return; // ignore.\r
275         }\r
276         uint32_t lba =\r
277                 (((uint32_t)cmd[1]) << 24) |\r
278                 (((uint32_t)cmd[2]) << 16) |\r
279                 (((uint32_t)cmd[3]) << 8) |\r
280                 ((uint32_t)cmd[4]);\r
281 \r
282         BSP_SD_ReadBlocks_DMA(configDmaBuf, lba, 1);\r
283         hidPacket_send(configDmaBuf, 512);\r
284 }\r
285 \r
286 static void\r
287 processCommand(const uint8_t* cmd, size_t cmdSize)\r
288 {\r
289         switch (cmd[0])\r
290         {\r
291         case S2S_CMD_PING:\r
292                 pingCommand();\r
293                 break;\r
294 \r
295         case S2S_CMD_REBOOT:\r
296                 s2s_enterBootloader();\r
297                 break;\r
298 \r
299         case S2S_CMD_SDINFO:\r
300                 sdInfoCommand();\r
301                 break;\r
302 \r
303         case S2S_CMD_SCSITEST:\r
304                 scsiTestCommand();\r
305                 break;\r
306 \r
307         case S2S_CMD_DEVINFO:\r
308                 scsiDevInfoCommand();\r
309                 break;\r
310 \r
311         case S2S_CMD_SD_WRITE:\r
312                 sdWriteCommand(cmd, cmdSize);\r
313                 break;\r
314 \r
315         case S2S_CMD_SD_READ:\r
316                 sdReadCommand(cmd, cmdSize);\r
317                 break;\r
318 \r
319         case S2S_CMD_DEBUG:\r
320                 if (debugTimerStarted == 0) {\r
321                         debugInit();\r
322                 }\r
323                 debugCommand();\r
324                 break;\r
325 \r
326         case S2S_CMD_NONE: // invalid\r
327         default:\r
328                 break;\r
329         }\r
330 }\r
331 \r
332 void s2s_configPoll()\r
333 {\r
334         s2s_spin_lock(&usbDevLock);\r
335 \r
336         if (!USBD_Composite_IsConfigured(&configUsbDev))\r
337         {\r
338                 usbInEpState = USB_IDLE;\r
339                 hidPacket_reset();\r
340                 goto out;\r
341         }\r
342 \r
343         if (USBD_HID_IsReportReady(&configUsbDev))\r
344         {\r
345                 // Check if we have a previous command outstanding\r
346                 // before accepting another\r
347                 size_t cmdSize;\r
348                 if (hidPacket_peekPacket(&cmdSize) == NULL)\r
349                 {\r
350                         uint8_t hidBuffer[USBHID_LEN];\r
351                         int byteCount = USBD_HID_GetReport(&configUsbDev, hidBuffer, sizeof(hidBuffer));\r
352                         hidPacket_recv(hidBuffer, byteCount);\r
353                 }\r
354         }\r
355 \r
356         if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send\r
357         {\r
358                 size_t cmdSize;\r
359                 const uint8_t* cmd = hidPacket_getPacket(&cmdSize);\r
360                 if (cmd && (cmdSize > 0))\r
361                 {\r
362                         s2s_ledOn();\r
363                         processCommand(cmd, cmdSize);\r
364                         s2s_ledOff();\r
365                 }\r
366         }\r
367 \r
368         switch (usbInEpState)\r
369         {\r
370         case USB_IDLE:\r
371                 {\r
372                         uint8_t hidBuffer[USBHID_LEN];\r
373                         const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
374 \r
375                         if (nextChunk)\r
376                         {\r
377                                 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));\r
378                                 usbInEpState = USB_DATA_SENT;\r
379                         }\r
380                 }\r
381                 break;\r
382 \r
383         case USB_DATA_SENT:\r
384                 if (!USBD_HID_IsBusy(&configUsbDev))\r
385                 {\r
386                         // Data accepted.\r
387                         usbInEpState = USB_IDLE;\r
388                 }\r
389                 break;\r
390         }\r
391 \r
392 out:\r
393         s2s_spin_unlock(&usbDevLock);\r
394 }\r
395 \r
396 void s2s_debugTimer()\r
397 {\r
398         if (!USBD_Composite_IsConfigured(&configUsbDev))\r
399         {\r
400                 usbInEpState = USB_IDLE;\r
401                 return;\r
402         }\r
403 \r
404         if (USBD_HID_IsReportReady(&configUsbDev))\r
405         {\r
406                 // Check if we have a previous command outstanding\r
407                 // before accepting another\r
408                 size_t cmdSize;\r
409                 if (hidPacket_peekPacket(&cmdSize) == NULL)\r
410                 {\r
411                         uint8_t hidBuffer[USBHID_LEN];\r
412                         int byteCount = USBD_HID_GetReport(&configUsbDev, hidBuffer, sizeof(hidBuffer));\r
413                         hidPacket_recv(hidBuffer, byteCount);\r
414                 }\r
415         }\r
416 \r
417         if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send\r
418         {\r
419                 size_t cmdSize;\r
420                 const uint8_t* cmd = hidPacket_peekPacket(&cmdSize);\r
421                 // This is called from an ISR, only process simple commands.\r
422                 if (cmd && (cmdSize > 0))\r
423                 {\r
424                         if (cmd[0] == S2S_CMD_DEBUG)\r
425                         {\r
426                                 hidPacket_getPacket(&cmdSize);\r
427                                 debugCommand();\r
428                         }\r
429                         else if (cmd[0] == S2S_CMD_PING)\r
430                         {\r
431                                 hidPacket_getPacket(&cmdSize);\r
432                                 pingCommand();\r
433                         }\r
434                 }\r
435         }\r
436 \r
437         switch (usbInEpState)\r
438         {\r
439                 case USB_IDLE:\r
440                 {\r
441                         uint8_t hidBuffer[USBHID_LEN];\r
442                         const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
443 \r
444                         if (nextChunk)\r
445                         {\r
446                                 USBD_HID_SendReport (&configUsbDev, nextChunk, sizeof(hidBuffer));\r
447                                 usbInEpState = USB_DATA_SENT;\r
448                         }\r
449                 }\r
450                 break;\r
451 \r
452                 case USB_DATA_SENT:\r
453                         if (!USBD_HID_IsBusy(&configUsbDev))\r
454                         {\r
455                                 // Data accepted.\r
456                                 usbInEpState = USB_IDLE;\r
457                         }\r
458                         break;\r
459         }\r
460 }\r
461 \r
462 \r
463 \r
464 // Public method for storing MODE SELECT results.\r
465 void s2s_configSave(int scsiId, uint16_t bytesPerSector)\r
466 {\r
467         S2S_TargetCfg* cfg = (S2S_TargetCfg*) s2s_getConfigById(scsiId);\r
468         cfg->bytesPerSector = bytesPerSector;\r
469 \r
470         BSP_SD_WriteBlocks_DMA(\r
471                 &s2s_cfg[0],\r
472                 sdDev.capacity - S2S_CFG_SIZE,\r
473                 (S2S_CFG_SIZE + 511) / 512);\r
474 }\r
475 \r
476 \r
477 const S2S_TargetCfg* s2s_getConfigByIndex(int i)\r
478 {\r
479         return (const S2S_TargetCfg*)\r
480                 (s2s_cfg + sizeof(S2S_BoardCfg) + (i * sizeof(S2S_TargetCfg)));\r
481 }\r
482 \r
483 const S2S_TargetCfg* s2s_getConfigById(int scsiId)\r
484 {\r
485         int i;\r
486         for (i = 0; i < S2S_MAX_TARGETS; ++i)\r
487         {\r
488                 const S2S_TargetCfg* tgt = s2s_getConfigByIndex(i);\r
489                 if ((tgt->scsiId & S2S_CFG_TARGET_ID_BITS) == scsiId)\r
490                 {\r
491                         return tgt;\r
492                 }\r
493         }\r
494         return NULL;\r
495 \r
496 }\r
497 \r