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