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