| LPCOpen Platform v1.03 LPCOpen Platform for NXP LPC Microcontrollers

- Main Page
- Modules
- Data Structures
- Files
- Related Pages

- File List
- Globals

All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
SCSI.c
Go to the documentation of this file.
1 /*
2 * @brief SCSI command processing routines, for SCSI commands issues by the host
3 *
4 * @note
5 * Copyright(C) NXP Semiconductors, 2012
6 * Copyright(C) Dean Camera, 2011, 2012
7 * All rights reserved.
8 *
9 * @par
10 * Software that is described herein is for illustrative purposes only
11 * which provides customers with programming information regarding the
12 * LPC products. This software is supplied "AS IS" without any warranties of
13 * any kind, and NXP Semiconductors and its licensor disclaim any and
14 * all warranties, express or implied, including all implied warranties of
15 * merchantability, fitness for a particular purpose and non-infringement of
16 * intellectual property rights. NXP Semiconductors assumes no responsibility
17 * or liability for the use of the software, conveys no license or rights under any
18 * patent, copyright, mask work right, or any other intellectual property rights in
19 * or to any products. NXP Semiconductors reserves the right to make changes
20 * in the software without notification. NXP Semiconductors also makes no
21 * representation or warranty that such application will be suitable for the
22 * specified use without further testing or modification.
23 *
24 * @par
25 * Permission to use, copy, modify, and distribute this software and its
26 * documentation is hereby granted, under NXP Semiconductors' and its
27 * licensor's relevant copyrights in the software, without fee, provided that it
28 * is used in conjunction with NXP Semiconductors microcontrollers. This
29 * copyright, permission, and disclaimer notice must appear in all copies of
30 * this code.
31 */
32
33 #define INCLUDE_FROM_SCSI_C
34 #include "SCSI.h"
35
36 /*****************************************************************************
37 * Private types/enumerations/variables
38 ****************************************************************************/
39
40 /*****************************************************************************
41 * Public types/enumerations/variables
42 ****************************************************************************/
43
47 static const SCSI_Inquiry_Response_t InquiryData = {
48 .DeviceType = DEVICE_TYPE_BLOCK,
49 .PeripheralQualifier = 0,
50
51 .Removable = true,
52
53 .Version = 0,
54
55 .ResponseDataFormat = 2,
56 .NormACA = false,
57 .TrmTsk = false,
58 .AERC = false,
59
60 .AdditionalLength = 0x1F,
61
62 .SoftReset = false,
63 .CmdQue = false,
64 .Linked = false,
65 .Sync = false,
66 .WideBus16Bit = false,
67 .WideBus32Bit = false,
68 .RelAddr = false,
69
70 .VendorID = "NXP",
71 .ProductID = "Dataflash Disk",
72 .RevisionID = {'0', '.', '0', '0'},
73 };
74
78 static SCSI_Request_Sense_Response_t SenseData = {
79 .ResponseCode = 0x70,
80 .AdditionalLength = 0x0A,
81 };
82
84 #ifdef CFG_SDCARD
85 uint8_t *disk_cache = (uint8_t *) 0x20000000;
86 static uint32_t MSC_Get_Block_Count(void)
87 {
88 return (uint32_t) Chip_SDMMC_GetDeviceBlocks(LPC_SDMMC);
89 }
90
91 #endif
92
93 /*****************************************************************************
94 * Private functions
95 ****************************************************************************/
96
97 /*****************************************************************************
98 * Public functions
99 ****************************************************************************/
100
105 bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
106 {
107 bool CommandSuccess = false;
108
109 /* Run the appropriate SCSI command hander function based on the passed command */
110 switch (MSInterfaceInfo->State.CommandBlock.SCSICommandData[0]) {
111 case SCSI_CMD_INQUIRY:
112 CommandSuccess = SCSI_Command_Inquiry(MSInterfaceInfo);
113 break;
114
115 case SCSI_CMD_REQUEST_SENSE:
116 CommandSuccess = SCSI_Command_Request_Sense(MSInterfaceInfo);
117 break;
118
119 case SCSI_CMD_READ_CAPACITY_10:
120 CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo);
121 break;
122
123 case SCSI_CMD_SEND_DIAGNOSTIC:
124 CommandSuccess = SCSI_Command_Send_Diagnostic(MSInterfaceInfo);
125 break;
126
127 case SCSI_CMD_WRITE_10:
128 CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE);
129 break;
130
131 case SCSI_CMD_READ_10:
132 CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_READ);
133 break;
134
135 case SCSI_CMD_MODE_SENSE_6:
136 CommandSuccess = SCSI_Command_ModeSense_6(MSInterfaceInfo);
137 break;
138
139 case SCSI_CMD_TEST_UNIT_READY:
140 case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
141 case SCSI_CMD_VERIFY_10:
142 /* These commands should just succeed, no handling required */
143 CommandSuccess = true;
144 MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0;
145 break;
146
147 default:
148 /* Update the SENSE key to reflect the invalid command */
149 SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST,
150 SCSI_ASENSE_INVALID_COMMAND,
151 SCSI_ASENSEQ_NO_QUALIFIER);
152 break;
153 }
154
155 /* Check if command was successfully processed */
156 if (CommandSuccess) {
157 SCSI_SET_SENSE(SCSI_SENSE_KEY_GOOD,
158 SCSI_ASENSE_NO_ADDITIONAL_INFORMATION,
159 SCSI_ASENSEQ_NO_QUALIFIER);
160
161 return true;
162 }
163
164 return false;
165 }
166
170 static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
171 {
172 uint16_t AllocationLength = SwapEndian_16(*(uint16_t *) &MSInterfaceInfo->State.CommandBlock.SCSICommandData[3]);
173 uint16_t BytesTransferred = MIN(AllocationLength, sizeof(InquiryData));
174
175 /* Only the standard INQUIRY data is supported, check if any optional INQUIRY bits set */
176 if ((MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & ((1 << 0) | (1 << 1))) ||
177 MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]) {
178 /* Optional but unsupported bits set - update the SENSE key and fail the request */
179 SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST,
180 SCSI_ASENSE_INVALID_FIELD_IN_CDB,
181 SCSI_ASENSEQ_NO_QUALIFIER);
182
183 return false;
184 }
185
186 Endpoint_Write_Stream_LE(MSInterfaceInfo->Config.PortNumber, &InquiryData, BytesTransferred, NULL);
187
188 /* Pad out remaining bytes with 0x00 */
189 Endpoint_Null_Stream(MSInterfaceInfo->Config.PortNumber, (AllocationLength - BytesTransferred), NULL);
190
191 /* Finalize the stream transfer to send the last packet */
192 Endpoint_ClearIN(MSInterfaceInfo->Config.PortNumber);
193
194 /* Succeed the command and update the bytes transferred counter */
195 MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred;
196
197 return true;
198 }
199
203 static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
204 {
205 uint8_t AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4];
206 uint8_t BytesTransferred = MIN(AllocationLength, sizeof(SenseData));
207
208 Endpoint_Write_Stream_LE(MSInterfaceInfo->Config.PortNumber, &SenseData, BytesTransferred, NULL);
209 Endpoint_Null_Stream(MSInterfaceInfo->Config.PortNumber, (AllocationLength - BytesTransferred), NULL);
210 Endpoint_ClearIN(MSInterfaceInfo->Config.PortNumber);
211
212 /* Succeed the command and update the bytes transferred counter */
213 MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred;
214
215 return true;
216 }
217
221 static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
222 {
224 #ifdef CFG_SDCARD
225 uint32_t LastBlockAddressInLUN = MSC_Get_Block_Count() - 1;
226 #else
227 uint32_t LastBlockAddressInLUN = (LUN_MEDIA_BLOCKS - 1);
228 #endif
229 uint32_t MediaBlockSize = VIRTUAL_MEMORY_BLOCK_SIZE;
230
231 Endpoint_Write_Stream_BE(MSInterfaceInfo->Config.PortNumber,
232 &LastBlockAddressInLUN,
233 sizeof(LastBlockAddressInLUN),
234 NULL);
235 Endpoint_Write_Stream_BE(MSInterfaceInfo->Config.PortNumber, &MediaBlockSize, sizeof(MediaBlockSize), NULL);
236 Endpoint_ClearIN(MSInterfaceInfo->Config.PortNumber);
237
238 /* Succeed the command and update the bytes transferred counter */
239 MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8;
240
241 return true;
242 }
243
248 static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
249 {
250 /* Check to see if the SELF TEST bit is not set */
251 if (!(MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & (1 << 2))) {
252 /* Only self-test supported - update SENSE key and fail the command */
253 SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST,
254 SCSI_ASENSE_INVALID_FIELD_IN_CDB,
255 SCSI_ASENSEQ_NO_QUALIFIER);
256
257 return false;
258 }
259 /* Succeed the command and update the bytes transferred counter */
260 MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0;
261
262 return true;
263 }
264
269 static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo,
270 const bool IsDataRead)
271 {
272 uint32_t BlockAddress;
273 uint16_t TotalBlocks;
274
275 /* Check if the disk is write protected or not */
276 if ((IsDataRead == DATA_WRITE) && DISK_READ_ONLY) {
277 /* Block address is invalid, update SENSE key and return command fail */
278 SCSI_SET_SENSE(SCSI_SENSE_KEY_DATA_PROTECT,
279 SCSI_ASENSE_WRITE_PROTECTED,
280 SCSI_ASENSEQ_NO_QUALIFIER);
281
282 return false;
283 }
284
285 /* Load in the 32-bit block address (SCSI uses big-endian, so have to reverse the byte order) */
286 BlockAddress =
287 (MSInterfaceInfo->State.CommandBlock.SCSICommandData[2] <<
288 24) + (MSInterfaceInfo->State.CommandBlock.SCSICommandData[3] << 16) +
289 (MSInterfaceInfo->State.CommandBlock.SCSICommandData[4] <<
290 8) + MSInterfaceInfo->State.CommandBlock.SCSICommandData[5];
291
292 /* Load in the 16-bit total blocks (SCSI uses big-endian, so have to reverse the byte order) */
293 TotalBlocks =
294 (MSInterfaceInfo->State.CommandBlock.SCSICommandData[7] <<
295 8) + MSInterfaceInfo->State.CommandBlock.SCSICommandData[8];
296
297 /* Check if the block address is outside the maximum allowable value for the LUN */
298
300 #ifdef CFG_SDCARD
301 if (BlockAddress >= MSC_Get_Block_Count())
302 #else
303 if (BlockAddress >= LUN_MEDIA_BLOCKS)
304 #endif
305 {
306 /* Block address is invalid, update SENSE key and return command fail */
307 SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST,
308 SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
309 SCSI_ASENSEQ_NO_QUALIFIER);
310
311 return false;
312 }
313
314 #if (TOTAL_LUNS > 1)
315 /* Adjust the given block address to the real media address based on the selected LUN */
316 BlockAddress += ((uint32_t) MSInterfaceInfo->State.CommandBlock.LUN * LUN_MEDIA_BLOCKS);
317 #endif
318
320 #ifdef CFG_SDCARD
321 /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */
322 if (IsDataRead == DATA_READ) {
323 Chip_SDMMC_ReadBlocks(LPC_SDMMC, (void *) disk_cache, BlockAddress, TotalBlocks);
324 while (!Endpoint_IsINReady(MSInterfaceInfo->Config.PortNumber)) {}
325 Endpoint_Streaming(MSInterfaceInfo->Config.PortNumber,
326 (uint8_t *) disk_cache,
327 VIRTUAL_MEMORY_BLOCK_SIZE,
328 TotalBlocks,
329 0);
330 }
331 else {
332 Endpoint_Streaming(MSInterfaceInfo->Config.PortNumber,
333 (uint8_t *) disk_cache,
334 VIRTUAL_MEMORY_BLOCK_SIZE,
335 TotalBlocks,
336 0);
337 while (!Endpoint_IsOUTReceived(MSInterfaceInfo->Config.PortNumber)) {}
338 Chip_SDMMC_WriteBlocks(LPC_SDMMC, (void *) disk_cache, BlockAddress, TotalBlocks);
339 }
340 #else
341 uint32_t startaddr;
342 uint16_t blocks, dummyblocks;
343
344 startaddr = MassStorage_GetAddressInImage(BlockAddress, TotalBlocks, &blocks);
345 if (blocks == 0) {
346 dummyblocks = TotalBlocks;
347 }
348 else if (blocks < TotalBlocks) {
349 dummyblocks = TotalBlocks - blocks;
350 }
351 else {dummyblocks = 0; }
352 Endpoint_Streaming(MSInterfaceInfo->Config.PortNumber,
353 (uint8_t *) startaddr,
354 VIRTUAL_MEMORY_BLOCK_SIZE,
355 blocks,
356 dummyblocks);
357 while (!Endpoint_IsReadWriteAllowed(MSInterfaceInfo->Config.PortNumber)) {}
358 #endif
359 /* Update the bytes transferred counter and succeed the command */
360 MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t) TotalBlocks * VIRTUAL_MEMORY_BLOCK_SIZE);
361
362 return true;
363 }
364
372 static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
373 {
374 /* Send an empty header response with the Write Protect flag status */
375 Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, 0x00);
376 Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, 0x00);
377 Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, DISK_READ_ONLY ? 0x80 : 0x00);
378 Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, 0x00);
379 Endpoint_ClearIN(MSInterfaceInfo->Config.PortNumber);
380
381 /* Update the bytes transferred counter and succeed the command */
382 MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4;
383
384 return true;
385 }