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