LPCOpen Platform  v1.03
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
MassStorageClassHost.c
Go to the documentation of this file.
1 /*
2  * @brief Host mode driver for the library USB Mass Storage Class driver
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 
34 #define __INCLUDE_FROM_USB_DRIVER
35 #include "../../Core/USBMode.h"
36 
37 #if defined(USB_CAN_BE_HOST)
38 
39 #define __INCLUDE_FROM_MS_DRIVER
40 #define __INCLUDE_FROM_MASSSTORAGE_HOST_C
41 #include "MassStorageClassHost.h"
42 
43 uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
44  uint16_t ConfigDescriptorSize,
45  void* ConfigDescriptorData)
46 {
47  USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
48  USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
49  USB_Descriptor_Interface_t* MassStorageInterface = NULL;
50  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
51 
52  memset(&MSInterfaceInfo->State, 0x00, sizeof(MSInterfaceInfo->State));
53 
54  if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
56 
57  while (!(DataINEndpoint) || !(DataOUTEndpoint))
58  {
59  if (!(MassStorageInterface) ||
60  USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
62  {
63  if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
65  {
67  }
68 
69  MassStorageInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
70 
71  DataINEndpoint = NULL;
72  DataOUTEndpoint = NULL;
73 
74  continue;
75  }
76 
77  USB_Descriptor_Endpoint_t* EndpointData = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Endpoint_t);
78 
79  if ((EndpointData->EndpointAddress & ENDPOINT_DIR_MASK) == ENDPOINT_DIR_IN)
80  DataINEndpoint = EndpointData;
81  else
82  DataOUTEndpoint = EndpointData;
83  }
84 
85  for (uint8_t PipeNum = 1; PipeNum < PIPE_TOTAL_PIPES; PipeNum++)
86  {
87  uint16_t Size;
88  uint8_t Type;
89  uint8_t Token;
90  uint8_t EndpointAddress;
91  bool DoubleBanked;
92 
93  if (PipeNum == MSInterfaceInfo->Config.DataINPipeNumber)
94  {
95  Size = le16_to_cpu(DataINEndpoint->EndpointSize);
96  EndpointAddress = DataINEndpoint->EndpointAddress;
97  Token = PIPE_TOKEN_IN;
98  Type = EP_TYPE_BULK;
99  DoubleBanked = MSInterfaceInfo->Config.DataINPipeDoubleBank;
100 
101  MSInterfaceInfo->State.DataINPipeSize = DataINEndpoint->EndpointSize;
102  }
103  else if (PipeNum == MSInterfaceInfo->Config.DataOUTPipeNumber)
104  {
105  Size = le16_to_cpu(DataOUTEndpoint->EndpointSize);
106  EndpointAddress = DataOUTEndpoint->EndpointAddress;
107  Token = PIPE_TOKEN_OUT;
108  Type = EP_TYPE_BULK;
109  DoubleBanked = MSInterfaceInfo->Config.DataOUTPipeDoubleBank;
110 
111  MSInterfaceInfo->State.DataOUTPipeSize = DataOUTEndpoint->EndpointSize;
112  }
113  else
114  {
115  continue;
116  }
117 
118  if (!(Pipe_ConfigurePipe(portnum,PipeNum, Type, Token, EndpointAddress, Size,
119  DoubleBanked ? PIPE_BANK_DOUBLE : PIPE_BANK_SINGLE)))
120  {
122  }
123  }
124 
125  MSInterfaceInfo->State.InterfaceNumber = MassStorageInterface->InterfaceNumber;
126  MSInterfaceInfo->State.IsActive = true;
127 
128  return MS_ENUMERROR_NoError;
129 }
130 
131 static uint8_t DCOMP_MS_Host_NextMSInterface(void* const CurrentDescriptor)
132 {
134 
135  if (Header->Type == DTYPE_Interface)
136  {
138 
139  if ((Interface->Class == MS_CSCP_MassStorageClass) &&
140  (Interface->SubClass == MS_CSCP_SCSITransparentSubclass) &&
141  (Interface->Protocol == MS_CSCP_BulkOnlyTransportProtocol))
142  {
144  }
145  }
146 
148 }
149 
150 static uint8_t DCOMP_MS_Host_NextMSInterfaceEndpoint(void* const CurrentDescriptor)
151 {
153 
154  if (Header->Type == DTYPE_Endpoint)
155  {
157 
158  uint8_t EndpointType = (Endpoint->Attributes & EP_TYPE_MASK);
159 
160  if ((EndpointType == EP_TYPE_BULK) && (!(Pipe_IsEndpointBound(Endpoint->EndpointAddress))))
161  {
163  }
164  }
165  else if (Header->Type == DTYPE_Interface)
166  {
167  return DESCRIPTOR_SEARCH_Fail;
168  }
169 
171 }
172 
173 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
174  MS_CommandBlockWrapper_t* const SCSICommandBlock,
175  const void* const BufferPtr)
176 {
177  uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
178  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
179 
180  if (++MSInterfaceInfo->State.TransactionTag == 0xFFFFFFFF)
181  MSInterfaceInfo->State.TransactionTag = 1;
182 
183  SCSICommandBlock->Signature = CPU_TO_LE32(MS_CBW_SIGNATURE);
184  SCSICommandBlock->Tag = cpu_to_le32(MSInterfaceInfo->State.TransactionTag);
185 
186  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataOUTPipeNumber);
187  Pipe_Unfreeze();
188 
189  if ((ErrorCode = Pipe_Write_Stream_LE(portnum,SCSICommandBlock, sizeof(MS_CommandBlockWrapper_t),
191  {
192  return ErrorCode;
193  }
194 
195  Pipe_ClearOUT(portnum);
196  Pipe_WaitUntilReady(portnum);
197 
198  Pipe_Freeze();
199 
200  if (BufferPtr != NULL)
201  {
202  ErrorCode = MS_Host_SendReceiveData(MSInterfaceInfo, SCSICommandBlock, (void*)BufferPtr);
203 
204  if ((ErrorCode != PIPE_RWSTREAM_NoError) && (ErrorCode != PIPE_RWSTREAM_PipeStalled))
205  {
206  Pipe_Freeze();
207  return ErrorCode;
208  }
209  }
210 
211  MS_CommandStatusWrapper_t SCSIStatusBlock;
212  return MS_Host_GetReturnedStatus(MSInterfaceInfo, &SCSIStatusBlock);
213 }
214 
215 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo)
216 {
217  uint16_t TimeoutMSRem = MS_COMMAND_DATA_TIMEOUT_MS;
218  uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber();
219  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
220 
221  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataINPipeNumber);
222  Pipe_Unfreeze();
223 
224  while (!(Pipe_IsINReceived(portnum)))
225  {
226  uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber();
227 
228  if (CurrentFrameNumber != PreviousFrameNumber)
229  {
230  PreviousFrameNumber = CurrentFrameNumber;
231 
232  if (!(TimeoutMSRem--))
233  return PIPE_RWSTREAM_Timeout;
234  }
235 
236  Pipe_Freeze();
237  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataOUTPipeNumber);
238  Pipe_Unfreeze();
239 
240  if (Pipe_IsStalled(portnum))
241  {
242  Pipe_ClearStall(portnum);
245  }
246 
247  Pipe_Freeze();
248  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataINPipeNumber);
249  Pipe_Unfreeze();
250 
251  if (Pipe_IsStalled(portnum))
252  {
253  Pipe_ClearStall(portnum);
256  }
257 
258  if (USB_HostState[portnum] == HOST_STATE_Unattached)
260  };
261 
262  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataINPipeNumber);
263  Pipe_Freeze();
264 
265  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataOUTPipeNumber);
266  Pipe_Freeze();
267 
268  return PIPE_RWSTREAM_NoError;
269 }
270 
271 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
272  MS_CommandBlockWrapper_t* const SCSICommandBlock,
273  void* BufferPtr)
274 {
275  uint16_t BytesRem = le32_to_cpu(SCSICommandBlock->DataTransferLength);
276  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
277 #if defined(__LPC177X_8X__) || defined(__LPC407X_8X__)
278  uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
279 
280  if (SCSICommandBlock->Flags & MS_COMMAND_DIR_DATA_IN)
281  {
282  if ((ErrorCode = MS_Host_WaitForDataReceived(MSInterfaceInfo)) != PIPE_RWSTREAM_NoError)
283  {
284  Pipe_Freeze();
285  return ErrorCode;
286  }
287 
288  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataINPipeNumber);
289  Pipe_Unfreeze();
290 
291  if ((ErrorCode = Pipe_Read_Stream_LE(portnum,BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
292  return ErrorCode;
293 
294  Pipe_ClearIN(portnum);
295  }
296  else
297  {
298  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataOUTPipeNumber);
299  Pipe_Unfreeze();
300 
301  if ((ErrorCode = Pipe_Write_Stream_LE(portnum,BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
302  return ErrorCode;
303 
304  Pipe_ClearOUT(portnum);
305 
306  while (!(Pipe_IsOUTReady(portnum)))
307  {
308  if (USB_HostState[portnum] == HOST_STATE_Unattached)
310  }
311  }
312 
313  Pipe_Freeze();
314 
315  return ErrorCode;
316 #else
317  uint16_t packsize;
318  if (SCSICommandBlock->Flags & MS_COMMAND_DIR_DATA_IN)
319  {
320  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataINPipeNumber);
321  packsize = MSInterfaceInfo->State.DataINPipeSize;
322  }
323  else
324  {
325  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataOUTPipeNumber);
326  packsize = MSInterfaceInfo->State.DataOUTPipeSize;
327  }
328 
329  Pipe_Streaming(portnum,(uint8_t*)BufferPtr,BytesRem,packsize);
330 
331  while(!Pipe_IsStatusOK(portnum));
332 
333  Pipe_ClearIN(portnum);
334  return PIPE_RWSTREAM_NoError;
335 #endif
336 }
337 
338 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
339  MS_CommandStatusWrapper_t* const SCSICommandStatus)
340 {
341  uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
342  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
343 
344  if ((ErrorCode = MS_Host_WaitForDataReceived(MSInterfaceInfo)) != PIPE_RWSTREAM_NoError)
345  return ErrorCode;
346 
347  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataINPipeNumber);
348  Pipe_Unfreeze();
349 
350  if ((ErrorCode = Pipe_Read_Stream_LE(portnum,SCSICommandStatus, sizeof(MS_CommandStatusWrapper_t),
352  {
353  return ErrorCode;
354  }
355 
356  Pipe_ClearIN(portnum);
357  Pipe_Freeze();
358 
359  if (SCSICommandStatus->Status != MS_SCSI_COMMAND_Pass)
360  ErrorCode = MS_ERROR_LOGICAL_CMD_FAILED;
361 
362  return ErrorCode;
363 }
364 
365 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo)
366 {
367  uint8_t ErrorCode;
368  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
370  {
371  .bmRequestType = (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE),
373  .wValue = 0,
374  .wIndex = MSInterfaceInfo->State.InterfaceNumber,
375  .wLength = 0,
376  };
377 
379 
380  if ((ErrorCode = USB_Host_SendControlRequest(portnum,NULL)) != HOST_SENDCONTROL_Successful)
381  return ErrorCode;
382 
383  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataINPipeNumber);
384 
386  return ErrorCode;
387 
388  Pipe_SelectPipe(portnum,MSInterfaceInfo->Config.DataOUTPipeNumber);
389 
391  return ErrorCode;
392 
394 }
395 
396 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
397  uint8_t* const MaxLUNIndex)
398 {
399  uint8_t ErrorCode;
400  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
402  {
403  .bmRequestType = (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE),
405  .wValue = 0,
406  .wIndex = MSInterfaceInfo->State.InterfaceNumber,
407  .wLength = 1,
408  };
409 
411 
412  if ((ErrorCode = USB_Host_SendControlRequest(portnum,MaxLUNIndex)) == HOST_SENDCONTROL_SetupStalled)
413  {
414  *MaxLUNIndex = 0;
415  ErrorCode = HOST_SENDCONTROL_Successful;
416  }
417 
418  return ErrorCode;
419 }
420 
421 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
422  const uint8_t LUNIndex,
424 {
425  uint8_t portnum = MSInterfaceInfo->Config.PortNumber;
426  uint8_t ErrorCode;
427  if ((USB_HostState[portnum] != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
429 
431  {
432  .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Inquiry_Response_t)),
433  .Flags = MS_COMMAND_DIR_DATA_IN,
434  .LUN = LUNIndex,
435  .SCSICommandLength = 6,
436  .SCSICommandData =
437  {
439  0x00, // Reserved
440  0x00, // Reserved
441  0x00, // Reserved
442  sizeof(SCSI_Inquiry_Response_t), // Allocation Length
443  0x00 // Unused (control)
444  }
445  };
446 
447  if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, InquiryData)) != PIPE_RWSTREAM_NoError)
448  return ErrorCode;
449 
450  return PIPE_RWSTREAM_NoError;
451 }
452 
453 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
454  const uint8_t LUNIndex)
455 {
456  if ((USB_HostState[MSInterfaceInfo->Config.PortNumber] != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
458 
459  uint8_t ErrorCode;
460 
462  {
463  .DataTransferLength = CPU_TO_LE32(0),
464  .Flags = MS_COMMAND_DIR_DATA_IN,
465  .LUN = LUNIndex,
466  .SCSICommandLength = 6,
467  .SCSICommandData =
468  {
470  0x00, // Reserved
471  0x00, // Reserved
472  0x00, // Reserved
473  0x00, // Reserved
474  0x00 // Unused (control)
475  }
476  };
477 
478  if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, NULL)) != PIPE_RWSTREAM_NoError)
479  return ErrorCode;
480 
481  return PIPE_RWSTREAM_NoError;
482 }
483 
485  const uint8_t LUNIndex,
486  SCSI_Capacity_t* const DeviceCapacity)
487 {
488  if ((USB_HostState[MSInterfaceInfo->Config.PortNumber] != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
490 
491  uint8_t ErrorCode;
492 
494  {
495  .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Capacity_t)),
496  .Flags = MS_COMMAND_DIR_DATA_IN,
497  .LUN = LUNIndex,
498  .SCSICommandLength = 10,
499  .SCSICommandData =
500  {
502  0x00, // Reserved
503  0x00, // MSB of Logical block address
504  0x00,
505  0x00,
506  0x00, // LSB of Logical block address
507  0x00, // Reserved
508  0x00, // Reserved
509  0x00, // Partial Medium Indicator
510  0x00 // Unused (control)
511  }
512  };
513 
514  if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, DeviceCapacity)) != PIPE_RWSTREAM_NoError)
515  return ErrorCode;
516 
517  DeviceCapacity->Blocks = BE32_TO_CPU(DeviceCapacity->Blocks);
518  DeviceCapacity->BlockSize = BE32_TO_CPU(DeviceCapacity->BlockSize);
519 
520  return PIPE_RWSTREAM_NoError;
521 }
522 
523 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
524  const uint8_t LUNIndex,
526 {
527  if ((USB_HostState[MSInterfaceInfo->Config.PortNumber] != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
529 
530  uint8_t ErrorCode;
531 
533  {
534  .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Request_Sense_Response_t)),
535  .Flags = MS_COMMAND_DIR_DATA_IN,
536  .LUN = LUNIndex,
537  .SCSICommandLength = 6,
538  .SCSICommandData =
539  {
541  0x00, // Reserved
542  0x00, // Reserved
543  0x00, // Reserved
544  sizeof(SCSI_Request_Sense_Response_t), // Allocation Length
545  0x00 // Unused (control)
546  }
547  };
548 
549  if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, SenseData)) != PIPE_RWSTREAM_NoError)
550  return ErrorCode;
551 
552  return PIPE_RWSTREAM_NoError;
553 }
554 
556  const uint8_t LUNIndex,
557  const bool PreventRemoval)
558 {
559  if ((USB_HostState[MSInterfaceInfo->Config.PortNumber] != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
561 
562  uint8_t ErrorCode;
563 
565  {
566  .DataTransferLength = CPU_TO_LE32(0),
567  .Flags = MS_COMMAND_DIR_DATA_OUT,
568  .LUN = LUNIndex,
569  .SCSICommandLength = 6,
570  .SCSICommandData =
571  {
573  0x00, // Reserved
574  0x00, // Reserved
575  PreventRemoval, // Prevent flag
576  0x00, // Reserved
577  0x00 // Unused (control)
578  }
579  };
580 
581  if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, NULL)) != PIPE_RWSTREAM_NoError)
582  return ErrorCode;
583 
584  return PIPE_RWSTREAM_NoError;
585 }
586 
587 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
588  const uint8_t LUNIndex,
589  const uint32_t BlockAddress,
590  const uint8_t Blocks,
591  const uint16_t BlockSize,
592  void* BlockBuffer)
593 {
594  if ((USB_HostState[MSInterfaceInfo->Config.PortNumber] != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
596 
597  uint8_t ErrorCode;
598 
600  {
601  .DataTransferLength = cpu_to_le32((uint32_t)Blocks * BlockSize),
602  .Flags = MS_COMMAND_DIR_DATA_IN,
603  .LUN = LUNIndex,
604  .SCSICommandLength = 10,
605  .SCSICommandData =
606  {
608  0x00, // Unused (control bits, all off)
609  (BlockAddress >> 24), // MSB of Block Address
610  (BlockAddress >> 16),
611  (BlockAddress >> 8),
612  (BlockAddress & 0xFF), // LSB of Block Address
613  0x00, // Reserved
614  0x00, // MSB of Total Blocks to Read
615  Blocks, // LSB of Total Blocks to Read
616  0x00 // Unused (control)
617  }
618  };
619 
620  if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, BlockBuffer)) != PIPE_RWSTREAM_NoError)
621  return ErrorCode;
622 
623  return PIPE_RWSTREAM_NoError;
624 }
625 
626 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
627  const uint8_t LUNIndex,
628  const uint32_t BlockAddress,
629  const uint8_t Blocks,
630  const uint16_t BlockSize,
631  const void* BlockBuffer)
632 {
633  if ((USB_HostState[MSInterfaceInfo->Config.PortNumber] != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
635 
636  uint8_t ErrorCode;
637 
639  {
640  .DataTransferLength = cpu_to_le32((uint32_t)Blocks * BlockSize),
641  .Flags = MS_COMMAND_DIR_DATA_OUT,
642  .LUN = LUNIndex,
643  .SCSICommandLength = 10,
644  .SCSICommandData =
645  {
647  0x00, // Unused (control bits, all off)
648  (BlockAddress >> 24), // MSB of Block Address
649  (BlockAddress >> 16),
650  (BlockAddress >> 8),
651  (BlockAddress & 0xFF), // LSB of Block Address
652  0x00, // Reserved
653  0x00, // MSB of Total Blocks to Write
654  Blocks, // LSB of Total Blocks to Write
655  0x00 // Unused (control)
656  }
657  };
658 
659  if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, BlockBuffer)) != PIPE_RWSTREAM_NoError)
660  return ErrorCode;
661 
662  return PIPE_RWSTREAM_NoError;
663 }
664 
665 #endif
666