Many bug fixes, including selection fixes.
[SCSI2SD.git] / software / SCSI2SD / SCSI2SD.cydsn / 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 \r
26 #include <string.h>\r
27 \r
28 // CYDEV_EEPROM_ROW_SIZE == 16.\r
29 static char magic[CYDEV_EEPROM_ROW_SIZE] = "codesrc_00000001";\r
30 \r
31 // Config shadow RAM (copy of EEPROM)\r
32 static Config shadow =\r
33 {\r
34         0, // SCSI ID\r
35         " codesrc", // vendor  (68k Apple Drive Setup: Set to " SEAGATE")\r
36         "         SCSI2SD", //prodId (68k Apple Drive Setup: Set to "          ST225N")\r
37         " 3.2", // revision (68k Apple Drive Setup: Set to "1.0 ")\r
38         1, // enable parity\r
39         0, // disable unit attention,\r
40         0 // Max blocks (0 == disabled)\r
41         // reserved bytes will be initialised to 0.\r
42 };\r
43 \r
44 enum USB_ENDPOINTS\r
45 {\r
46         USB_EP_OUT = 1,\r
47         USB_EP_IN = 2\r
48 };\r
49 enum USB_STATE\r
50 {\r
51         USB_IDLE,\r
52         USB_DATA_SENT\r
53 };\r
54 int usbInEpState;\r
55 \r
56 int usbReady;\r
57 \r
58 // Global\r
59 Config* config = NULL;\r
60 \r
61 // The PSoC 5LP compile to little-endian format.\r
62 static uint32_t ntohl(uint32_t val)\r
63 {\r
64         return\r
65                 ((val & 0xFF) << 24) |\r
66                 ((val & 0xFF00) << 8) |\r
67                 ((val >> 8) & 0xFF00) |\r
68                 ((val >> 24) & 0xFF);\r
69 }\r
70 static uint32_t htonl(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 \r
79 static void saveConfig()\r
80 {\r
81         int shadowRows = (sizeof(shadow) / CYDEV_EEPROM_ROW_SIZE) + 1;\r
82         int row;\r
83         int status = CYRET_SUCCESS;\r
84 \r
85         CySetTemp();\r
86         for (row = 0; (row < shadowRows) && (status == CYRET_SUCCESS); ++row)\r
87         {\r
88                 CFG_EEPROM_Write(((uint8*)&shadow) + (row * CYDEV_EEPROM_ROW_SIZE), row);\r
89         }\r
90         if (status == CYRET_SUCCESS)\r
91         {\r
92                 CFG_EEPROM_Write((uint8*)magic, row);\r
93         }\r
94 }\r
95 \r
96 void configInit()\r
97 {\r
98         int shadowRows, shadowBytes;\r
99         uint8* eeprom = (uint8*)CYDEV_EE_BASE;\r
100 \r
101         // We could map cfgPtr directly into the EEPROM memory,\r
102         // but that would waste power. Copy it to RAM then turn off\r
103         // the EEPROM.\r
104         CFG_EEPROM_Start();\r
105         CyDelayUs(5); // 5us to start per datasheet.\r
106 \r
107         // Check magic\r
108         shadowRows = (sizeof(shadow) / CYDEV_EEPROM_ROW_SIZE) + 1;\r
109         shadowBytes = CYDEV_EEPROM_ROW_SIZE * shadowRows;\r
110 \r
111         if (memcmp(eeprom + shadowBytes, magic, sizeof(magic)))\r
112         {\r
113                 saveConfig();\r
114         }\r
115         else\r
116         {\r
117                 memcpy(&shadow, eeprom, sizeof(shadow));\r
118         }\r
119         config = &shadow;\r
120         CFG_EEPROM_Stop();\r
121 \r
122         // The USB block will be powered by an internal 3.3V regulator.\r
123         // The PSoC must be operating between 4.6V and 5V for the regulator\r
124         // to work.\r
125         USBFS_Start(0, USBFS_5V_OPERATION);\r
126         usbInEpState = USB_IDLE;\r
127         usbReady = 0; // We don't know if host is connected yet.\r
128 }\r
129 \r
130 void configPoll()\r
131 {\r
132         int reset = 0;\r
133         if (!usbReady || USBFS_IsConfigurationChanged())\r
134         {\r
135                 reset = 1;\r
136         }\r
137         usbReady = USBFS_bGetConfiguration();\r
138 \r
139         if (!usbReady)\r
140         {\r
141                 return;\r
142         }\r
143 \r
144         if (reset)\r
145         {\r
146                 USBFS_EnableOutEP(USB_EP_OUT);\r
147                 usbInEpState = USB_IDLE;\r
148         }\r
149 \r
150         if(USBFS_GetEPState(USB_EP_OUT) == USBFS_OUT_BUFFER_FULL)\r
151         {\r
152                 int byteCount;\r
153 \r
154                 ledOn();\r
155 \r
156                 // The host sent us some data!\r
157                 byteCount = USBFS_GetEPCount(USB_EP_OUT);\r
158 \r
159                 // Assume that byteCount <= sizeof(shadow).\r
160                 // shadow should be padded out to 64bytes, which is the largest\r
161                 // possible HID transfer.\r
162                 USBFS_ReadOutEP(USB_EP_OUT, (uint8 *)&shadow, byteCount);\r
163                 shadow.maxBlocks = htonl(shadow.maxBlocks);\r
164 \r
165                 CFG_EEPROM_Start();\r
166                 saveConfig(); // write to eeprom\r
167                 CFG_EEPROM_Stop();\r
168 \r
169                 // Send the updated data.\r
170                 usbInEpState = USB_IDLE;\r
171 \r
172                 // Allow the host to send us another updated config.\r
173                 USBFS_EnableOutEP(USB_EP_OUT);\r
174 \r
175                 ledOff();\r
176         }\r
177 \r
178         switch (usbInEpState)\r
179         {\r
180         case USB_IDLE:\r
181                 shadow.maxBlocks = htonl(shadow.maxBlocks);\r
182                 \r
183                 #ifdef MM_DEBUG\r
184                 memcpy(&shadow.reserved, &scsiDev.cdb, 12);\r
185                 shadow.reserved[12] = scsiDev.msgIn;\r
186                 shadow.reserved[13] = scsiDev.msgOut;\r
187                 shadow.reserved[14] = scsiDev.lastStatus;\r
188                 shadow.reserved[15] = scsiDev.lastSense;\r
189                 shadow.reserved[16] = scsiDev.phase;\r
190                 shadow.reserved[17] = SCSI_ReadPin(SCSI_In_BSY);\r
191                 shadow.reserved[18] = SCSI_ReadPin(SCSI_In_SEL);\r
192                 shadow.reserved[19] = SCSI_ReadPin(SCSI_ATN_INT);\r
193                 shadow.reserved[20] = SCSI_ReadPin(SCSI_RST_INT);\r
194                 shadow.reserved[21] = scsiDev.rstCount;\r
195                 shadow.reserved[22] = scsiDev.selCount;\r
196                 shadow.reserved[23] = scsiDev.msgCount;\r
197                 shadow.reserved[24] = scsiDev.watchdogTick++;\r
198                 #endif\r
199 \r
200                 USBFS_LoadInEP(USB_EP_IN, (uint8 *)&shadow, sizeof(shadow));\r
201                 shadow.maxBlocks = ntohl(shadow.maxBlocks);\r
202                 usbInEpState = USB_DATA_SENT;\r
203                 break;\r
204 \r
205         case USB_DATA_SENT:\r
206                 if (USBFS_bGetEPAckState(USB_EP_IN))\r
207                 {\r
208                         // Data accepted.\r
209                         usbInEpState = USB_IDLE;\r
210                 }\r
211                 break;\r
212         }\r
213 }\r
214 \r