//***************************************************************************** // // usbhmsc.c - USB MSC host driver. // // Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. // Software License Agreement // // Texas Instruments (TI) is supplying this software for use solely and // exclusively on TI's microcontroller products. The software is owned by // TI and/or its suppliers, and is protected under applicable copyright // laws. You may not combine this software with "viral" open-source // software in order to form a larger program. // // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL // DAMAGES, FOR ANY REASON WHATSOEVER. // // This is part of revision 2.1.0.12573 of the Tiva USB Library. // //***************************************************************************** #include #include #include "inc/hw_types.h" #include "driverlib/usb.h" #include "usblib/usblib.h" #include "usblib/usblibpriv.h" #include "usblib/usbmsc.h" #include "usblib/host/usbhost.h" #include "usblib/host/usbhostpriv.h" #include "usblib/host/usbhmsc.h" #include "usblib/host/usbhscsi.h" //***************************************************************************** // //! \addtogroup usblib_host_class //! @{ // //***************************************************************************** //***************************************************************************** // // Forward declarations for the driver open and close calls. // //***************************************************************************** static void *USBHMSCOpen(tUSBHostDevice *psDevice); static void USBHMSCClose(void *pvInstance); //***************************************************************************** // // This is the structure for an instance of a USB MSC host driver. // //***************************************************************************** struct tUSBHMSCInstance { // // Save the device instance. // tUSBHostDevice *psDevice; // // Used to save the callback. // tUSBHMSCCallback pfnCallback; // // The Maximum LUNs // uint32_t ui32MaxLUN; // // The total number of blocks associated with this device. // uint32_t ui32NumBlocks; // // The size of the blocks associated with this device. // uint32_t ui32BlockSize; // // Bulk IN pipe. // uint32_t ui32BulkInPipe; // // Bulk OUT pipe. // uint32_t ui32BulkOutPipe; }; //***************************************************************************** // // The array of USB MSC host drivers. // //***************************************************************************** static tUSBHMSCInstance g_sUSBHMSCDevice = { 0 }; //***************************************************************************** // //! This constant global structure defines the Mass Storage Class Driver that //! is provided with the USB library. // //***************************************************************************** const tUSBHostClassDriver g_sUSBHostMSCClassDriver = { USB_CLASS_MASS_STORAGE, USBHMSCOpen, USBHMSCClose, 0 }; //***************************************************************************** // //! This function is used to open an instance of the MSC driver. //! //! \param psDevice is a pointer to the device information structure. //! //! This function will attempt to open an instance of the MSC driver based on //! the information contained in the \e psDevice structure. This call can fail //! if there are not sufficient resources to open the device. The function //! returns a value that should be passed back into USBMSCClose() when the //! driver is no longer needed. //! //! \return The function will return a pointer to a MSC driver instance. // //***************************************************************************** static void * USBHMSCOpen(tUSBHostDevice *psDevice) { int32_t i32Idx; tEndpointDescriptor *psEndpointDescriptor; tInterfaceDescriptor *psInterface; // // Don't allow the device to be opened without closing first. // if(g_sUSBHMSCDevice.psDevice) { return(0); } // // Save the device pointer. // g_sUSBHMSCDevice.psDevice = psDevice; // // Get the interface descriptor. // psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); // // Loop through the endpoints of the device. // for(i32Idx = 0; i32Idx < 3; i32Idx++) { // // Get the first endpoint descriptor. // psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, i32Idx, psDevice->ui32ConfigDescriptorSize); // // If no more endpoints then break out. // if(psEndpointDescriptor == 0) { break; } // // See if this is a bulk endpoint. // if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == USB_EP_ATTR_BULK) { // // See if this is bulk IN or bulk OUT. // if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) { // // Allocate the USB Pipe for this Bulk IN endpoint. // g_sUSBHMSCDevice.ui32BulkInPipe = USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_IN_DMA, psDevice, psEndpointDescriptor->wMaxPacketSize, 0); // // Configure the USB pipe as a Bulk IN endpoint. // USBHCDPipeConfig(g_sUSBHMSCDevice.ui32BulkInPipe, psEndpointDescriptor->wMaxPacketSize, 0, (psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_NUM_M)); } else { // // Allocate the USB Pipe for this Bulk OUT endpoint. // g_sUSBHMSCDevice.ui32BulkOutPipe = USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_OUT_DMA, psDevice, psEndpointDescriptor->wMaxPacketSize, 0); // // Configure the USB pipe as a Bulk OUT endpoint. // USBHCDPipeConfig(g_sUSBHMSCDevice.ui32BulkOutPipe, psEndpointDescriptor->wMaxPacketSize, 0, (psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_NUM_M)); } } } // // If the callback exists, call it with an Open event. // if(g_sUSBHMSCDevice.pfnCallback != 0) { g_sUSBHMSCDevice.pfnCallback(&g_sUSBHMSCDevice, MSC_EVENT_OPEN, 0); } g_sUSBHMSCDevice.ui32MaxLUN = 0xffffffff; // // Return the only instance of this device. // return(&g_sUSBHMSCDevice); } //***************************************************************************** // //! This function is used to release an instance of the MSC driver. //! //! \param pvInstance is an instance pointer that needs to be released. //! //! This function will free up any resources in use by the MSC driver instance //! that is passed in. The \e pvInstance pointer should be a valid value that //! was returned from a call to USBMSCOpen(). //! //! \return None. // //***************************************************************************** static void USBHMSCClose(void *pvInstance) { // // Do nothing if there is not a driver open. // if(g_sUSBHMSCDevice.psDevice == 0) { return; } // // Reset the device pointer. // g_sUSBHMSCDevice.psDevice = 0; // // Free the Bulk IN pipe. // if(g_sUSBHMSCDevice.ui32BulkInPipe != 0) { USBHCDPipeFree(g_sUSBHMSCDevice.ui32BulkInPipe); } // // Free the Bulk OUT pipe. // if(g_sUSBHMSCDevice.ui32BulkOutPipe != 0) { USBHCDPipeFree(g_sUSBHMSCDevice.ui32BulkOutPipe); } // // If the callback exists then call it. // if(g_sUSBHMSCDevice.pfnCallback != 0) { g_sUSBHMSCDevice.pfnCallback(&g_sUSBHMSCDevice, MSC_EVENT_CLOSE, 0); } } //***************************************************************************** // //! This function retrieves the maximum number of the logical units on a //! mass storage device. //! //! \param psDevice is the device instance pointer for this request. //! \param ui32Interface is the interface number on the device specified by the //! \e ui32Address parameter. //! \param pui8MaxLUN is the byte value returned from the device for the //! device's maximum logical unit. //! //! The device will return one byte of data that contains the maximum LUN //! supported by the device. For example, if the device supports four LUNs //! then the LUNs would be numbered from 0 to 3 and the return value would be //! 3. If no LUN is associated with the device, the value returned shall be 0. //! //! \return None. // //***************************************************************************** static void USBHMSCGetMaxLUN(tUSBHostDevice *psDevice, uint32_t ui32Interface, uint8_t *pui8MaxLUN) { tUSBRequest sSetupPacket; // // This is a Class specific interface IN request. // sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; // // Request a the Max LUN for this interface. // sSetupPacket.bRequest = USBREQ_GET_MAX_LUN; sSetupPacket.wValue = 0; // // Indicate the interface to use. // sSetupPacket.wIndex = (uint16_t)ui32Interface; // // Only request a single byte of data. // sSetupPacket.wLength = 1; // // Put the setup packet in the buffer and send the command. // if(USBHCDControlTransfer(0, &sSetupPacket, psDevice, pui8MaxLUN, 1, MAX_PACKET_SIZE_EP0) != 1) { *pui8MaxLUN = 0; } } //***************************************************************************** // //! This function checks if a drive is ready to be accessed. //! //! \param psMSCInstance is the device instance to use for this read. //! //! This function checks if the current device is ready to be accessed. //! It uses the \e psMSCInstance parameter to determine which device to check //! and returns zero when the device is ready. Any non-zero return code //! indicates that the device was not ready. //! //! \return This function returns zero if the device is ready and it //! returns a other value if the device is not ready or if an error occurred. // //***************************************************************************** int32_t USBHMSCDriveReady(tUSBHMSCInstance *psMSCInstance) { uint8_t ui8MaxLUN, pui8Buffer[SCSI_INQUIRY_DATA_SZ]; uint32_t ui32Size; // // If there is no device present then return an error. // if(psMSCInstance->psDevice == 0) { return(-1); } // // Only request the maximum number of LUNs once. // if(g_sUSBHMSCDevice.ui32MaxLUN == 0xffffffff) { // // Get the Maximum LUNs on this device. // USBHMSCGetMaxLUN(g_sUSBHMSCDevice.psDevice, g_sUSBHMSCDevice.psDevice->ui32Interface, &ui8MaxLUN); // // Save the Maximum number of LUNs on this device. // g_sUSBHMSCDevice.ui32MaxLUN = ui8MaxLUN; } // // Just return if the device is returning not present. // ui32Size = SCSI_REQUEST_SENSE_SZ; if(USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, pui8Buffer, &ui32Size) != SCSI_CMD_STATUS_PASS) { return(-1); } if((pui8Buffer[SCSI_RS_SKEY] == SCSI_RS_KEY_UNIT_ATTN) && (pui8Buffer[SCSI_RS_SKEY_AD_SKEY] == SCSI_RS_KEY_NOTPRSNT)) { return(-1); } // // Issue a SCSI Inquiry to get basic information on the device // ui32Size = SCSI_INQUIRY_DATA_SZ; if((USBHSCSIInquiry(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, pui8Buffer, &ui32Size) != SCSI_CMD_STATUS_PASS)) { return(-1); } // // Get the size of the drive. // ui32Size = SCSI_INQUIRY_DATA_SZ; if(USBHSCSIReadCapacity(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, pui8Buffer, &ui32Size) != SCSI_CMD_STATUS_PASS) { // // Get the current sense data from the device to see why it failed // the Read Capacity command. // ui32Size = SCSI_REQUEST_SENSE_SZ; USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, pui8Buffer, &ui32Size); // // If the read capacity failed then check if the drive is ready. // if(USBHSCSITestUnitReady(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe) != SCSI_CMD_STATUS_PASS) { // // Get the current sense data from the device to see why it failed // the Test Unit Ready command. // ui32Size = SCSI_REQUEST_SENSE_SZ; USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, pui8Buffer, &ui32Size); } return(-1); } else { // // Read the block size out, value is stored big endian. // psMSCInstance->ui32BlockSize = (pui8Buffer[7] | (pui8Buffer[6] << 8) | pui8Buffer[5] << 16 | (pui8Buffer[4] << 24)); // // Read the block size out. // psMSCInstance->ui32NumBlocks = (pui8Buffer[3] | (pui8Buffer[2] << 8) | pui8Buffer[1] << 16 | (pui8Buffer[0] << 24)); } // // See if the drive is ready to use. // if(USBHSCSITestUnitReady(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe) != SCSI_CMD_STATUS_PASS) { // // Get the current sense data from the device to see why it failed // the Test Unit Ready command. // ui32Size = SCSI_REQUEST_SENSE_SZ; USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, pui8Buffer, &ui32Size); return(-1); } // // Success. // return(0); } //***************************************************************************** // //! This function should be called before any devices are present to enable //! the mass storage device class driver. //! //! \param ui32Drive is the drive number to open. //! \param pfnCallback is the driver callback for any mass storage events. //! //! This function is called to open an instance of a mass storage device. It //! should be called before any devices are connected to allow for proper //! notification of drive connection and disconnection. The \e ui32Drive //! parameter is a zero based index of the drives present in the system. //! There are a constant number of drives, and this number should only //! be greater than 0 if there is a USB hub present in the system. The //! application should also provide the \e pfnCallback to be notified of mass //! storage related events like device enumeration and device removal. //! //! \return This function will return the driver instance to use for the other //! mass storage functions. If there is no driver available at the time of //! this call, this function will return zero. // //***************************************************************************** tUSBHMSCInstance * USBHMSCDriveOpen(uint32_t ui32Drive, tUSBHMSCCallback pfnCallback) { // // Only the first drive is supported and only one callback is supported. // if((ui32Drive != 0) || (g_sUSBHMSCDevice.pfnCallback)) { return(0); } // // Save the callback. // g_sUSBHMSCDevice.pfnCallback = pfnCallback; // // Return the requested device instance. // return(&g_sUSBHMSCDevice); } //***************************************************************************** // //! This function should be called to release a drive instance. //! //! \param psMSCInstance is the device instance that is to be released. //! //! This function is called when an MSC drive is to be released in preparation //! for shutdown or a switch to USB device mode, for example. Following this //! call, the drive is available for other clients who may open it again using //! a call to USBHMSCDriveOpen(). //! //! \return None. // //***************************************************************************** void USBHMSCDriveClose(tUSBHMSCInstance *psMSCInstance) { // // Close the drive (if it is already open) // USBHMSCClose((void *)psMSCInstance); // // Clear the callback indicating that the device is now closed. // psMSCInstance->pfnCallback = 0; } //***************************************************************************** // //! This function performs a block read to an MSC device. //! //! \param psMSCInstance is the device instance to use for this read. //! \param ui32LBA is the logical block address to read on the device. //! \param pui8Data is a pointer to the returned data buffer. //! \param ui32NumBlocks is the number of blocks to read from the device. //! //! This function will perform a block sized read from the device associated //! with the \e psMSCInstance parameter. The \e ui32LBA parameter specifies //! the logical block address to read on the device. This function will only //! perform \e ui32NumBlocks block sized reads. In most cases this is a read //! of 512 bytes of data. The \e *pui8Data buffer should be at least //! \e ui32NumBlocks * 512 bytes in size. //! //! \return The function returns zero for success and any negative value //! indicates a failure. // //***************************************************************************** int32_t USBHMSCBlockRead(tUSBHMSCInstance *psMSCInstance, uint32_t ui32LBA, uint8_t *pui8Data, uint32_t ui32NumBlocks) { uint32_t ui32Size; // // If there is no device present then return an error. // if(psMSCInstance->psDevice == 0) { return(-1); } // // Calculate the actual byte size of the read. // ui32Size = psMSCInstance->ui32BlockSize * ui32NumBlocks; // // Perform the SCSI read command. // if(USBHSCSIRead10(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, ui32LBA, pui8Data, &ui32Size, ui32NumBlocks) != SCSI_CMD_STATUS_PASS) { return(-1); } // // Success. // return(0); } //***************************************************************************** // //! This function performs a block write to an MSC device. //! //! \param psMSCInstance is the device instance to use for this write. //! \param ui32LBA is the logical block address to write on the device. //! \param pui8Data is a pointer to the data to write out. //! \param ui32NumBlocks is the number of blocks to write to the device. //! //! This function will perform a block sized write to the device associated //! with the \e psMSCInstance parameter. The \e ui32LBA parameter specifies //! the logical block address to write on the device. This function will only //! perform \e ui32NumBlocks block sized writes. In most cases this is a write //! of 512 bytes of data. The \e *pui8Data buffer should contain at least //! \e ui32NumBlocks * 512 bytes in size to prevent unwanted data being written //! to the device. //! //! \return The function returns zero for success and any negative value //! indicates a failure. // //***************************************************************************** int32_t USBHMSCBlockWrite(tUSBHMSCInstance *psMSCInstance, uint32_t ui32LBA, uint8_t *pui8Data, uint32_t ui32NumBlocks) { uint32_t ui32Size; // // If there is no device present then return an error. // if(psMSCInstance->psDevice == 0) { return(-1); } // // Calculate the actual byte size of the write. // ui32Size = psMSCInstance->ui32BlockSize * ui32NumBlocks; // // Perform the SCSI write command. // if(USBHSCSIWrite10(psMSCInstance->ui32BulkInPipe, psMSCInstance->ui32BulkOutPipe, ui32LBA, pui8Data, &ui32Size, ui32NumBlocks) != SCSI_CMD_STATUS_PASS) { return(-1); } // // Success. // return(0); } //***************************************************************************** // //! This function forwards an LPM request for a device to enter L1 sleep state. //! //! \param psMSCInstance is the host mass storage class instance that was //! returned from the call to USBHMSCDriveOpen(). //! //! This function forwards a request from an application to the mass storage //! device class to request that a device enter the LPM L1 sleep state. The //! caller must check the return value to see if the request can be //! attempted at this time. If another LPM transaction is busy on this or //! another device, then this function returns \b USBHCD_LPM_PENDING. If //! the LPM request was scheduled to be sent the function returns //! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() //! function to determine if the request completed successfully or if there //! was an error. //! //! \return This function returns the following values: //! - USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. //! - USBHCD_LPM_PENDING - There is already an LPM request pending. // //***************************************************************************** uint32_t USBHMSCLPMSleep(tUSBHMSCInstance *psMSCInstance) { // // Call the host controller function to send the sleep command. // return(USBHCDLPMSleep(psMSCInstance->psDevice)); } //***************************************************************************** // //! This function returns the current status of an LPM request. //! //! \param psMSCInstance is the host mass storage class instance that was //! returned from the call to USBHMSCDriveOpen(). //! //! This function returns the current status of LPM requests for a given //! device. This is called to determine if a previous request completed //! successfully or if there was an error. //! //! \return This function returns the following values: //! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific //! device or the last request completed successfully. //! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not //! complete successfully. //! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. // //***************************************************************************** uint32_t USBHMSCLPMStatus(tUSBHMSCInstance *psMSCInstance) { // // Call the host controller function to get the current LPM status. // return(USBHCDLPMStatus(psMSCInstance->psDevice)); } //***************************************************************************** // // Close the Doxygen group. //! @} // //*****************************************************************************