Implement WRITE BUFFER and WRITE WITH VERIFY commands
[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 = 0x0411;\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 static void\r
136 processCommand(const uint8_t* cmd, size_t cmdSize)\r
137 {\r
138         switch (cmd[0])\r
139         {\r
140         case CONFIG_PING:\r
141                 pingCommand();\r
142                 break;\r
143 \r
144         case CONFIG_READFLASH:\r
145                 readFlashCommand(cmd, cmdSize);\r
146                 break;\r
147 \r
148         case CONFIG_WRITEFLASH:\r
149                 writeFlashCommand(cmd, cmdSize);\r
150                 break;\r
151 \r
152         case CONFIG_REBOOT:\r
153                 Bootloadable_1_Load();\r
154                 break;\r
155 \r
156         case CONFIG_SDINFO:\r
157                 sdInfoCommand();\r
158                 break;\r
159 \r
160         case CONFIG_NONE: // invalid\r
161         default:\r
162                 break;\r
163         }\r
164 }\r
165 \r
166 void configPoll()\r
167 {\r
168         int reset = 0;\r
169         if (!usbReady || USBFS_IsConfigurationChanged())\r
170         {\r
171                 reset = 1;\r
172         }\r
173         usbReady = USBFS_bGetConfiguration();\r
174 \r
175         if (!usbReady)\r
176         {\r
177                 return;\r
178         }\r
179 \r
180         if (reset)\r
181         {\r
182                 USBFS_EnableOutEP(USB_EP_OUT);\r
183                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
184                 usbInEpState = usbDebugEpState = USB_IDLE;\r
185         }\r
186 \r
187         if(USBFS_GetEPState(USB_EP_OUT) == USBFS_OUT_BUFFER_FULL)\r
188         {\r
189                 ledOn();\r
190 \r
191                 // The host sent us some data!\r
192                 int byteCount = USBFS_GetEPCount(USB_EP_OUT);\r
193                 USBFS_ReadOutEP(USB_EP_OUT, hidBuffer, sizeof(hidBuffer));\r
194                 hidPacket_recv(hidBuffer, byteCount);\r
195 \r
196                 size_t cmdSize;\r
197                 const uint8_t* cmd = hidPacket_getPacket(&cmdSize);\r
198                 if (cmd && (cmdSize > 0))\r
199                 {\r
200                         processCommand(cmd, cmdSize);\r
201                 }\r
202 \r
203                 // Allow the host to send us another updated config.\r
204                 USBFS_EnableOutEP(USB_EP_OUT);\r
205 \r
206                 ledOff();\r
207         }\r
208 \r
209         switch (usbInEpState)\r
210         {\r
211         case USB_IDLE:\r
212                 {\r
213                         const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
214 \r
215                         if (nextChunk)\r
216                         {\r
217                                 USBFS_LoadInEP(USB_EP_IN, nextChunk, sizeof(hidBuffer));\r
218                                 usbInEpState = USB_DATA_SENT;\r
219                         }\r
220                 }\r
221                 break;\r
222 \r
223         case USB_DATA_SENT:\r
224                 if (USBFS_bGetEPAckState(USB_EP_IN))\r
225                 {\r
226                         // Data accepted.\r
227                         usbInEpState = USB_IDLE;\r
228                 }\r
229                 break;\r
230         }\r
231 }\r
232 \r
233 void debugPoll()\r
234 {\r
235         if (!usbReady)\r
236         {\r
237                 return;\r
238         }\r
239 \r
240         if(USBFS_GetEPState(USB_EP_COMMAND) == USBFS_OUT_BUFFER_FULL)\r
241         {\r
242                 // The host sent us some data!\r
243                 int byteCount = USBFS_GetEPCount(USB_EP_COMMAND);\r
244                 USBFS_ReadOutEP(USB_EP_COMMAND, (uint8 *)&hidBuffer, byteCount);\r
245 \r
246                 if (byteCount >= 1 &&\r
247                         hidBuffer[0] == 0x01)\r
248                 {\r
249                         // Reboot command.\r
250                         Bootloadable_1_Load();\r
251                 }\r
252 \r
253                 // Allow the host to send us another command.\r
254                 // (assuming we didn't reboot outselves)\r
255                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
256         }\r
257 \r
258         switch (usbDebugEpState)\r
259         {\r
260         case USB_IDLE:\r
261                 memcpy(&hidBuffer, &scsiDev.cdb, 12);\r
262                 hidBuffer[12] = scsiDev.msgIn;\r
263                 hidBuffer[13] = scsiDev.msgOut;\r
264                 hidBuffer[14] = scsiDev.lastStatus;\r
265                 hidBuffer[15] = scsiDev.lastSense;\r
266                 hidBuffer[16] = scsiDev.phase;\r
267                 hidBuffer[17] = SCSI_ReadFilt(SCSI_Filt_BSY);\r
268                 hidBuffer[18] = SCSI_ReadFilt(SCSI_Filt_SEL);\r
269                 hidBuffer[19] = SCSI_ReadFilt(SCSI_Filt_ATN);\r
270                 hidBuffer[20] = SCSI_ReadFilt(SCSI_Filt_RST);\r
271                 hidBuffer[21] = scsiDev.rstCount;\r
272                 hidBuffer[22] = scsiDev.selCount;\r
273                 hidBuffer[23] = scsiDev.msgCount;\r
274                 hidBuffer[24] = scsiDev.cmdCount;\r
275                 hidBuffer[25] = scsiDev.watchdogTick;\r
276                 hidBuffer[26] = blockDev.state;\r
277                 hidBuffer[27] = scsiDev.lastSenseASC >> 8;\r
278                 hidBuffer[28] = scsiDev.lastSenseASC;\r
279                 hidBuffer[29] = scsiReadDBxPins();\r
280 \r
281                 hidBuffer[58] = sdDev.capacity >> 24;\r
282                 hidBuffer[59] = sdDev.capacity >> 16;\r
283                 hidBuffer[60] = sdDev.capacity >> 8;\r
284                 hidBuffer[61] = sdDev.capacity;\r
285 \r
286                 hidBuffer[62] = FIRMWARE_VERSION >> 8;\r
287                 hidBuffer[63] = FIRMWARE_VERSION;\r
288 \r
289                 USBFS_LoadInEP(USB_EP_DEBUG, (uint8 *)&hidBuffer, sizeof(hidBuffer));\r
290                 usbDebugEpState = USB_DATA_SENT;\r
291                 break;\r
292 \r
293         case USB_DATA_SENT:\r
294                 if (USBFS_bGetEPAckState(USB_EP_DEBUG))\r
295                 {\r
296                         // Data accepted.\r
297                         usbDebugEpState = USB_IDLE;\r
298                 }\r
299                 break;\r
300         }\r
301 }\r
302 \r
303 CY_ISR(debugTimerISR)\r
304 {\r
305         Debug_Timer_ReadStatusRegister();\r
306         Debug_Timer_Interrupt_ClearPending();\r
307         uint8 savedIntrStatus = CyEnterCriticalSection();\r
308         debugPoll();\r
309         CyExitCriticalSection(savedIntrStatus);\r
310 }\r
311 \r
312 void debugInit()\r
313 {\r
314         Debug_Timer_Interrupt_StartEx(debugTimerISR);\r
315         Debug_Timer_Start();\r
316 }\r
317 \r
318 void debugPause()\r
319 {\r
320         Debug_Timer_Stop();\r
321 }\r
322 \r
323 void debugResume()\r
324 {\r
325         Debug_Timer_Start();\r
326 }\r
327 \r
328 int isDebugEnabled()\r
329 {\r
330         return usbReady;\r
331 }\r
332 \r
333 // Public method for storing MODE SELECT results.\r
334 void configSave(int scsiId, uint16_t bytesPerSector)\r
335 {\r
336         int cfgIdx;\r
337         for (cfgIdx = 0; cfgIdx < MAX_SCSI_TARGETS; ++cfgIdx)\r
338         {\r
339                 const TargetConfig* tgt = getConfigByIndex(cfgIdx);\r
340                 if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)\r
341                 {\r
342                         // Save row to flash\r
343                         // We only save the first row of the configuration\r
344                         // this contains the parameters changeable by a MODE SELECT command\r
345                         uint8_t rowData[CYDEV_FLS_ROW_SIZE];\r
346                         TargetConfig* rowCfgData = (TargetConfig*)&rowData;\r
347                         memcpy(rowCfgData, tgt, sizeof(rowData));\r
348                         rowCfgData->bytesPerSector = bytesPerSector;\r
349 \r
350                         CySetTemp();\r
351                         CyWriteRowData(\r
352                                 SCSI_CONFIG_ARRAY,\r
353                                 SCSI_CONFIG_0_ROW + (cfgIdx * SCSI_CONFIG_ROWS),\r
354                                 (uint8_t*)rowCfgData);\r
355                         return;\r
356                 }\r
357         }\r
358 }\r
359 \r
360 \r
361 const TargetConfig* getConfigByIndex(int i)\r
362 {\r
363         size_t row = SCSI_CONFIG_0_ROW + (i * SCSI_CONFIG_ROWS);\r
364         return (const TargetConfig*)\r
365                 (\r
366                         CY_FLASH_BASE +\r
367                         (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +\r
368                         (CY_FLASH_SIZEOF_ROW * row)\r
369                         );\r
370 }\r
371 \r
372 const TargetConfig* getConfigById(int scsiId)\r
373 {\r
374         int i;\r
375         for (i = 0; i < MAX_SCSI_TARGETS; ++i)\r
376         {\r
377                 const TargetConfig* tgt = getConfigByIndex(i);\r
378                 if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)\r
379                 {\r
380                         return tgt;\r
381                 }\r
382         }\r
383         return NULL;\r
384 \r
385 }\r
386 \r
387 #pragma GCC pop_options\r