ddc146adfbf88994c1c8ef360278e1351492b2d3
[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 = 0x0485;\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 static uint8_t dbgHidBuffer[USBHID_LEN];\r
62 \r
63 static int usbInEpState;\r
64 static int usbDebugEpState;\r
65 static int usbReady;\r
66 \r
67 static void initBoardConfig(S2S_BoardConfig* config) {\r
68         memcpy(\r
69                 config,\r
70                 (\r
71                         CY_FLASH_BASE +\r
72                         (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +\r
73                         (CY_FLASH_SIZEOF_ROW * SCSI_CONFIG_BOARD_ROW)\r
74                         ),\r
75                 sizeof(S2S_BoardConfig));\r
76 \r
77         if (memcmp(config->magic, "BCFG", 4)) {\r
78                 // Set a default from the deprecated flags, or 0 if\r
79                 // there is no initial config.\r
80                 config->flags = getConfigByIndex(0)->flagsDEPRECATED;\r
81 \r
82                 config->selectionDelay = 255; // auto\r
83                 config->flags6 = S2S_CFG_ENABLE_TERMINATOR;\r
84         }\r
85 }\r
86 \r
87 void configInit(S2S_BoardConfig* config)\r
88 {\r
89         // The USB block will be powered by an internal 3.3V regulator.\r
90         // The PSoC must be operating between 4.6V and 5V for the regulator\r
91         // to work.\r
92         USBFS_Start(0, USBFS_5V_OPERATION);\r
93         usbInEpState = usbDebugEpState = USB_IDLE;\r
94         usbReady = 0; // We don't know if host is connected yet.\r
95 \r
96         int invalid = 1;\r
97         uint8_t* rawConfig = (uint8_t*)getConfigByIndex(0);\r
98         int i;\r
99         for (i = 0; i < 64; ++i)\r
100         {\r
101                 if (rawConfig[i])\r
102                 {\r
103                         invalid = 0;\r
104                         break;\r
105                 }\r
106         }\r
107         if (invalid)\r
108         {\r
109                 // Save a default config.\r
110                 CySetTemp();\r
111                 CyWriteRowData(SCSI_CONFIG_ARRAY, SCSI_CONFIG_0_ROW, DEFAULT_CONFIG);\r
112         }\r
113 \r
114         initBoardConfig(config);\r
115 }\r
116 \r
117 static void\r
118 readFlashCommand(const uint8_t* cmd, size_t cmdSize)\r
119 {\r
120         if (cmdSize < 3)\r
121         {\r
122                 return; // ignore.\r
123         }\r
124         uint8_t flashArray = cmd[1];\r
125         uint8_t flashRow = cmd[2];\r
126 \r
127         uint8_t* flash =\r
128                 CY_FLASH_BASE +\r
129                 (CY_FLASH_SIZEOF_ARRAY * (size_t) flashArray) +\r
130                 (CY_FLASH_SIZEOF_ROW * (size_t) flashRow);\r
131 \r
132         hidPacket_send(flash, SCSI_CONFIG_ROW_SIZE);\r
133 }\r
134 \r
135 static void\r
136 writeFlashCommand(const uint8_t* cmd, size_t cmdSize)\r
137 {\r
138         if (cmdSize < 259)\r
139         {\r
140                 return; // ignore.\r
141         }\r
142         uint8_t flashArray = cmd[257];\r
143         uint8_t flashRow = cmd[258];\r
144 \r
145         // Be very careful not to overwrite the bootloader or other\r\r
146         // code. Bootloader updates no longer supported. Use v5.1 board\r
147         // instead.\r
148         if ((flashArray != SCSI_CONFIG_ARRAY) ||\r
149                 (flashRow < SCSI_CONFIG_4_ROW) ||\r
150                 (flashRow >= SCSI_CONFIG_3_ROW + SCSI_CONFIG_ROWS))\r
151         {\r
152                 uint8_t response[] = { CONFIG_STATUS_ERR };\r
153                 hidPacket_send(response, sizeof(response));\r
154         }\r
155 \r
156         CySetTemp();\r
157         int status = CyWriteRowData(flashArray, flashRow, cmd + 1);\r
158 \r
159         uint8_t response[] =\r
160         {\r
161                 status == CYRET_SUCCESS ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR\r
162         };\r
163         hidPacket_send(response, sizeof(response));\r
164 }\r
165 \r
166 static void\r
167 pingCommand()\r
168 {\r
169         uint8_t response[] =\r
170         {\r
171                 CONFIG_STATUS_GOOD\r
172         };\r
173         hidPacket_send(response, sizeof(response));\r
174 }\r
175 \r
176 static void\r
177 sdInfoCommand()\r
178 {\r
179         uint8_t response[sizeof(sdCard.csd) + sizeof(sdCard.cid)];\r
180         memcpy(response, sdCard.csd, sizeof(sdCard.csd));\r
181         memcpy(response + sizeof(sdCard.csd), sdCard.cid, sizeof(sdCard.cid));\r
182 \r
183         hidPacket_send(response, sizeof(response));\r
184 }\r
185 \r
186 \r
187 static void\r
188 scsiTestCommand()\r
189 {\r
190         int resultCode = scsiSelfTest();\r
191         uint8_t response[] =\r
192         {\r
193                 resultCode == 0 ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR,\r
194                 resultCode\r
195         };\r
196         hidPacket_send(response, sizeof(response));\r
197 }\r
198 \r
199 static void\r
200 deviceListCommand()\r
201 {\r
202     int deviceCount;\r
203     S2S_Device** devices = s2s_GetDevices(&deviceCount);\r
204     \r
205     uint8_t response[16] = // Make larger if there can be more than 2 devices\r
206     {\r
207         deviceCount\r
208     };\r
209     \r
210     int pos = 1;\r
211     \r
212     for (int i = 0; i < deviceCount; ++i)\r
213     {\r
214         response[pos++] = devices[i]->deviceType;\r
215         \r
216         uint32_t capacity = devices[i]->getCapacity(devices[i]);\r
217         response[pos++] = capacity >> 24;\r
218         response[pos++] = capacity >> 16;\r
219         response[pos++] = capacity >> 8;\r
220         response[pos++] = capacity;\r
221     }\r
222     \r
223     hidPacket_send(response, pos);\r
224 }\r
225 \r
226 static void\r
227 deviceEraseCommand(const uint8_t* cmd)\r
228 {\r
229     int deviceCount;\r
230     S2S_Device** devices = s2s_GetDevices(&deviceCount);\r
231     \r
232     uint32_t sectorNum =\r
233         ((uint32_t)cmd[2]) << 24 |\r
234         ((uint32_t)cmd[3]) << 16 |\r
235         ((uint32_t)cmd[4]) << 8 |\r
236         ((uint32_t)cmd[5]);\r
237 \r
238     uint32_t count =\r
239         ((uint32_t)cmd[6]) << 24 |\r
240         ((uint32_t)cmd[7]) << 16 |\r
241         ((uint32_t)cmd[8]) << 8 |\r
242         ((uint32_t)cmd[9]);\r
243 \r
244     devices[cmd[1]]->erase(devices[cmd[1]], sectorNum, count);\r
245     \r
246         uint8_t response[] =\r
247         {\r
248                 CONFIG_STATUS_GOOD\r
249         };\r
250     hidPacket_send(response, sizeof(response));\r
251 }\r
252 \r
253 static void\r
254 deviceWriteCommand(const uint8_t* cmd)\r
255 {\r
256     int deviceCount;\r
257     S2S_Device** devices = s2s_GetDevices(&deviceCount);\r
258     \r
259     uint32_t sectorNum =\r
260         ((uint32_t)cmd[2]) << 24 |\r
261         ((uint32_t)cmd[3]) << 16 |\r
262         ((uint32_t)cmd[4]) << 8 |\r
263         ((uint32_t)cmd[5]);\r
264 \r
265     devices[cmd[1]]->write(devices[cmd[1]], sectorNum, 1, &cmd[6]);\r
266     \r
267         uint8_t response[] =\r
268         {\r
269                 CONFIG_STATUS_GOOD\r
270         };\r
271     hidPacket_send(response, sizeof(response));\r
272 }\r
273 \r
274 \r
275 static void\r
276 deviceReadCommand(const uint8_t* cmd)\r
277 {\r
278     int deviceCount;\r
279     S2S_Device** devices = s2s_GetDevices(&deviceCount);\r
280     \r
281     uint32_t sectorNum =\r
282         ((uint32_t)cmd[2]) << 24 |\r
283         ((uint32_t)cmd[3]) << 16 |\r
284         ((uint32_t)cmd[4]) << 8 |\r
285         ((uint32_t)cmd[5]);\r
286 \r
287     uint8_t response[512];\r
288     devices[cmd[1]]->read(devices[cmd[1]], sectorNum, 1, &response[0]);\r
289     \r
290     hidPacket_send(&response[0], 512);\r
291 }\r
292 \r
293 static void\r
294 processCommand(const uint8_t* cmd, size_t cmdSize)\r
295 {\r
296         switch (cmd[0])\r
297         {\r
298         case CONFIG_PING:\r
299                 pingCommand();\r
300                 break;\r
301 \r
302         case CONFIG_READFLASH:\r
303                 readFlashCommand(cmd, cmdSize);\r
304                 break;\r
305 \r
306         case CONFIG_WRITEFLASH:\r
307                 writeFlashCommand(cmd, cmdSize);\r
308                 break;\r
309 \r
310         case CONFIG_REBOOT:\r
311                 Bootloadable_1_Load();\r
312                 break;\r
313 \r
314         case CONFIG_SDINFO:\r
315                 sdInfoCommand();\r
316                 break;\r
317 \r
318         case CONFIG_SCSITEST:\r
319                 scsiTestCommand();\r
320                 break;\r
321 \r
322     case S2S_CMD_DEV_LIST:\r
323         deviceListCommand();\r
324         break;\r
325 \r
326     case S2S_CMD_DEV_ERASE:\r
327         deviceEraseCommand(cmd);\r
328         break;\r
329 \r
330     case S2S_CMD_DEV_WRITE:\r
331         deviceWriteCommand(cmd);\r
332         break;\r
333 \r
334     case S2S_CMD_DEV_READ:\r
335         deviceReadCommand(cmd);\r
336         break;\r
337         \r
338         case CONFIG_NONE: // invalid\r
339         default:\r
340                 break;\r
341         }\r
342 }\r
343 \r
344 void configPoll()\r
345 {\r
346         int reset = 0;\r
347         if (!usbReady || USBFS_IsConfigurationChanged())\r
348         {\r
349                 reset = 1;\r
350         }\r
351         usbReady = USBFS_bGetConfiguration();\r
352 \r
353         if (!usbReady)\r
354         {\r
355                 return;\r
356         }\r
357 \r
358         if (reset)\r
359         {\r
360         hidPacket_reset();\r
361                 USBFS_EnableOutEP(USB_EP_OUT);\r
362                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
363                 usbInEpState = usbDebugEpState = USB_IDLE;\r
364         }\r
365 \r
366         if(USBFS_GetEPState(USB_EP_OUT) == USBFS_OUT_BUFFER_FULL)\r
367         {\r
368                 // The host sent us some data!\r
369                 int byteCount = USBFS_GetEPCount(USB_EP_OUT);\r
370                 USBFS_ReadOutEP(USB_EP_OUT, hidBuffer, sizeof(hidBuffer));\r
371                 hidPacket_recv(hidBuffer, byteCount);\r
372         \r
373         size_t cmdSize;\r
374         if (hidPacket_peekPacket(&cmdSize) == NULL)\r
375         {\r
376             // Allow the host to send us another updated config.\r
377                     USBFS_EnableOutEP(USB_EP_OUT);\r
378         }\r
379     }\r
380     \r
381     if (hidPacket_getHIDBytesReady() == 0) // Nothing queued to send\r
382     {\r
383                 size_t cmdSize;\r
384                 const uint8_t* cmd = hidPacket_getPacket(&cmdSize);\r
385                 if (cmd && (cmdSize > 0))\r
386                 {\r
387             ledOn();\r
388                         processCommand(cmd, cmdSize);\r
389             ledOff();\r
390             \r
391             // Allow the host to send us another updated config.\r
392                     USBFS_EnableOutEP(USB_EP_OUT);\r
393                 }\r
394         }\r
395 \r
396         switch (usbInEpState)\r
397         {\r
398         case USB_IDLE:\r
399                 {\r
400                         const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
401 \r
402                         if (nextChunk)\r
403                         {\r
404                                 USBFS_LoadInEP(USB_EP_IN, nextChunk, sizeof(hidBuffer));\r
405                                 usbInEpState = USB_DATA_SENT;\r
406                         }\r
407                 }\r
408                 break;\r
409 \r
410         case USB_DATA_SENT:\r
411                 if (USBFS_bGetEPAckState(USB_EP_IN))\r
412                 {\r
413                         // Data accepted.\r
414                         usbInEpState = USB_IDLE;\r
415                 }\r
416                 break;\r
417         }\r
418 }\r
419 \r
420 void debugPoll()\r
421 {\r
422         if (!usbReady)\r
423         {\r
424                 return;\r
425         }\r
426 \r
427         if(USBFS_GetEPState(USB_EP_COMMAND) == USBFS_OUT_BUFFER_FULL)\r
428         {\r
429                 // The host sent us some data!\r
430                 int byteCount = USBFS_GetEPCount(USB_EP_COMMAND);\r
431                 USBFS_ReadOutEP(USB_EP_COMMAND, (uint8 *)&dbgHidBuffer, byteCount);\r
432 \r
433                 if (byteCount >= 1 &&\r
434                         dbgHidBuffer[0] == 0x01)\r
435                 {\r
436                         // Reboot command.\r
437                         Bootloadable_1_Load();\r
438                 }\r
439 \r
440                 // Allow the host to send us another command.\r
441                 // (assuming we didn't reboot outselves)\r
442                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
443         }\r
444 \r
445         switch (usbDebugEpState)\r
446         {\r
447         case USB_IDLE:\r
448                 memcpy(&dbgHidBuffer, &scsiDev.cdb, 12);\r
449                 dbgHidBuffer[12] = scsiDev.msgIn;\r
450                 dbgHidBuffer[13] = scsiDev.msgOut;\r
451                 dbgHidBuffer[14] = scsiDev.lastStatus;\r
452                 dbgHidBuffer[15] = scsiDev.lastSense;\r
453                 dbgHidBuffer[16] = scsiDev.phase;\r
454                 dbgHidBuffer[17] = SCSI_ReadFilt(SCSI_Filt_BSY);\r
455                 dbgHidBuffer[18] = SCSI_ReadFilt(SCSI_Filt_SEL);\r
456                 dbgHidBuffer[19] = SCSI_ReadFilt(SCSI_Filt_ATN);\r
457                 dbgHidBuffer[20] = SCSI_ReadFilt(SCSI_Filt_RST);\r
458                 dbgHidBuffer[21] = scsiDev.rstCount;\r
459                 dbgHidBuffer[22] = scsiDev.selCount;\r
460                 dbgHidBuffer[23] = scsiDev.msgCount;\r
461                 dbgHidBuffer[24] = scsiDev.cmdCount;\r
462                 dbgHidBuffer[25] = scsiDev.watchdogTick;\r
463                 dbgHidBuffer[26] = 0; // OBSOLETE. Previously media state\r
464                 dbgHidBuffer[27] = scsiDev.lastSenseASC >> 8;\r
465                 dbgHidBuffer[28] = scsiDev.lastSenseASC;\r
466                 dbgHidBuffer[29] = scsiReadDBxPins();\r
467                 dbgHidBuffer[30] = LastTrace;\r
468 \r
469                 dbgHidBuffer[58] = sdCard.capacity >> 24;\r
470                 dbgHidBuffer[59] = sdCard.capacity >> 16;\r
471                 dbgHidBuffer[60] = sdCard.capacity >> 8;\r
472                 dbgHidBuffer[61] = sdCard.capacity;\r
473 \r
474                 dbgHidBuffer[62] = FIRMWARE_VERSION >> 8;\r
475                 dbgHidBuffer[63] = FIRMWARE_VERSION;\r
476 \r
477                 USBFS_LoadInEP(USB_EP_DEBUG, (uint8 *)&dbgHidBuffer, sizeof(dbgHidBuffer));\r
478                 usbDebugEpState = USB_DATA_SENT;\r
479                 break;\r
480 \r
481         case USB_DATA_SENT:\r
482                 if (USBFS_bGetEPAckState(USB_EP_DEBUG))\r
483                 {\r
484                         // Data accepted.\r
485                         usbDebugEpState = USB_IDLE;\r
486                 }\r
487                 break;\r
488         }\r
489 }\r
490 \r
491 CY_ISR(debugTimerISR)\r
492 {\r
493         Debug_Timer_ReadStatusRegister();\r
494         Debug_Timer_Interrupt_ClearPending();\r
495         uint8 savedIntrStatus = CyEnterCriticalSection();\r
496         debugPoll();\r
497         CyExitCriticalSection(savedIntrStatus);\r
498 }\r
499 \r
500 void debugInit()\r
501 {\r
502         Debug_Timer_Interrupt_StartEx(debugTimerISR);\r
503         Debug_Timer_Start();\r
504 }\r
505 \r
506 void debugPause()\r
507 {\r
508         Debug_Timer_Stop();\r
509 }\r
510 \r
511 void debugResume()\r
512 {\r
513         Debug_Timer_Start();\r
514 }\r
515 \r
516 int isDebugEnabled()\r
517 {\r
518         return usbReady;\r
519 }\r
520 \r
521 // Public method for storing MODE SELECT results.\r
522 void configSave(int scsiId, uint16_t bytesPerSector)\r
523 {\r
524         int cfgIdx;\r
525         for (cfgIdx = 0; cfgIdx < MAX_SCSI_TARGETS; ++cfgIdx)\r
526         {\r
527                 const S2S_TargetCfg* tgt = getConfigByIndex(cfgIdx);\r
528                 if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)\r
529                 {\r
530                         // Save row to flash\r
531                         // We only save the first row of the configuration\r
532                         // this contains the parameters changeable by a MODE SELECT command\r
533                         uint8_t rowData[CYDEV_FLS_ROW_SIZE];\r
534                         S2S_TargetCfg* rowCfgData = (S2S_TargetCfg*)&rowData;\r
535                         memcpy(rowCfgData, tgt, sizeof(rowData));\r
536                         rowCfgData->bytesPerSector = bytesPerSector;\r
537 \r
538                         CySetTemp();\r
539                         CyWriteRowData(\r
540                                 SCSI_CONFIG_ARRAY,\r
541                                 SCSI_CONFIG_0_ROW + (cfgIdx * SCSI_CONFIG_ROWS),\r
542                                 (uint8_t*)rowCfgData);\r
543                         return;\r
544                 }\r
545         }\r
546 }\r
547 \r
548 \r
549 const S2S_TargetCfg* getConfigByIndex(int i)\r
550 {\r
551         if (i <= 3)\r
552         {\r
553                 size_t row = SCSI_CONFIG_0_ROW + (i * SCSI_CONFIG_ROWS);\r
554                 return (const S2S_TargetCfg*)\r
555                         (\r
556                                 CY_FLASH_BASE +\r
557                                 (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +\r
558                                 (CY_FLASH_SIZEOF_ROW * row)\r
559                                 );\r
560         } else {\r
561                 size_t row = SCSI_CONFIG_4_ROW + ((i-4) * SCSI_CONFIG_ROWS);\r
562                 return (const S2S_TargetCfg*)\r
563                         (\r
564                                 CY_FLASH_BASE +\r
565                                 (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +\r
566                                 (CY_FLASH_SIZEOF_ROW * row)\r
567                                 );\r
568         }\r
569 }\r