Remove old OTG demo directory with useless TestApp demo. Add new DualRole directory...
[pub/USBasp.git] / Demos / Host / LowLevel / MassStorageHost / Lib / MassStoreCommands.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2009.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.fourwalledcubicle.com
7 */
8
9 /*
10 Copyright 2009 Dean Camera (dean [at] fourwalledcubicle [dot] com)
11
12 Permission to use, copy, modify, and distribute this software
13 and its documentation for any purpose and without fee is hereby
14 granted, provided that the above copyright notice appear in all
15 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 disclaim 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 /** \file
32 *
33 * Mass Storage Device commands, to issue MSD commands to the device for
34 * reading device status, capacity, and other characteristics. This file
35 * also contains block read and write functions, so that device blocks
36 * can be read and written. In general, these functions would be chained
37 * to a FAT library to give file-level access to an attached device's contents.
38 *
39 * \note Many Mass Storage devices on the market are non-compliant to the
40 * specifications and thus can prove difficult to interface with. It
41 * may be necessary to retry the functions in the module several times
42 * after they have returned and error to successfully send the command
43 * to the device. Some devices may also need to have the stream function
44 * timeout period extended beyond 100ms (some badly designed devices exceeding
45 * 1.5 seconds occasionally) by defining USB_STREAM_TIMEOUT_MS to a
46 * larger value in the project makefile and passing it to the compiler
47 * via the -D switch.
48 */
49
50 #define INCLUDE_FROM_MASSSTORE_COMMANDS_C
51 #include "MassStoreCommands.h"
52
53 /* Globals: */
54 /** Current Tag value used in issued CBWs to the device. This is automatically incremented
55 * each time a command is sent, and is not externally accessible.
56 */
57 static uint32_t MassStore_Tag = 1;
58
59
60 /** Routine to send the current CBW to the device, and increment the Tag value as needed.
61 *
62 * \param[in] SCSICommandBlock Pointer to a SCSI command block structure to send to the attached device
63 * \param[in,out] BufferPtr Pointer to a buffer for the data to send or receive to/from the device, or NULL if no data
64 *
65 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum
66 */
67 static uint8_t MassStore_SendCommand(CommandBlockWrapper_t* SCSICommandBlock, void* BufferPtr)
68 {
69 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
70
71 /* Each transmission should have a unique tag value, increment before use */
72 SCSICommandBlock->Tag = ++MassStore_Tag;
73
74 /* Wrap Tag value when invalid - MS class defines tag values of 0 and 0xFFFFFFFF to be invalid */
75 if (MassStore_Tag == 0xFFFFFFFF)
76 MassStore_Tag = 1;
77
78 /* Select the OUT data pipe for CBW transmission */
79 Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
80 Pipe_Unfreeze();
81
82 /* Write the CBW command to the OUT pipe */
83 if ((ErrorCode = Pipe_Write_Stream_LE(SCSICommandBlock, sizeof(CommandBlockWrapper_t))) != PIPE_RWSTREAM_NoError)
84 return ErrorCode;
85
86 /* Send the data in the OUT pipe to the attached device */
87 Pipe_ClearOUT();
88
89 /* Wait until command has been sent */
90 Pipe_WaitUntilReady();
91
92 /* Freeze pipe after use */
93 Pipe_Freeze();
94
95 /* Send data if any */
96 if ((BufferPtr != NULL) &&
97 ((ErrorCode = MassStore_SendReceiveData(SCSICommandBlock, BufferPtr)) != PIPE_READYWAIT_NoError))
98 {
99 Pipe_Freeze();
100 return ErrorCode;
101 }
102
103 return ErrorCode;
104 }
105
106 /** Waits until the attached device is ready to accept data following a CBW, checking
107 * to ensure that the device has not stalled the transaction.
108 *
109 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum
110 */
111 static uint8_t MassStore_WaitForDataReceived(void)
112 {
113 uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS;
114
115 /* Select the IN data pipe for data reception */
116 Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
117 Pipe_Unfreeze();
118
119 /* Wait until data received in the IN pipe */
120 while (!(Pipe_IsINReceived()))
121 {
122 /* Check to see if a new frame has been issued (1ms elapsed) */
123 if (USB_INT_HasOccurred(USB_INT_HSOFI))
124 {
125 /* Clear the flag and decrement the timeout period counter */
126 USB_INT_Clear(USB_INT_HSOFI);
127 TimeoutMSRem--;
128
129 /* Check to see if the timeout period for the command has elapsed */
130 if (!(TimeoutMSRem))
131 return PIPE_RWSTREAM_Timeout;
132 }
133
134 Pipe_Freeze();
135 Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
136 Pipe_Unfreeze();
137
138 /* Check if pipe stalled (command failed by device) */
139 if (Pipe_IsStalled())
140 {
141 /* Clear the stall condition on the OUT pipe */
142 USB_Host_ClearPipeStall(MASS_STORE_DATA_OUT_PIPE);
143
144 return PIPE_RWSTREAM_PipeStalled;
145 }
146
147 Pipe_Freeze();
148 Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
149 Pipe_Unfreeze();
150
151 /* Check if pipe stalled (command failed by device) */
152 if (Pipe_IsStalled())
153 {
154 /* Clear the stall condition on the IN pipe */
155 USB_Host_ClearPipeStall(MASS_STORE_DATA_IN_PIPE);
156
157 return PIPE_RWSTREAM_PipeStalled;
158 }
159
160 /* Check to see if the device was disconnected, if so exit function */
161 if (USB_HostState == HOST_STATE_Unattached)
162 return PIPE_RWSTREAM_DeviceDisconnected;
163 };
164
165 Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
166 Pipe_Freeze();
167
168 Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
169 Pipe_Freeze();
170
171 return PIPE_RWSTREAM_NoError;
172 }
173
174 /** Sends or receives the transaction's data stage to or from the attached device, reading or
175 * writing to the nominated buffer.
176 *
177 * \param[in] SCSICommandBlock Pointer to a SCSI command block structure being sent to the attached device
178 * \param[in,out] BufferPtr Pointer to the data buffer to read from or write to
179 *
180 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum
181 */
182 static uint8_t MassStore_SendReceiveData(CommandBlockWrapper_t* SCSICommandBlock, void* BufferPtr)
183 {
184 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
185 uint16_t BytesRem = SCSICommandBlock->DataTransferLength;
186
187 /* Check the direction of the SCSI command data stage */
188 if (SCSICommandBlock->Flags & COMMAND_DIRECTION_DATA_IN)
189 {
190 /* Wait until the device has replied with some data */
191 if ((ErrorCode = MassStore_WaitForDataReceived()) != PIPE_RWSTREAM_NoError)
192 return ErrorCode;
193
194 /* Select the IN data pipe for data reception */
195 Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
196 Pipe_Unfreeze();
197
198 /* Read in the block data from the pipe */
199 if ((ErrorCode = Pipe_Read_Stream_LE(BufferPtr, BytesRem)) != PIPE_RWSTREAM_NoError)
200 return ErrorCode;
201
202 /* Acknowledge the packet */
203 Pipe_ClearIN();
204 }
205 else
206 {
207 /* Select the OUT data pipe for data transmission */
208 Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
209 Pipe_Unfreeze();
210
211 /* Write the block data to the pipe */
212 if ((ErrorCode = Pipe_Write_Stream_LE(BufferPtr, BytesRem)) != PIPE_RWSTREAM_NoError)
213 return ErrorCode;
214
215 /* Acknowledge the packet */
216 Pipe_ClearOUT();
217
218 while (!(Pipe_IsOUTReady()))
219 {
220 if (USB_HostState == HOST_STATE_Unattached)
221 return PIPE_RWSTREAM_DeviceDisconnected;
222 }
223 }
224
225 /* Freeze used pipe after use */
226 Pipe_Freeze();
227
228 return PIPE_RWSTREAM_NoError;
229 }
230
231 /** Routine to receive the current CSW from the device.
232 *
233 * \param[out] SCSICommandStatus Pointer to a destination where the returned status data should be stored
234 *
235 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
236 */
237 static uint8_t MassStore_GetReturnedStatus(CommandStatusWrapper_t* SCSICommandStatus)
238 {
239 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
240
241 /* If an error in the command ocurred, abort */
242 if ((ErrorCode = MassStore_WaitForDataReceived()) != PIPE_RWSTREAM_NoError)
243 return ErrorCode;
244
245 /* Select the IN data pipe for data reception */
246 Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
247 Pipe_Unfreeze();
248
249 /* Load in the CSW from the attached device */
250 if ((ErrorCode = Pipe_Read_Stream_LE(SCSICommandStatus, sizeof(CommandStatusWrapper_t))) != PIPE_RWSTREAM_NoError)
251 return ErrorCode;
252
253 /* Clear the data ready for next reception */
254 Pipe_ClearIN();
255
256 /* Freeze the IN pipe after use */
257 Pipe_Freeze();
258
259 /* Check to see if command failed */
260 if (SCSICommandStatus->Status != Command_Pass)
261 ErrorCode = MASS_STORE_SCSI_COMMAND_FAILED;
262
263 return ErrorCode;
264 }
265
266 /** Issues a Mass Storage class specific request to reset the attached device's Mass Storage interface,
267 * readying the device for the next CBW.
268 *
269 * \return A value from the USB_Host_SendControlErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
270 */
271 uint8_t MassStore_MassStorageReset(void)
272 {
273 USB_ControlRequest = (USB_Request_Header_t)
274 {
275 .bmRequestType = (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE),
276 .bRequest = REQ_MassStorageReset,
277 .wValue = 0,
278 .wIndex = 0,
279 .wLength = 0,
280 };
281
282 /* Select the control pipe for the request transfer */
283 Pipe_SelectPipe(PIPE_CONTROLPIPE);
284
285 return USB_Host_SendControlRequest(NULL);
286 }
287
288 /** Issues a Mass Storage class specific request to determine the index of the highest numbered Logical
289 * Unit in the attached device.
290 *
291 * \param[out] MaxLUNIndex Pointer to the location that the maximum LUN index value should be stored
292 *
293 * \return A value from the USB_Host_SendControlErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
294 */
295 uint8_t MassStore_GetMaxLUN(uint8_t* const MaxLUNIndex)
296 {
297 uint8_t ErrorCode;
298
299 USB_ControlRequest = (USB_Request_Header_t)
300 {
301 .bmRequestType = (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE),
302 .bRequest = REQ_GetMaxLUN,
303 .wValue = 0,
304 .wIndex = 0,
305 .wLength = 1,
306 };
307
308 /* Select the control pipe for the request transfer */
309 Pipe_SelectPipe(PIPE_CONTROLPIPE);
310
311 if ((ErrorCode = USB_Host_SendControlRequest(MaxLUNIndex)) == HOST_SENDCONTROL_SetupStalled)
312 {
313 /* Clear the pipe stall */
314 Pipe_ClearStall();
315
316 /* Some faulty Mass Storage devices don't implement the GET_MAX_LUN request, so assume a single LUN */
317 *MaxLUNIndex = 0;
318 }
319
320 return ErrorCode;
321 }
322
323 /** Issues a SCSI Inquiry command to the attached device, to determine the device's information. This
324 * gives information on the device's capabilities.
325 *
326 * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
327 * \param[out] InquiryPtr Pointer to the inquiry data structure where the inquiry data from the device is to be stored
328 *
329 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
330 */
331 uint8_t MassStore_Inquiry(const uint8_t LUNIndex, SCSI_Inquiry_Response_t* const InquiryPtr)
332 {
333 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
334
335 /* Create a CBW with a SCSI command to issue INQUIRY command */
336 CommandBlockWrapper_t SCSICommandBlock = (CommandBlockWrapper_t)
337 {
338 .Signature = CBW_SIGNATURE,
339 .DataTransferLength = sizeof(SCSI_Inquiry_Response_t),
340 .Flags = COMMAND_DIRECTION_DATA_IN,
341 .LUN = LUNIndex,
342 .SCSICommandLength = 6,
343 .SCSICommandData =
344 {
345 SCSI_CMD_INQUIRY,
346 0x00, // Reserved
347 0x00, // Reserved
348 0x00, // Reserved
349 sizeof(SCSI_Inquiry_Response_t), // Allocation Length
350 0x00 // Unused (control)
351 }
352 };
353
354 CommandStatusWrapper_t SCSICommandStatus;
355
356 /* Send the command and any data to the attached device */
357 if ((ErrorCode = MassStore_SendCommand(&SCSICommandBlock, InquiryPtr)) != PIPE_RWSTREAM_NoError)
358 {
359 Pipe_Freeze();
360 return ErrorCode;
361 }
362
363 /* Retrieve status information from the attached device */
364 if ((ErrorCode = MassStore_GetReturnedStatus(&SCSICommandStatus)) != PIPE_RWSTREAM_NoError)
365 {
366 Pipe_Freeze();
367 return ErrorCode;
368 }
369
370 return ErrorCode;
371 }
372
373 /** Issues a SCSI Request Sense command to the attached device, to determine the current SCSI sense information. This
374 * gives error codes for the last issued SCSI command to the device.
375 *
376 * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
377 * \param[out] SensePtr Pointer to the sense data structure where the sense data from the device is to be stored
378 *
379 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
380 */
381 uint8_t MassStore_RequestSense(const uint8_t LUNIndex, SCSI_Request_Sense_Response_t* const SensePtr)
382 {
383 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
384
385 /* Create a CBW with a SCSI command to issue REQUEST SENSE command */
386 CommandBlockWrapper_t SCSICommandBlock = (CommandBlockWrapper_t)
387 {
388 .Signature = CBW_SIGNATURE,
389 .DataTransferLength = sizeof(SCSI_Request_Sense_Response_t),
390 .Flags = COMMAND_DIRECTION_DATA_IN,
391 .LUN = LUNIndex,
392 .SCSICommandLength = 6,
393 .SCSICommandData =
394 {
395 SCSI_CMD_REQUEST_SENSE,
396 0x00, // Reserved
397 0x00, // Reserved
398 0x00, // Reserved
399 sizeof(SCSI_Request_Sense_Response_t), // Allocation Length
400 0x00 // Unused (control)
401 }
402 };
403
404 CommandStatusWrapper_t SCSICommandStatus;
405
406 /* Send the command and any data to the attached device */
407 if ((ErrorCode = MassStore_SendCommand(&SCSICommandBlock, SensePtr)) != PIPE_RWSTREAM_NoError)
408 {
409 Pipe_Freeze();
410 return ErrorCode;
411 }
412
413 /* Retrieve status information from the attached device */
414 if ((ErrorCode = MassStore_GetReturnedStatus(&SCSICommandStatus)) != PIPE_RWSTREAM_NoError)
415 {
416 Pipe_Freeze();
417 return ErrorCode;
418 }
419
420 return ErrorCode;
421 }
422
423 /** Issues a SCSI Device Block Read command to the attached device, to read in one or more data blocks from the
424 * storage medium into a buffer.
425 *
426 * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
427 * \param[in] BlockAddress Start block address to read from
428 * \param[in] Blocks Number of blocks to read from the device
429 * \param[in] BlockSize Size in bytes of each block to read
430 * \param[out] BufferPtr Pointer to the buffer where the read data is to be written to
431 *
432 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
433 */
434 uint8_t MassStore_ReadDeviceBlock(const uint8_t LUNIndex, const uint32_t BlockAddress,
435 const uint8_t Blocks, const uint16_t BlockSize, void* BufferPtr)
436 {
437 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
438
439 /* Create a CBW with a SCSI command to read in the given blocks from the device */
440 CommandBlockWrapper_t SCSICommandBlock = (CommandBlockWrapper_t)
441 {
442 .Signature = CBW_SIGNATURE,
443 .DataTransferLength = ((uint32_t)Blocks * BlockSize),
444 .Flags = COMMAND_DIRECTION_DATA_IN,
445 .LUN = LUNIndex,
446 .SCSICommandLength = 10,
447 .SCSICommandData =
448 {
449 SCSI_CMD_READ_10,
450 0x00, // Unused (control bits, all off)
451 (BlockAddress >> 24), // MSB of Block Address
452 (BlockAddress >> 16),
453 (BlockAddress >> 8),
454 (BlockAddress & 0xFF), // LSB of Block Address
455 0x00, // Unused (reserved)
456 0x00, // MSB of Total Blocks to Read
457 Blocks, // LSB of Total Blocks to Read
458 0x00 // Unused (control)
459 }
460 };
461
462 CommandStatusWrapper_t SCSICommandStatus;
463
464 /* Send the command and any data to the attached device */
465 if ((ErrorCode = MassStore_SendCommand(&SCSICommandBlock, BufferPtr)) != PIPE_RWSTREAM_NoError)
466 {
467 Pipe_Freeze();
468 return ErrorCode;
469 }
470
471 /* Retrieve status information from the attached device */
472 if ((ErrorCode = MassStore_GetReturnedStatus(&SCSICommandStatus)) != PIPE_RWSTREAM_NoError)
473 {
474 Pipe_Freeze();
475 return ErrorCode;
476 }
477
478 return ErrorCode;
479 }
480
481 /** Issues a SCSI Device Block Write command to the attached device, to write one or more data blocks to the
482 * storage medium from a buffer.
483 *
484 * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
485 * \param[in] BlockAddress Start block address to write to
486 * \param[in] Blocks Number of blocks to write to in the device
487 * \param[in] BlockSize Size in bytes of each block to write
488 * \param[in] BufferPtr Pointer to the buffer where the write data is to be sourced from
489 *
490 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
491 */
492 uint8_t MassStore_WriteDeviceBlock(const uint8_t LUNIndex, const uint32_t BlockAddress,
493 const uint8_t Blocks, const uint16_t BlockSize, void* BufferPtr)
494 {
495 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
496
497 /* Create a CBW with a SCSI command to write the given blocks to the device */
498 CommandBlockWrapper_t SCSICommandBlock = (CommandBlockWrapper_t)
499 {
500 .Signature = CBW_SIGNATURE,
501 .DataTransferLength = ((uint32_t)Blocks * BlockSize),
502 .Flags = COMMAND_DIRECTION_DATA_OUT,
503 .LUN = LUNIndex,
504 .SCSICommandLength = 10,
505 .SCSICommandData =
506 {
507 SCSI_CMD_WRITE_10,
508 0x00, // Unused (control bits, all off)
509 (BlockAddress >> 24), // MSB of Block Address
510 (BlockAddress >> 16),
511 (BlockAddress >> 8),
512 (BlockAddress & 0xFF), // LSB of Block Address
513 0x00, // Unused (reserved)
514 0x00, // MSB of Total Blocks to Write
515 Blocks, // LSB of Total Blocks to Write
516 0x00 // Unused (control)
517 }
518 };
519
520 CommandStatusWrapper_t SCSICommandStatus;
521
522 /* Send the command and any data to the attached device */
523 if ((ErrorCode = MassStore_SendCommand(&SCSICommandBlock, BufferPtr)) != PIPE_RWSTREAM_NoError)
524 {
525 Pipe_Freeze();
526 return ErrorCode;
527 }
528
529 /* Retrieve status information from the attached device */
530 if ((ErrorCode = MassStore_GetReturnedStatus(&SCSICommandStatus)) != PIPE_RWSTREAM_NoError)
531 {
532 Pipe_Freeze();
533 return ErrorCode;
534 }
535
536 return ErrorCode;
537 }
538
539 /** Issues a SCSI Device Test Unit Ready command to the attached device, to determine if the device is ready to accept
540 * other commands.
541 *
542 * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
543 *
544 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
545 */
546 uint8_t MassStore_TestUnitReady(const uint8_t LUNIndex)
547 {
548 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
549
550 /* Create a CBW with a SCSI command to issue TEST UNIT READY command */
551 CommandBlockWrapper_t SCSICommandBlock = (CommandBlockWrapper_t)
552 {
553 .Signature = CBW_SIGNATURE,
554 .DataTransferLength = 0,
555 .Flags = COMMAND_DIRECTION_DATA_IN,
556 .LUN = LUNIndex,
557 .SCSICommandLength = 6,
558 .SCSICommandData =
559 {
560 SCSI_CMD_TEST_UNIT_READY,
561 0x00, // Reserved
562 0x00, // Reserved
563 0x00, // Reserved
564 0x00, // Reserved
565 0x00 // Unused (control)
566 }
567 };
568
569 CommandStatusWrapper_t SCSICommandStatus;
570
571 /* Send the command and any data to the attached device */
572 if ((ErrorCode = MassStore_SendCommand(&SCSICommandBlock, NULL)) != PIPE_RWSTREAM_NoError)
573 {
574 Pipe_Freeze();
575 return ErrorCode;
576 }
577
578 /* Retrieve status information from the attached device */
579 if ((ErrorCode = MassStore_GetReturnedStatus(&SCSICommandStatus)) != PIPE_RWSTREAM_NoError)
580 {
581 Pipe_Freeze();
582 return ErrorCode;
583 }
584
585 return ErrorCode;
586 }
587
588 /** Issues a SCSI Device Read Capacity command to the attached device, to determine the capacity of the
589 * given Logical Unit within the device.
590 *
591 * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
592 * \param[out] CapacityPtr Device capacity structure where the capacity data is to be stored
593 *
594 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
595 */
596 uint8_t MassStore_ReadCapacity(const uint8_t LUNIndex, SCSI_Capacity_t* const CapacityPtr)
597 {
598 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
599
600 /* Create a CBW with a SCSI command to issue READ CAPACITY command */
601 CommandBlockWrapper_t SCSICommandBlock = (CommandBlockWrapper_t)
602 {
603 .Signature = CBW_SIGNATURE,
604 .DataTransferLength = sizeof(SCSI_Capacity_t),
605 .Flags = COMMAND_DIRECTION_DATA_IN,
606 .LUN = LUNIndex,
607 .SCSICommandLength = 10,
608 .SCSICommandData =
609 {
610 SCSI_CMD_READ_CAPACITY_10,
611 0x00, // Reserved
612 0x00, // MSB of Logical block address
613 0x00,
614 0x00,
615 0x00, // LSB of Logical block address
616 0x00, // Reserved
617 0x00, // Reserved
618 0x00, // Partial Medium Indicator
619 0x00 // Unused (control)
620 }
621 };
622
623 CommandStatusWrapper_t SCSICommandStatus;
624
625 /* Send the command and any data to the attached device */
626 if ((ErrorCode = MassStore_SendCommand(&SCSICommandBlock, CapacityPtr)) != PIPE_RWSTREAM_NoError)
627 {
628 Pipe_Freeze();
629 return ErrorCode;
630 }
631
632 /* Endian-correct the read data */
633 CapacityPtr->Blocks = SwapEndian_32(CapacityPtr->Blocks);
634 CapacityPtr->BlockSize = SwapEndian_32(CapacityPtr->BlockSize);
635
636 /* Retrieve status information from the attached device */
637 if ((ErrorCode = MassStore_GetReturnedStatus(&SCSICommandStatus)) != PIPE_RWSTREAM_NoError)
638 {
639 Pipe_Freeze();
640 return ErrorCode;
641 }
642
643 return ErrorCode;
644 }
645
646 /** Issues a SCSI Device Prevent/Allow Medium Removal command to the attached device, to lock the physical media from
647 * being removed. This is a legacy command for SCSI disks with removable storage (such as ZIP disks), but should still
648 * be issued before the first read or write command is sent.
649 *
650 * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
651 * \param[in] PreventRemoval Whether or not the LUN media should be locked to prevent removal or not
652 *
653 * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
654 */
655 uint8_t MassStore_PreventAllowMediumRemoval(const uint8_t LUNIndex, const bool PreventRemoval)
656 {
657 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
658
659 /* Create a CBW with a SCSI command to issue PREVENT ALLOW MEDIUM REMOVAL command */
660 CommandBlockWrapper_t SCSICommandBlock = (CommandBlockWrapper_t)
661 {
662 .Signature = CBW_SIGNATURE,
663 .DataTransferLength = 0,
664 .Flags = COMMAND_DIRECTION_DATA_OUT,
665 .LUN = LUNIndex,
666 .SCSICommandLength = 6,
667 .SCSICommandData =
668 {
669 SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL,
670 0x00, // Reserved
671 0x00, // Reserved
672 PreventRemoval, // Prevent flag
673 0x00, // Reserved
674 0x00 // Unused (control)
675 }
676 };
677
678 CommandStatusWrapper_t SCSICommandStatus;
679
680 /* Send the command and any data to the attached device */
681 if ((ErrorCode = MassStore_SendCommand(&SCSICommandBlock, NULL)) != PIPE_RWSTREAM_NoError)
682 {
683 Pipe_Freeze();
684 return ErrorCode;
685 }
686
687 /* Retrieve status information from the attached device */
688 if ((ErrorCode = MassStore_GetReturnedStatus(&SCSICommandStatus)) != PIPE_RWSTREAM_NoError)
689 {
690 Pipe_Freeze();
691 return ErrorCode;
692 }
693
694 return ErrorCode;
695 }