1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-12-12 20:08:48 +01:00
LibrePilot/flight/PiOS/STM32F2xx/Libraries/msd/msd.c

442 lines
12 KiB
C

/**
******************************************************************************
*
* @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 <pios.h>
#include <usb_lib.h>
#include <string.h>
#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.<BR>
* 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));
}
}