Small compatibility improvements, and added scsi2sd-monitor test program
[SCSI2SD-V6.git] / software / SCSI2SD / src / config.c
1 // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
2 //
3 // This file is part of SCSI2SD.
4 //
5 // SCSI2SD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // SCSI2SD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
17 #pragma GCC push_options
18 #pragma GCC optimize("-flto")
19
20 #include "device.h"
21 #include "config.h"
22 #include "debug.h"
23 #include "USBFS.h"
24 #include "led.h"
25
26 #include "scsi.h"
27 #include "scsiPhy.h"
28 #include "disk.h"
29
30 #include "../../include/scsi2sd.h"
31 #include "../../include/hidpacket.h"
32
33 #include <string.h>
34
35 static const uint16_t FIRMWARE_VERSION = 0x0422;
36
37 enum USB_ENDPOINTS
38 {
39 USB_EP_OUT = 1,
40 USB_EP_IN = 2,
41 USB_EP_COMMAND = 3,
42 USB_EP_DEBUG = 4
43 };
44 enum USB_STATE
45 {
46 USB_IDLE,
47 USB_DATA_SENT
48 };
49
50 static uint8_t hidBuffer[USBHID_LEN];
51
52 static int usbInEpState;
53 static int usbDebugEpState;
54 static int usbReady;
55
56 uint8_t DEFAULT_CONFIG[256]
57 __attribute__ ((section(".DEFAULT_CONFIG"))) =
58 {
59 CONFIG_TARGET_ENABLED,
60 CONFIG_FIXED,
61 0,
62 0,
63 0, 0, 0, 0,
64 0xff, 0xff, 0x3f, 0x00, // 4194303, 2GB - 1 sector
65 0x00, 0x02, //512
66 63, 0,
67 255, 0,
68 ' ', 'c', 'o', 'd', 'e', 's', 'r', 'c',
69 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'S', 'C', 'S', 'I', '2', 'S', 'D',
70 ' ', '4', '.', '2',
71 '1','2','3','4','5','6','7','8','1','2','3','4','5','6','7','8'
72 };
73 // otherwise linker removes unused section.
74 volatile uint8_t trickLinker;
75
76 void configInit()
77 {
78 trickLinker = DEFAULT_CONFIG[0];
79
80 // The USB block will be powered by an internal 3.3V regulator.
81 // The PSoC must be operating between 4.6V and 5V for the regulator
82 // to work.
83 USBFS_Start(0, USBFS_5V_OPERATION);
84 usbInEpState = usbDebugEpState = USB_IDLE;
85 usbReady = 0; // We don't know if host is connected yet.
86 }
87
88 static void
89 readFlashCommand(const uint8_t* cmd, size_t cmdSize)
90 {
91 if (cmdSize < 3)
92 {
93 return; // ignore.
94 }
95 uint8_t flashArray = cmd[1];
96 uint8_t flashRow = cmd[2];
97
98 uint8_t* flash =
99 CY_FLASH_BASE +
100 (CY_FLASH_SIZEOF_ARRAY * (size_t) flashArray) +
101 (CY_FLASH_SIZEOF_ROW * (size_t) flashRow);
102
103 hidPacket_send(flash, SCSI_CONFIG_ROW_SIZE);
104 }
105
106 static void
107 writeFlashCommand(const uint8_t* cmd, size_t cmdSize)
108 {
109 if (cmdSize < 259)
110 {
111 return; // ignore.
112 }
113 uint8_t flashArray = cmd[257];
114 uint8_t flashRow = cmd[258];
115
116 // Be very careful not to overwrite the bootloader or other
117 // code
118 if ((flashArray != SCSI_CONFIG_ARRAY) ||
119 (flashRow < SCSI_CONFIG_0_ROW) ||
120 (flashRow >= SCSI_CONFIG_3_ROW + SCSI_CONFIG_ROWS))
121 {
122 uint8_t response[] = { CONFIG_STATUS_ERR};
123 hidPacket_send(response, sizeof(response));
124 }
125 else
126 {
127 CySetTemp();
128 int status = CyWriteRowData(flashArray, flashRow, cmd + 1);
129
130 uint8_t response[] =
131 {
132 status == CYRET_SUCCESS ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR
133 };
134 hidPacket_send(response, sizeof(response));
135 }
136 }
137
138 static void
139 pingCommand()
140 {
141 uint8_t response[] =
142 {
143 CONFIG_STATUS_GOOD
144 };
145 hidPacket_send(response, sizeof(response));
146 }
147
148 static void
149 sdInfoCommand()
150 {
151 uint8_t response[sizeof(sdDev.csd) + sizeof(sdDev.cid)];
152 memcpy(response, sdDev.csd, sizeof(sdDev.csd));
153 memcpy(response + sizeof(sdDev.csd), sdDev.cid, sizeof(sdDev.cid));
154
155 hidPacket_send(response, sizeof(response));
156 }
157
158
159 static void
160 scsiTestCommand()
161 {
162 int resultCode = scsiSelfTest();
163 uint8_t response[] =
164 {
165 resultCode == 0 ? CONFIG_STATUS_GOOD : CONFIG_STATUS_ERR,
166 resultCode
167 };
168 hidPacket_send(response, sizeof(response));
169 }
170
171 static void
172 processCommand(const uint8_t* cmd, size_t cmdSize)
173 {
174 switch (cmd[0])
175 {
176 case CONFIG_PING:
177 pingCommand();
178 break;
179
180 case CONFIG_READFLASH:
181 readFlashCommand(cmd, cmdSize);
182 break;
183
184 case CONFIG_WRITEFLASH:
185 writeFlashCommand(cmd, cmdSize);
186 break;
187
188 case CONFIG_REBOOT:
189 Bootloadable_1_Load();
190 break;
191
192 case CONFIG_SDINFO:
193 sdInfoCommand();
194 break;
195
196 case CONFIG_SCSITEST:
197 scsiTestCommand();
198 break;
199
200 case CONFIG_NONE: // invalid
201 default:
202 break;
203 }
204 }
205
206 void configPoll()
207 {
208 int reset = 0;
209 if (!usbReady || USBFS_IsConfigurationChanged())
210 {
211 reset = 1;
212 }
213 usbReady = USBFS_bGetConfiguration();
214
215 if (!usbReady)
216 {
217 return;
218 }
219
220 if (reset)
221 {
222 USBFS_EnableOutEP(USB_EP_OUT);
223 USBFS_EnableOutEP(USB_EP_COMMAND);
224 usbInEpState = usbDebugEpState = USB_IDLE;
225 }
226
227 if(USBFS_GetEPState(USB_EP_OUT) == USBFS_OUT_BUFFER_FULL)
228 {
229 ledOn();
230
231 // The host sent us some data!
232 int byteCount = USBFS_GetEPCount(USB_EP_OUT);
233 USBFS_ReadOutEP(USB_EP_OUT, hidBuffer, sizeof(hidBuffer));
234 hidPacket_recv(hidBuffer, byteCount);
235
236 size_t cmdSize;
237 const uint8_t* cmd = hidPacket_getPacket(&cmdSize);
238 if (cmd && (cmdSize > 0))
239 {
240 processCommand(cmd, cmdSize);
241 }
242
243 // Allow the host to send us another updated config.
244 USBFS_EnableOutEP(USB_EP_OUT);
245
246 ledOff();
247 }
248
249 switch (usbInEpState)
250 {
251 case USB_IDLE:
252 {
253 const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);
254
255 if (nextChunk)
256 {
257 USBFS_LoadInEP(USB_EP_IN, nextChunk, sizeof(hidBuffer));
258 usbInEpState = USB_DATA_SENT;
259 }
260 }
261 break;
262
263 case USB_DATA_SENT:
264 if (USBFS_bGetEPAckState(USB_EP_IN))
265 {
266 // Data accepted.
267 usbInEpState = USB_IDLE;
268 }
269 break;
270 }
271 }
272
273 void debugPoll()
274 {
275 if (!usbReady)
276 {
277 return;
278 }
279
280 if(USBFS_GetEPState(USB_EP_COMMAND) == USBFS_OUT_BUFFER_FULL)
281 {
282 // The host sent us some data!
283 int byteCount = USBFS_GetEPCount(USB_EP_COMMAND);
284 USBFS_ReadOutEP(USB_EP_COMMAND, (uint8 *)&hidBuffer, byteCount);
285
286 if (byteCount >= 1 &&
287 hidBuffer[0] == 0x01)
288 {
289 // Reboot command.
290 Bootloadable_1_Load();
291 }
292
293 // Allow the host to send us another command.
294 // (assuming we didn't reboot outselves)
295 USBFS_EnableOutEP(USB_EP_COMMAND);
296 }
297
298 switch (usbDebugEpState)
299 {
300 case USB_IDLE:
301 memcpy(&hidBuffer, &scsiDev.cdb, 12);
302 hidBuffer[12] = scsiDev.msgIn;
303 hidBuffer[13] = scsiDev.msgOut;
304 hidBuffer[14] = scsiDev.lastStatus;
305 hidBuffer[15] = scsiDev.lastSense;
306 hidBuffer[16] = scsiDev.phase;
307 hidBuffer[17] = SCSI_ReadFilt(SCSI_Filt_BSY);
308 hidBuffer[18] = SCSI_ReadFilt(SCSI_Filt_SEL);
309 hidBuffer[19] = SCSI_ReadFilt(SCSI_Filt_ATN);
310 hidBuffer[20] = SCSI_ReadFilt(SCSI_Filt_RST);
311 hidBuffer[21] = scsiDev.rstCount;
312 hidBuffer[22] = scsiDev.selCount;
313 hidBuffer[23] = scsiDev.msgCount;
314 hidBuffer[24] = scsiDev.cmdCount;
315 hidBuffer[25] = scsiDev.watchdogTick;
316 hidBuffer[26] = blockDev.state;
317 hidBuffer[27] = scsiDev.lastSenseASC >> 8;
318 hidBuffer[28] = scsiDev.lastSenseASC;
319 hidBuffer[29] = scsiReadDBxPins();
320
321 hidBuffer[58] = sdDev.capacity >> 24;
322 hidBuffer[59] = sdDev.capacity >> 16;
323 hidBuffer[60] = sdDev.capacity >> 8;
324 hidBuffer[61] = sdDev.capacity;
325
326 hidBuffer[62] = FIRMWARE_VERSION >> 8;
327 hidBuffer[63] = FIRMWARE_VERSION;
328
329 USBFS_LoadInEP(USB_EP_DEBUG, (uint8 *)&hidBuffer, sizeof(hidBuffer));
330 usbDebugEpState = USB_DATA_SENT;
331 break;
332
333 case USB_DATA_SENT:
334 if (USBFS_bGetEPAckState(USB_EP_DEBUG))
335 {
336 // Data accepted.
337 usbDebugEpState = USB_IDLE;
338 }
339 break;
340 }
341 }
342
343 CY_ISR(debugTimerISR)
344 {
345 Debug_Timer_ReadStatusRegister();
346 Debug_Timer_Interrupt_ClearPending();
347 uint8 savedIntrStatus = CyEnterCriticalSection();
348 debugPoll();
349 CyExitCriticalSection(savedIntrStatus);
350 }
351
352 void debugInit()
353 {
354 Debug_Timer_Interrupt_StartEx(debugTimerISR);
355 Debug_Timer_Start();
356 }
357
358 void debugPause()
359 {
360 Debug_Timer_Stop();
361 }
362
363 void debugResume()
364 {
365 Debug_Timer_Start();
366 }
367
368 int isDebugEnabled()
369 {
370 return usbReady;
371 }
372
373 // Public method for storing MODE SELECT results.
374 void configSave(int scsiId, uint16_t bytesPerSector)
375 {
376 int cfgIdx;
377 for (cfgIdx = 0; cfgIdx < MAX_SCSI_TARGETS; ++cfgIdx)
378 {
379 const TargetConfig* tgt = getConfigByIndex(cfgIdx);
380 if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)
381 {
382 // Save row to flash
383 // We only save the first row of the configuration
384 // this contains the parameters changeable by a MODE SELECT command
385 uint8_t rowData[CYDEV_FLS_ROW_SIZE];
386 TargetConfig* rowCfgData = (TargetConfig*)&rowData;
387 memcpy(rowCfgData, tgt, sizeof(rowData));
388 rowCfgData->bytesPerSector = bytesPerSector;
389
390 CySetTemp();
391 CyWriteRowData(
392 SCSI_CONFIG_ARRAY,
393 SCSI_CONFIG_0_ROW + (cfgIdx * SCSI_CONFIG_ROWS),
394 (uint8_t*)rowCfgData);
395 return;
396 }
397 }
398 }
399
400
401 const TargetConfig* getConfigByIndex(int i)
402 {
403 size_t row = SCSI_CONFIG_0_ROW + (i * SCSI_CONFIG_ROWS);
404 return (const TargetConfig*)
405 (
406 CY_FLASH_BASE +
407 (CY_FLASH_SIZEOF_ARRAY * (size_t) SCSI_CONFIG_ARRAY) +
408 (CY_FLASH_SIZEOF_ROW * row)
409 );
410 }
411
412 const TargetConfig* getConfigById(int scsiId)
413 {
414 int i;
415 for (i = 0; i < MAX_SCSI_TARGETS; ++i)
416 {
417 const TargetConfig* tgt = getConfigByIndex(i);
418 if ((tgt->scsiId & CONFIG_TARGET_ID_BITS) == scsiId)
419 {
420 return tgt;
421 }
422 }
423 return NULL;
424
425 }
426
427 #pragma GCC pop_options