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