]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - tmk_core/protocol/lufa/LUFA-git/LUFA/Drivers/USB/Class/Host/MassStorageClassHost.c
Merge commit '657d9f23fe47fb88cf221adb23095082f191ba6a'
[max/tmk_keyboard.git] / tmk_core / protocol / lufa / LUFA-git / LUFA / Drivers / USB / Class / Host / MassStorageClassHost.c
1 /*
2              LUFA Library
3      Copyright (C) Dean Camera, 2014.
4
5   dean [at] fourwalledcubicle [dot] com
6            www.lufa-lib.org
7 */
8
9 /*
10   Copyright 2014  Dean Camera (dean [at] fourwalledcubicle [dot] com)
11
12   Permission to use, copy, modify, distribute, and sell this
13   software and its documentation for any purpose is hereby granted
14   without fee, provided that the above copyright notice appear in
15   all copies and that both that the copyright notice and this
16   permission notice and warranty disclaimer appear in supporting
17   documentation, and that the name of the author not be used in
18   advertising or publicity pertaining to distribution of the
19   software without specific, written prior permission.
20
21   The author disclaims all warranties with regard to this
22   software, including all implied warranties of merchantability
23   and fitness.  In no event shall the author be liable for any
24   special, indirect or consequential damages or any damages
25   whatsoever resulting from loss of use, data or profits, whether
26   in an action of contract, negligence or other tortious action,
27   arising out of or in connection with the use or performance of
28   this software.
29 */
30
31 #define  __INCLUDE_FROM_USB_DRIVER
32 #include "../../Core/USBMode.h"
33
34 #if defined(USB_CAN_BE_HOST)
35
36 #define  __INCLUDE_FROM_MS_DRIVER
37 #define  __INCLUDE_FROM_MASSSTORAGE_HOST_C
38 #include "MassStorageClassHost.h"
39
40 uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
41                                uint16_t ConfigDescriptorSize,
42                                                            void* ConfigDescriptorData)
43 {
44         USB_Descriptor_Endpoint_t*  DataINEndpoint       = NULL;
45         USB_Descriptor_Endpoint_t*  DataOUTEndpoint      = NULL;
46         USB_Descriptor_Interface_t* MassStorageInterface = NULL;
47
48         memset(&MSInterfaceInfo->State, 0x00, sizeof(MSInterfaceInfo->State));
49
50         if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
51           return MS_ENUMERROR_InvalidConfigDescriptor;
52
53         while (!(DataINEndpoint) || !(DataOUTEndpoint))
54         {
55                 if (!(MassStorageInterface) ||
56                     USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
57                                               DCOMP_MS_Host_NextMSInterfaceEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
58                 {
59                         if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
60                                                       DCOMP_MS_Host_NextMSInterface) != DESCRIPTOR_SEARCH_COMP_Found)
61                         {
62                                 return MS_ENUMERROR_NoCompatibleInterfaceFound;
63                         }
64
65                         MassStorageInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
66
67                         DataINEndpoint  = NULL;
68                         DataOUTEndpoint = NULL;
69
70                         continue;
71                 }
72
73                 USB_Descriptor_Endpoint_t* EndpointData = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Endpoint_t);
74
75                 if ((EndpointData->EndpointAddress & ENDPOINT_DIR_MASK) == ENDPOINT_DIR_IN)
76                   DataINEndpoint  = EndpointData;
77                 else
78                   DataOUTEndpoint = EndpointData;
79         }
80
81         MSInterfaceInfo->Config.DataINPipe.Size  = le16_to_cpu(DataINEndpoint->EndpointSize);
82         MSInterfaceInfo->Config.DataINPipe.EndpointAddress = DataINEndpoint->EndpointAddress;
83         MSInterfaceInfo->Config.DataINPipe.Type  = EP_TYPE_BULK;
84
85         MSInterfaceInfo->Config.DataOUTPipe.Size = le16_to_cpu(DataOUTEndpoint->EndpointSize);
86         MSInterfaceInfo->Config.DataOUTPipe.EndpointAddress = DataOUTEndpoint->EndpointAddress;
87         MSInterfaceInfo->Config.DataOUTPipe.Type = EP_TYPE_BULK;
88
89         if (!(Pipe_ConfigurePipeTable(&MSInterfaceInfo->Config.DataINPipe, 1)))
90           return MS_ENUMERROR_PipeConfigurationFailed;
91
92         if (!(Pipe_ConfigurePipeTable(&MSInterfaceInfo->Config.DataOUTPipe, 1)))
93           return MS_ENUMERROR_PipeConfigurationFailed;
94
95         MSInterfaceInfo->State.InterfaceNumber = MassStorageInterface->InterfaceNumber;
96         MSInterfaceInfo->State.IsActive = true;
97
98         return MS_ENUMERROR_NoError;
99 }
100
101 static uint8_t DCOMP_MS_Host_NextMSInterface(void* const CurrentDescriptor)
102 {
103         USB_Descriptor_Header_t* Header = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Header_t);
104
105         if (Header->Type == DTYPE_Interface)
106         {
107                 USB_Descriptor_Interface_t* Interface = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Interface_t);
108
109                 if ((Interface->Class    == MS_CSCP_MassStorageClass)        &&
110                     (Interface->SubClass == MS_CSCP_SCSITransparentSubclass) &&
111                     (Interface->Protocol == MS_CSCP_BulkOnlyTransportProtocol))
112                 {
113                         return DESCRIPTOR_SEARCH_Found;
114                 }
115         }
116
117         return DESCRIPTOR_SEARCH_NotFound;
118 }
119
120 static uint8_t DCOMP_MS_Host_NextMSInterfaceEndpoint(void* const CurrentDescriptor)
121 {
122         USB_Descriptor_Header_t* Header = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Header_t);
123
124         if (Header->Type == DTYPE_Endpoint)
125         {
126                 USB_Descriptor_Endpoint_t* Endpoint = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Endpoint_t);
127
128                 uint8_t EndpointType = (Endpoint->Attributes & EP_TYPE_MASK);
129
130                 if ((EndpointType == EP_TYPE_BULK) && (!(Pipe_IsEndpointBound(Endpoint->EndpointAddress))))
131                 {
132                         return DESCRIPTOR_SEARCH_Found;
133                 }
134         }
135         else if (Header->Type == DTYPE_Interface)
136         {
137                 return DESCRIPTOR_SEARCH_Fail;
138         }
139
140         return DESCRIPTOR_SEARCH_NotFound;
141 }
142
143 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
144                                    MS_CommandBlockWrapper_t* const SCSICommandBlock,
145                                    const void* const BufferPtr)
146 {
147         uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
148
149         if (++MSInterfaceInfo->State.TransactionTag == 0xFFFFFFFF)
150           MSInterfaceInfo->State.TransactionTag = 1;
151
152         SCSICommandBlock->Signature = CPU_TO_LE32(MS_CBW_SIGNATURE);
153         SCSICommandBlock->Tag       = cpu_to_le32(MSInterfaceInfo->State.TransactionTag);
154
155         Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
156         Pipe_Unfreeze();
157
158         if ((ErrorCode = Pipe_Write_Stream_LE(SCSICommandBlock, sizeof(MS_CommandBlockWrapper_t),
159                                               NULL)) != PIPE_RWSTREAM_NoError)
160         {
161                 return ErrorCode;
162         }
163
164         Pipe_ClearOUT();
165         Pipe_WaitUntilReady();
166
167         Pipe_Freeze();
168
169         if (BufferPtr != NULL)
170         {
171                 ErrorCode = MS_Host_SendReceiveData(MSInterfaceInfo, SCSICommandBlock, (void*)BufferPtr);
172
173                 if ((ErrorCode != PIPE_RWSTREAM_NoError) && (ErrorCode != PIPE_RWSTREAM_PipeStalled))
174                 {
175                         Pipe_Freeze();
176                         return ErrorCode;
177                 }
178         }
179
180         MS_CommandStatusWrapper_t SCSIStatusBlock;
181         return MS_Host_GetReturnedStatus(MSInterfaceInfo, &SCSIStatusBlock);
182 }
183
184 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo)
185 {
186         uint16_t TimeoutMSRem        = MS_COMMAND_DATA_TIMEOUT_MS;
187         uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber();
188
189         Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
190         Pipe_Unfreeze();
191
192         while (!(Pipe_IsINReceived()))
193         {
194                 uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber();
195
196                 if (CurrentFrameNumber != PreviousFrameNumber)
197                 {
198                         PreviousFrameNumber = CurrentFrameNumber;
199
200                         if (!(TimeoutMSRem--))
201                           return PIPE_RWSTREAM_Timeout;
202                 }
203
204                 Pipe_Freeze();
205                 Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
206                 Pipe_Unfreeze();
207
208                 if (Pipe_IsStalled())
209                 {
210                         USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
211                         return PIPE_RWSTREAM_PipeStalled;
212                 }
213
214                 Pipe_Freeze();
215                 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
216                 Pipe_Unfreeze();
217
218                 if (Pipe_IsStalled())
219                 {
220                         USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
221                         return PIPE_RWSTREAM_PipeStalled;
222                 }
223
224                 if (USB_HostState == HOST_STATE_Unattached)
225                   return PIPE_RWSTREAM_DeviceDisconnected;
226         };
227
228         Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
229         Pipe_Freeze();
230
231         Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
232         Pipe_Freeze();
233
234         return PIPE_RWSTREAM_NoError;
235 }
236
237 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
238                                        MS_CommandBlockWrapper_t* const SCSICommandBlock,
239                                        void* BufferPtr)
240 {
241         uint8_t  ErrorCode = PIPE_RWSTREAM_NoError;
242         uint16_t BytesRem  = le32_to_cpu(SCSICommandBlock->DataTransferLength);
243
244         if (SCSICommandBlock->Flags & MS_COMMAND_DIR_DATA_IN)
245         {
246                 if ((ErrorCode = MS_Host_WaitForDataReceived(MSInterfaceInfo)) != PIPE_RWSTREAM_NoError)
247                 {
248                         Pipe_Freeze();
249                         return ErrorCode;
250                 }
251
252                 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
253                 Pipe_Unfreeze();
254
255                 if ((ErrorCode = Pipe_Read_Stream_LE(BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
256                   return ErrorCode;
257
258                 Pipe_ClearIN();
259         }
260         else
261         {
262                 Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
263                 Pipe_Unfreeze();
264
265                 if ((ErrorCode = Pipe_Write_Stream_LE(BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
266                   return ErrorCode;
267
268                 Pipe_ClearOUT();
269
270                 while (!(Pipe_IsOUTReady()))
271                 {
272                         if (USB_HostState == HOST_STATE_Unattached)
273                           return PIPE_RWSTREAM_DeviceDisconnected;
274                 }
275         }
276
277         Pipe_Freeze();
278
279         return ErrorCode;
280 }
281
282 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
283                                          MS_CommandStatusWrapper_t* const SCSICommandStatus)
284 {
285         uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
286
287         if ((ErrorCode = MS_Host_WaitForDataReceived(MSInterfaceInfo)) != PIPE_RWSTREAM_NoError)
288           return ErrorCode;
289
290         Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
291         Pipe_Unfreeze();
292
293         if ((ErrorCode = Pipe_Read_Stream_LE(SCSICommandStatus, sizeof(MS_CommandStatusWrapper_t),
294                                              NULL)) != PIPE_RWSTREAM_NoError)
295         {
296                 return ErrorCode;
297         }
298
299         Pipe_ClearIN();
300         Pipe_Freeze();
301
302         if (SCSICommandStatus->Status != MS_SCSI_COMMAND_Pass)
303           ErrorCode = MS_ERROR_LOGICAL_CMD_FAILED;
304
305         return ErrorCode;
306 }
307
308 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo)
309 {
310         uint8_t ErrorCode;
311
312         USB_ControlRequest = (USB_Request_Header_t)
313                 {
314                         .bmRequestType = (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE),
315                         .bRequest      = MS_REQ_MassStorageReset,
316                         .wValue        = 0,
317                         .wIndex        = MSInterfaceInfo->State.InterfaceNumber,
318                         .wLength       = 0,
319                 };
320
321         Pipe_SelectPipe(PIPE_CONTROLPIPE);
322
323         if ((ErrorCode = USB_Host_SendControlRequest(NULL)) != HOST_SENDCONTROL_Successful)
324           return ErrorCode;
325
326         Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
327
328         if ((ErrorCode = USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful)
329           return ErrorCode;
330
331         Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
332
333         if ((ErrorCode = USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful)
334           return ErrorCode;
335
336         return HOST_SENDCONTROL_Successful;
337 }
338
339 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
340                           uint8_t* const MaxLUNIndex)
341 {
342         uint8_t ErrorCode;
343
344         USB_ControlRequest = (USB_Request_Header_t)
345                 {
346                         .bmRequestType = (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE),
347                         .bRequest      = MS_REQ_GetMaxLUN,
348                         .wValue        = 0,
349                         .wIndex        = MSInterfaceInfo->State.InterfaceNumber,
350                         .wLength       = 1,
351                 };
352
353         Pipe_SelectPipe(PIPE_CONTROLPIPE);
354
355         if ((ErrorCode = USB_Host_SendControlRequest(MaxLUNIndex)) == HOST_SENDCONTROL_SetupStalled)
356         {
357                 *MaxLUNIndex = 0;
358                 ErrorCode    = HOST_SENDCONTROL_Successful;
359         }
360
361         return ErrorCode;
362 }
363
364 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
365                                const uint8_t LUNIndex,
366                                SCSI_Inquiry_Response_t* const InquiryData)
367 {
368         if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
369           return HOST_SENDCONTROL_DeviceDisconnected;
370
371         MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
372                 {
373                         .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Inquiry_Response_t)),
374                         .Flags              = MS_COMMAND_DIR_DATA_IN,
375                         .LUN                = LUNIndex,
376                         .SCSICommandLength  = 6,
377                         .SCSICommandData    =
378                                 {
379                                         SCSI_CMD_INQUIRY,
380                                         0x00,                            // Reserved
381                                         0x00,                            // Reserved
382                                         0x00,                            // Reserved
383                                         sizeof(SCSI_Inquiry_Response_t), // Allocation Length
384                                         0x00                             // Unused (control)
385                                 }
386                 };
387
388         return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, InquiryData);
389 }
390
391 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
392                               const uint8_t LUNIndex)
393 {
394         if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
395           return HOST_SENDCONTROL_DeviceDisconnected;
396
397         MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
398                 {
399                         .DataTransferLength = CPU_TO_LE32(0),
400                         .Flags              = MS_COMMAND_DIR_DATA_IN,
401                         .LUN                = LUNIndex,
402                         .SCSICommandLength  = 6,
403                         .SCSICommandData    =
404                                 {
405                                         SCSI_CMD_TEST_UNIT_READY,
406                                         0x00,                   // Reserved
407                                         0x00,                   // Reserved
408                                         0x00,                   // Reserved
409                                         0x00,                   // Reserved
410                                         0x00                    // Unused (control)
411                                 }
412                 };
413
414         return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, NULL);
415 }
416
417 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
418                                    const uint8_t LUNIndex,
419                                    SCSI_Capacity_t* const DeviceCapacity)
420 {
421         if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
422           return HOST_SENDCONTROL_DeviceDisconnected;
423
424         uint8_t ErrorCode;
425
426         MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
427                 {
428                         .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Capacity_t)),
429                         .Flags              = MS_COMMAND_DIR_DATA_IN,
430                         .LUN                = LUNIndex,
431                         .SCSICommandLength  = 10,
432                         .SCSICommandData    =
433                                 {
434                                         SCSI_CMD_READ_CAPACITY_10,
435                                         0x00,                   // Reserved
436                                         0x00,                   // MSB of Logical block address
437                                         0x00,
438                                         0x00,
439                                         0x00,                   // LSB of Logical block address
440                                         0x00,                   // Reserved
441                                         0x00,                   // Reserved
442                                         0x00,                   // Partial Medium Indicator
443                                         0x00                    // Unused (control)
444                                 }
445                 };
446
447         if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, DeviceCapacity)) != PIPE_RWSTREAM_NoError)
448           return ErrorCode;
449
450         DeviceCapacity->Blocks    = BE32_TO_CPU(DeviceCapacity->Blocks);
451         DeviceCapacity->BlockSize = BE32_TO_CPU(DeviceCapacity->BlockSize);
452
453         return PIPE_RWSTREAM_NoError;
454 }
455
456 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
457                              const uint8_t LUNIndex,
458                              SCSI_Request_Sense_Response_t* const SenseData)
459 {
460         if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
461           return HOST_SENDCONTROL_DeviceDisconnected;
462
463         MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
464                 {
465                         .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Request_Sense_Response_t)),
466                         .Flags              = MS_COMMAND_DIR_DATA_IN,
467                         .LUN                = LUNIndex,
468                         .SCSICommandLength  = 6,
469                         .SCSICommandData    =
470                                 {
471                                         SCSI_CMD_REQUEST_SENSE,
472                                         0x00,                                  // Reserved
473                                         0x00,                                  // Reserved
474                                         0x00,                                  // Reserved
475                                         sizeof(SCSI_Request_Sense_Response_t), // Allocation Length
476                                         0x00                                   // Unused (control)
477                                 }
478                 };
479
480         return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, SenseData);
481 }
482
483 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
484                                           const uint8_t LUNIndex,
485                                           const bool PreventRemoval)
486 {
487         if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
488           return HOST_SENDCONTROL_DeviceDisconnected;
489
490         MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
491                 {
492                         .DataTransferLength = CPU_TO_LE32(0),
493                         .Flags              = MS_COMMAND_DIR_DATA_OUT,
494                         .LUN                = LUNIndex,
495                         .SCSICommandLength  = 6,
496                         .SCSICommandData    =
497                                 {
498                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL,
499                                         0x00,                   // Reserved
500                                         0x00,                   // Reserved
501                                         PreventRemoval,         // Prevent flag
502                                         0x00,                   // Reserved
503                                         0x00                    // Unused (control)
504                                 }
505                 };
506
507         return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, NULL);
508 }
509
510 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
511                                  const uint8_t LUNIndex,
512                                  const uint32_t BlockAddress,
513                                  const uint8_t Blocks,
514                                  const uint16_t BlockSize,
515                                  void* BlockBuffer)
516 {
517         if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
518           return HOST_SENDCONTROL_DeviceDisconnected;
519
520         MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
521                 {
522                         .DataTransferLength = cpu_to_le32((uint32_t)Blocks * BlockSize),
523                         .Flags              = MS_COMMAND_DIR_DATA_IN,
524                         .LUN                = LUNIndex,
525                         .SCSICommandLength  = 10,
526                         .SCSICommandData    =
527                                 {
528                                         SCSI_CMD_READ_10,
529                                         0x00,                   // Unused (control bits, all off)
530                                         (BlockAddress >> 24),   // MSB of Block Address
531                                         (BlockAddress >> 16),
532                                         (BlockAddress >> 8),
533                                         (BlockAddress & 0xFF),  // LSB of Block Address
534                                         0x00,                   // Reserved
535                                         0x00,                   // MSB of Total Blocks to Read
536                                         Blocks,                 // LSB of Total Blocks to Read
537                                         0x00                    // Unused (control)
538                                 }
539                 };
540
541         return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, BlockBuffer);
542 }
543
544 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
545                                   const uint8_t LUNIndex,
546                                   const uint32_t BlockAddress,
547                                   const uint8_t Blocks,
548                                   const uint16_t BlockSize,
549                                   const void* BlockBuffer)
550 {
551         if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
552           return HOST_SENDCONTROL_DeviceDisconnected;
553
554         MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
555                 {
556                         .DataTransferLength = cpu_to_le32((uint32_t)Blocks * BlockSize),
557                         .Flags              = MS_COMMAND_DIR_DATA_OUT,
558                         .LUN                = LUNIndex,
559                         .SCSICommandLength  = 10,
560                         .SCSICommandData    =
561                                 {
562                                         SCSI_CMD_WRITE_10,
563                                         0x00,                   // Unused (control bits, all off)
564                                         (BlockAddress >> 24),   // MSB of Block Address
565                                         (BlockAddress >> 16),
566                                         (BlockAddress >> 8),
567                                         (BlockAddress & 0xFF),  // LSB of Block Address
568                                         0x00,                   // Reserved
569                                         0x00,                   // MSB of Total Blocks to Write
570                                         Blocks,                 // LSB of Total Blocks to Write
571                                         0x00                    // Unused (control)
572                                 }
573                 };
574
575         return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, BlockBuffer);
576 }
577
578 #endif
579