SAMV71 Xplained Ultra Software Package 1.3

uhi_msc.c

Go to the documentation of this file.
00001 /**
00002  * \file
00003  *
00004  * \brief USB host Mass Storage Class interface.
00005  *
00006  * Copyright (C) 2011-2015 Atmel Corporation. All rights reserved.
00007  *
00008  * \asf_license_start
00009  *
00010  * \page License
00011  *
00012  * Redistribution and use in source and binary forms, with or without
00013  * modification, are permitted provided that the following conditions are met:
00014  *
00015  * 1. Redistributions of source code must retain the above copyright notice,
00016  *    this list of conditions and the following disclaimer.
00017  *
00018  * 2. Redistributions in binary form must reproduce the above copyright notice,
00019  *    this list of conditions and the following disclaimer in the documentation
00020  *    and/or other materials provided with the distribution.
00021  *
00022  * 3. The name of Atmel may not be used to endorse or promote products derived
00023  *    from this software without specific prior written permission.
00024  *
00025  * 4. This software may only be redistributed and used in connection with an
00026  *    Atmel microcontroller product.
00027  *
00028  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
00029  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00030  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
00031  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
00032  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00033  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00034  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00035  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
00036  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00037  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00038  * POSSIBILITY OF SUCH DAMAGE.
00039  *
00040  * \asf_license_stop
00041  *
00042  */
00043 /*
00044  * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
00045  */
00046 
00047 #include "conf_usb_host.h"
00048 #include "USBHDriver.h"
00049 #include "USBH.h"
00050 #include "uhi_msc.h"
00051 #include <string.h>
00052 
00053 #ifdef USB_HOST_HUB_SUPPORT
00054 // TODO
00055 #  error USB HUB support is not implemented on UHI MSC
00056 #endif
00057 
00058 #ifndef UHI_MSC_NOTIFY_NEW_LUN_EXT
00059 #  define UHI_MSC_NOTIFY_NEW_LUN_EXT
00060 #endif
00061 
00062 /**
00063  * \ingroup uhi_msc_group
00064  * \defgroup uhi_msc_group_internal Implementation of UHI Mass Storage Class
00065  *
00066  * Class internal implementation 
00067  * @{
00068  */
00069 
00070 //! Timeout on SCSI commands
00071 #define  UHI_MSC_SCSI_TIMEOUT 20000
00072 
00073 /**
00074  * \name Internal defines and variables to manage MSC unit
00075  */
00076 //@{
00077 
00078 //! Flag to secure the UHI MSC resources
00079 static volatile bool b_uhi_msc_free = true;
00080 
00081 //! USB MSC device information
00082 typedef struct {
00083     USBH_device_t *dev;
00084     USBHS_Ep_t ep_in;
00085     USBHS_Ep_t ep_out;
00086     uint8_t iface_num;
00087     uint8_t nb_lun;
00088     uhi_msc_lun_t *lun;
00089 } uhi_msc_dev_t;
00090 
00091 //! Information about the enumerated USB MSC device
00092 static uhi_msc_dev_t uhi_msc_dev = {
00093     .dev = NULL,
00094     .lun = NULL,
00095     .nb_lun = 0,
00096 };
00097 
00098 //! Current USB MSC device selected by the UHI MSC
00099 //@{
00100 #ifdef USB_HOST_HUB_SUPPORT
00101     static uhi_msc_dev_t *uhi_msc_dev_sel;
00102 #else
00103 # define uhi_msc_dev_sel (&uhi_msc_dev)
00104 #endif
00105 //@}
00106 
00107 //! Current LUN selected in USB MSC device by the UHI MSC
00108 //@{
00109 static uhi_msc_lun_t *uhi_msc_lun_sel;
00110 #define uhi_msc_lun_num_sel uhi_msc_cbw.bCBWLUN
00111 //@}
00112 
00113 //! Temporary structures used to read LUN information via a SCSI command
00114 //@{
00115 static SBCReadCapacity10Data uhi_msc_capacity;
00116 static SBCInquiryData uhi_msc_inquiry;
00117 static SBCRequestSenseData uhi_msc_sense;
00118 static struct {
00119     SBCModeParameterHeader6 header;
00120     SBCInformationalExceptionsControl sense_data;
00121 } uhi_msc_sense6;
00122 //@}
00123 
00124 //! Callback type used by scsi sense command
00125 typedef void (*uhi_msc_scsi_sense_callback_t) (void);
00126 
00127 //! Internal callbacks
00128 //@{
00129 static uhi_msc_scsi_callback_t uhi_msc_scsi_sub_callback;
00130 static uhi_msc_scsi_callback_t uhi_msc_scsi_callback;
00131 static uhi_msc_scsi_sense_callback_t uhi_msc_scsi_sense_callback;
00132 //@}
00133 //@}
00134 
00135 /**
00136  * \name Variables to manage SCSI requests
00137  */
00138 //@{
00139 //! Structure to send a CBW packet
00140 static MSCbw uhi_msc_cbw =
00141         {.dCBWSignature = MSD_CBW_SIGNATURE };
00142 
00143 //! Structure to receive a CSW packet
00144 static MSCsw uhi_msc_csw;
00145 
00146 //! Structure to sent or received DATA packet
00147 static uint8_t *uhi_msc_data;
00148 //@}
00149 
00150 /**
00151  * \name Internal routines
00152  */
00153 //@{
00154 
00155 /**
00156  * \name Routines to initialize the MSC devices
00157  */
00158 //@{
00159 static void uhi_msc_enable_step1(void);
00160 static void uhi_msc_enable_step2(
00161         USBHS_Add_t add,
00162         USBH_XfrStatus_t status,
00163         uint16_t payload_trans);
00164 static void uhi_msc_enable_step3(void);
00165 static void uhi_msc_enable_step4(bool b_success);
00166 static void uhi_msc_enable_step5(bool b_success);
00167 //@}
00168 
00169 /**
00170  * \name SCSI commands
00171  */
00172 //@{
00173 static bool uhi_msc_select_lun(uint8_t lun);
00174 static void uhi_msc_scsi_inquiry(uhi_msc_scsi_callback_t callback);
00175 static void uhi_msc_scsi_inquiry_done(bool b_cbw_succes);
00176 static void uhi_msc_scsi_test_unit_ready_done(bool b_cbw_succes);
00177 static void uhi_msc_scsi_test_unit_ready_sense(void);
00178 static void uhi_msc_scsi_read_capacity(uhi_msc_scsi_callback_t callback);
00179 static void uhi_msc_scsi_read_capacity_done(bool b_cbw_succes);
00180 static void uhi_msc_scsi_read_capacity_sense(void);
00181 static void uhi_msc_scsi_mode_sense6(uhi_msc_scsi_callback_t callback);
00182 static void uhi_msc_scsi_mode_sense6_done(bool b_cbw_succes);
00183 static void uhi_msc_scsi_read_10_done(bool b_cbw_succes);
00184 static void uhi_msc_scsi_write_10_done(bool b_cbw_succes);
00185 static void uhi_msc_scsi_request_sense(uhi_msc_scsi_sense_callback_t callback);
00186 static void uhi_msc_scsi_request_sense_done(bool b_cbw_succes);
00187 //@}
00188 
00189 /**
00190  * \name SCSI protocol sub routines
00191  */
00192 //@{
00193 static void uhi_msc_scsi(uhi_msc_scsi_callback_t callback, uint8_t *payload);
00194 static void uhi_msc_cbw_sent(
00195         USBHS_Add_t add,
00196         USBHS_Ep_t ep,
00197         USBH_XfrStatus_t status,
00198         uint32_t nb_transfered);
00199 static void uhi_msc_data_transfered(
00200         USBHS_Add_t add,
00201         USBHS_Ep_t ep,
00202         USBH_XfrStatus_t status,
00203         uint32_t nb_transfered);
00204 static void uhi_msc_csw_wait(void);
00205 static void uhi_msc_csw_received(
00206         USBHS_Add_t add,
00207         USBHS_Ep_t ep,
00208         USBH_XfrStatus_t status,
00209         uint32_t nb_transfered);
00210 static void uhi_msc_cbw_rst_stall(
00211         USBHS_Add_t add,
00212         USBH_XfrStatus_t status,
00213         uint16_t payload_trans);
00214 static void uhi_msc_data_csw_rst_stall(
00215         USBHS_Add_t add,
00216         USBH_XfrStatus_t status,
00217         uint16_t payload_trans);
00218 static void uhi_msc_transfer(USBHS_Ep_t endp,
00219         uint8_t* payload,
00220         uint16_t payload_size,
00221         uhd_callback_trans_t callback_end);
00222 static void uhi_msc_reset_endpoint(
00223         USBHS_Ep_t endp, uhd_callback_setup_end_t callback );
00224 //@}
00225 
00226 //@}
00227 
00228 
00229 /**
00230  * \name Interface used by UHC module
00231  */
00232 //@{
00233 
00234 USBH_enum_status_t uhi_msc_install(USBH_device_t * dev)
00235 {
00236     bool b_iface_supported;
00237     uint16_t conf_desc_lgt;
00238     USBInterfaceDescriptor *ptr_iface;
00239 
00240     if (uhi_msc_dev.dev != NULL) {
00241         return UHC_ENUM_SOFTWARE_LIMIT; // Device already allocated
00242     }
00243     conf_desc_lgt = dev->conf_desc->wTotalLength;
00244     ptr_iface = (USBInterfaceDescriptor *) dev->conf_desc;
00245     b_iface_supported = false;
00246     while (conf_desc_lgt) {
00247         switch (ptr_iface->bDescriptorType) {
00248 
00249         case USBGenericDescriptor_INTERFACE:
00250             if ((ptr_iface->bInterfaceClass   == MSInterfaceDescriptor_CLASS)
00251             && (ptr_iface->bInterfaceSubClass == MSD_SUBCLASS_SCSI)
00252             && (ptr_iface->bInterfaceProtocol == MSD_PROTOCOL_BULK_ONLY) ) {
00253                 // USB HID Mouse interface found
00254                 b_iface_supported = true;
00255                 uhi_msc_dev_sel->iface_num = ptr_iface->bInterfaceNumber;
00256                 uhi_msc_dev.ep_in = 0;
00257                 uhi_msc_dev.ep_out = 0;
00258             } else {
00259                 // Stop allocation endpoint(s)
00260                 b_iface_supported = false;
00261             }
00262             break;
00263 
00264         case USBGenericDescriptor_ENDPOINT:
00265             //  Allocate the endpoint
00266             if (!b_iface_supported) {
00267                 break;
00268             }
00269             USBEndpointDescriptor *ptr_ep = (USBEndpointDescriptor *) ptr_iface;
00270             if (ptr_ep->bmAttributes != USBEndpointDescriptor_BULK) {
00271                 // A bad endpoint interrupt may be present on no compliance U-disk
00272                 break;
00273             }
00274             if (!USBH_HAL_ConfigurePipe(dev->address, ptr_ep)) {
00275                 return UHC_ENUM_HARDWARE_LIMIT; // Endpoint allocation fail
00276             }
00277             if (ptr_ep->bEndpointAddress & USB_EP_DIR_IN) {
00278                 uhi_msc_dev.ep_in = ptr_ep->bEndpointAddress;
00279             } else {
00280                 uhi_msc_dev.ep_out = ptr_ep->bEndpointAddress;
00281             }
00282             if (uhi_msc_dev.ep_out && uhi_msc_dev.ep_in) {
00283                 // All endpoints allocated
00284                 uhi_msc_dev.dev = dev;
00285                 uhi_msc_dev.nb_lun = 0;
00286                 return UHC_ENUM_SUCCESS;
00287             }
00288             break;
00289 
00290         }
00291         assert(conf_desc_lgt >= ptr_iface->bLength);
00292         conf_desc_lgt -= ptr_iface->bLength;
00293         ptr_iface = (USBInterfaceDescriptor*)((uint8_t*)ptr_iface + ptr_iface->bLength);
00294     }
00295 
00296     return UHC_ENUM_UNSUPPORTED; // No interface supported
00297 }
00298 
00299 void uhi_msc_enable(USBH_device_t * dev)
00300 {
00301     if (uhi_msc_dev.dev != dev) {
00302         return; // No interface to enable
00303     }
00304     // ** Process to enable device **
00305     // Setup request - Get Number of LUN
00306     // For each LUN {
00307     //   MSC request - Inquiry
00308     //   MSC request - Read capacity*
00309     // }
00310     // *It is required by specific U-disk which does not respect USB MSC norm.
00311 
00312 #ifdef USB_HOST_HUB_SUPPORT
00313     uhi_msc_dev_sel = &uhi_msc_dev[];
00314     if (!b_uhi_msc_free) {
00315         // Install must be postponed
00316     }
00317 #else
00318     assert(uhi_msc_dev.dev != NULL);
00319 #endif
00320     b_uhi_msc_free = false;
00321     uhi_msc_enable_step1();
00322 }
00323 
00324 void uhi_msc_uninstall(USBH_device_t * dev)
00325 {
00326     if (uhi_msc_dev.dev != dev) {
00327         return; // Device not enabled in this interface
00328     }
00329     uhi_msc_dev.dev = NULL;
00330     if (uhi_msc_dev_sel->lun != NULL) {
00331         free(uhi_msc_dev_sel->lun);
00332     }
00333     UHI_MSC_CHANGE(dev, false);
00334 }
00335 
00336 //@}
00337 
00338 /**
00339  * \name Routines to initialize the MSC devices
00340  */
00341 //@{
00342 
00343 /**
00344  * \brief Step 1 of the initialization of a USB MSC device
00345  * Sends the setup request GET_MAX_LUN.
00346  */
00347 static void uhi_msc_enable_step1(void)
00348 {
00349     // Default value is 0, because the Get LUN number request
00350     // can be stalled if the device have only 1 LUN
00351     uhi_msc_dev_sel->nb_lun = 0;
00352 
00353     // Request Get LUN number
00354     USBGenericRequest req;
00355     req.bmRequestType = USB_REQ_RECIP_INTERFACE|USB_REQ_TYPE_CLASS|USB_REQ_DIR_IN;
00356     req.bRequest = MSD_GET_MAX_LUN;
00357     req.wValue = 0;
00358     req.wIndex = uhi_msc_dev_sel->iface_num;
00359     req.wLength = 1;
00360     USBH_HAL_SetupReq(uhi_msc_dev_sel->dev->address,
00361             &req,
00362             &(uhi_msc_dev_sel->nb_lun),
00363             1,
00364             NULL,
00365             uhi_msc_enable_step2);
00366 }
00367 
00368 /**
00369  * \brief Step 2 of the initialization of a USB MSC device
00370  * Decode the setup request GET_MAX_LUN and allocs LUN structures.
00371  *
00372  * \param add           USB address of the setup request
00373  * \param status        Transfer status
00374  * \param payload_trans Number of data transfered during DATA phase
00375  */
00376 static void uhi_msc_enable_step2(
00377         USBHS_Add_t add,
00378         USBH_XfrStatus_t status,
00379         uint16_t payload_trans)
00380 {
00381     UNUSED(add);
00382     // Compute number of LUN
00383     if (status == UHD_TRANS_NOERROR) {
00384         if (payload_trans) {
00385             // The received value is the maximum index
00386             uhi_msc_dev_sel->nb_lun++;
00387         } else {
00388             uhi_msc_dev_sel->nb_lun = 1;
00389         }
00390     } else if (status == UHD_TRANS_STALL) {
00391         uhi_msc_dev_sel->nb_lun = 1;
00392     } else {
00393         // Error set no LUN
00394         b_uhi_msc_free = true;
00395         return;
00396     }
00397 
00398     // Alloc LUN structures
00399     uhi_msc_dev_sel->lun = malloc(uhi_msc_dev_sel->nb_lun*sizeof(uhi_msc_lun_t));
00400     if (uhi_msc_dev_sel->lun == NULL) {
00401         assert(false);
00402         b_uhi_msc_free = true;
00403         return;
00404     }
00405     // Initialize each LUN
00406     uhi_msc_lun_num_sel = (uint8_t) - 1;
00407     uhi_msc_enable_step3();
00408 }
00409 
00410 /**
00411  * \brief Step 3 of the initialization of a USB MSC device
00412  * Selects the next LUN.
00413  * Sends the SCSI INQUIRY request on the LUN.
00414  */
00415 static void uhi_msc_enable_step3(void)
00416 {
00417     if (!uhi_msc_select_lun(++uhi_msc_lun_num_sel)) {
00418         // End of enable MSC interface
00419         b_uhi_msc_free = true;
00420         UHI_MSC_CHANGE(uhi_msc_dev.dev, true);
00421         return;
00422     }
00423     uhi_msc_lun_sel->status = LUN_NOT_PRESENT;
00424     uhi_msc_scsi_inquiry(uhi_msc_enable_step4);
00425 }
00426 
00427 /**
00428  * \brief Step 4 of the initialization of a USB MSC device
00429  * Sends the SCSI READ CAPACITY request on the current LUN.
00430  *
00431  * \param b_success true, if the scsi command is successful
00432  */
00433 static void uhi_msc_enable_step4(bool b_success)
00434 {
00435     if (!b_success) {
00436         uhi_msc_enable_step3();
00437         return;
00438     }
00439     uhi_msc_scsi_read_capacity(uhi_msc_enable_step5);
00440 }
00441 
00442 /**
00443  * \brief Step 5 of the initialization of a USB MSC device
00444  * Go to the initialization of the next LUN.
00445  *
00446  * \param b_success true, if the scsi command is successful
00447  */
00448 static void uhi_msc_enable_step5(bool b_success)
00449 {
00450     UNUSED(b_success);
00451     // Initialize next LUN
00452     uhi_msc_enable_step3();
00453 }
00454 
00455 //@}
00456 
00457 /**
00458  * \name External SCSI commands
00459  */
00460 //@{
00461 bool uhi_msc_is_available(void)
00462 {
00463     return b_uhi_msc_free;
00464 }
00465 
00466 uint8_t uhi_msc_get_lun(void)
00467 {
00468     if (uhi_msc_dev.dev == NULL) {
00469         return 0;
00470     }
00471     return uhi_msc_dev.nb_lun;
00472 }
00473 
00474 uhi_msc_lun_t *uhi_msc_get_lun_desc(uint8_t lun)
00475 {
00476     if (lun >= uhi_msc_dev.nb_lun) {
00477         return NULL;
00478     }
00479     return &uhi_msc_dev.lun[lun];
00480 }
00481 
00482 bool uhi_msc_scsi_test_unit_ready(uint8_t lun, uhi_msc_scsi_callback_t callback)
00483 {
00484     if (!uhi_msc_select_lun(lun)) {
00485         return false;
00486     }
00487     uhi_msc_scsi_callback = callback;
00488 
00489     // Prepare specific value of CBW packet
00490     uhi_msc_cbw.dCBWDataTransferLength = 0;
00491     uhi_msc_cbw.bmCBWFlags = MSD_CBW_DEVICE_TO_DEVICE;
00492     uhi_msc_cbw.bCBWCBLength = 6;
00493     memset(uhi_msc_cbw.pCommand, 0, sizeof(uhi_msc_cbw.pCommand));
00494     uhi_msc_cbw.pCommand[0] = SBC_TEST_UNIT_READY;
00495 
00496     uhi_msc_scsi(uhi_msc_scsi_test_unit_ready_done, NULL);
00497     return true;
00498 }
00499 
00500 bool uhi_msc_scsi_read_10(uint8_t lun, uint32_t addr, uint8_t *ram,
00501         uint8_t nb_sector, uhi_msc_scsi_callback_t callback )
00502 {
00503     uint32_t addrTmp = addr;
00504     uint32_t *pBlockLen = (uint32_t*)&uhi_msc_lun_sel->capacity.pLogicalBlockLength[0];
00505     if (!uhi_msc_select_lun(lun)) {
00506         return false;
00507     }
00508     uhi_msc_scsi_callback = callback;
00509 
00510     // Prepare specific value of CBW packet
00511     uhi_msc_cbw.dCBWDataTransferLength =
00512             (*pBlockLen) * nb_sector;
00513     uhi_msc_cbw.bmCBWFlags = MSD_CBW_DEVICE_TO_HOST;
00514     uhi_msc_cbw.bCBWCBLength = 10;
00515     memset(uhi_msc_cbw.pCommand, 0, sizeof(uhi_msc_cbw.pCommand));
00516 
00517     /**pCommand++ = SBC_WRITE10;
00518     memcpy(pCommand, &addr, sizeof(addr));*/
00519     // CBWCB0 - Operation Code
00520     uhi_msc_cbw.pCommand[0] = SBC_READ_10;
00521 
00522     // CBWCB1 - RDPROTECT, DPO, FUA, Obsolete (0x00) (done by previous memset())
00523 
00524     addrTmp = CPU_TO_BE32(addr);
00525     // CBWCB2 to 5 - Logical Block Address (BE16)
00526     memcpy(&uhi_msc_cbw.pCommand[2], &addrTmp, sizeof(addr));
00527 
00528     // CBWCW6 - Reserved (0x00) (done by previous memset())
00529     // CBWCW7 to 8 - Transfer Length
00530     // uhi_msc_cbw.pCommand[7] = 0x00; // MSB (done by previous memset())
00531     uhi_msc_cbw.pCommand[8] = nb_sector; // LSB
00532 
00533     // CBWCW9 - Control (0x00) (done by previous memset())
00534     uhi_msc_scsi(uhi_msc_scsi_read_10_done, ram);
00535     return true;
00536 }
00537 
00538 bool uhi_msc_scsi_write_10(uint8_t lun, uint32_t addr, const uint8_t *ram,
00539         uint8_t nb_sector, uhi_msc_scsi_callback_t callback )
00540 {
00541     uint32_t addrTmp = addr;
00542     uint32_t *pBlockLen = (uint32_t*)&uhi_msc_lun_sel->capacity.pLogicalBlockLength[0];
00543     
00544     if (!uhi_msc_select_lun(lun)) {
00545         return false;
00546     }
00547     uhi_msc_scsi_callback = callback;
00548 
00549     // Prepare specific value of CBW packet
00550     uhi_msc_cbw.dCBWDataTransferLength =
00551             (*pBlockLen) * nb_sector ;
00552     uhi_msc_cbw.bmCBWFlags = MSD_CBW_DEVICE_TO_DEVICE;
00553     uhi_msc_cbw.bCBWCBLength = 10;
00554     memset(uhi_msc_cbw.pCommand, 0, sizeof(uhi_msc_cbw.pCommand));
00555 
00556     // CBWCB0 - Operation Code
00557     uhi_msc_cbw.pCommand[0] = SBC_WRITE_10;
00558     
00559     addrTmp = CPU_TO_BE32(addr);
00560     // CBWCB1 - RDPROTECT, DPO, FUA, Obsolete (0x00) (done by previous memset())
00561 
00562     // CBWCB2 to 5 - Logical Block Address (BE16)
00563     memcpy(&uhi_msc_cbw.pCommand[2], &addrTmp, sizeof(addr));
00564 
00565     // CBWCW6 - Reserved (0x00) (done by previous memset())
00566 
00567     // CBWCW7 to 8 - Transfer Length
00568     // uhi_msc_cbw.pCommand[7] = 0x00; // MSB (done by previous memset())
00569     uhi_msc_cbw.pCommand[8] = nb_sector; // LSB
00570 
00571     // CBWCW9 - Control (0x00) (done by previous memset())
00572     uhi_msc_scsi(uhi_msc_scsi_write_10_done, (uint8_t *) ram);
00573     return true;
00574 }
00575 
00576 //@}
00577 
00578 
00579 /**
00580  * \name Internal SCSI commands and sub routines
00581  */
00582 //@{
00583 
00584 /**
00585  * \brief Selects a LUN
00586  *
00587  * \param lun   LUN number to select
00588  *
00589  * \return  true, if the LUN number is correct
00590  */
00591 static bool uhi_msc_select_lun(uint8_t lun)
00592 {
00593     if (lun >= uhi_msc_dev_sel->nb_lun) {
00594         return false;
00595     }
00596     uhi_msc_lun_num_sel = lun;
00597     uhi_msc_lun_sel = &uhi_msc_dev_sel->lun[lun];
00598     return true;
00599 }
00600 
00601 /**
00602  * \brief Sends the CBW packet of the scsi INQUIRY command
00603  *
00604  * \param callback  Callback to call at the end of scsi command
00605  */
00606 static void uhi_msc_scsi_inquiry(uhi_msc_scsi_callback_t callback)
00607 {
00608     uhi_msc_scsi_callback = callback;
00609 
00610     // Prepare specific value of CBW packet
00611     uhi_msc_cbw.dCBWDataTransferLength = sizeof( SBCInquiryData);
00612     uhi_msc_cbw.bmCBWFlags = MSD_CBW_DEVICE_TO_HOST;
00613     uhi_msc_cbw.bCBWCBLength = 6;
00614     memset(uhi_msc_cbw.pCommand, 0, sizeof(uhi_msc_cbw.pCommand));
00615     uhi_msc_cbw.pCommand[0] = SBC_INQUIRY;
00616     uhi_msc_cbw.pCommand[4] = sizeof(SBCInquiryData);
00617     uhi_msc_scsi(uhi_msc_scsi_inquiry_done, (uint8_t *) & uhi_msc_inquiry);
00618 }
00619 
00620 /**
00621  * \brief Call the callback at the end of scsi INQUIRY command
00622  *
00623  * \param b_cbw_succes true, if the scsi command is successful
00624  */
00625 static void uhi_msc_scsi_inquiry_done(bool b_cbw_succes)
00626 {
00627     if ((!b_cbw_succes) || uhi_msc_csw.dCSWDataResidue) {
00628         uhi_msc_scsi_callback(false);
00629         return;
00630     }
00631     // Inquiry successful
00632     uhi_msc_scsi_callback(true);
00633 }
00634 
00635 /**
00636  * \brief Decodes the result of scsi TEST UNIT READY command
00637  * Launches READ CAPACITY command, if a new LUN has been detected.
00638  *
00639  * \param b_cbw_succes true, if the scsi command is successful
00640  */
00641 static void uhi_msc_scsi_test_unit_ready_done(bool b_cbw_succes)
00642 {
00643     if (!b_cbw_succes) {
00644         uhi_msc_lun_sel->status = LUN_FAIL;
00645         uhi_msc_scsi_callback(false);
00646         return;
00647     }
00648     // Test unit ready successful
00649     if (uhi_msc_csw.bCSWStatus != MSD_CSW_COMMAND_PASSED) {
00650         uhi_msc_lun_sel->status = LUN_FAIL; // By default
00651         // LUN is not ready
00652         // Read a sense code
00653         uhi_msc_scsi_request_sense(uhi_msc_scsi_test_unit_ready_sense);
00654         return;
00655     }
00656     if (uhi_msc_lun_sel->status != LUN_GOOD) {
00657         // It is a new LUN
00658         if (uhi_msc_lun_sel->status == LUN_BUSY) {
00659             // To initialize the new LUN read capacity and write protection flag
00660             uhi_msc_lun_sel->status = LUN_GOOD;
00661             uhi_msc_scsi_read_capacity(uhi_msc_scsi_callback);
00662             return;
00663         }
00664         // The USB device has not returned a BUSY
00665         // to notify the LUN change to high level,
00666         // then add the notify
00667         uhi_msc_lun_sel->status = LUN_BUSY;
00668     }
00669     uhi_msc_scsi_callback(true);
00670 }
00671 
00672 /**
00673  * \brief Decodes the scsi sense code after an error on TEST UNIT READY command
00674  */
00675 static void uhi_msc_scsi_test_unit_ready_sense(void)
00676 {
00677     uint16_t sense_key = uhi_msc_sense.bSenseKey;
00678     uint16_t add_sense = (uint16_t)uhi_msc_sense.bAdditionalSenseCode<<8
00679         | uhi_msc_sense.bAdditionalSenseCodeQualifier;
00680 
00681     // Decode data sense
00682     if ((uhi_msc_sense.bResponseCode
00683             & SBC_SENSE_RESPONSE_CODE_MASK) == SBC_SENSE_DATA_FIXED_CURRENT) {
00684         // Valid data
00685         if ((sense_key == SBC_SENSE_KEY_NOT_READY)
00686                 && (add_sense==(SBC_ASC_MEDIUM_NOT_PRESENT <<8))) {
00687             uhi_msc_lun_sel->status = LUN_NOT_PRESENT;
00688         }
00689         if ((sense_key == SBC_SENSE_KEY_UNIT_ATTENTION)
00690                 && (add_sense == (SBC_ASC_NOT_READY_TO_READY_CHANGE << 8))) {
00691             uhi_msc_lun_sel->status = LUN_BUSY;
00692         }
00693     }
00694     uhi_msc_scsi_callback(true);
00695 }
00696 
00697 /**
00698  * \brief Sends the CBW packet of the scsi READ CAPACITY command
00699  *
00700  * \param callback  Callback to call at the end of scsi command
00701  */
00702 static void uhi_msc_scsi_read_capacity(uhi_msc_scsi_callback_t callback)
00703 {
00704     uhi_msc_scsi_callback = callback;
00705 
00706     // Prepare specific value of CBW packet
00707     uhi_msc_cbw.dCBWDataTransferLength = sizeof(SBCReadCapacity10Data);
00708     uhi_msc_cbw.bmCBWFlags = MSD_CBW_DEVICE_TO_HOST;
00709     uhi_msc_cbw.bCBWCBLength = 10;
00710     memset(uhi_msc_cbw.pCommand, 0, sizeof(uhi_msc_cbw.pCommand));
00711     uhi_msc_cbw.pCommand[0] = SBC_READ_CAPACITY_10;
00712     uhi_msc_scsi(uhi_msc_scsi_read_capacity_done, (uint8_t*)&uhi_msc_capacity);
00713 }
00714 
00715 /**
00716  * \brief Decodes the result of scsi READ CAPACITY command
00717  * Launches MODE SENSE 6 command.
00718  *
00719  * \param b_cbw_succes true, if the scsi command is successful
00720  */
00721 static void uhi_msc_scsi_read_capacity_done(bool b_cbw_succes)
00722 {
00723     uint32_t *pBlockLen, *pBlockAddr;
00724     pBlockLen = (uint32_t *)&uhi_msc_lun_sel->capacity.pLogicalBlockLength[0];
00725     pBlockAddr = (uint32_t *)&uhi_msc_lun_sel->capacity.pLogicalBlockAddress[0];
00726     if ((!b_cbw_succes) || (uhi_msc_csw.bCSWStatus != MSD_CSW_COMMAND_PASSED)
00727             || uhi_msc_csw.dCSWDataResidue) {
00728         // Read capacity has failed
00729         uhi_msc_lun_sel->status = LUN_FAIL;
00730 
00731         // (WA-Udisk) Read request sense
00732         uhi_msc_scsi_request_sense(uhi_msc_scsi_read_capacity_sense);
00733         return;
00734     }
00735     // Format capacity data
00736     *pBlockLen = be32_to_cpu(*( (uint32_t *)&uhi_msc_capacity.pLogicalBlockLength[0]));
00737     *pBlockAddr = be32_to_cpu(*( (uint32_t *)&uhi_msc_capacity.pLogicalBlockAddress[0]));
00738     
00739     // Now, read flag write protection
00740     uhi_msc_scsi_mode_sense6(uhi_msc_scsi_callback);
00741 }
00742 
00743 /**
00744  * \brief Decodes the scsi sense code after a scsi READ CAPACITY command failed
00745  */
00746 static void uhi_msc_scsi_read_capacity_sense(void)
00747 {
00748     uhi_msc_scsi_callback(false);
00749 }
00750 
00751 /**
00752  * \brief Sends the CBW packet of the scsi MODE SENSE 6 command
00753  * This function returns the write-protected mode.
00754  * Field optional provided only by the LUN with a write protection feature.
00755  *
00756  * \param callback  Callback to call at the end of scsi command
00757  */
00758 static void uhi_msc_scsi_mode_sense6(uhi_msc_scsi_callback_t callback)
00759 {
00760     uhi_msc_scsi_callback = callback;
00761 
00762     // Prepare specific value of CBW packet
00763     uhi_msc_cbw.dCBWDataTransferLength = sizeof(uhi_msc_sense6);
00764     uhi_msc_cbw.bmCBWFlags = MSD_CBW_DEVICE_TO_HOST;
00765     uhi_msc_cbw.bCBWCBLength = 6;
00766     memset(uhi_msc_cbw.pCommand, 0, sizeof(uhi_msc_cbw.pCommand));
00767     uhi_msc_cbw.pCommand[0] = SBC_MODE_SENSE_6;
00768     uhi_msc_cbw.pCommand[2] = SBC_PAGE_INFORMATIONAL_EXCEPTIONS_CONTROL;
00769     uhi_msc_cbw.pCommand[4] = sizeof(uhi_msc_sense6);
00770     uhi_msc_scsi(uhi_msc_scsi_mode_sense6_done,
00771             (uint8_t *) & uhi_msc_sense6);
00772 }
00773 
00774 /**
00775  * \brief Decodes the result of scsi MODE SENSE 6 command
00776  *
00777  * \param b_cbw_succes true, if the scsi command is successful
00778  */
00779 static void uhi_msc_scsi_mode_sense6_done(bool b_cbw_succes)
00780 {
00781     if ((!b_cbw_succes) || (uhi_msc_csw.bCSWStatus != MSD_CSW_COMMAND_PASSED)
00782             || (uhi_msc_csw.dCSWDataResidue < 4)) {
00783         // Sense6 command is not supported,
00784         // The device must be not write protected.
00785         uhi_msc_lun_sel->b_write_protected = false;
00786         uhi_msc_scsi_callback(true);
00787         return;
00788     }
00789     // Decode field
00790     uhi_msc_lun_sel->b_write_protected =
00791             (uhi_msc_sense6.header.isWP);
00792     uhi_msc_scsi_callback(true);
00793 }
00794 
00795 /**
00796  * \brief Decodes the result of scsi READ 10 command
00797  *
00798  * \param b_cbw_succes true, if the scsi command is successful
00799  */
00800 static void uhi_msc_scsi_read_10_done(bool b_cbw_succes)
00801 {
00802     if ((!b_cbw_succes) || (uhi_msc_csw.bCSWStatus != MSD_CSW_COMMAND_PASSED)
00803             || uhi_msc_csw.dCSWDataResidue) {
00804         // Read10 has failed
00805         uhi_msc_lun_sel->status = LUN_FAIL;
00806         uhi_msc_scsi_callback(false);
00807         return;
00808     }
00809     uhi_msc_scsi_callback(true);
00810 }
00811 
00812 /**
00813  * \brief Decodes the result of scsi WRITE 10 command
00814  *
00815  * \param b_cbw_succes true, if the scsi command is successful
00816  */
00817 static void uhi_msc_scsi_write_10_done(bool b_cbw_succes)
00818 {
00819     if ((!b_cbw_succes) || (uhi_msc_csw.bCSWStatus != MSD_CSW_COMMAND_PASSED)
00820             || uhi_msc_csw.dCSWDataResidue) {
00821         // Write10 has failed
00822         uhi_msc_lun_sel->status = LUN_FAIL;
00823         uhi_msc_scsi_callback(false);
00824         return;
00825     }
00826     uhi_msc_scsi_callback(true);
00827 }
00828 
00829 /**
00830  * \brief Sends the CBW packet of the scsi REQUESRT SENSE command
00831  * Called by TEST UNIT READY and READ CAPACITY command in case of error.
00832  *
00833  * \param callback  Callback to call at the end of scsi command
00834  */
00835 static void uhi_msc_scsi_request_sense(uhi_msc_scsi_sense_callback_t callback)
00836 {
00837     uhi_msc_scsi_sense_callback = callback;
00838 
00839     // Prepare specific value of CBW packet
00840     uhi_msc_cbw.dCBWDataTransferLength = sizeof(SBCRequestSenseData);
00841     uhi_msc_cbw.bmCBWFlags = MSD_CBW_DEVICE_TO_HOST;
00842     uhi_msc_cbw.bCBWCBLength = 6;
00843     memset(uhi_msc_cbw.pCommand, 0, sizeof(uhi_msc_cbw.pCommand));
00844     uhi_msc_cbw.pCommand[0] = SBC_REQUEST_SENSE;
00845     uhi_msc_cbw.pCommand[4] = sizeof(SBCRequestSenseData);
00846 
00847     uhi_msc_scsi(uhi_msc_scsi_request_sense_done, (uint8_t*)&uhi_msc_sense);
00848 }
00849 
00850 /**
00851  * \brief Decodes the result of scsi REQUEST SENSE command
00852  *
00853  * \param b_cbw_succes true, if the scsi command is successful
00854  */
00855 static void uhi_msc_scsi_request_sense_done(bool b_cbw_succes)
00856 {
00857     if ((!b_cbw_succes) || uhi_msc_csw.dCSWDataResidue) {
00858         uhi_msc_scsi_callback(false);
00859         return;
00860     }
00861     // Request sense successful
00862     uhi_msc_scsi_sense_callback();
00863 }
00864 
00865 //@}
00866 
00867 /**
00868  * \name Internal SCSI protocol routines
00869  */
00870 //@{
00871 
00872 /**
00873  * \brief Sends the CBW packet
00874  *
00875  * \param callback  Callback to call at the end of scsi protocol
00876  * \param payload   Pointer on the data to transfer (Optional)
00877  */
00878 static void uhi_msc_scsi(uhi_msc_scsi_callback_t callback, uint8_t *payload)
00879 {
00880     // Save context
00881     uhi_msc_scsi_sub_callback = callback;
00882     uhi_msc_data = payload;
00883 
00884     // Prepare CBW
00885     uhi_msc_cbw.dCBWTag++;
00886     uhi_msc_cbw.dCBWDataTransferLength =
00887             (uhi_msc_cbw.dCBWDataTransferLength);
00888     // CBWCB0 - Operation Code
00889     // CBWCB1 - Obsolete, EVPD = 0
00890     // CBWCB2 - Page Code = 0
00891     // CBWCB3 - MSB(Allocation Length)
00892     // CBWCB4 - LSB(Allocation Length)
00893     // CBWCW5 - Control = 0
00894 
00895     // Start transfer of CBW packet on bulk endpoint OUT
00896     uhi_msc_transfer(uhi_msc_dev_sel->ep_out, (uint8_t *) &uhi_msc_cbw,
00897             sizeof(uhi_msc_cbw), uhi_msc_cbw_sent);
00898 }
00899 
00900 /**
00901  * \brief Checks the CBW packet transfer and launch the next step
00902  * The next step can be a DATA phase or a CSW packet.
00903  *
00904  * \param add           USB address used by the transfer
00905  * \param status        Transfer status
00906  * \param nb_transfered Number of data transfered
00907  */
00908 static void uhi_msc_cbw_sent(
00909         USBHS_Add_t add,
00910         USBHS_Ep_t ep,
00911         USBH_XfrStatus_t status,
00912         uint32_t nb_transfered)
00913 {
00914     USBHS_Ep_t endp;
00915     UNUSED(add);
00916     UNUSED(ep);
00917     UNUSED(nb_transfered);
00918 
00919     // Checks the result of CBW transfer
00920     if (status != UHD_TRANS_NOERROR) {
00921         if (status == UHD_TRANS_STALL) {
00922             uhi_msc_reset_endpoint(uhi_msc_dev_sel->ep_out,
00923                     uhi_msc_cbw_rst_stall);
00924             return;
00925         }
00926         uhi_msc_scsi_sub_callback(false);
00927         return;
00928     }
00929     assert(nb_transfered == sizeof(uhi_msc_cbw));
00930     uhi_msc_cbw.dCBWDataTransferLength =
00931             (uhi_msc_cbw.dCBWDataTransferLength);
00932     // Here CBW is success
00933 
00934     if (!uhi_msc_cbw.dCBWDataTransferLength) {
00935         // Start CSW phase
00936         uhi_msc_csw_wait();
00937         return;
00938     }
00939     // Start DATA phase
00940     if (uhi_msc_cbw.bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) {
00941         endp = uhi_msc_dev_sel->ep_in;
00942     } else {
00943         endp = uhi_msc_dev_sel->ep_out;
00944     }
00945     uhi_msc_transfer(endp, uhi_msc_data, uhi_msc_cbw.dCBWDataTransferLength,
00946             uhi_msc_data_transfered);
00947 }
00948 
00949 /**
00950  * \brief Checks the DATA phase transfer and launch the next step
00951  * The next step can be a CSW packet or a endpoint reset in case of STALL.
00952  *
00953  * \param add           USB address used by the transfer
00954  * \param status        Transfer status
00955  * \param nb_transfered Number of data transfered
00956  */
00957 static void uhi_msc_data_transfered(
00958         USBHS_Add_t add,
00959         USBHS_Ep_t ep,
00960         USBH_XfrStatus_t status,
00961         uint32_t nb_transfered)
00962 {
00963     USBHS_Ep_t endp;
00964     UNUSED(add);
00965     UNUSED(ep);
00966     UNUSED(nb_transfered);
00967 
00968     if (status != UHD_TRANS_NOERROR) {
00969         if (status == UHD_TRANS_STALL) {
00970             if (uhi_msc_cbw.bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) {
00971                 endp = uhi_msc_dev_sel->ep_in;
00972             } else {
00973                 endp = uhi_msc_dev_sel->ep_out;
00974             }
00975             uhi_msc_reset_endpoint(endp, uhi_msc_data_csw_rst_stall);
00976             return;
00977         }
00978         uhi_msc_scsi_sub_callback(false);
00979         return;
00980     }
00981     // DATA phase complete
00982 
00983     // Start CSW phase
00984     uhi_msc_csw_wait();
00985     return;
00986 }
00987 
00988 /**
00989  * \brief Start the transfer of the CSW packet
00990  */
00991 static void uhi_msc_csw_wait(void)
00992 {
00993     // Start transfer of CSW packet on bulk endpoint IN
00994     uhi_msc_transfer(uhi_msc_dev_sel->ep_in, (uint8_t *) & uhi_msc_csw,
00995             sizeof(uhi_msc_csw), uhi_msc_csw_received);
00996 }
00997 
00998 /**
00999  * \brief Checks the CSW packet transfer
01000  *
01001  * \param add           USB address used by the transfer
01002  * \param status        Transfer status
01003  * \param nb_transfered Number of data transfered
01004  */
01005 static void uhi_msc_csw_received(
01006         USBHS_Add_t add,
01007         USBHS_Ep_t ep,
01008         USBH_XfrStatus_t status,
01009         uint32_t nb_transfered)
01010 {
01011     UNUSED(add);
01012     UNUSED(ep);
01013     if (status != UHD_TRANS_NOERROR) {
01014         if (status == UHD_TRANS_STALL) {
01015             uhi_msc_reset_endpoint(uhi_msc_dev_sel->ep_in,
01016                     uhi_msc_data_csw_rst_stall);
01017             return;
01018         }
01019         uhi_msc_scsi_sub_callback(false);
01020         return;
01021     }
01022     if ((nb_transfered != sizeof(uhi_msc_csw))
01023             || (uhi_msc_csw.dCSWTag != uhi_msc_cbw.dCBWTag)
01024             || (uhi_msc_csw.dCSWSignature != MSD_CSW_SIGNATURE)) {
01025         // Error in CSW fields
01026         uhi_msc_scsi_sub_callback(false);
01027         return;
01028     }
01029 
01030     // CSW is success
01031     uhi_msc_csw.dCSWDataResidue = uhi_msc_csw.dCSWDataResidue;
01032     uhi_msc_scsi_sub_callback(true);
01033 }
01034 
01035 /**
01036  * \brief Manages the end of setup request RESET ENDPOINT after a CBW packet
01037  *
01038  * \param add           USB address of the setup request
01039  * \param status        Transfer status
01040  * \param payload_trans Number of data transfered during DATA phase
01041  */
01042 static void uhi_msc_cbw_rst_stall(
01043         USBHS_Add_t add,
01044         USBH_XfrStatus_t status,
01045         uint16_t payload_trans)
01046 {
01047     UNUSED(add);
01048     UNUSED(status);
01049     UNUSED(payload_trans);
01050     uhi_msc_scsi_sub_callback(false);
01051 }
01052 
01053 /**
01054  * \brief Manages the end of RESET ENDPOINT request after a DATA or CSW packet
01055  *
01056  * \param add           USB address of the setup request
01057  * \param status        Transfer status
01058  * \param payload_trans Number of data transfered during DATA phase
01059  */
01060 static void uhi_msc_data_csw_rst_stall(
01061         USBHS_Add_t add,
01062         USBH_XfrStatus_t status,
01063         uint16_t payload_trans)
01064 {
01065     UNUSED(add);
01066     UNUSED(payload_trans);
01067     if (status != UHD_TRANS_NOERROR) {
01068         uhi_msc_scsi_sub_callback(false);
01069         return;
01070     }
01071     // DATA stalled but CSW must be received after clear of STALL
01072     uhi_msc_csw_wait();
01073 }
01074 
01075 /**
01076  * \brief Start a transfer on an endpoint of current USB MSC device
01077  * Used to send a CBW packet, DATA packet, or a CSW packet.
01078  *
01079  * \param endp          Endpoint to use for this transfer
01080  * \param payload       Pointer on the data to transfer
01081  * \param payload_size  Size of the data to transfer
01082  * \param callback_end  Callback to call at the end of transfer
01083  */
01084 static void uhi_msc_transfer(USBHS_Ep_t endp,
01085         uint8_t* payload,
01086         uint16_t payload_size,
01087         uhd_callback_trans_t callback_end)
01088 {
01089     if (!USBH_HAL_RunEndpoint(uhi_msc_dev_sel->dev->address,
01090             endp, false, payload, payload_size,
01091             UHI_MSC_SCSI_TIMEOUT, callback_end)) {
01092         uhi_msc_scsi_sub_callback(false);
01093     }
01094 }
01095 
01096 /**
01097  * \brief Sends a setup request RESET ENDPOINT
01098  * Used after a STALL received during a scsi command.
01099  *
01100  * \param endp          Endpoint to reset
01101  * \param callback_end  Callback to call at the end of request
01102  */
01103 static void uhi_msc_reset_endpoint(
01104         USBHS_Ep_t endp, uhd_callback_setup_end_t callback )
01105 {
01106     // Setup Request to reset endpoint
01107     USBGenericRequest req;
01108     req.bmRequestType = USB_REQ_RECIP_ENDPOINT
01109             | USB_REQ_TYPE_STANDARD | USB_REQ_DIR_OUT;
01110     req.bRequest = USB_REQ_CLEAR_FEATURE;
01111     req.wValue = USB_EP_FEATURE_HALT;
01112     req.wIndex = endp;
01113     req.wLength = 0;
01114     if (!USBH_HAL_SetupReq(uhi_msc_dev_sel->dev->address,
01115             &req, NULL, 0, NULL, callback)) {
01116         callback(uhi_msc_dev_sel->dev->address,UHD_TRANS_DISCONNECT,0);
01117     }
01118 }
01119 
01120 //@}
01121 
01122 //@}
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines