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