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