LPCOpen Platform  v1.03
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 
36 /*****************************************************************************
37  * Private types/enumerations/variables
38  ****************************************************************************/
39 
40 /*****************************************************************************
41  * Public types/enumerations/variables
42  ****************************************************************************/
43 
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 
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 {
89 }
90 
91 #endif
92 
93 /*****************************************************************************
94  * Private functions
95  ****************************************************************************/
96 
97 /*****************************************************************************
98  * Public functions
99  ****************************************************************************/
100 
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 
116  CommandSuccess = SCSI_Command_Request_Sense(MSInterfaceInfo);
117  break;
118 
120  CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo);
121  break;
122 
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 
136  CommandSuccess = SCSI_Command_ModeSense_6(MSInterfaceInfo);
137  break;
138 
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 */
152  break;
153  }
154 
155  /* Check if command was successfully processed */
156  if (CommandSuccess) {
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 */
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 */
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 {
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 */
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 */
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,
328  TotalBlocks,
329  0);
330  }
331  else {
332  Endpoint_Streaming(MSInterfaceInfo->Config.PortNumber,
333  (uint8_t *) disk_cache,
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,
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 }