81fdef009fc0ce0b5a999b24c5af5480155dea01
[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 "USBFS.h"\r
21 #include "led.h"\r
22 \r
23 #include "scsi.h"\r
24 #include "scsiPhy.h"\r
25 #include "disk.h"\r
26 \r
27 #include <string.h>\r
28 \r
29 // CYDEV_EEPROM_ROW_SIZE == 16.\r
30 static const char magic[CYDEV_EEPROM_ROW_SIZE] = "codesrc_00000002";\r
31 static const uint16_t FIRMWARE_VERSION = 0x0352;\r
32 \r
33 // Config shadow RAM (copy of EEPROM)\r
34 static Config shadow =\r
35 {\r
36         0, // SCSI ID\r
37         " codesrc", // vendor  (68k Apple Drive Setup: Set to " SEAGATE")\r
38         "         SCSI2SD", //prodId (68k Apple Drive Setup: Set to "          ST225N")\r
39         " 3.5", // revision (68k Apple Drive Setup: Set to "1.0 ")\r
40         1, // enable parity\r
41         1, // enable unit attention,\r
42         0, // RESERVED\r
43         0, // Max sectors (0 == disabled)\r
44         512 // Sector size\r
45         // reserved bytes will be initialised to 0.\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 int usbInEpState;\r
61 int usbDebugEpState;\r
62 uint8_t debugBuffer[64];\r
63 \r
64 int usbReady;\r
65 \r
66 // Global\r
67 Config* config = NULL;\r
68 \r
69 // The PSoC 5LP compile to little-endian format.\r
70 static uint32_t ntohl(uint32_t val)\r
71 {\r
72         return\r
73                 ((val & 0xFF) << 24) |\r
74                 ((val & 0xFF00) << 8) |\r
75                 ((val >> 8) & 0xFF00) |\r
76                 ((val >> 24) & 0xFF);\r
77 }\r
78 static uint16_t ntohs(uint16_t val)\r
79 {\r
80         return\r
81                 ((val & 0xFF) << 8) |\r
82                 ((val >> 8) & 0xFF);\r
83 }\r
84 static uint32_t htonl(uint32_t val)\r
85 {\r
86         return\r
87                 ((val & 0xFF) << 24) |\r
88                 ((val & 0xFF00) << 8) |\r
89                 ((val >> 8) & 0xFF00) |\r
90                 ((val >> 24) & 0xFF);\r
91 }\r
92 static uint16_t htons(uint16_t val)\r
93 {\r
94         return\r
95                 ((val & 0xFF) << 8) |\r
96                 ((val >> 8) & 0xFF);\r
97 }\r
98 \r
99 static void saveConfig()\r
100 {\r
101         int shadowRows = (sizeof(shadow) / CYDEV_EEPROM_ROW_SIZE) + 1;\r
102         int row;\r
103         int status = CYRET_SUCCESS;\r
104 \r
105         CySetTemp();\r
106         for (row = 0; (row < shadowRows) && (status == CYRET_SUCCESS); ++row)\r
107         {\r
108                 CFG_EEPROM_Write(((uint8*)&shadow) + (row * CYDEV_EEPROM_ROW_SIZE), row);\r
109         }\r
110         if (status == CYRET_SUCCESS)\r
111         {\r
112                 CFG_EEPROM_Write((uint8*)magic, row);\r
113         }\r
114 }\r
115 \r
116 void configInit()\r
117 {\r
118         int shadowRows, shadowBytes;\r
119         uint8* eeprom = (uint8*)CYDEV_EE_BASE;\r
120 \r
121         // We could map cfgPtr directly into the EEPROM memory,\r
122         // but that would waste power. Copy it to RAM then turn off\r
123         // the EEPROM.\r
124         CFG_EEPROM_Start();\r
125         CyDelayUs(5); // 5us to start per datasheet.\r
126 \r
127         // Check magic\r
128         shadowRows = (sizeof(shadow) / CYDEV_EEPROM_ROW_SIZE) + 1;\r
129         shadowBytes = CYDEV_EEPROM_ROW_SIZE * shadowRows;\r
130 \r
131         if (memcmp(eeprom + shadowBytes, magic, sizeof(magic)))\r
132         {\r
133                 // Initial state, invalid, or upgrade required.\r
134                 if (!memcmp(eeprom + shadowBytes, magic, sizeof(magic) - 1) &&\r
135                         ((eeprom + shadowBytes)[sizeof(magic) - 2] == '1'))\r
136                 {\r
137                         // Upgrade from version 1.\r
138                         memcpy(&shadow, eeprom, sizeof(shadow));\r
139                         shadow.bytesPerSector = 512;\r
140                 }\r
141 \r
142                 saveConfig();\r
143         }\r
144         else\r
145         {\r
146                 memcpy(&shadow, eeprom, sizeof(shadow));\r
147         }\r
148 \r
149         config = &shadow;\r
150         CFG_EEPROM_Stop();\r
151 \r
152         // The USB block will be powered by an internal 3.3V regulator.\r
153         // The PSoC must be operating between 4.6V and 5V for the regulator\r
154         // to work.\r
155         USBFS_Start(0, USBFS_5V_OPERATION);\r
156         usbInEpState = usbDebugEpState = USB_IDLE;\r
157         usbReady = 0; // We don't know if host is connected yet.\r
158 }\r
159 \r
160 void configPoll()\r
161 {\r
162         int reset = 0;\r
163         if (!usbReady || USBFS_IsConfigurationChanged())\r
164         {\r
165                 reset = 1;\r
166         }\r
167         usbReady = USBFS_bGetConfiguration();\r
168 \r
169         if (!usbReady)\r
170         {\r
171                 return;\r
172         }\r
173 \r
174         if (reset)\r
175         {\r
176                 USBFS_EnableOutEP(USB_EP_OUT);\r
177                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
178                 usbInEpState = usbDebugEpState = USB_IDLE;\r
179         }\r
180 \r
181         if(USBFS_GetEPState(USB_EP_OUT) == USBFS_OUT_BUFFER_FULL)\r
182         {\r
183                 int byteCount;\r
184 \r
185                 ledOn();\r
186 \r
187                 // The host sent us some data!\r
188                 byteCount = USBFS_GetEPCount(USB_EP_OUT);\r
189 \r
190                 // Assume that byteCount <= sizeof(shadow).\r
191                 // shadow should be padded out to 64bytes, which is the largest\r
192                 // possible HID transfer.\r
193                 USBFS_ReadOutEP(USB_EP_OUT, (uint8 *)&shadow, byteCount);\r
194                 shadow.maxSectors = ntohl(shadow.maxSectors);\r
195                 shadow.bytesPerSector = ntohs(shadow.bytesPerSector);\r
196 \r
197                 if (shadow.bytesPerSector > MAX_SECTOR_SIZE)\r
198                 {\r
199                         shadow.bytesPerSector = MAX_SECTOR_SIZE;\r
200                 }\r
201                 else if (shadow.bytesPerSector < MIN_SECTOR_SIZE)\r
202                 {\r
203                         shadow.bytesPerSector = MIN_SECTOR_SIZE;\r
204                 }\r
205 \r
206                 CFG_EEPROM_Start();\r
207                 saveConfig(); // write to eeprom\r
208                 CFG_EEPROM_Stop();\r
209 \r
210                 // Send the updated data.\r
211                 usbInEpState = USB_IDLE;\r
212 \r
213                 // Allow the host to send us another updated config.\r
214                 USBFS_EnableOutEP(USB_EP_OUT);\r
215 \r
216                 // Set unt attention as the block size may have changed.\r
217                 scsiDev.unitAttention = MODE_PARAMETERS_CHANGED;\r
218 \r
219                 ledOff();\r
220         }\r
221 \r
222         switch (usbInEpState)\r
223         {\r
224         case USB_IDLE:\r
225                 shadow.maxSectors = htonl(shadow.maxSectors);\r
226                 shadow.bytesPerSector = htons(shadow.bytesPerSector);\r
227 \r
228                 USBFS_LoadInEP(USB_EP_IN, (uint8 *)&shadow, sizeof(shadow));\r
229                 shadow.maxSectors = ntohl(shadow.maxSectors);\r
230                 shadow.bytesPerSector = ntohs(shadow.bytesPerSector);\r
231                 usbInEpState = USB_DATA_SENT;\r
232                 break;\r
233 \r
234         case USB_DATA_SENT:\r
235                 if (USBFS_bGetEPAckState(USB_EP_IN))\r
236                 {\r
237                         // Data accepted.\r
238                         usbInEpState = USB_IDLE;\r
239                 }\r
240                 break;\r
241         }\r
242 }\r
243 \r
244 void debugPoll()\r
245 {\r
246         if (!usbReady)\r
247         {\r
248                 return;\r
249         }\r
250 \r
251         if(USBFS_GetEPState(USB_EP_COMMAND) == USBFS_OUT_BUFFER_FULL)\r
252         {\r
253                 // The host sent us some data!\r
254                 int byteCount = USBFS_GetEPCount(USB_EP_COMMAND);\r
255                 USBFS_ReadOutEP(USB_EP_COMMAND, (uint8 *)&debugBuffer, byteCount);\r
256 \r
257                 if (byteCount >= 1 &&\r
258                         debugBuffer[0] == 0x01)\r
259                 {\r
260                         // Reboot command.\r
261                         Bootloadable_1_Load();\r
262                 }\r
263 \r
264                 // Allow the host to send us another command.\r
265                 // (assuming we didn't reboot outselves)\r
266                 USBFS_EnableOutEP(USB_EP_COMMAND);\r
267         }\r
268 \r
269         switch (usbDebugEpState)\r
270         {\r
271         case USB_IDLE:\r
272                 memcpy(&debugBuffer, &scsiDev.cdb, 12);\r
273                 debugBuffer[12] = scsiDev.msgIn;\r
274                 debugBuffer[13] = scsiDev.msgOut;\r
275                 debugBuffer[14] = scsiDev.lastStatus;\r
276                 debugBuffer[15] = scsiDev.lastSense;\r
277                 debugBuffer[16] = scsiDev.phase;\r
278                 debugBuffer[17] = SCSI_ReadPin(SCSI_In_BSY);\r
279                 debugBuffer[18] = SCSI_ReadPin(SCSI_In_SEL);\r
280                 debugBuffer[19] = SCSI_ReadPin(SCSI_ATN_INT);\r
281                 debugBuffer[20] = SCSI_ReadPin(SCSI_RST_INT);\r
282                 debugBuffer[21] = scsiDev.rstCount;\r
283                 debugBuffer[22] = scsiDev.selCount;\r
284                 debugBuffer[23] = scsiDev.msgCount;\r
285                 debugBuffer[24] = scsiDev.cmdCount;\r
286                 debugBuffer[25] = scsiDev.watchdogTick;\r
287 \r
288                 debugBuffer[58] = sdDev.capacity >> 24;\r
289                 debugBuffer[59] = sdDev.capacity >> 16;\r
290                 debugBuffer[60] = sdDev.capacity >> 8;\r
291                 debugBuffer[61] = sdDev.capacity;\r
292 \r
293                 debugBuffer[62] = FIRMWARE_VERSION >> 8;\r
294                 debugBuffer[63] = FIRMWARE_VERSION;\r
295 \r
296                 USBFS_LoadInEP(USB_EP_DEBUG, (uint8 *)&debugBuffer, sizeof(debugBuffer));\r
297                 usbDebugEpState = USB_DATA_SENT;\r
298                 break;\r
299 \r
300         case USB_DATA_SENT:\r
301                 if (USBFS_bGetEPAckState(USB_EP_DEBUG))\r
302                 {\r
303                         // Data accepted.\r
304                         usbDebugEpState = USB_IDLE;\r
305                 }\r
306                 break;\r
307         }\r
308 }\r
309 \r
310 CY_ISR(debugTimerISR)\r
311 {\r
312         Debug_Timer_ReadStatusRegister();\r
313         Debug_Timer_Interrupt_ClearPending();\r
314         uint8 savedIntrStatus = CyEnterCriticalSection();\r
315         debugPoll();\r
316         CyExitCriticalSection(savedIntrStatus); \r
317 }\r
318 \r
319 void debugInit()\r
320 {\r
321         Debug_Timer_Interrupt_StartEx(debugTimerISR);\r
322         Debug_Timer_Start();\r
323 }\r
324 \r
325 // Public method for storing MODE SELECT results.\r
326 void configSave()\r
327 {\r
328         CFG_EEPROM_Start();\r
329         saveConfig(); // write to eeprom\r
330         CFG_EEPROM_Stop();\r
331 }\r
332 \r