V4.7 release fixes
[SCSI2SD.git] / software / SCSI2SD / src / 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 #pragma GCC push_options\r
18 #pragma GCC optimize("-flto")\r
19 \r
20 #include "device.h"\r
21 #include "config.h"\r
22 #include "debug.h"\r
23 #include "USBFS.h"\r
24 #include "led.h"\r
25 \r
26 #include "scsi.h"\r
27 #include "scsiPhy.h"\r
28 #include "disk.h"\r
29 #include "trace.h"\r
30 \r
31 #include "../../include/scsi2sd.h"\r
32 #include "../../include/hidpacket.h"\r
33 \r
34 #include <string.h>\r
35 \r
36 static const uint16_t FIRMWARE_VERSION = 0x0470;\r
37 \r
38 // 1 flash row\r
39 static const uint8_t DEFAULT_CONFIG[256] =\r
40 {\r
41         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00,\r
42         0x00, 0x02, 0x3F, 0x00, 0xFF, 0x00, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x73,\r
43         0x72, 0x63, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53,\r
44         0x43, 0x53, 0x49, 0x32, 0x53, 0x44, 0x20, 0x31, 0x2E, 0x30, 0x31, 0x32,\r
45         0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,\r
46         0x37, 0x38, 0x00, 0x00\r
47 };\r
48 \r
49 enum USB_ENDPOINTS\r
50 {\r
51         USB_EP_OUT = 1,\r
52         USB_EP_IN = 2,\r
53         USB_EP_COMMAND = 3,\r
54         USB_EP_DEBUG = 4\r
55 };\r
56 enum USB_STATE\r
57 {\r
58         USB_IDLE,\r
59         USB_DATA_SENT\r
60 };\r
61 \r
62 static uint8_t hidBuffer[USBHID_LEN];\r
63 \r
64 static int usbInEpState;\r
65 static int usbDebugEpState;\r
66 static int usbReady;\r
67 \r
68 static void initBoardConfig(BoardConfig* config) {\r
69         memcpy(\r
70                 config,\r
71                 (\r
72                         CY_FLASH_BASE +\r
73                         (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +\r
74                         (CY_FLASH_SIZEOF_ROW * SCSI_CONFIG_BOARD_ROW)\r
75                         ),\r
76                 sizeof(BoardConfig));\r
77 \r
78         if (memcmp(config->magic, "BCFG", 4)) {\r
79                 // Set a default from the deprecated flags, or 0 if\r
80                 // there is no initial config.\r
81                 config->flags = getConfigByIndex(0)->flagsDEPRECATED;\r
82 \r
83                 config->selectionDelay = 255; // auto\r
84         }\r
85 }\r
86 \r
87 void configInit(BoardConfig* config)\r
88 {\r
89         // The USB block will be powered by an internal 3.3V regulator.\r
90         // The PSoC must be operating between 4.6V and 5V for the regulator\r
91         // to work.\r
92         USBFS_Start(0, USBFS_5V_OPERATION);\r
93         usbInEpState = usbDebugEpState = USB_IDLE;\r
94         usbReady = 0; // We don't know if host is connected yet.\r
95 \r
96         int invalid = 1;\r
97         uint8_t* rawConfig = getConfigByIndex(0);\r
98         int i;\r
99         for (i = 0; i < 64; ++i)\r
100         {\r
101                 if (rawConfig[i])\r
102                 {\r
103                         invalid = 0;\r
104                         break;\r
105                 }\r
106         }\r
107         if (invalid)\r
108         {\r
109                 // Save a default config.\r
110                 CySetTemp();\r
111                 CyWriteRowData(SCSI_CONFIG_ARRAY, SCSI_CONFIG_0_ROW, DEFAULT_CONFIG);\r
112         }\r
113 \r
114         initBoardConfig(config);\r
115 }\r
116 \r
117 static void\r
118 readFlashCommand(const uint8_t* cmd, size_t cmdSize)\r
119 {\r
120         if (cmdSize < 3)\r
121         {\r
122                 return; // ignore.\r
123         }\r
124         uint8_t flashArray = cmd[1];\r
125         uint8_t flashRow = cmd[2];\r
126 \r
127         uint8_t* flash =\r
128                 CY_FLASH_BASE +\r
129                 (CY_FLASH_SIZEOF_ARRAY * (size_t) flashArray) +\r
130                 (CY_FLASH_SIZEOF_ROW * (size_t) flashRow);\r
131 \r
132         hidPacket_send(flash, SCSI_CONFIG_ROW_SIZE);\r
133 }\r
134 \r
135 static void\r
136 writeFlashCommand(const uint8_t* cmd, size_t cmdSize)\r
137 {\r
138         if (cmdSize < 259)\r
139         {\r
140                 return; // ignore.\r
141         }\r
142         uint8_t flashArray = cmd[257];\r
143         uint8_t flashRow = cmd[258];\r
144 \r
145         CySetTemp();\r
146         int status = CyWriteRowData(flashArray, flashRow, cmd + 1);\r
147 \r
148         uint8_t response[] =\r
149         {\r
150                 status == CYRET_SUCCESS ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR\r
151         };\r
152         hidPacket_send(response, sizeof(response));\r
153 }\r
154 \r
155 static void\r
156 pingCommand()\r
157 {\r
158         uint8_t response[] =\r
159         {\r
160                 CONFIG_STATUS_GOOD\r
161         };\r
162         hidPacket_send(response, sizeof(response));\r
163 }\r
164 \r
165 static void\r
166 sdInfoCommand()\r
167 {\r
168         uint8_t response[sizeof(sdDev.csd) + sizeof(sdDev.cid)];\r
169         memcpy(response, sdDev.csd, sizeof(sdDev.csd));\r
170         memcpy(response + sizeof(sdDev.csd), sdDev.cid, sizeof(sdDev.cid));\r
171 \r
172         hidPacket_send(response, sizeof(response));\r
173 }\r
174 \r
175 \r
176 static void\r
177 scsiTestCommand()\r
178 {\r
179         int resultCode = scsiSelfTest();\r
180         uint8_t response[] =\r
181         {\r
182                 resultCode == 0 ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR,\r
183                 resultCode\r
184         };\r
185         hidPacket_send(response, sizeof(response));\r
186 }\r
187 \r
188 static void\r
189 processCommand(const uint8_t* cmd, size_t cmdSize)\r
190 {\r
191         switch (cmd[0])\r
192         {\r
193         case CONFIG_PING:\r
194                 pingCommand();\r
195                 break;\r
196 \r
197         case CONFIG_READFLASH:\r
198                 readFlashCommand(cmd, cmdSize);\r
199                 break;\r
200 \r
201         case CONFIG_WRITEFLASH:\r
202                 writeFlashCommand(cmd, cmdSize);\r
203                 break;\r
204 \r
205         case CONFIG_REBOOT:\r
206                 Bootloadable_1_Load();\r
207                 break;\r
208 \r
209         case CONFIG_SDINFO:\r
210                 sdInfoCommand();\r
211                 break;\r
212 \r
213         case CONFIG_SCSITEST:\r
214                 scsiTestCommand();\r
215                 break;\r
216 \r
217         case CONFIG_NONE: // invalid\r
218         default:\r
219                 break;\r
220         }\r
221 }\r
222 \r
223 void configPoll()\r
224 {\r
225         int reset = 0;\r
226         if (!usbReady || USBFS_IsConfigurationChanged())\r
227         {\r
228                 reset = 1;\r
229         }\r
230         usbReady = USBFS_bGetConfiguration();\r
231 \r
232         if (!usbReady)\r
233         {\r
234                 return;\r
235         }\r
236 \r
237         if (reset)\r
238         {\r
239                 USBFS_EnableOutEP(USB_EP_OUT);\r
240                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
241                 usbInEpState = usbDebugEpState = USB_IDLE;\r
242         }\r
243 \r
244         if(USBFS_GetEPState(USB_EP_OUT) == USBFS_OUT_BUFFER_FULL)\r
245         {\r
246                 ledOn();\r
247 \r
248                 // The host sent us some data!\r
249                 int byteCount = USBFS_GetEPCount(USB_EP_OUT);\r
250                 USBFS_ReadOutEP(USB_EP_OUT, hidBuffer, sizeof(hidBuffer));\r
251                 hidPacket_recv(hidBuffer, byteCount);\r
252 \r
253                 size_t cmdSize;\r
254                 const uint8_t* cmd = hidPacket_getPacket(&cmdSize);\r
255                 if (cmd && (cmdSize > 0))\r
256                 {\r
257                         processCommand(cmd, cmdSize);\r
258                 }\r
259 \r
260                 // Allow the host to send us another updated config.\r
261                 USBFS_EnableOutEP(USB_EP_OUT);\r
262 \r
263                 ledOff();\r
264         }\r
265 \r
266         switch (usbInEpState)\r
267         {\r
268         case USB_IDLE:\r
269                 {\r
270                         const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
271 \r
272                         if (nextChunk)\r
273                         {\r
274                                 USBFS_LoadInEP(USB_EP_IN, nextChunk, sizeof(hidBuffer));\r
275                                 usbInEpState = USB_DATA_SENT;\r
276                         }\r
277                 }\r
278                 break;\r
279 \r
280         case USB_DATA_SENT:\r
281                 if (USBFS_bGetEPAckState(USB_EP_IN))\r
282                 {\r
283                         // Data accepted.\r
284                         usbInEpState = USB_IDLE;\r
285                 }\r
286                 break;\r
287         }\r
288 }\r
289 \r
290 void debugPoll()\r
291 {\r
292         if (!usbReady)\r
293         {\r
294                 return;\r
295         }\r
296 \r
297         if(USBFS_GetEPState(USB_EP_COMMAND) == USBFS_OUT_BUFFER_FULL)\r
298         {\r
299                 // The host sent us some data!\r
300                 int byteCount = USBFS_GetEPCount(USB_EP_COMMAND);\r
301                 USBFS_ReadOutEP(USB_EP_COMMAND, (uint8 *)&hidBuffer, byteCount);\r
302 \r
303                 if (byteCount >= 1 &&\r
304                         hidBuffer[0] == 0x01)\r
305                 {\r
306                         // Reboot command.\r
307                         Bootloadable_1_Load();\r
308                 }\r
309 \r
310                 // Allow the host to send us another command.\r
311                 // (assuming we didn't reboot outselves)\r
312                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
313         }\r
314 \r
315         switch (usbDebugEpState)\r
316         {\r
317         case USB_IDLE:\r
318                 memcpy(&hidBuffer, &scsiDev.cdb, 12);\r
319                 hidBuffer[12] = scsiDev.msgIn;\r
320                 hidBuffer[13] = scsiDev.msgOut;\r
321                 hidBuffer[14] = scsiDev.lastStatus;\r
322                 hidBuffer[15] = scsiDev.lastSense;\r
323                 hidBuffer[16] = scsiDev.phase;\r
324                 hidBuffer[17] = SCSI_ReadFilt(SCSI_Filt_BSY);\r
325                 hidBuffer[18] = SCSI_ReadFilt(SCSI_Filt_SEL);\r
326                 hidBuffer[19] = SCSI_ReadFilt(SCSI_Filt_ATN);\r
327                 hidBuffer[20] = SCSI_ReadFilt(SCSI_Filt_RST);\r
328                 hidBuffer[21] = scsiDev.rstCount;\r
329                 hidBuffer[22] = scsiDev.selCount;\r
330                 hidBuffer[23] = scsiDev.msgCount;\r
331                 hidBuffer[24] = scsiDev.cmdCount;\r
332                 hidBuffer[25] = scsiDev.watchdogTick;\r
333                 hidBuffer[26] = blockDev.state;\r
334                 hidBuffer[27] = scsiDev.lastSenseASC >> 8;\r
335                 hidBuffer[28] = scsiDev.lastSenseASC;\r
336                 hidBuffer[29] = scsiReadDBxPins();\r
337                 hidBuffer[30] = LastTrace;\r
338 \r
339                 hidBuffer[58] = sdDev.capacity >> 24;\r
340                 hidBuffer[59] = sdDev.capacity >> 16;\r
341                 hidBuffer[60] = sdDev.capacity >> 8;\r
342                 hidBuffer[61] = sdDev.capacity;\r
343 \r
344                 hidBuffer[62] = FIRMWARE_VERSION >> 8;\r
345                 hidBuffer[63] = FIRMWARE_VERSION;\r
346 \r
347                 USBFS_LoadInEP(USB_EP_DEBUG, (uint8 *)&hidBuffer, sizeof(hidBuffer));\r
348                 usbDebugEpState = USB_DATA_SENT;\r
349                 break;\r
350 \r
351         case USB_DATA_SENT:\r
352                 if (USBFS_bGetEPAckState(USB_EP_DEBUG))\r
353                 {\r
354                         // Data accepted.\r
355                         usbDebugEpState = USB_IDLE;\r
356                 }\r
357                 break;\r
358         }\r
359 }\r
360 \r
361 CY_ISR(debugTimerISR)\r
362 {\r
363         Debug_Timer_ReadStatusRegister();\r
364         Debug_Timer_Interrupt_ClearPending();\r
365         uint8 savedIntrStatus = CyEnterCriticalSection();\r
366         debugPoll();\r
367         CyExitCriticalSection(savedIntrStatus);\r
368 }\r
369 \r
370 void debugInit()\r
371 {\r
372         Debug_Timer_Interrupt_StartEx(debugTimerISR);\r
373         Debug_Timer_Start();\r
374 }\r
375 \r
376 void debugPause()\r
377 {\r
378         Debug_Timer_Stop();\r
379 }\r
380 \r
381 void debugResume()\r
382 {\r
383         Debug_Timer_Start();\r
384 }\r
385 \r
386 int isDebugEnabled()\r
387 {\r
388         return usbReady;\r
389 }\r
390 \r
391 // Public method for storing MODE SELECT results.\r
392 void configSave(int scsiId, uint16_t bytesPerSector)\r
393 {\r
394         int cfgIdx;\r
395         for (cfgIdx = 0; cfgIdx < MAX_SCSI_TARGETS; ++cfgIdx)\r
396         {\r
397                 const TargetConfig* tgt = getConfigByIndex(cfgIdx);\r
398                 if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)\r
399                 {\r
400                         // Save row to flash\r
401                         // We only save the first row of the configuration\r
402                         // this contains the parameters changeable by a MODE SELECT command\r
403                         uint8_t rowData[CYDEV_FLS_ROW_SIZE];\r
404                         TargetConfig* rowCfgData = (TargetConfig*)&rowData;\r
405                         memcpy(rowCfgData, tgt, sizeof(rowData));\r
406                         rowCfgData->bytesPerSector = bytesPerSector;\r
407 \r
408                         CySetTemp();\r
409                         CyWriteRowData(\r
410                                 SCSI_CONFIG_ARRAY,\r
411                                 SCSI_CONFIG_0_ROW + (cfgIdx * SCSI_CONFIG_ROWS),\r
412                                 (uint8_t*)rowCfgData);\r
413                         return;\r
414                 }\r
415         }\r
416 }\r
417 \r
418 \r
419 const TargetConfig* getConfigByIndex(int i)\r
420 {\r
421         if (i <= 3)\r
422         {\r
423                 size_t row = SCSI_CONFIG_0_ROW + (i * SCSI_CONFIG_ROWS);\r
424                 return (const TargetConfig*)\r
425                         (\r
426                                 CY_FLASH_BASE +\r
427                                 (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +\r
428                                 (CY_FLASH_SIZEOF_ROW * row)\r
429                                 );\r
430         } else {\r
431                 size_t row = SCSI_CONFIG_4_ROW + ((i-4) * SCSI_CONFIG_ROWS);\r
432                 return (const TargetConfig*)\r
433                         (\r
434                                 CY_FLASH_BASE +\r
435                                 (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +\r
436                                 (CY_FLASH_SIZEOF_ROW * row)\r
437                                 );\r
438         }\r
439 }\r
440 \r
441 const TargetConfig* getConfigById(int scsiId)\r
442 {\r
443         int i;\r
444         for (i = 0; i < MAX_SCSI_TARGETS; ++i)\r
445         {\r
446                 const TargetConfig* tgt = getConfigByIndex(i);\r
447                 if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)\r
448                 {\r
449                         return tgt;\r
450                 }\r
451         }\r
452         return NULL;\r
453 \r
454 }\r
455 \r
456 #pragma GCC pop_options\r