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