/** ****************************************************************************** * * @file msd.c * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org) * @brief USB Mass Storage Device Driver * @see The GNU Public License (GPL) Version 3 * @defgroup MSD MSD Functions * @{ * *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Include files */ #include #include #include #include "msd.h" #include "msd_desc.h" #include "msd_bot.h" #include "msd_memory.h" /* Local definitions */ /* MASS Storage Requests */ #define GET_MAX_LUN 0xFE #define MASS_STORAGE_RESET 0xFF #define LUN_DATA_LENGTH 1 /* ISTR events */ /* IMR_MSK */ /* mask defining which events has to be handled */ /* by the device application software */ #define MSD_IMR_MSK (CNTR_RESETM) /* Local prototypes */ static void MSD_MASS_Reset(void); static void MSD_Mass_Storage_SetConfiguration(void); static void MSD_Mass_Storage_ClearFeature(void); static void MSD_Mass_Storage_SetDeviceAddress(void); static void MSD_MASS_Status_In(void); static void MSD_MASS_Status_Out(void); static RESULT MSD_MASS_Data_Setup(uint8_t); static RESULT MSD_MASS_NoData_Setup(uint8_t); static RESULT MSD_MASS_Get_Interface_Setting(uint8_t Interface, uint8_t AlternateSetting); static uint8_t *MSD_MASS_GetDeviceDescriptor(uint16_t); static uint8_t *MSD_MASS_GetConfigDescriptor(uint16_t); static uint8_t *MSD_MASS_GetStringDescriptor(uint16_t); static uint8_t *Get_Max_Lun(uint16_t Length); /* Local variables */ static const DEVICE My_Device_Table = {MSD_EP_NUM, 1}; static const DEVICE_PROP My_Device_Property = { 0, // Init hook not used, done by PIOS_USB module! MSD_MASS_Reset, MSD_MASS_Status_In, MSD_MASS_Status_Out, MSD_MASS_Data_Setup, MSD_MASS_NoData_Setup, MSD_MASS_Get_Interface_Setting, MSD_MASS_GetDeviceDescriptor, MSD_MASS_GetConfigDescriptor, MSD_MASS_GetStringDescriptor, 0, 0x40 /*MAX PACKET SIZE*/ }; static const USER_STANDARD_REQUESTS My_User_Standard_Requests = { NOP_Process, MSD_Mass_Storage_SetConfiguration, NOP_Process, NOP_Process, NOP_Process, MSD_Mass_Storage_ClearFeature, NOP_Process, NOP_Process, MSD_Mass_Storage_SetDeviceAddress }; static ONE_DESCRIPTOR Device_Descriptor = {(uint8_t *) MSD_MASS_DeviceDescriptor, MSD_MASS_SIZ_DEVICE_DESC}; static ONE_DESCRIPTOR Config_Descriptor = {(uint8_t *) MSD_MASS_ConfigDescriptor, MSD_MASS_SIZ_CONFIG_DESC}; static ONE_DESCRIPTOR String_Descriptor[5] = { {(uint8_t *) MSD_MASS_StringLangID, MSD_MASS_SIZ_STRING_LANGID}, {(uint8_t *) MSD_MASS_StringVendor, MSD_MASS_SIZ_STRING_VENDOR}, {(uint8_t *) MSD_MASS_StringProduct, MSD_MASS_SIZ_STRING_PRODUCT}, {(uint8_t *) MSD_MASS_StringSerial, MSD_MASS_SIZ_STRING_SERIAL}, {(uint8_t *) MSD_MASS_StringInterface, MSD_MASS_SIZ_STRING_INTERFACE}, }; static uint8_t lun_available; /** * Initialises the USB Device Driver for a Mass Storage Device * * Should be called during runtime once a SD Card has been connected.
* It is possible to switch back to the original device driver provided * by calling PIOS_USB_Init(1) * * \param[in] mode currently only mode 0 supported * \return < 0 if initialisation failed */ int32_t MSD_Init(uint32_t mode) { /* Update the serial number string descriptor with the data from the unique ID*/ uint8_t serial_number_str[40]; int i, len; PIOS_SYS_SerialNumberGet((char *) serial_number_str); for(i = 0, len = 0; serial_number_str[i] != '\0' && len < 25; ++i) { MSD_MASS_StringSerial[len++] = serial_number_str[i]; MSD_MASS_StringSerial[len++] = 0; } lun_available = 0; /* All LUNs available after USB init */ for(i = 0; i < MSD_NUM_LUN; ++i) { MSD_LUN_AvailableSet(i, 1); } PIOS_IRQ_Disable(); /* Clear all USB interrupt requests */ _SetCNTR(0); /* Interrupt Mask */ _SetISTR(0); /* clear pending requests */ /* Switch to MSD driver hooks */ memcpy(&Device_Table, (DEVICE *) &My_Device_Table, sizeof(Device_Table)); pProperty = (DEVICE_PROP *) &My_Device_Property; pUser_Standard_Requests = (USER_STANDARD_REQUESTS *) &My_User_Standard_Requests; /* Change endpoints */ pEpInt_IN[0] = MSD_Mass_Storage_In; pEpInt_OUT[1] = MSD_Mass_Storage_Out; /* Force re-enumeration w/o overwriting PIOS hooks */ PIOS_USB_Init(2); /* Clear pending interrupts (again) */ _SetISTR(0); /* Set interrupts mask */ _SetCNTR(MSD_IMR_MSK); PIOS_IRQ_Enable(); /* No error */ return 0; } /** * Should be called periodically each millisecond so long this driver is * active (and only then!) to handle USBtransfers. * * Take care that no other task accesses SD Card while this function is * processed! * * Ensure that this function isn't called when a PIOS USB driver is running! * * \return < 0 on errors */ int32_t MSD_Periodic_mS(void) { /* Call endpoint handler of STM32 USB driver */ CTR_LP(); /* No error */ return 0; } /** * This function returns the connection status of the USB HID interface * \return 1: interface available * \return 0: interface not available */ int32_t MSD_CheckAvailable(void) { return pInformation->Current_Configuration ? 1 : 0; } /** * The logical unit is available whenever MSD_Init() is called, or the USB * cable has been reconnected. * * It will be disabled when the host unmounts the file system (like if the * SD Card would be removed. * * When this happens, the application can either call PIOS_USB_Init(1) * again, e.g. to switch to USB HID, or it can make the LUN available * again by calling MSD_LUN_AvailableSet(0, 1) * \param[in] lun Logical Unit number (0) * \param[in] available 0 or 1 * \return < 0 on errors */ int32_t MSD_LUN_AvailableSet(uint8_t lun, uint8_t available) { if(lun >= MSD_NUM_LUN) { return -1; } if(available) { lun_available |= (1 << lun); } else { lun_available &= ~(1 << lun); } /* No error */ return 0; } /** * \return 1 if device is mounted by host * \return 0 if device is not mounted by host */ int32_t MSD_LUN_AvailableGet(uint8_t lun) { if(lun >= MSD_NUM_LUN) return 0; return (lun_available & (1 << lun)) ? 1 : 0; } /** * Mass Storage reset routine. */ static void MSD_MASS_Reset() { /* Set the device as not configured */ pInformation->Current_Configuration = 0; /* Current Feature initialization */ pInformation->Current_Feature = MSD_MASS_ConfigDescriptor[7]; SetBTABLE(MSD_BTABLE_ADDRESS); /* Initialize Endpoint 0 */ SetEPType(ENDP0, EP_CONTROL); SetEPTxStatus(ENDP0, EP_TX_NAK); SetEPRxAddr(ENDP0, MSD_ENDP0_RXADDR); SetEPRxCount(ENDP0, pProperty->MaxPacketSize); SetEPTxAddr(ENDP0, MSD_ENDP0_TXADDR); Clear_Status_Out(ENDP0); SetEPRxValid(ENDP0); /* Initialize Endpoint 1 */ SetEPType(ENDP1, EP_BULK); SetEPTxAddr(ENDP1, MSD_ENDP1_TXADDR); SetEPTxStatus(ENDP1, EP_TX_NAK); SetEPRxStatus(ENDP1, EP_RX_DIS); /* Initialize Endpoint 2 */ SetEPType(ENDP2, EP_BULK); SetEPRxAddr(ENDP2, MSD_ENDP2_RXADDR); SetEPRxCount(ENDP2, pProperty->MaxPacketSize); SetEPRxStatus(ENDP2, EP_RX_VALID); SetEPTxStatus(ENDP2, EP_TX_DIS); SetEPRxCount(ENDP0, pProperty->MaxPacketSize); SetEPRxValid(ENDP0); /* Set the device to response on default address */ SetDeviceAddress(0); MSD_CBW.dSignature = BOT_CBW_SIGNATURE; MSD_Bot_State = BOT_IDLE; } /** * Handle the SetConfiguration request. */ static void MSD_Mass_Storage_SetConfiguration(void) { if(pInformation->Current_Configuration != 0) { ClearDTOG_TX(ENDP1); ClearDTOG_RX(ENDP2); MSD_Bot_State = BOT_IDLE; /* Set the Bot state machine to the IDLE state */ /* All LUNs available after USB (re-)connection */ for(int i = 0; i < MSD_NUM_LUN; ++i) { MSD_LUN_AvailableSet(i, 1); } } } /** * Handle the ClearFeature request. */ static void MSD_Mass_Storage_ClearFeature(void) { /* When the host send a CBW with invalid signature or invalid length the two */ /* Endpoints (IN & OUT) shall stall until receiving a Mass Storage Reset */ if(MSD_CBW.dSignature != BOT_CBW_SIGNATURE) { MSD_Bot_Abort(BOTH_DIR); } } /** * Udpade the device state to addressed. */ static void MSD_Mass_Storage_SetDeviceAddress(void) { } /** * Mass Storage Status IN routine. */ static void MSD_MASS_Status_In(void) { return; } /******************************************************************************* * Mass Storage Status OUT routine. */ static void MSD_MASS_Status_Out(void) { return; } /******************************************************************************* * Handle the data class specific requests.. * \param[in] RequestNo * \return RESULT */ static RESULT MSD_MASS_Data_Setup(uint8_t RequestNo) { uint8_t *(*CopyRoutine)( uint16_t); CopyRoutine = NULL; if((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) && (RequestNo == GET_MAX_LUN) && (pInformation->USBwValue == 0) && (pInformation->USBwIndex == 0) && (pInformation->USBwLength == 0x01)) { CopyRoutine = Get_Max_Lun; } else { return USB_UNSUPPORT; } if(CopyRoutine == NULL) { return USB_UNSUPPORT; } pInformation->Ctrl_Info.CopyData = CopyRoutine; pInformation->Ctrl_Info.Usb_wOffset = 0; (*CopyRoutine)(0); return USB_SUCCESS; } /** * Handle the no data class specific requests. * \param[in] RequestNo * \return RESULT */ static RESULT MSD_MASS_NoData_Setup(uint8_t RequestNo) { if((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) && (RequestNo == MASS_STORAGE_RESET) && (pInformation->USBwValue == 0) && (pInformation->USBwIndex == 0) && (pInformation->USBwLength == 0x00)) { /* Initialise Endpoint 1 */ ClearDTOG_TX(ENDP1); /* Initialise Endpoint 2 */ ClearDTOG_RX(ENDP2); /* Initialise the CBW signature to enable the clear feature*/ MSD_CBW.dSignature = BOT_CBW_SIGNATURE; MSD_Bot_State = BOT_IDLE; return USB_SUCCESS; } return USB_UNSUPPORT; } /** * Test the interface and the alternate setting according to the supported one. * \param[in] Interface * \param[in] AlternateSetting * \return RESULT */ static RESULT MSD_MASS_Get_Interface_Setting(uint8_t Interface, uint8_t AlternateSetting) { if(AlternateSetting > 0) { return USB_UNSUPPORT;/* In this application we don't have AlternateSetting*/ } else if(Interface > 0) { return USB_UNSUPPORT;/* In this application we have only 1 interfaces*/ } return USB_SUCCESS; } /** * Get the device descriptor. * \param[in] Length */ static uint8_t *MSD_MASS_GetDeviceDescriptor(uint16_t Length) { return Standard_GetDescriptorData(Length, &Device_Descriptor); } /** * Get the configuration descriptor. * \param[in] Length */ static uint8_t *MSD_MASS_GetConfigDescriptor(uint16_t Length) { return Standard_GetDescriptorData(Length, &Config_Descriptor); } /** * Get the string descriptors according to the needed index. * \param[in] Length */ static uint8_t *MSD_MASS_GetStringDescriptor(uint16_t Length) { uint8_t wValue0 = pInformation->USBwValue0; if(wValue0 > 5) { return NULL; } else { return Standard_GetDescriptorData(Length, &String_Descriptor[wValue0]); } } /** * Handle the Get Max Lun request. * \param[in] Length */ static uint8_t *Get_Max_Lun(uint16_t Length) { static uint32_t Max_Lun = MSD_NUM_LUN - 1; if(Length == 0) { pInformation->Ctrl_Info.Usb_wLength = LUN_DATA_LENGTH; return 0; } else { /* This copy concept requires statically allocated data... grr! */ return ((uint8_t*) (&Max_Lun)); } }