Hello,
I had recently raised a question about USB Hub support in TI-RTOS. At present it does not support any external hubs, As I am new to USB, I was able to test theUSB hub support example provided with Tivaware. I then modified the USBMSCHFatFsTiva.c incorporating the changes that was in the tivaware code. The TI-RTOS is rebuilt as per the instructions in TI-RTOS user guide. The USB pen drive is not getting enumerated and is getting timed out. I am attaching the original, as well as the modified file.
Are the changes done to the file enough to support USB hub?
Any other changes needed to support the USB Hub?
USBMSCHFatFsTiva.c is the original Ti-RTOS file
USBMSCHFatFsTiva-modified.c is the modified file to support USB Hub
Thanks in advance
Narendra
/*
* Copyright (c) 2015, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdbool.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/Assert.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>
#include <ti/drivers/usbmschfatfs/USBMSCHFatFsTiva.h>
/* driverlib header files */
#include <inc/hw_ints.h>
#include <inc/hw_types.h>
/* usblib Header files */
#include <usblib/usb-ids.h>
#include <usblib/usblib.h>
#include <usblib/usbmsc.h>
#include <usblib/host/usbhost.h>
#include <usblib/host/usbhmsc.h>
#include <usblib/host/usbhhub.h>
#if defined(TIVAWARE)
/* c99 types needed by TivaWare */
typedef uint32_t USBMSCEventType;
#else /* MWare */
#define g_sUSBHostMSCClassDriver g_USBHostMSCClassDriver
#define eUSBModeHost USB_MODE_HOST
#define ui32Event ulEvent
typedef unsigned long USBMSCEventType;
#endif
#define DRIVE_NOT_MOUNTED ~0
/*
* Array of USBMSCHFatFs_Handles to determine the association of the FatFs drive
* number with a USBMSCHFatFs_Handle
* _VOLUMES is defined in <ti/sysbios/fatfs/ffconf.h>
* As only one USB MSC Host class can be defined only the 1st element is used.
*/
extern USBMSCHFatFs_Config USBMSCHFatFs_config[];
/* Function prototypes */
static void USBMSCHFatFsTiva_cbMSCHandler(USBMSCType instance,
USBMSCEventType event,
void *eventMsgPtr);
static void USBMSCHFatFsTiva_hwiHandler(UArg arg0);
static void USBMSCHFatFsTiva_serviceUSBHost(UArg arg0, UArg arg1);
static void USBMSCHFatFsTiva_usbHCDEvents(void *cbData);
/* FatFs disk I/O functions */
DSTATUS USBMSCHFatFsTiva_diskInitialize(BYTE drv);
DRESULT USBMSCHFatFsTiva_diskIOctl(BYTE drv, BYTE ctrl, void *buf);
DRESULT USBMSCHFatFsTiva_diskRead(BYTE drv, BYTE *buf,
DWORD sector, BYTE count);
DSTATUS USBMSCHFatFsTiva_diskStatus(BYTE drv);
DRESULT USBMSCHFatFsTiva_diskWrite(BYTE drv, const BYTE *buf,
DWORD sector, BYTE count);
/* USBMSCHFatFs functions */
void USBMSCHFatFsTiva_init(USBMSCHFatFs_Handle handle);
USBMSCHFatFs_Handle USBMSCHFatFsTiva_open(USBMSCHFatFs_Handle handle,
unsigned char drv,
USBMSCHFatFs_Params *params);
void USBMSCHFatFsTiva_close(USBMSCHFatFs_Handle handle);
int USBMSCHFatFsTiva_control(USBMSCHFatFs_Handle handle,
unsigned int cmd,
void *arg);
bool USBMSCHFatFsTiva_waitForConnect(USBMSCHFatFs_Handle handle,
unsigned int timeout);
/* USBMSCHFatFs function table for USBMSCHFatFsTiva implementation */
const USBMSCHFatFs_FxnTable USBMSCHFatFsTiva_fxnTable = {
USBMSCHFatFsTiva_init,
USBMSCHFatFsTiva_open,
USBMSCHFatFsTiva_close,
USBMSCHFatFsTiva_control,
USBMSCHFatFsTiva_waitForConnect
};
/* Default USBMSCHFatFs params */
extern const USBMSCHFatFs_Params USBMSCHFatFs_defaultParams;
/* MACRO to create a generic USB event host driver */
DECLARE_EVENT_DRIVER(USBMSCHFatFs_eventDriver, 0, 0,
USBMSCHFatFsTiva_usbHCDEvents);
/* A list of available Host Class Drivers */
static tUSBHostClassDriver const * const usbHCDDriverList[] = {
&g_sUSBHubClassDriver, /* Hub class driver */
&g_sUSBHostMSCClassDriver, /* MSC Host class driver */
&USBMSCHFatFs_eventDriver /* Generic event notification handler */
};
/* Variable containing the number of HCDs in usbHCDDriverList */
static const unsigned long numHostClassDrivers =
sizeof(usbHCDDriverList) / sizeof(tUSBHostClassDriver *);
//*****************************************************************************
//
// This is the callback from the USB HUB handler.
//
// pvCBData is ignored by this function.
// ui32Event is one of the valid events for a mouse device.
// ui32MsgParam is defined by the event that occurs.
// pvMsgData is a pointer to data that is defined by the event that occurs.
//
//
// This function will return 0.
//
//*****************************************************************************
void
HubCallback(tHubInstance *psHubInstance, uint32_t ui32Event,
uint32_t ui32MsgParam, void *pvMsgData)
{
}
/*
* ======== USBMSCHFatFsTiva_cbMSCHandler ========
* Callback handler for the USB stack.
*
* Callback handler call by the USB stack to notify us on what has
* happened in regards to the mouse. This is done in a context of the priority
* of the USBMSCHFatFsTiva_serviceUSBHost task.
*
* @param instance A driver instance of MSC.
*
* @param event Identifies the event that occurred in regards to this
* device.
*
* @param eventMsgPtr A data pointer associated with a particular event.
*/
static void USBMSCHFatFsTiva_cbMSCHandler(USBMSCType instance,
USBMSCEventType event,
void *eventMsgPtr)
{
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
/* Determine what event has happened */
switch (event) {
case MSC_EVENT_OPEN:
object->state = USBMSCHFatFsTiva_CONNECTED;
Log_print0(Diags_USER2, "USBMSCHFatFs: MSC DEVICE CONNECTED; "
"Posting semUSBConnected");
Semaphore_post(Semaphore_handle(&object->semUSBConnected));
break;
case MSC_EVENT_CLOSE:
object->state = USBMSCHFatFsTiva_NO_DEVICE;
Log_print0(Diags_USER2, "USBMSCHFatFs: MSC DEVICE DISCONNECTED");
break;
default:
break;
}
}
/*
* ======== USBMSCHFatFsTiva_diskInitialize ========
* This function checks the USB Drive to see if it's ready to be accessed.
*
* This function attempts to read the USBHMSCDriveReady() function up to 10
* times to get a good return code. For some reason the first attempt to read
* this function doesn't return a good response value. Generally, you should
* get a good response on the second attempt.
* A gateMutex lock is required to prevent concurrent access to the USB
* Library's state machine variables within MSCInstance.
*
* @param drv Drive Number
*
* @return Returns the disk status to the FatFs module
*/
DSTATUS USBMSCHFatFsTiva_diskInitialize(BYTE drv)
{
unsigned char i;
unsigned int key;
uint32_t driveReady;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
/* Determine if the USB Drive is ready up to 10 times */
for (i = 0; i < 10; i++ ) {
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
driveReady = USBHMSCDriveReady(object->MSCInstance);
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
if (driveReady == 0) {
Log_print1(Diags_USER1, "USBMSCHFatFs: disk initialization: "
"ready after %d tries", i);
return (0x00); /* Disk OK */
}
}
Log_print0(Diags_USER1, "USBMSCHFatFs: disk initialization: not ready");
return (STA_NOINIT);
}
/*
* ======== USBMSCHFatFsTiva_diskIOctl ========
* This function performs misc. control functions documented by FatFs
*
* NOTE: Formatting the USB MSC f_mkfs() is NOT supported!
*
* @param drv Drive Number
*
* @param ctrl FatFs control function to be executed
*
* @param buf Pointer to a buffer to which data is written
*/
DRESULT USBMSCHFatFsTiva_diskIOctl(BYTE drv, BYTE ctrl, void *buf)
{
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: disk IO control: not "
"initialized");
return (RES_NOTRDY);
}
switch (ctrl) {
case CTRL_SYNC:
Log_print0(Diags_USER1, "USBMSCHFatFs: disk IO control: OK");
return (RES_OK);
default:
Log_print0(Diags_USER1, "USBMSCHFatFs: disk IO control: "
"parameter error");
return (RES_PARERR);
}
}
/*
* ======== USBMSCHFatFsTiva_diskRead ========
* This function reads sector(s) from the disk drive
*
* @param drv Drive Number
*
* @param buf Pointer to a buffer to which data is written
*
* @param sector Sector number to read from
*
* @param count Number of sectors to be read
*/
DRESULT USBMSCHFatFsTiva_diskRead(BYTE drv, BYTE *buf,
DWORD sector, BYTE count)
{
unsigned int key;
uint32_t driveRead;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
Log_print2(Diags_USER1, "USBMSCHFatFs: diskRead: Sector %d, Count %d",
sector, count);
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskRead: not initialized");
return (RES_NOTRDY);
}
/* READ BLOCK */
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
driveRead = USBHMSCBlockRead(object->MSCInstance, sector, buf, count);
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
if (driveRead == 0) {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskRead: OK");
return (RES_OK);
}
else {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskRead: ERROR");
return (RES_ERROR);
}
}
/*
* ======== USBMSCHFatFsTiva_diskStatus ========
* Returns the current status of a drive
*
* @param drv Drive Number
*/
DSTATUS USBMSCHFatFsTiva_diskStatus(BYTE drv)
{
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskStatus: not initialized");
return (STA_NOINIT);
}
return (0x00); /* Disk OK */
}
#if _READONLY == 0
/*
* ======== USBMSCHFatFsTiva_diskWrite ========
* This function writes sector(s) to the disk drive
*
* @param drv Drive Number
*
* @param buf Pointer to a buffer from which data is read
*
* @param sector Sector number to write to
*
* @param count Number of sectors to be written
*/
DRESULT USBMSCHFatFsTiva_diskWrite(BYTE drv, const BYTE *buf,
DWORD sector, BYTE count)
{
unsigned int key;
uint32_t driveWrite;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
Log_print2(Diags_USER1, "USBMSCHFatFs: diskWrite: Sector %d, Count %d",
sector, count);
if (!count) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskWrite: ERROR");
return (RES_PARERR);
}
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskWrite: not initialized");
return (RES_NOTRDY);
}
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
driveWrite = USBHMSCBlockWrite(object->MSCInstance, sector, (uint8_t *)buf, count);
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
if (driveWrite == 0) {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskWrite: OK");
return (RES_OK);
}
else {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskWrite: ERROR");
return (RES_ERROR);
}
}
#endif /* _READONLY */
/*
* ======== USBMSCHFatFsTiva_hwiHandler ========
* This function calls the USB library's interrupt handler.
*/
static void USBMSCHFatFsTiva_hwiHandler(UArg arg0)
{
/*
* This function call generates a VBUS error interrupts; therefore we call
* the OTG equivalent instead based on working TivaWare examples
*/
//USB0HostIntHandler();
USB0OTGModeIntHandler();
}
/*
* ======== USBMSCHFatFsTiva_serviceUSBHost ========
* Task to periodically service the USB Stack
*
* USBHCDMain handles the USB Stack's statemachine. For example it handles the
* enumeration process when a device connects.
* Future USB library improvement goal is to remove this polling requirement..
*/
static void USBMSCHFatFsTiva_serviceUSBHost(UArg arg0, UArg arg1)
{
unsigned int key;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
while (true) {
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
USBHCDMain();
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
/* Future enhancement to remove the Task_sleep */
Task_sleep(10);
}
}
/*
* ======== USBMSCHFatFsTiva_usbHCDEvents ========
* Generic USB Host Class Driver event callback.
*
* This callback is called to notify the application that an unknown
* device was connected.
*/
static void USBMSCHFatFsTiva_usbHCDEvents(void *cbData)
{
tEventInfo *pEventInfo = (tEventInfo *)cbData;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
switch (pEventInfo->ui32Event) {
case USB_EVENT_UNKNOWN_CONNECTED:
/* An unknown device was detected. */
object->state = USBMSCHFatFsTiva_UNKNOWN;
Log_print0(Diags_USER2, "USBMSCHFatFs: usbHCDEvent Callback: "
"UNKNOWN DEVICE CONNECTED");
break;
case USB_EVENT_DISCONNECTED:
/* Unknown device has been removed. */
object->state = USBMSCHFatFsTiva_NO_DEVICE;
Log_print0(Diags_USER2, "USBMSCHFatFs: usbHCDEvent Callback: "
"UNKNOWN DEVICE DISCONNECTED");
break;
case USB_EVENT_POWER_FAULT:
/* No power means no device is present. */
object->state = USBMSCHFatFsTiva_POWER_FAULT;
Log_print0(Diags_USER2, "USBMSCHFatFs: usbHCDEvent Callback: "
"POWER FAULT");
break;
default:
break;
}
}
/*
* ======== USBMSCHFatFsTiva_init ========
*/
void USBMSCHFatFsTiva_init(USBMSCHFatFs_Handle handle)
{
USBMSCHFatFsTiva_Object *object = handle->object;
object->driveNumber = DRIVE_NOT_MOUNTED;
object->state = USBMSCHFatFsTiva_NO_DEVICE;
}
/*
* ======== USBMSCHFatFsTiva_open ========
*/
USBMSCHFatFs_Handle USBMSCHFatFsTiva_open(USBMSCHFatFs_Handle handle,
unsigned char drv,
USBMSCHFatFs_Params *params)
{
unsigned int key;
DRESULT dresult;
FRESULT fresult;
USBMSCHFatFsTiva_Object *object = handle->object;
USBMSCHFatFsTiva_HWAttrs const *hwAttrs = handle->hwAttrs;
union {
Task_Params taskParams;
Semaphore_Params semParams;
GateMutex_Params gateParams;
Hwi_Params hwiParams;
} paramsUnion;
/* Determine if the device was already opened */
key = Hwi_disable();
if (object->driveNumber != DRIVE_NOT_MOUNTED) {
Hwi_restore(key);
return (NULL);
}
/* Mark as being used */
object->driveNumber = drv;
Hwi_restore(key);
/* Store the USBMSCHFatFs parameters */
if (params == NULL) {
/* No params passed in, so use the defaults */
params = (USBMSCHFatFs_Params *) &USBMSCHFatFs_defaultParams;
}
/* Initialize the USB stack for host mode. */
USBStackModeSet(0, eUSBModeForceHost, NULL);
/* Register host class drivers */
USBHCDRegisterDrivers(0, usbHCDDriverList, numHostClassDrivers);
//
// Open a hub instance and provide it with the memory required to hold
// configuration descriptors for each attached device.
//
USBHHubOpen(HubCallback);
/* Open an instance of the MSC host driver */
object->MSCInstance = USBHMSCDriveOpen(0, USBMSCHFatFsTiva_cbMSCHandler);
if (!(object->MSCInstance)) {
Log_print0(Diags_USER1,"USBMSCHFatFs: Error initializing the MSC Host");
USBMSCHFatFsTiva_close(handle);
return (NULL);
}
/* Create the Hwi object to service interrupts */
Hwi_Params_init(&(paramsUnion.hwiParams));
paramsUnion.hwiParams.priority = hwAttrs->intPriority;
Hwi_construct(&(object->hwi), hwAttrs->intNum, USBMSCHFatFsTiva_hwiHandler,
&(paramsUnion.hwiParams), NULL);
/* Initialize USB power configuration */
USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);
/* Enable the USB stack */
USBHCDInit(0, object->memPoolHCD, HCDMEMORYPOOLSIZE);
/* RTOS primitives */
Semaphore_Params_init(&(paramsUnion.semParams));
paramsUnion.semParams.mode = Semaphore_Mode_BINARY;
Semaphore_construct(&(object->semUSBConnected), 0, &(paramsUnion.semParams));
GateMutex_Params_init(&(paramsUnion.gateParams));
paramsUnion.gateParams.instance->name = "USB Library Access";
GateMutex_construct(&(object->gateUSBLibAccess), &(paramsUnion.gateParams));
paramsUnion.gateParams.instance->name = "USB Wait";
GateMutex_construct(&(object->gateUSBWait), &(paramsUnion.gateParams));
/*
* Note that serviceUSBHost() should not be run until the USB Stack has been
* initialized!!
*/
Task_Params_init(&(paramsUnion.taskParams));
/*
* If serviceTaskStackPtr is null, then Task_construct performs a
* Memory_alloc - requiring a Heap
*/
paramsUnion.taskParams.stack = params->serviceTaskStackPtr;
/*
* If service priority passed in is higher than what is configured by the
* Task module, then use the highest priority available.
*/
if (Task_numPriorities - 1 < params->servicePriority) {
paramsUnion.taskParams.priority = (Task_numPriorities - 1);
}
else {
paramsUnion.taskParams.priority = params->servicePriority;
}
/* If no stack size is passed in, then use the default task stack size */
if (params->serviceTaskStackSize) {
paramsUnion.taskParams.stackSize = params->serviceTaskStackSize;
}
else {
paramsUnion.taskParams.stackSize = Task_defaultStackSize;
}
Task_construct(&(object->taskHCDMain),USBMSCHFatFsTiva_serviceUSBHost,
&(paramsUnion.taskParams), NULL);
/* Register the new disk_*() functions */
dresult = disk_register(drv,
USBMSCHFatFsTiva_diskInitialize,
USBMSCHFatFsTiva_diskStatus,
USBMSCHFatFsTiva_diskRead,
USBMSCHFatFsTiva_diskWrite,
USBMSCHFatFsTiva_diskIOctl);
/* Check for drive errors */
if (dresult != RES_OK) {
Log_error0("USBMSCHFatFs: disk functions not registered");
USBMSCHFatFsTiva_close(handle);
return (NULL);
}
/* Mount the FatFs (this function does not access the SDCard yet...) */
fresult = f_mount(drv, &(object->filesystem));
if (fresult != FR_OK) {
Log_error1("USBMSCHFatFs: drive %d not mounted", drv);
USBMSCHFatFsTiva_close(handle);
return (NULL);
}
Log_print1(Diags_USER1, "USBMSCHFatFs: drive %d opened", drv);
return (handle);
}
/*
* ======== USBMSCHFatFsTiva_close ========
*/
void USBMSCHFatFsTiva_close(USBMSCHFatFs_Handle handle)
{
unsigned int key;
DRESULT dresult;
FRESULT fresult;
USBMSCHFatFsTiva_Object *object = handle->object;
/* Unmount the FatFs drive */
fresult = f_mount(object->driveNumber, NULL);
if (fresult != FR_OK) {
Log_print0(Diags_USER1, "USBMSCHFatFs: could not unmount FatFs volume");
}
/* Close USB Drive */
USBHMSCDriveClose(object->MSCInstance);
/* Unregister the disk_*() functions */
dresult = disk_unregister(object->driveNumber);
if (dresult != RES_OK) {
Log_print0(Diags_USER1, "USBMSCHFatFs: error unregistering disk "
"functions");
}
/* Delete the HCDMain service task*/
Task_destruct(&(object->taskHCDMain));
/* Delete the semaphore */
Semaphore_destruct(&(object->semUSBConnected));
/* Delete the gate */
GateMutex_destruct(&(object->gateUSBLibAccess));
/* Delete the gate */
GateMutex_destruct(&(object->gateUSBWait));
/* Delete the hwi */
Hwi_destruct(&(object->hwi));
Log_print1(Diags_USER1, "USBMSCHFatFs: drive %d closed",
object->driveNumber);
key = Hwi_disable();
object->driveNumber = DRIVE_NOT_MOUNTED;
Hwi_restore(key);
}
/*
* ======== USBMSCHFatFsTiva_control ========
* @pre Function assumes that the handle is not NULL
*/
int USBMSCHFatFsTiva_control(USBMSCHFatFs_Handle handle,
unsigned int cmd,
void *arg)
{
/* No implementation yet */
return (USBMSCHFatFs_CMD_RESERVED);
}
/*
* ======== USBMSCHFatFsTiva_waitForConnect ========
*/
bool USBMSCHFatFsTiva_waitForConnect(USBMSCHFatFs_Handle handle,
unsigned int timeout)
{
unsigned int key;
bool ret = true;
USBMSCHFatFsTiva_Object *object = handle->object;
/* Need exclusive access to prevent a race condition */
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBWait)));
if (object->state == USBMSCHFatFsTiva_NO_DEVICE) {
if (!Semaphore_pend(Semaphore_handle(&(object->semUSBConnected)),
timeout)) {
ret = false;
}
}
GateMutex_leave(GateMutex_handle(&(object->gateUSBWait)), key);
return (ret);
}
/*
* Copyright (c) 2015, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdbool.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/Assert.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>
#include <ti/drivers/usbmschfatfs/USBMSCHFatFsTiva.h>
/* driverlib header files */
#include <inc/hw_ints.h>
#include <inc/hw_types.h>
/* usblib Header files */
#include <usblib/usb-ids.h>
#include <usblib/usblib.h>
#include <usblib/usbmsc.h>
#include <usblib/host/usbhost.h>
#include <usblib/host/usbhmsc.h>
#if defined(TIVAWARE)
/* c99 types needed by TivaWare */
typedef uint32_t USBMSCEventType;
#else /* MWare */
#define g_sUSBHostMSCClassDriver g_USBHostMSCClassDriver
#define eUSBModeHost USB_MODE_HOST
#define ui32Event ulEvent
typedef unsigned long USBMSCEventType;
#endif
#define DRIVE_NOT_MOUNTED ~0
/*
* Array of USBMSCHFatFs_Handles to determine the association of the FatFs drive
* number with a USBMSCHFatFs_Handle
* _VOLUMES is defined in <ti/sysbios/fatfs/ffconf.h>
* As only one USB MSC Host class can be defined only the 1st element is used.
*/
extern USBMSCHFatFs_Config USBMSCHFatFs_config[];
/* Function prototypes */
static void USBMSCHFatFsTiva_cbMSCHandler(USBMSCType instance,
USBMSCEventType event,
void *eventMsgPtr);
static void USBMSCHFatFsTiva_hwiHandler(UArg arg0);
static void USBMSCHFatFsTiva_serviceUSBHost(UArg arg0, UArg arg1);
static void USBMSCHFatFsTiva_usbHCDEvents(void *cbData);
/* FatFs disk I/O functions */
DSTATUS USBMSCHFatFsTiva_diskInitialize(BYTE drv);
DRESULT USBMSCHFatFsTiva_diskIOctl(BYTE drv, BYTE ctrl, void *buf);
DRESULT USBMSCHFatFsTiva_diskRead(BYTE drv, BYTE *buf,
DWORD sector, BYTE count);
DSTATUS USBMSCHFatFsTiva_diskStatus(BYTE drv);
DRESULT USBMSCHFatFsTiva_diskWrite(BYTE drv, const BYTE *buf,
DWORD sector, BYTE count);
/* USBMSCHFatFs functions */
void USBMSCHFatFsTiva_init(USBMSCHFatFs_Handle handle);
USBMSCHFatFs_Handle USBMSCHFatFsTiva_open(USBMSCHFatFs_Handle handle,
unsigned char drv,
USBMSCHFatFs_Params *params);
void USBMSCHFatFsTiva_close(USBMSCHFatFs_Handle handle);
int USBMSCHFatFsTiva_control(USBMSCHFatFs_Handle handle,
unsigned int cmd,
void *arg);
bool USBMSCHFatFsTiva_waitForConnect(USBMSCHFatFs_Handle handle,
unsigned int timeout);
/* USBMSCHFatFs function table for USBMSCHFatFsTiva implementation */
const USBMSCHFatFs_FxnTable USBMSCHFatFsTiva_fxnTable = {
USBMSCHFatFsTiva_init,
USBMSCHFatFsTiva_open,
USBMSCHFatFsTiva_close,
USBMSCHFatFsTiva_control,
USBMSCHFatFsTiva_waitForConnect
};
/* Default USBMSCHFatFs params */
extern const USBMSCHFatFs_Params USBMSCHFatFs_defaultParams;
/* MACRO to create a generic USB event host driver */
DECLARE_EVENT_DRIVER(USBMSCHFatFs_eventDriver, 0, 0,
USBMSCHFatFsTiva_usbHCDEvents);
/* A list of available Host Class Drivers */
static tUSBHostClassDriver const * const usbHCDDriverList[] = {
&g_sUSBHostMSCClassDriver, /* MSC Host class driver */
&USBMSCHFatFs_eventDriver /* Generic event notification handler */
};
/* Variable containing the number of HCDs in usbHCDDriverList */
static const unsigned long numHostClassDrivers =
sizeof(usbHCDDriverList) / sizeof(tUSBHostClassDriver *);
/*
* ======== USBMSCHFatFsTiva_cbMSCHandler ========
* Callback handler for the USB stack.
*
* Callback handler call by the USB stack to notify us on what has
* happened in regards to the mouse. This is done in a context of the priority
* of the USBMSCHFatFsTiva_serviceUSBHost task.
*
* @param instance A driver instance of MSC.
*
* @param event Identifies the event that occurred in regards to this
* device.
*
* @param eventMsgPtr A data pointer associated with a particular event.
*/
static void USBMSCHFatFsTiva_cbMSCHandler(USBMSCType instance,
USBMSCEventType event,
void *eventMsgPtr)
{
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
/* Determine what event has happened */
switch (event) {
case MSC_EVENT_OPEN:
object->state = USBMSCHFatFsTiva_CONNECTED;
Log_print0(Diags_USER2, "USBMSCHFatFs: MSC DEVICE CONNECTED; "
"Posting semUSBConnected");
Semaphore_post(Semaphore_handle(&object->semUSBConnected));
break;
case MSC_EVENT_CLOSE:
object->state = USBMSCHFatFsTiva_NO_DEVICE;
Log_print0(Diags_USER2, "USBMSCHFatFs: MSC DEVICE DISCONNECTED");
break;
default:
break;
}
}
/*
* ======== USBMSCHFatFsTiva_diskInitialize ========
* This function checks the USB Drive to see if it's ready to be accessed.
*
* This function attempts to read the USBHMSCDriveReady() function up to 10
* times to get a good return code. For some reason the first attempt to read
* this function doesn't return a good response value. Generally, you should
* get a good response on the second attempt.
* A gateMutex lock is required to prevent concurrent access to the USB
* Library's state machine variables within MSCInstance.
*
* @param drv Drive Number
*
* @return Returns the disk status to the FatFs module
*/
DSTATUS USBMSCHFatFsTiva_diskInitialize(BYTE drv)
{
unsigned char i;
unsigned int key;
uint32_t driveReady;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
/* Determine if the USB Drive is ready up to 10 times */
for (i = 0; i < 10; i++ ) {
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
driveReady = USBHMSCDriveReady(object->MSCInstance);
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
if (driveReady == 0) {
Log_print1(Diags_USER1, "USBMSCHFatFs: disk initialization: "
"ready after %d tries", i);
return (0x00); /* Disk OK */
}
}
Log_print0(Diags_USER1, "USBMSCHFatFs: disk initialization: not ready");
return (STA_NOINIT);
}
/*
* ======== USBMSCHFatFsTiva_diskIOctl ========
* This function performs misc. control functions documented by FatFs
*
* NOTE: Formatting the USB MSC f_mkfs() is NOT supported!
*
* @param drv Drive Number
*
* @param ctrl FatFs control function to be executed
*
* @param buf Pointer to a buffer to which data is written
*/
DRESULT USBMSCHFatFsTiva_diskIOctl(BYTE drv, BYTE ctrl, void *buf)
{
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: disk IO control: not "
"initialized");
return (RES_NOTRDY);
}
switch (ctrl) {
case CTRL_SYNC:
Log_print0(Diags_USER1, "USBMSCHFatFs: disk IO control: OK");
return (RES_OK);
default:
Log_print0(Diags_USER1, "USBMSCHFatFs: disk IO control: "
"parameter error");
return (RES_PARERR);
}
}
/*
* ======== USBMSCHFatFsTiva_diskRead ========
* This function reads sector(s) from the disk drive
*
* @param drv Drive Number
*
* @param buf Pointer to a buffer to which data is written
*
* @param sector Sector number to read from
*
* @param count Number of sectors to be read
*/
DRESULT USBMSCHFatFsTiva_diskRead(BYTE drv, BYTE *buf,
DWORD sector, BYTE count)
{
unsigned int key;
uint32_t driveRead;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
Log_print2(Diags_USER1, "USBMSCHFatFs: diskRead: Sector %d, Count %d",
sector, count);
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskRead: not initialized");
return (RES_NOTRDY);
}
/* READ BLOCK */
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
driveRead = USBHMSCBlockRead(object->MSCInstance, sector, buf, count);
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
if (driveRead == 0) {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskRead: OK");
return (RES_OK);
}
else {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskRead: ERROR");
return (RES_ERROR);
}
}
/*
* ======== USBMSCHFatFsTiva_diskStatus ========
* Returns the current status of a drive
*
* @param drv Drive Number
*/
DSTATUS USBMSCHFatFsTiva_diskStatus(BYTE drv)
{
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskStatus: not initialized");
return (STA_NOINIT);
}
return (0x00); /* Disk OK */
}
#if _READONLY == 0
/*
* ======== USBMSCHFatFsTiva_diskWrite ========
* This function writes sector(s) to the disk drive
*
* @param drv Drive Number
*
* @param buf Pointer to a buffer from which data is read
*
* @param sector Sector number to write to
*
* @param count Number of sectors to be written
*/
DRESULT USBMSCHFatFsTiva_diskWrite(BYTE drv, const BYTE *buf,
DWORD sector, BYTE count)
{
unsigned int key;
uint32_t driveWrite;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
Log_print2(Diags_USER1, "USBMSCHFatFs: diskWrite: Sector %d, Count %d",
sector, count);
if (!count) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskWrite: ERROR");
return (RES_PARERR);
}
if (object->state != USBMSCHFatFsTiva_CONNECTED) {
Log_print0(Diags_USER1, "USBMSCHFatFs: diskWrite: not initialized");
return (RES_NOTRDY);
}
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
driveWrite = USBHMSCBlockWrite(object->MSCInstance, sector, (uint8_t *)buf, count);
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
if (driveWrite == 0) {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskWrite: OK");
return (RES_OK);
}
else {
Log_print0(Diags_USER2, "USBMSCHFatFs: diskWrite: ERROR");
return (RES_ERROR);
}
}
#endif /* _READONLY */
/*
* ======== USBMSCHFatFsTiva_hwiHandler ========
* This function calls the USB library's interrupt handler.
*/
static void USBMSCHFatFsTiva_hwiHandler(UArg arg0)
{
/*
* This function call generates a VBUS error interrupts; therefore we call
* the OTG equivalent instead based on working TivaWare examples
*/
//USB0HostIntHandler();
USB0OTGModeIntHandler();
}
/*
* ======== USBMSCHFatFsTiva_serviceUSBHost ========
* Task to periodically service the USB Stack
*
* USBHCDMain handles the USB Stack's statemachine. For example it handles the
* enumeration process when a device connects.
* Future USB library improvement goal is to remove this polling requirement..
*/
static void USBMSCHFatFsTiva_serviceUSBHost(UArg arg0, UArg arg1)
{
unsigned int key;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
while (true) {
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBLibAccess)));
USBHCDMain();
GateMutex_leave(GateMutex_handle(&(object->gateUSBLibAccess)), key);
/* Future enhancement to remove the Task_sleep */
Task_sleep(10);
}
}
/*
* ======== USBMSCHFatFsTiva_usbHCDEvents ========
* Generic USB Host Class Driver event callback.
*
* This callback is called to notify the application that an unknown
* device was connected.
*/
static void USBMSCHFatFsTiva_usbHCDEvents(void *cbData)
{
tEventInfo *pEventInfo = (tEventInfo *)cbData;
USBMSCHFatFsTiva_Object *object = USBMSCHFatFs_config->object;
switch (pEventInfo->ui32Event) {
case USB_EVENT_UNKNOWN_CONNECTED:
/* An unknown device was detected. */
object->state = USBMSCHFatFsTiva_UNKNOWN;
Log_print0(Diags_USER2, "USBMSCHFatFs: usbHCDEvent Callback: "
"UNKNOWN DEVICE CONNECTED");
break;
case USB_EVENT_DISCONNECTED:
/* Unknown device has been removed. */
object->state = USBMSCHFatFsTiva_NO_DEVICE;
Log_print0(Diags_USER2, "USBMSCHFatFs: usbHCDEvent Callback: "
"UNKNOWN DEVICE DISCONNECTED");
break;
case USB_EVENT_POWER_FAULT:
/* No power means no device is present. */
object->state = USBMSCHFatFsTiva_POWER_FAULT;
Log_print0(Diags_USER2, "USBMSCHFatFs: usbHCDEvent Callback: "
"POWER FAULT");
break;
default:
break;
}
}
/*
* ======== USBMSCHFatFsTiva_init ========
*/
void USBMSCHFatFsTiva_init(USBMSCHFatFs_Handle handle)
{
USBMSCHFatFsTiva_Object *object = handle->object;
object->driveNumber = DRIVE_NOT_MOUNTED;
object->state = USBMSCHFatFsTiva_NO_DEVICE;
}
/*
* ======== USBMSCHFatFsTiva_open ========
*/
USBMSCHFatFs_Handle USBMSCHFatFsTiva_open(USBMSCHFatFs_Handle handle,
unsigned char drv,
USBMSCHFatFs_Params *params)
{
unsigned int key;
DRESULT dresult;
FRESULT fresult;
USBMSCHFatFsTiva_Object *object = handle->object;
USBMSCHFatFsTiva_HWAttrs const *hwAttrs = handle->hwAttrs;
union {
Task_Params taskParams;
Semaphore_Params semParams;
GateMutex_Params gateParams;
Hwi_Params hwiParams;
} paramsUnion;
/* Determine if the device was already opened */
key = Hwi_disable();
if (object->driveNumber != DRIVE_NOT_MOUNTED) {
Hwi_restore(key);
return (NULL);
}
/* Mark as being used */
object->driveNumber = drv;
Hwi_restore(key);
/* Store the USBMSCHFatFs parameters */
if (params == NULL) {
/* No params passed in, so use the defaults */
params = (USBMSCHFatFs_Params *) &USBMSCHFatFs_defaultParams;
}
/* Initialize the USB stack for host mode. */
USBStackModeSet(0, eUSBModeHost, NULL);
/* Register host class drivers */
USBHCDRegisterDrivers(0, usbHCDDriverList, numHostClassDrivers);
/* Open an instance of the MSC host driver */
object->MSCInstance = USBHMSCDriveOpen(0, USBMSCHFatFsTiva_cbMSCHandler);
if (!(object->MSCInstance)) {
Log_print0(Diags_USER1,"USBMSCHFatFs: Error initializing the MSC Host");
USBMSCHFatFsTiva_close(handle);
return (NULL);
}
/* Create the Hwi object to service interrupts */
Hwi_Params_init(&(paramsUnion.hwiParams));
paramsUnion.hwiParams.priority = hwAttrs->intPriority;
Hwi_construct(&(object->hwi), hwAttrs->intNum, USBMSCHFatFsTiva_hwiHandler,
&(paramsUnion.hwiParams), NULL);
/* Initialize USB power configuration */
USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);
/* Enable the USB stack */
USBHCDInit(0, object->memPoolHCD, HCDMEMORYPOOLSIZE);
/* RTOS primitives */
Semaphore_Params_init(&(paramsUnion.semParams));
paramsUnion.semParams.mode = Semaphore_Mode_BINARY;
Semaphore_construct(&(object->semUSBConnected), 0, &(paramsUnion.semParams));
GateMutex_Params_init(&(paramsUnion.gateParams));
paramsUnion.gateParams.instance->name = "USB Library Access";
GateMutex_construct(&(object->gateUSBLibAccess), &(paramsUnion.gateParams));
paramsUnion.gateParams.instance->name = "USB Wait";
GateMutex_construct(&(object->gateUSBWait), &(paramsUnion.gateParams));
/*
* Note that serviceUSBHost() should not be run until the USB Stack has been
* initialized!!
*/
Task_Params_init(&(paramsUnion.taskParams));
/*
* If serviceTaskStackPtr is null, then Task_construct performs a
* Memory_alloc - requiring a Heap
*/
paramsUnion.taskParams.stack = params->serviceTaskStackPtr;
/*
* If service priority passed in is higher than what is configured by the
* Task module, then use the highest priority available.
*/
if (Task_numPriorities - 1 < params->servicePriority) {
paramsUnion.taskParams.priority = (Task_numPriorities - 1);
}
else {
paramsUnion.taskParams.priority = params->servicePriority;
}
/* If no stack size is passed in, then use the default task stack size */
if (params->serviceTaskStackSize) {
paramsUnion.taskParams.stackSize = params->serviceTaskStackSize;
}
else {
paramsUnion.taskParams.stackSize = Task_defaultStackSize;
}
Task_construct(&(object->taskHCDMain),USBMSCHFatFsTiva_serviceUSBHost,
&(paramsUnion.taskParams), NULL);
/* Register the new disk_*() functions */
dresult = disk_register(drv,
USBMSCHFatFsTiva_diskInitialize,
USBMSCHFatFsTiva_diskStatus,
USBMSCHFatFsTiva_diskRead,
USBMSCHFatFsTiva_diskWrite,
USBMSCHFatFsTiva_diskIOctl);
/* Check for drive errors */
if (dresult != RES_OK) {
Log_error0("USBMSCHFatFs: disk functions not registered");
USBMSCHFatFsTiva_close(handle);
return (NULL);
}
/* Mount the FatFs (this function does not access the SDCard yet...) */
fresult = f_mount(drv, &(object->filesystem));
if (fresult != FR_OK) {
Log_error1("USBMSCHFatFs: drive %d not mounted", drv);
USBMSCHFatFsTiva_close(handle);
return (NULL);
}
Log_print1(Diags_USER1, "USBMSCHFatFs: drive %d opened", drv);
return (handle);
}
/*
* ======== USBMSCHFatFsTiva_close ========
*/
void USBMSCHFatFsTiva_close(USBMSCHFatFs_Handle handle)
{
unsigned int key;
DRESULT dresult;
FRESULT fresult;
USBMSCHFatFsTiva_Object *object = handle->object;
/* Unmount the FatFs drive */
fresult = f_mount(object->driveNumber, NULL);
if (fresult != FR_OK) {
Log_print0(Diags_USER1, "USBMSCHFatFs: could not unmount FatFs volume");
}
/* Close USB Drive */
USBHMSCDriveClose(object->MSCInstance);
/* Unregister the disk_*() functions */
dresult = disk_unregister(object->driveNumber);
if (dresult != RES_OK) {
Log_print0(Diags_USER1, "USBMSCHFatFs: error unregistering disk "
"functions");
}
/* Delete the HCDMain service task*/
Task_destruct(&(object->taskHCDMain));
/* Delete the semaphore */
Semaphore_destruct(&(object->semUSBConnected));
/* Delete the gate */
GateMutex_destruct(&(object->gateUSBLibAccess));
/* Delete the gate */
GateMutex_destruct(&(object->gateUSBWait));
/* Delete the hwi */
Hwi_destruct(&(object->hwi));
Log_print1(Diags_USER1, "USBMSCHFatFs: drive %d closed",
object->driveNumber);
key = Hwi_disable();
object->driveNumber = DRIVE_NOT_MOUNTED;
Hwi_restore(key);
}
/*
* ======== USBMSCHFatFsTiva_control ========
* @pre Function assumes that the handle is not NULL
*/
int USBMSCHFatFsTiva_control(USBMSCHFatFs_Handle handle,
unsigned int cmd,
void *arg)
{
/* No implementation yet */
return (USBMSCHFatFs_CMD_RESERVED);
}
/*
* ======== USBMSCHFatFsTiva_waitForConnect ========
*/
bool USBMSCHFatFsTiva_waitForConnect(USBMSCHFatFs_Handle handle,
unsigned int timeout)
{
unsigned int key;
bool ret = true;
USBMSCHFatFsTiva_Object *object = handle->object;
/* Need exclusive access to prevent a race condition */
key = GateMutex_enter(GateMutex_handle(&(object->gateUSBWait)));
if (object->state == USBMSCHFatFsTiva_NO_DEVICE) {
if (!Semaphore_pend(Semaphore_handle(&(object->semUSBConnected)),
timeout)) {
ret = false;
}
}
GateMutex_leave(GateMutex_handle(&(object->gateUSBWait)), key);
return (ret);
}