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 USB Mass Storage device SCSI command processing module
3  *
4  * SCSI command processing routines, for SCSI commands issued by the host.
5  * Mass Storage devices use a thin "Bulk-Only Transport" protocol for issuing
6  * commands and status information, which wrap around standard SCSI device
7  * commands for controlling the actual storage medium.
8  *
9  * @note
10  * Copyright(C) NXP Semiconductors, 2012
11  * All rights reserved.
12  *
13  * @par
14  * Software that is described herein is for illustrative purposes only
15  * which provides customers with programming information regarding the
16  * LPC products. This software is supplied "AS IS" without any warranties of
17  * any kind, and NXP Semiconductors and its licensor disclaim any and
18  * all warranties, express or implied, including all implied warranties of
19  * merchantability, fitness for a particular purpose and non-infringement of
20  * intellectual property rights. NXP Semiconductors assumes no responsibility
21  * or liability for the use of the software, conveys no license or rights under any
22  * patent, copyright, mask work right, or any other intellectual property rights in
23  * or to any products. NXP Semiconductors reserves the right to make changes
24  * in the software without notification. NXP Semiconductors also makes no
25  * representation or warranty that such application will be suitable for the
26  * specified use without further testing or modification.
27  *
28  * @par
29  * Permission to use, copy, modify, and distribute this software and its
30  * documentation is hereby granted, under NXP Semiconductors' and its
31  * licensor's relevant copyrights in the software, without fee, provided that it
32  * is used in conjunction with NXP Semiconductors microcontrollers. This
33  * copyright, permission, and disclaimer notice must appear in all copies of
34  * this code.
35  */
36 
37 #define INCLUDE_FROM_SCSI_C
38 #include "SCSI.h"
39 
40 /*****************************************************************************
41  * Private types/enumerations/variables
42  ****************************************************************************/
43 
49  .DeviceType = DEVICE_TYPE_BLOCK,
50  .PeripheralQualifier = 0,
51 
52  .Removable = true,
53 
54  .Version = 0,
55 
56  .ResponseDataFormat = 2,
57  .NormACA = false,
58  .TrmTsk = false,
59  .AERC = false,
60 
61  .AdditionalLength = 0x1F,
62 
63  .SoftReset = false,
64  .CmdQue = false,
65  .Linked = false,
66  .Sync = false,
67  .WideBus16Bit = false,
68  .WideBus32Bit = false,
69  .RelAddr = false,
70 
71  .VendorID = "NXP",
72  .ProductID = "Dataflash Disk",
73  .RevisionID = {'0', '.', '0', '0'},
74 };
75 
83  .ResponseCode = 0x70,
84  .AdditionalLength = 0x0A,
85 };
86 
87 /*****************************************************************************
88  * Public types/enumerations/variables
89  ****************************************************************************/
90 
91 /*****************************************************************************
92  * Private functions
93  ****************************************************************************/
94 
95 /*****************************************************************************
96  * Public functions
97  ****************************************************************************/
98 
99 /* Command processing for an issued SCSI INQUIRY command */
100 static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
101 {
102  uint16_t AllocationLength =
103  SwapEndian_16(*(uint16_t *) &MSInterfaceInfo->State.CommandBlock.SCSICommandData[3]);
104  uint16_t BytesTransferred = MIN(AllocationLength, sizeof(InquiryData));
105 
106  /* Only the standard INQUIRY data is supported, check if any optional INQUIRY bits set */
107  if ((MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & ((1 << 0) | (1 << 1))) ||
108  MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]) {
109  /* Optional but unsupported bits set - update the SENSE key and fail the request */
113 
114  return false;
115  }
116 
117  Endpoint_Write_Stream_LE(&InquiryData, BytesTransferred, NULL);
118 
119  /* Pad out remaining bytes with 0x00 */
120  Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL);
121 
122  /* Finalize the stream transfer to send the last packet */
124 
125  /* Succeed the command and update the bytes transferred counter */
126  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred;
127 
128  return true;
129 }
130 
131 /* Command processing for an issued SCSI REQUEST SENSE command */
132 static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
133 {
134  uint8_t AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4];
135  uint8_t BytesTransferred = MIN(AllocationLength, sizeof(SenseData));
136 
137  Endpoint_Write_Stream_LE(&SenseData, BytesTransferred, NULL);
138  Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL);
140 
141  /* Succeed the command and update the bytes transferred counter */
142  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred;
143 
144  return true;
145 }
146 
147 /* Command processing for an issued SCSI READ CAPACITY (10) command */
148 static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
149 {
150  uint32_t LastBlockAddressInLUN = (LUN_MEDIA_BLOCKS - 1);
151  uint32_t MediaBlockSize = VIRTUAL_MEMORY_BLOCK_SIZE;
152 
153  Endpoint_Write_Stream_BE(&LastBlockAddressInLUN, sizeof(LastBlockAddressInLUN), NULL);
154  Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NULL);
156 
157  /* Succeed the command and update the bytes transferred counter */
158  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8;
159 
160  return true;
161 }
162 
163 /* Command processing for an issued SCSI SEND DIAGNOSTIC command */
164 static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
165 {
166  /* Check to see if the SELF TEST bit is not set */
167  if (!(MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & (1 << 2))) {
168  /* Only self-test supported - update SENSE key and fail the command */
172 
173  return false;
174  }
175  /* Succeed the command and update the bytes transferred counter */
176  MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0;
177 
178  return true;
179 }
180 
181 /* Command processing for an issued SCSI READ (10) or WRITE (10) command */
182 static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo,
183  const bool IsDataRead)
184 {
185  uint32_t BlockAddress;
186  uint16_t TotalBlocks;
187 
188  /* Check if the disk is write protected or not */
189  if ((IsDataRead == DATA_WRITE) && DISK_READ_ONLY) {
190  /* Block address is invalid, update SENSE key and return command fail */
194 
195  return false;
196  }
197 
198  /* SCSI uses big-endian, so have to reverse the byte order */
199  /* Load 32-bit block addr */
200  BlockAddress = (MSInterfaceInfo->State.CommandBlock.SCSICommandData[2] << 24) +
201  (MSInterfaceInfo->State.CommandBlock.SCSICommandData[3] << 16) +
202  (MSInterfaceInfo->State.CommandBlock.SCSICommandData[4] << 8) +
203  MSInterfaceInfo->State.CommandBlock.SCSICommandData[5];
204 
205  /* Load in the 16-bit total blocks */
206  TotalBlocks = (MSInterfaceInfo->State.CommandBlock.SCSICommandData[7] << 8) +
207  MSInterfaceInfo->State.CommandBlock.SCSICommandData[8];
208 
209  /* Check if the block address is outside the maximum allowable value for the LUN */
210  if (BlockAddress >= LUN_MEDIA_BLOCKS) {
211  /* Block address is invalid, update SENSE key and return command fail */
215 
216  return false;
217  }
218 
219  #if (TOTAL_LUNS > 1)
220  /* Adjust the given block address to the real media address based on the selected LUN */
221  BlockAddress += ((uint32_t) MSInterfaceInfo->State.CommandBlock.LUN * LUN_MEDIA_BLOCKS);
222  #endif
223 #if 0
224  /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */
225  if (IsDataRead == DATA_READ) {
226  DataRam_ReadBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks);
227  }
228  else {
229  DataRam_WriteBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks);
230  }
231 #else
232  uint32_t startaddr;
233  uint16_t blocks, dummyblocks;
234 
235  startaddr = MassStorage_GetAddressInImage(BlockAddress, TotalBlocks, &blocks);
236  if (blocks == 0) {
237  dummyblocks = TotalBlocks;
238  }
239  else if (blocks < TotalBlocks) {
240  dummyblocks = TotalBlocks - blocks;
241  }
242  else {dummyblocks = 0; }
243  Endpoint_Streaming((uint8_t *) startaddr, VIRTUAL_MEMORY_BLOCK_SIZE, blocks, dummyblocks);
244 #endif
245  /* Update the bytes transferred counter and succeed the command */
246  MSInterfaceInfo->State.CommandBlock.DataTransferLength -=
247  ((uint32_t) TotalBlocks * VIRTUAL_MEMORY_BLOCK_SIZE);
248 
249  return true;
250 }
251 
252 /* Command processing for an issued SCSI MODE SENSE (6) command */
253 static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo)
254 {
255  /* Send an empty header response with the Write Protect flag status */
256  Endpoint_Write_8(0x00);
257  Endpoint_Write_8(0x00);
258  Endpoint_Write_8(DISK_READ_ONLY ? 0x80 : 0x00);
259  Endpoint_Write_8(0x00);
261 
262  /* Update the bytes transferred counter and succeed the command */
263  MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4;
264 
265  return true;
266 }
267 
268 /* SCSI Command processing function */
270 {
271  bool CommandSuccess = false;
272 
273  /* Run the appropriate SCSI command hander function based on the passed command */
274  switch (MSInterfaceInfo->State.CommandBlock.SCSICommandData[0]) {
275  case SCSI_CMD_INQUIRY:
276  CommandSuccess = SCSI_Command_Inquiry(MSInterfaceInfo);
277  break;
278 
280  CommandSuccess = SCSI_Command_Request_Sense(MSInterfaceInfo);
281  break;
282 
284  CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo);
285  break;
286 
288  CommandSuccess = SCSI_Command_Send_Diagnostic(MSInterfaceInfo);
289  break;
290 
291  case SCSI_CMD_WRITE_10:
292  CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE);
293  break;
294 
295  case SCSI_CMD_READ_10:
296  CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_READ);
297  break;
298 
300  CommandSuccess = SCSI_Command_ModeSense_6(MSInterfaceInfo);
301  break;
302 
305  case SCSI_CMD_VERIFY_10:
306  /* These commands should just succeed, no handling required */
307  CommandSuccess = true;
308  MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0;
309  break;
310 
311  default:
312  /* Update the SENSE key to reflect the invalid command */
316  break;
317  }
318 
319  /* Check if command was successfully processed */
320  if (CommandSuccess) {
324 
325  return true;
326  }
327 
328  return false;
329 }