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