LPCOpen Platform
LPCOpen Platform for NXP LPC Microcontrollers
 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 
40  .DeviceType = DEVICE_TYPE_BLOCK,
41  .PeripheralQualifier = 0,
42 
43  .Removable = true,
44 
45  .Version = 0,
46 
47  .ResponseDataFormat = 2,
48  .NormACA = false,
49  .TrmTsk = false,
50  .AERC = false,
51 
52  .AdditionalLength = 0x1F,
53 
54  .SoftReset = false,
55  .CmdQue = false,
56  .Linked = false,
57  .Sync = false,
58  .WideBus16Bit = false,
59  .WideBus32Bit = false,
60  .RelAddr = false,
61 
62  .VendorID = "NXP",
63  .ProductID = "Dataflash Disk",
64  .RevisionID = {'0', '.', '0', '0'},
65 };
66 
71  .ResponseCode = 0x70,
72  .AdditionalLength = 0x0A,
73 };
74 
76 #ifdef CFG_SDCARD
77 
78 #define CACHE_SIZE 8 * 1024
79 #define CACHE_SECTORS (CACHE_SIZE / 512)
80 uint8_t *disk_cache = (uint8_t *) 0x10008000;
81 int32_t current_ncache = -1;
82 
83 static uint32_t MSC_Get_Block_Count(void)
84 {
85  return (uint32_t) (Chip_SDMMC_GetDeviceSize()) / 512;
86 }
87 
88 #endif
89 
94 {
95  bool CommandSuccess = false;
96 
97  /* Run the appropriate SCSI command hander function based on the passed command */
98  switch (MSInterfaceInfo->State.CommandBlock.SCSICommandData[0]) {
99  case SCSI_CMD_INQUIRY:
100  CommandSuccess = SCSI_Command_Inquiry(MSInterfaceInfo);
101  break;
102 
104  CommandSuccess = SCSI_Command_Request_Sense(MSInterfaceInfo);
105  break;
106 
108  CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo);
109  break;
110 
112  CommandSuccess = SCSI_Command_Send_Diagnostic(MSInterfaceInfo);
113  break;
114 
115  case SCSI_CMD_WRITE_10:
116  CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE);
117  break;
118 
119  case SCSI_CMD_READ_10:
120  CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_READ);
121  break;
122 
124  CommandSuccess = SCSI_Command_ModeSense_6(MSInterfaceInfo);
125  break;
126 
129  case SCSI_CMD_VERIFY_10:
130  /* These commands should just succeed, no handling required */
131  CommandSuccess = true;
132  MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0;
133  break;
134 
135  default:
136  /* Update the SENSE key to reflect the invalid command */
140  break;
141  }
142 
143  /* Check if command was successfully processed */
144  if (CommandSuccess) {
148 
149  return true;
150  }
151 
152  return false;
153 }
154 
158 static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
159 {
160  uint16_t AllocationLength = SwapEndian_16(*(uint16_t *) &MSInterfaceInfo->State.CommandBlock.SCSICommandData[3]);
161  uint16_t BytesTransferred = MIN(AllocationLength, sizeof(InquiryData));
162 
163  /* Only the standard INQUIRY data is supported, check if any optional INQUIRY bits set */
164  if ((MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & ((1 << 0) | (1 << 1))) ||
165  MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]) {
166  /* Optional but unsupported bits set - update the SENSE key and fail the request */
170 
171  return false;
172  }
173 
174  Endpoint_Write_Stream_LE(&InquiryData, BytesTransferred, NULL);
175 
176  /* Pad out remaining bytes with 0x00 */
177  Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL);
178 
179  /* Finalize the stream transfer to send the last packet */
181 
182  /* Succeed the command and update the bytes transferred counter */
183  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred;
184 
185  return true;
186 }
187 
191 static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
192 {
193  uint8_t AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4];
194  uint8_t BytesTransferred = MIN(AllocationLength, sizeof(SenseData));
195 
196  Endpoint_Write_Stream_LE(&SenseData, BytesTransferred, NULL);
197  Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL);
199 
200  /* Succeed the command and update the bytes transferred counter */
201  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred;
202 
203  return true;
204 }
205 
209 static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
210 {
212 #ifdef CFG_SDCARD
213  uint32_t LastBlockAddressInLUN = MSC_Get_Block_Count() - 1;
214 #else
215  uint32_t LastBlockAddressInLUN = (LUN_MEDIA_BLOCKS - 1);
216 #endif
217  uint32_t MediaBlockSize = VIRTUAL_MEMORY_BLOCK_SIZE;
218 
219  Endpoint_Write_Stream_BE(&LastBlockAddressInLUN, sizeof(LastBlockAddressInLUN), NULL);
220  Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NULL);
222 
223  /* Succeed the command and update the bytes transferred counter */
224  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8;
225 
226  return true;
227 }
228 
233 static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
234 {
235  /* Check to see if the SELF TEST bit is not set */
236  if (!(MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & (1 << 2))) {
237  /* Only self-test supported - update SENSE key and fail the command */
241 
242  return false;
243  }
244  /* Succeed the command and update the bytes transferred counter */
245  MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0;
246 
247  return true;
248 }
249 
254 static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo,
255  const bool IsDataRead)
256 {
257  uint32_t BlockAddress;
258  uint16_t TotalBlocks;
259 
260  /* Check if the disk is write protected or not */
261  if ((IsDataRead == DATA_WRITE) && DISK_READ_ONLY) {
262  /* Block address is invalid, update SENSE key and return command fail */
266 
267  return false;
268  }
269 
270  /* Load in the 32-bit block address (SCSI uses big-endian, so have to reverse the byte order) */
271  BlockAddress =
272  (MSInterfaceInfo->State.CommandBlock.SCSICommandData[2] <<
273  24) + (MSInterfaceInfo->State.CommandBlock.SCSICommandData[3] << 16) +
274  (MSInterfaceInfo->State.CommandBlock.SCSICommandData[4] <<
275  8) + MSInterfaceInfo->State.CommandBlock.SCSICommandData[5];
276 
277  /* Load in the 16-bit total blocks (SCSI uses big-endian, so have to reverse the byte order) */
278  TotalBlocks =
279  (MSInterfaceInfo->State.CommandBlock.SCSICommandData[7] <<
280  8) + MSInterfaceInfo->State.CommandBlock.SCSICommandData[8];
281 
282  /* Check if the block address is outside the maximum allowable value for the LUN */
283 
285 #ifdef CFG_SDCARD
286  if (BlockAddress >= MSC_Get_Block_Count())
287 #else
288  if (BlockAddress >= LUN_MEDIA_BLOCKS)
289 #endif
290  {
291  /* Block address is invalid, update SENSE key and return command fail */
295 
296  return false;
297  }
298 
299  #if (TOTAL_LUNS > 1)
300  /* Adjust the given block address to the real media address based on the selected LUN */
301  BlockAddress += ((uint32_t) MSInterfaceInfo->State.CommandBlock.LUN * LUN_MEDIA_BLOCKS);
302  #endif
303 
305 #ifdef CFG_SDCARD
306  /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */
307  if (IsDataRead == DATA_READ) {
308  uint32_t ncache = (BlockAddress * 512) / CACHE_SIZE;
309  uint32_t modcache = (BlockAddress * 512) % CACHE_SIZE;
310  if (current_ncache != ncache) {
311  current_ncache = ncache;
312  Chip_SDMMC_ReadBlocks((void *) disk_cache, BlockAddress, CACHE_SECTORS);
313  }
314  Endpoint_Streaming((uint8_t *) (disk_cache + modcache), VIRTUAL_MEMORY_BLOCK_SIZE, TotalBlocks, 0);
315  }
316  else {
317  Endpoint_Streaming((uint8_t *) disk_cache, VIRTUAL_MEMORY_BLOCK_SIZE, TotalBlocks, 0);
318  Chip_SDMMC_WriteBlocks((void *) disk_cache, BlockAddress, TotalBlocks);
319  }
320 #else
321  uint32_t startaddr;
322  uint16_t blocks, dummyblocks;
323 
324  startaddr = MassStorage_GetAddressInImage(BlockAddress, TotalBlocks, &blocks);
325  if (blocks == 0) {
326  dummyblocks = TotalBlocks;
327  }
328  else if (blocks < TotalBlocks) {
329  dummyblocks = TotalBlocks - blocks;
330  }
331  else {dummyblocks = 0; }
332  Endpoint_Streaming((uint8_t *) startaddr, VIRTUAL_MEMORY_BLOCK_SIZE, blocks, dummyblocks);
333 #endif
334  /* Update the bytes transferred counter and succeed the command */
335  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t) TotalBlocks * VIRTUAL_MEMORY_BLOCK_SIZE);
336 
337  return true;
338 }
339 
347 static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
348 {
349  /* Send an empty header response with the Write Protect flag status */
350  Endpoint_Write_8(0x00);
351  Endpoint_Write_8(0x00);
352  Endpoint_Write_8(DISK_READ_ONLY ? 0x80 : 0x00);
353  Endpoint_Write_8(0x00);
355 
356  /* Update the bytes transferred counter and succeed the command */
357  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4;
358 
359  return true;
360 }