From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- usblib/Makefile | 103 + usblib/ccs/.ccsproject | 9 + usblib/ccs/.cproject | 164 + usblib/ccs/.project | 185 + .../ccs/.settings/org.eclipse.cdt.codan.core.prefs | 3 + usblib/ccs/Debug/usblib.lib | Bin 0 -> 2044400 bytes usblib/ccs/macros.ini_initial | 1 + usblib/device/usbdaudio.c | 1510 +++++ usblib/device/usbdaudio.h | 382 ++ usblib/device/usbdbulk.c | 1562 +++++ usblib/device/usbdbulk.h | 300 + usblib/device/usbdcdc.c | 3043 ++++++++++ usblib/device/usbdcdc.h | 447 ++ usblib/device/usbdcdesc.c | 643 ++ usblib/device/usbdcomp.c | 1544 +++++ usblib/device/usbdcomp.h | 261 + usblib/device/usbdconfig.c | 555 ++ usblib/device/usbddfu-rt.c | 661 ++ usblib/device/usbddfu-rt.h | 184 + usblib/device/usbdenum.c | 3190 ++++++++++ usblib/device/usbdevice.h | 232 + usblib/device/usbdevicepriv.h | 248 + usblib/device/usbdhandler.c | 85 + usblib/device/usbdhid.c | 2502 ++++++++ usblib/device/usbdhid.h | 1102 ++++ usblib/device/usbdhidgamepad.c | 845 +++ usblib/device/usbdhidgamepad.h | 274 + usblib/device/usbdhidkeyb.c | 1321 ++++ usblib/device/usbdhidkeyb.h | 365 ++ usblib/device/usbdhidmouse.c | 1008 ++++ usblib/device/usbdhidmouse.h | 299 + usblib/device/usbdmsc.c | 2441 ++++++++ usblib/device/usbdmsc.h | 420 ++ usblib/ewarm/Exe/usblib.a | Bin 0 -> 1276396 bytes usblib/gcc/libusb.a | Bin 0 -> 183108 bytes usblib/host/usbhaudio.c | 1557 +++++ usblib/host/usbhaudio.h | 163 + usblib/host/usbhhid.c | 746 +++ usblib/host/usbhhid.h | 166 + usblib/host/usbhhidkeyboard.c | 752 +++ usblib/host/usbhhidkeyboard.h | 89 + usblib/host/usbhhidmouse.c | 452 ++ usblib/host/usbhhidmouse.h | 81 + usblib/host/usbhhub.c | 1522 +++++ usblib/host/usbhhub.h | 174 + usblib/host/usbhmsc.c | 756 +++ usblib/host/usbhmsc.h | 99 + usblib/host/usbhost.h | 295 + usblib/host/usbhostenum.c | 6284 ++++++++++++++++++++ usblib/host/usbhostpriv.h | 201 + usblib/host/usbhscsi.c | 777 +++ usblib/host/usbhscsi.h | 87 + usblib/readme.txt | 21 + usblib/rvmdk/usblib.lib | Bin 0 -> 3025636 bytes usblib/usb-ids.h | 57 + usblib/usbaudio.h | 718 +++ usblib/usbbuffer.c | 1192 ++++ usblib/usbcdc.h | 947 +++ usblib/usbdesc.c | 480 ++ usblib/usbdfu.h | 504 ++ usblib/usbdma.c | 1453 +++++ usblib/usbhid.h | 667 +++ usblib/usbkeyboardmap.c | 145 + usblib/usblib.ewp | 860 +++ usblib/usblib.h | 1951 ++++++ usblib/usblib.uvopt | 650 ++ usblib/usblib.uvproj | 555 ++ usblib/usblibpriv.h | 529 ++ usblib/usbmode.c | 1180 ++++ usblib/usbmsc.h | 444 ++ usblib/usbringbuf.c | 715 +++ usblib/usbtick.c | 219 + usblib/usbulpi.c | 176 + usblib/usbulpi.h | 40 + utils/cmdline.c | 193 + utils/cmdline.h | 137 + utils/cpu_usage.c | 206 + utils/cpu_usage.h | 57 + utils/flash_pb.c | 493 ++ utils/flash_pb.h | 58 + utils/fswrapper.c | 860 +++ utils/fswrapper.h | 142 + utils/isqrt.c | 118 + utils/isqrt.h | 55 + utils/locator.c | 342 ++ utils/locator.h | 61 + utils/lwiplib.c | 1399 +++++ utils/lwiplib.h | 121 + utils/ptpdlib.c | 56 + utils/ptpdlib.h | 55 + utils/random.c | 169 + utils/random.h | 56 + utils/ringbuf.c | 712 +++ utils/ringbuf.h | 105 + utils/scheduler.c | 310 + utils/scheduler.h | 140 + utils/sine.c | 126 + utils/sine.h | 85 + utils/smbus.c | 5173 ++++++++++++++++ utils/smbus.h | 463 ++ utils/softi2c.c | 1321 ++++ utils/softi2c.h | 195 + utils/softssi.c | 1297 ++++ utils/softssi.h | 280 + utils/softuart.c | 2591 ++++++++ utils/softuart.h | 375 ++ utils/speexlib.c | 377 ++ utils/speexlib.h | 63 + utils/spi_flash.c | 2484 ++++++++ utils/spi_flash.h | 166 + utils/swupdate.c | 355 ++ utils/swupdate.h | 66 + utils/tftp.c | 710 +++ utils/tftp.h | 215 + utils/uartstdio.c | 1720 ++++++ utils/uartstdio.h | 86 + utils/ustdlib.c | 1826 ++++++ utils/ustdlib.h | 82 + utils/wavfile.c | 291 + utils/wavfile.h | 97 + 120 files changed, 79882 insertions(+) create mode 100644 usblib/Makefile create mode 100644 usblib/ccs/.ccsproject create mode 100644 usblib/ccs/.cproject create mode 100644 usblib/ccs/.project create mode 100644 usblib/ccs/.settings/org.eclipse.cdt.codan.core.prefs create mode 100644 usblib/ccs/Debug/usblib.lib create mode 100644 usblib/ccs/macros.ini_initial create mode 100644 usblib/device/usbdaudio.c create mode 100644 usblib/device/usbdaudio.h create mode 100644 usblib/device/usbdbulk.c create mode 100644 usblib/device/usbdbulk.h create mode 100644 usblib/device/usbdcdc.c create mode 100644 usblib/device/usbdcdc.h create mode 100644 usblib/device/usbdcdesc.c create mode 100644 usblib/device/usbdcomp.c create mode 100644 usblib/device/usbdcomp.h create mode 100644 usblib/device/usbdconfig.c create mode 100644 usblib/device/usbddfu-rt.c create mode 100644 usblib/device/usbddfu-rt.h create mode 100644 usblib/device/usbdenum.c create mode 100644 usblib/device/usbdevice.h create mode 100644 usblib/device/usbdevicepriv.h create mode 100644 usblib/device/usbdhandler.c create mode 100644 usblib/device/usbdhid.c create mode 100644 usblib/device/usbdhid.h create mode 100644 usblib/device/usbdhidgamepad.c create mode 100644 usblib/device/usbdhidgamepad.h create mode 100644 usblib/device/usbdhidkeyb.c create mode 100644 usblib/device/usbdhidkeyb.h create mode 100644 usblib/device/usbdhidmouse.c create mode 100644 usblib/device/usbdhidmouse.h create mode 100644 usblib/device/usbdmsc.c create mode 100644 usblib/device/usbdmsc.h create mode 100644 usblib/ewarm/Exe/usblib.a create mode 100644 usblib/gcc/libusb.a create mode 100644 usblib/host/usbhaudio.c create mode 100644 usblib/host/usbhaudio.h create mode 100644 usblib/host/usbhhid.c create mode 100644 usblib/host/usbhhid.h create mode 100644 usblib/host/usbhhidkeyboard.c create mode 100644 usblib/host/usbhhidkeyboard.h create mode 100644 usblib/host/usbhhidmouse.c create mode 100644 usblib/host/usbhhidmouse.h create mode 100644 usblib/host/usbhhub.c create mode 100644 usblib/host/usbhhub.h create mode 100644 usblib/host/usbhmsc.c create mode 100644 usblib/host/usbhmsc.h create mode 100644 usblib/host/usbhost.h create mode 100644 usblib/host/usbhostenum.c create mode 100644 usblib/host/usbhostpriv.h create mode 100644 usblib/host/usbhscsi.c create mode 100644 usblib/host/usbhscsi.h create mode 100644 usblib/readme.txt create mode 100644 usblib/rvmdk/usblib.lib create mode 100644 usblib/usb-ids.h create mode 100644 usblib/usbaudio.h create mode 100644 usblib/usbbuffer.c create mode 100644 usblib/usbcdc.h create mode 100644 usblib/usbdesc.c create mode 100644 usblib/usbdfu.h create mode 100644 usblib/usbdma.c create mode 100644 usblib/usbhid.h create mode 100644 usblib/usbkeyboardmap.c create mode 100644 usblib/usblib.ewp create mode 100644 usblib/usblib.h create mode 100644 usblib/usblib.uvopt create mode 100644 usblib/usblib.uvproj create mode 100644 usblib/usblibpriv.h create mode 100644 usblib/usbmode.c create mode 100644 usblib/usbmsc.h create mode 100644 usblib/usbringbuf.c create mode 100644 usblib/usbtick.c create mode 100644 usblib/usbulpi.c create mode 100644 usblib/usbulpi.h create mode 100644 utils/cmdline.c create mode 100644 utils/cmdline.h create mode 100644 utils/cpu_usage.c create mode 100644 utils/cpu_usage.h create mode 100644 utils/flash_pb.c create mode 100644 utils/flash_pb.h create mode 100644 utils/fswrapper.c create mode 100644 utils/fswrapper.h create mode 100644 utils/isqrt.c create mode 100644 utils/isqrt.h create mode 100644 utils/locator.c create mode 100644 utils/locator.h create mode 100644 utils/lwiplib.c create mode 100644 utils/lwiplib.h create mode 100644 utils/ptpdlib.c create mode 100644 utils/ptpdlib.h create mode 100644 utils/random.c create mode 100644 utils/random.h create mode 100644 utils/ringbuf.c create mode 100644 utils/ringbuf.h create mode 100644 utils/scheduler.c create mode 100644 utils/scheduler.h create mode 100644 utils/sine.c create mode 100644 utils/sine.h create mode 100644 utils/smbus.c create mode 100644 utils/smbus.h create mode 100644 utils/softi2c.c create mode 100644 utils/softi2c.h create mode 100644 utils/softssi.c create mode 100644 utils/softssi.h create mode 100644 utils/softuart.c create mode 100644 utils/softuart.h create mode 100644 utils/speexlib.c create mode 100644 utils/speexlib.h create mode 100644 utils/spi_flash.c create mode 100644 utils/spi_flash.h create mode 100644 utils/swupdate.c create mode 100644 utils/swupdate.h create mode 100644 utils/tftp.c create mode 100644 utils/tftp.h create mode 100644 utils/uartstdio.c create mode 100644 utils/uartstdio.h create mode 100644 utils/ustdlib.c create mode 100644 utils/ustdlib.h create mode 100644 utils/wavfile.c create mode 100644 utils/wavfile.h diff --git a/usblib/Makefile b/usblib/Makefile new file mode 100644 index 0000000..afd50ba --- /dev/null +++ b/usblib/Makefile @@ -0,0 +1,103 @@ +#****************************************************************************** +# +# Makefile - Rules for building the USB library. +# +# 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. +# +#****************************************************************************** + +# +# The base directory for TivaWare. +# +ROOT=.. + +# +# Include the common make definitions. +# +include ${ROOT}/makedefs + +# +# Where to find source files that do not live in this directory. +# +VPATH=./device +VPATH+=./host + +# +# Where to find header files that do not live in the source directory. +# +IPATH=.. + +# +# The default rule, which causes the USB library to be built. +# +all: ${COMPILER} +all: ${COMPILER}/libusb.a + +# +# The rule to clean out all the build products. +# +clean: + @rm -rf ${COMPILER} ${wildcard *~} + +# +# The rule to create the target directory. +# +${COMPILER}: + @mkdir -p ${COMPILER} + +# +# Rules for building the USB library. +# +${COMPILER}/libusb.a: ${COMPILER}/usbbuffer.o +${COMPILER}/libusb.a: ${COMPILER}/usbdaudio.o +${COMPILER}/libusb.a: ${COMPILER}/usbdbulk.o +${COMPILER}/libusb.a: ${COMPILER}/usbdcdc.o +${COMPILER}/libusb.a: ${COMPILER}/usbdcdesc.o +${COMPILER}/libusb.a: ${COMPILER}/usbdcomp.o +${COMPILER}/libusb.a: ${COMPILER}/usbdconfig.o +${COMPILER}/libusb.a: ${COMPILER}/usbddfu-rt.o +${COMPILER}/libusb.a: ${COMPILER}/usbdenum.o +${COMPILER}/libusb.a: ${COMPILER}/usbdesc.o +${COMPILER}/libusb.a: ${COMPILER}/usbdhandler.o +${COMPILER}/libusb.a: ${COMPILER}/usbdhid.o +${COMPILER}/libusb.a: ${COMPILER}/usbdhidgamepad.o +${COMPILER}/libusb.a: ${COMPILER}/usbdhidkeyb.o +${COMPILER}/libusb.a: ${COMPILER}/usbdhidmouse.o +${COMPILER}/libusb.a: ${COMPILER}/usbdma.o +${COMPILER}/libusb.a: ${COMPILER}/usbdmsc.o +${COMPILER}/libusb.a: ${COMPILER}/usbhaudio.o +${COMPILER}/libusb.a: ${COMPILER}/usbhhid.o +${COMPILER}/libusb.a: ${COMPILER}/usbhhidkeyboard.o +${COMPILER}/libusb.a: ${COMPILER}/usbhhidmouse.o +${COMPILER}/libusb.a: ${COMPILER}/usbhhub.o +${COMPILER}/libusb.a: ${COMPILER}/usbhmsc.o +${COMPILER}/libusb.a: ${COMPILER}/usbhostenum.o +${COMPILER}/libusb.a: ${COMPILER}/usbhscsi.o +${COMPILER}/libusb.a: ${COMPILER}/usbkeyboardmap.o +${COMPILER}/libusb.a: ${COMPILER}/usbmode.o +${COMPILER}/libusb.a: ${COMPILER}/usbringbuf.o +${COMPILER}/libusb.a: ${COMPILER}/usbtick.o +${COMPILER}/libusb.a: ${COMPILER}/usbulpi.o + +# +# Include the automatically generated dependency files. +# +ifneq (${MAKECMDGOALS},clean) +-include ${wildcard ${COMPILER}/*.d} __dummy__ +endif diff --git a/usblib/ccs/.ccsproject b/usblib/ccs/.ccsproject new file mode 100644 index 0000000..b4eaafa --- /dev/null +++ b/usblib/ccs/.ccsproject @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/usblib/ccs/.cproject b/usblib/ccs/.cproject new file mode 100644 index 0000000..d5a180a --- /dev/null +++ b/usblib/ccs/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usblib/ccs/.project b/usblib/ccs/.project new file mode 100644 index 0000000..d9015db --- /dev/null +++ b/usblib/ccs/.project @@ -0,0 +1,185 @@ + + + usblib + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.ti.ccstudio.core.ccsNature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + usbbuffer.c + 1 + SW_ROOT/usblib/usbbuffer.c + + + usbdesc.c + 1 + SW_ROOT/usblib/usbdesc.c + + + usbdma.c + 1 + SW_ROOT/usblib/usbdma.c + + + usbkeyboardmap.c + 1 + SW_ROOT/usblib/usbkeyboardmap.c + + + usbmode.c + 1 + SW_ROOT/usblib/usbmode.c + + + usbringbuf.c + 1 + SW_ROOT/usblib/usbringbuf.c + + + usbtick.c + 1 + SW_ROOT/usblib/usbtick.c + + + usbulpi.c + 1 + SW_ROOT/usblib/usbulpi.c + + + device/usbdaudio.c + 1 + SW_ROOT/usblib/device/usbdaudio.c + + + device/usbdbulk.c + 1 + SW_ROOT/usblib/device/usbdbulk.c + + + device/usbdcdc.c + 1 + SW_ROOT/usblib/device/usbdcdc.c + + + device/usbdcdesc.c + 1 + SW_ROOT/usblib/device/usbdcdesc.c + + + device/usbdcomp.c + 1 + SW_ROOT/usblib/device/usbdcomp.c + + + device/usbdconfig.c + 1 + SW_ROOT/usblib/device/usbdconfig.c + + + device/usbddfu-rt.c + 1 + SW_ROOT/usblib/device/usbddfu-rt.c + + + device/usbdenum.c + 1 + SW_ROOT/usblib/device/usbdenum.c + + + device/usbdhandler.c + 1 + SW_ROOT/usblib/device/usbdhandler.c + + + device/usbdhid.c + 1 + SW_ROOT/usblib/device/usbdhid.c + + + device/usbdhidgamepad.c + 1 + SW_ROOT/usblib/device/usbdhidgamepad.c + + + device/usbdhidkeyb.c + 1 + SW_ROOT/usblib/device/usbdhidkeyb.c + + + device/usbdhidmouse.c + 1 + SW_ROOT/usblib/device/usbdhidmouse.c + + + device/usbdmsc.c + 1 + SW_ROOT/usblib/device/usbdmsc.c + + + host/usbhaudio.c + 1 + SW_ROOT/usblib/host/usbhaudio.c + + + host/usbhhid.c + 1 + SW_ROOT/usblib/host/usbhhid.c + + + host/usbhhidkeyboard.c + 1 + SW_ROOT/usblib/host/usbhhidkeyboard.c + + + host/usbhhidmouse.c + 1 + SW_ROOT/usblib/host/usbhhidmouse.c + + + host/usbhhub.c + 1 + SW_ROOT/usblib/host/usbhhub.c + + + host/usbhmsc.c + 1 + SW_ROOT/usblib/host/usbhmsc.c + + + host/usbhostenum.c + 1 + SW_ROOT/usblib/host/usbhostenum.c + + + host/usbhscsi.c + 1 + SW_ROOT/usblib/host/usbhscsi.c + + + + + SW_ROOT + $%7BPARENT-2-PROJECT_LOC%7D + + + diff --git a/usblib/ccs/.settings/org.eclipse.cdt.codan.core.prefs b/usblib/ccs/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000..98b6350 --- /dev/null +++ b/usblib/ccs/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +inEditor=false +onBuild=false diff --git a/usblib/ccs/Debug/usblib.lib b/usblib/ccs/Debug/usblib.lib new file mode 100644 index 0000000..95c0b66 Binary files /dev/null and b/usblib/ccs/Debug/usblib.lib differ diff --git a/usblib/ccs/macros.ini_initial b/usblib/ccs/macros.ini_initial new file mode 100644 index 0000000..08b716d --- /dev/null +++ b/usblib/ccs/macros.ini_initial @@ -0,0 +1 @@ +SW_ROOT = ../.. diff --git a/usblib/device/usbdaudio.c b/usblib/device/usbdaudio.c new file mode 100644 index 0000000..18c90b5 --- /dev/null +++ b/usblib/device/usbdaudio.c @@ -0,0 +1,1510 @@ +//***************************************************************************** +// +// usbdaudio.c - USB audio device class driver. +// +// Copyright (c) 2009-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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbaudio.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdaudio.h" + +//***************************************************************************** +// +//! \addtogroup audio_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The following are the USB audio descriptor identifiers. +// +//***************************************************************************** +#define AUDIO_IN_TERMINAL_ID 1 +#define AUDIO_OUT_TERMINAL_ID 2 +#define AUDIO_CONTROL_ID 3 + +//***************************************************************************** +// +// The following are the USB interface numbers for this audio device. +// +//***************************************************************************** +#define AUDIO_INTERFACE_CONTROL 0 +#define AUDIO_INTERFACE_OUTPUT 1 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define ISOC_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// Max size is (48000 samples/sec * 4 bytes/sample) * 0.001 seconds/frame. +// +//***************************************************************************** +#define ISOC_OUT_EP_MAX_SIZE ((48000*4)/1000) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +static uint8_t g_pui8AudioDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + 0, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + 0, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during USBDAudioInit). + USBShort(0), // Product ID (filled in during USBDAudioInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Audio class device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +static uint8_t g_pui8AudioDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(32), // The total size of this full structure. + 2, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 0, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_BUS_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +uint8_t g_pui8IADAudioDescriptor[AUDIODESCRIPTOR_SIZE] = +{ + + 8, // Size of the interface descriptor. + USB_DTYPE_INTERFACE_ASC, // Interface Association Type. + 0x0, // Default starting interface is 0. + 0x2, // Number of interfaces in this association. + USB_CLASS_AUDIO, // The device class for this association. + USB_SUBCLASS_UNDEFINED, // The device subclass for this association. + USB_PROTOCOL_UNDEFINED, // The protocol for this association. + 0 // The string index for this association. +}; + +const tConfigSection g_sIADAudioConfigSection = +{ + sizeof(g_pui8IADAudioDescriptor), + g_pui8IADAudioDescriptor +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const uint8_t g_pui8AudioControlInterface[CONTROLINTERFACE_SIZE] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + AUDIO_INTERFACE_CONTROL, // The index for this interface. + 0, // The alternate setting for this interface. + 0, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_CONTROL, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Audio Header Descriptor. + // + 9, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_HEADER, // Descriptor sub-type is HEADER. + USBShort(0x0100), // Audio Device Class Specification Release + // Number in Binary-Coded Decimal. + // Total number of bytes in + // g_pui8AudioControlInterface + USBShort((9 + 9 + 12 + 13 + 9)), + 1, // Number of streaming interfaces. + 1, // Index of the first and only streaming + // interface. + + // + // Audio Input Terminal Descriptor. + // + 12, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_IN_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL. + AUDIO_IN_TERMINAL_ID, // Terminal ID for this interface. + // USB streaming interface. + USBShort(USB_TTYPE_STREAMING), + 0, // ID of the Output Terminal to which this + // Input Terminal is associated. + 2, // Number of logical output channels in the + // Terminal's output audio channel cluster. + USBShort((USB_CHANNEL_L | // Describes the spatial location of the + USB_CHANNEL_R)), // logical channels. + 0, // Channel Name string index. + 0, // Terminal Name string index. + + // + // Audio Feature Unit Descriptor + // + 13, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_FEATURE_UNIT, // Descriptor sub-type is FEATURE_UNIT. + AUDIO_CONTROL_ID, // Unit ID for this interface. + AUDIO_IN_TERMINAL_ID, // ID of the Unit or Terminal to which this + // Feature Unit is connected. + 2, // Size in bytes of an element of the + // bmaControls() array that follows. + // Master Mute control. + USBShort(USB_ACONTROL_MUTE), + // Left channel volume control. + USBShort(USB_ACONTROL_VOLUME), + // Right channel volume control. + USBShort(USB_ACONTROL_VOLUME), + 0, // Feature unit string index. + + // + // Audio Output Terminal Descriptor. + // + 9, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_OUT_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL. + AUDIO_OUT_TERMINAL_ID, // Terminal ID for this interface. + // Output type is a generic speaker. + USBShort(USB_ATTYPE_SPEAKER), + AUDIO_IN_TERMINAL_ID, // ID of the input terminal to which this + // output terminal is connected. + AUDIO_CONTROL_ID, // ID of the feature unit that this output + // terminal is connected to. + 0, // Output terminal string index. + +}; + +//***************************************************************************** +// +// The audio streaming interface descriptor. This describes the two valid +// interfaces for this class. The first interface has no endpoints and is used +// by host operating systems to put the device in idle mode, while the second +// is used when the audio device is active. +// +//***************************************************************************** +const uint8_t g_pui8AudioStreamInterface[STREAMINTERFACE_SIZE] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + AUDIO_INTERFACE_OUTPUT, // The index for this interface. + 0, // The alternate setting for this interface. + 0, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_STREAMING, // The interface sub-class. + 0, // Unused must be 0. + 0, // The string index for this interface. + + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 1, // The index for this interface. + 1, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_STREAMING, // The interface sub-class. + 0, // Unused must be 0. + 0, // The string index for this interface. + + // + // Class specific Audio Streaming Interface descriptor. + // + 7, // Size of the interface descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ASDSTYPE_GENERAL, // General information. + AUDIO_IN_TERMINAL_ID, // ID of the terminal to which this streaming + // interface is connected. + 1, // One frame delay. + USBShort(USB_ADF_PCM), // + + // + // Format type Audio Streaming descriptor. + // + 11, // Size of the interface descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ASDSTYPE_FORMAT_TYPE, // Audio Streaming format type. + USB_AF_TYPE_TYPE_I, // Type I audio format type. + 2, // Two audio channels. + 2, // Two bytes per audio sub-frame. + 16, // 16 bits per sample. + 1, // One sample rate provided. + USB3Byte(48000), // Only 48000 sample rate supported. + + // + // Endpoint Descriptor + // + 9, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + // OUT endpoint with address + // ISOC_OUT_ENDPOINT. + USB_EP_DESC_OUT | USBEPToIndex(ISOC_OUT_ENDPOINT), + USB_EP_ATTR_ISOC | // Endpoint is an adaptive isochronous data + USB_EP_ATTR_ISOC_ADAPT | // endpoint. + USB_EP_ATTR_USAGE_DATA, + USBShort(ISOC_OUT_EP_MAX_SIZE), // The maximum packet size. + 1, // The polling interval for this endpoint. + 0, // Refresh is unused. + 0, // Synch endpoint address. + + // + // Audio Streaming Isochronous Audio Data Endpoint Descriptor + // + 7, // The size of the descriptor. + USB_ACSDT_ENDPOINT, // Audio Class Specific Endpoint + // Descriptor. + USB_ASDSTYPE_GENERAL, // This is a general descriptor. + USB_EP_ATTR_ACG_SAMPLING, // Sampling frequency is supported. + USB_EP_LOCKDELAY_UNDEF, // Undefined lock delay units. + USBShort(0), // No lock delay. +}; + +//***************************************************************************** +// +// The audio device configuration descriptor is defined as three sections, +// one containing just the 9 byte USB configuration descriptor. The second +// holds the audio streaming interface and the third holds the audio control +// interface. +// +//***************************************************************************** +const tConfigSection g_sAudioConfigSection = +{ + sizeof(g_pui8AudioDescriptor), + g_pui8AudioDescriptor +}; + +const tConfigSection g_sAudioStreamInterfaceSection = +{ + sizeof(g_pui8AudioStreamInterface), + g_pui8AudioStreamInterface +}; + +const tConfigSection g_sAudioControlInterfaceSection = +{ + sizeof(g_pui8AudioControlInterface), + g_pui8AudioControlInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete audio device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psAudioSections[] = +{ + &g_sAudioConfigSection, + &g_sIADAudioConfigSection, + &g_sAudioControlInterfaceSection, + &g_sAudioStreamInterfaceSection +}; + +#define NUM_AUDIO_SECTIONS (sizeof(g_psAudioSections) / \ + sizeof(g_psAudioSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sAudioConfigHeader = +{ + NUM_AUDIO_SECTIONS, + g_psAudioSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppAudioConfigDescriptors[] = +{ + &g_sAudioConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvAudioDevice); +static void InterfaceChange(void *pvAudioDevice, uint8_t ui8Interface, + uint8_t ui8AlternateSetting); +static void ConfigChangeHandler(void *pvAudioDevice, uint32_t ui32Value); +static void DataReceived(void *pvAudioDevice, uint32_t ui32Info); +static void HandleEndpoints(void *pvAudioDevice, uint32_t ui32Status); +static void HandleRequests(void *pvAudioDevice, tUSBRequest *psUSBRequest); +static void HandleDevice(void *pvAudioDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB Audio device. +// +//***************************************************************************** +static const tCustomHandlers g_sAudioHandlers = +{ + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice +}; + +//***************************************************************************** +// +// This function is called to handle data being received back from the host so +// that the application callback can be called when the new data is ready. +// +//***************************************************************************** +static void +DataReceived(void *pvAudioDevice, uint32_t ui32Info) +{ + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // Create the instance pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = &psAudioDevice->sPrivateData; + + // + // If there is an update pending and the request was to set a current + // value then check which value was set. + // + if(psInst->ui16Update && (psInst->ui8Request == USB_AC_SET_CUR)) + { + // + // Only handling interface requests. + // + if((psInst->ui16RequestType & USB_RTYPE_RECIPIENT_M) == + USB_RTYPE_INTERFACE) + { + if(psInst->ui16Update == VOLUME_CONTROL) + { + // + // Inform the callback of the new volume. + // + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_VOLUME, + psInst->i16Volume, 0); + } + else if(psAudioDevice->sPrivateData.ui16Update == MUTE_CONTROL) + { + // + // Inform the callback of the new data. + // + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_MUTE, + psInst->ui8Mute, 0); + } + } + psInst->ui16Update = 0; + } +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the isochronous endpoint +// for the audio device class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvAudioDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus; + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + uint32_t ui32Size; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // Read out the current endpoint status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // See if there is a receive interrupt pending. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Get the amount of data available in the FIFO. + // + ui32Size = USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Configure the next DMA transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8OUTDMA, + psInst->sBuffer.pvData, ui32Size); + } + else if((USBLibDMAChannelStatus(psInst->psDMAInstance, + psInst->ui8OUTDMA) == + USBLIBSTATUS_DMA_COMPLETE)) + { + USBEndpointDMADisable(USB0_BASE, + psInst->ui8OUTEndpoint, USB_EP_DEV_OUT); + + // + // Acknowledge that the data was read, this will not cause a bus + // acknowledgment. + // + MAP_USBDevEndpointDataAck(USB0_BASE, psInst->ui8OUTEndpoint, 0); + + // + // Inform the callback of the new data. + // + psInst->sBuffer.pfnCallback(psInst->sBuffer.pvData, + psInst->sBuffer.ui32Size, + USBD_AUDIO_EVENT_DATAOUT); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvAudioDevice, uint32_t ui32Request, void *pvRequestData) +{ + tAudioInstance *psInst; + uint8_t *pui8Data; + tUSBDAudioDevice *psAudioDevice; + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB CDC + // serial class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pui8Data[0] == AUDIO_INTERFACE_CONTROL) + { + psInst->ui8InterfaceControl = pui8Data[1]; + } + else if(pui8Data[0] == AUDIO_INTERFACE_OUTPUT) + { + psInst->ui8InterfaceAudio = pui8Data[1]; + } + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if((pui8Data[0] & USB_EP_DESC_IN) == 0) + { + // + // Extract the new endpoint number without the DIR bit. + // + psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f); + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psInst->ui8OUTDMA != 0) + { + USBLibDMAChannelRelease(psInst->psDMAInstance, + psInst->ui8OUTDMA); + } + + // + // Allocate a DMA channel to the endpoint. + // + psInst->ui8OUTDMA = + USBLibDMAChannelAllocate(psInst->psDMAInstance, + psInst->ui8OUTEndpoint, + ISOC_OUT_EP_MAX_SIZE, + (USB_DMA_EP_RX | + USB_DMA_EP_TYPE_ISOC | + USB_DMA_EP_DEVICE)); + + // + // Set the DMA individual transfer size. + // + USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 32); + + // + // Set the DMA arbitration size. + // + USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 16); + } + break; + } + + // + // Handle class specific reconfiguring of the configuration descriptor + // once the composite class has built the full descriptor. + // + case USB_EVENT_COMP_CONFIG: + { + // + // This sets the bFirstInterface of the Interface Association + // descriptor to the first interface which is the control + // interface used by this instance. + // + pui8Data[2] = psInst->ui8InterfaceControl; + + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psAudioDevice->pfnCallback) + { + // + // Pass the LPM resume event to the client. + // + psAudioDevice->pfnCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psAudioDevice->pfnCallback) + { + // + // Pass the LPM sleep event to the client. + // + psAudioDevice->pfnCallback(0, USB_EVENT_LPM_SLEEP, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psAudioDevice->pfnCallback) + { + // + // Pass the LPM error event to the client. + // + psAudioDevice->pfnCallback(0, USB_EVENT_LPM_ERROR, 0, + (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvAudioDevice) +{ + const tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (const tUSBDAudioDevice *)pvAudioDevice; + + // + // Inform the application that the device has been disconnected. + // + psAudioDevice->pfnCallback(0, USB_EVENT_DISCONNECTED, 0, 0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// interface changes. This occurs when the audio device transitions between +// being active and inactive. Interface AUDIO_INTERFACE_CONTROL is the +// inactive interface that has no endpoints, while interface +// AUDIO_INTERFACE_AUDIO has the single Isochronous OUT endpoint. +// +//***************************************************************************** +static void +InterfaceChange(void *pvAudioDevice, uint8_t ui8Interface, + uint8_t ui8AlternateSetting) +{ + const tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (const tUSBDAudioDevice *)pvAudioDevice; + + // + // Check which interface to change into. + // + if(ui8AlternateSetting == 0) + { + // + // Alternate setting 0 is an inactive state. + // + if(psAudioDevice->pfnCallback) + { + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_IDLE, 0, 0); + } + } + else + { + // + // Alternate setting 1 is the active state. + // + if(psAudioDevice->pfnCallback) + { + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_ACTIVE, 0, 0); + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvAudioDevice, uint32_t ui32Value) +{ + const tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (const tUSBDAudioDevice *)pvAudioDevice; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psAudioDevice->pfnCallback) + { + // + // Pass the connected event to the client. + // + psAudioDevice->pfnCallback(pvAudioDevice, USB_EVENT_CONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psAudioDevice points to a structure containing parameters +//! customizing the operation of the audio device. +//! +//! In order for an application to initialize the USB audio device class, it +//! must first call this function with the a valid audio device class structure +//! in the \e psAudioDevice parameter. This allows this function to initialize +//! the USB controller and device code to be prepared to enumerate and function +//! as a USB audio device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the audio class. +//! +//! See the documentation on the tUSBDAudioDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return Returns 0 on failure or a non-zero void pointer on success. +// +//***************************************************************************** +void * +USBDAudioInit(uint32_t ui32Index, tUSBDAudioDevice *psAudioDevice) +{ + tConfigDescriptor *psConfigDesc; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psAudioDevice); + ASSERT(psAudioDevice->ppui8StringDescriptors); + + // + // Composite Init handles all initialization that is not specific to a + // multiple instance device. + // + USBDAudioCompositeInit(ui32Index, psAudioDevice, 0); + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8AudioDeviceDescriptor; + psDevDesc->idVendor = psAudioDevice->ui16VID; + psDevDesc->idProduct = psAudioDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psConfigDesc = (tConfigDescriptor *)g_pui8AudioDescriptor; + psConfigDesc->bmAttributes = psAudioDevice->ui8PwrAttributes; + psConfigDesc->bMaxPower = (uint8_t)(psAudioDevice->ui16MaxPowermA / 2); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psAudioDevice->sPrivateData.sDevInfo, + (void *)psAudioDevice); + + // + // Configure the DMA for the OUT endpoint. + // + psAudioDevice->sPrivateData.ui8OUTDMA = + USBLibDMAChannelAllocate(psAudioDevice->sPrivateData.psDMAInstance, + psAudioDevice->sPrivateData.ui8OUTEndpoint, + ISOC_OUT_EP_MAX_SIZE, + USB_DMA_EP_RX | USB_DMA_EP_TYPE_ISOC | + USB_DMA_EP_DEVICE); + + USBLibDMAUnitSizeSet(psAudioDevice->sPrivateData.psDMAInstance, + psAudioDevice->sPrivateData.ui8OUTDMA, 32); + USBLibDMAArbSizeSet(psAudioDevice->sPrivateData.psDMAInstance, + psAudioDevice->sPrivateData.ui8OUTDMA, 16); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psAudioDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psAudioDevice points to a structure containing parameters +//! customizing the operation of the audio device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! In order for an application to initialize the USB audio device class, it +//! must first call this function with the a valid audio device class structure +//! in the \e psAudioDevice parameter. This allows this function to initialize +//! the USB controller and device code to be prepared to enumerate and function +//! as a USB audio device. When this audio device is part of a composite +//! device, then the \e psCompEntry should point to the composite device entry +//! to initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the audio class. +//! +//! See the documentation on the tUSBDAudioDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB audio APIs. +// +//***************************************************************************** +void * +USBDAudioCompositeInit(uint32_t ui32Index, tUSBDAudioDevice *psAudioDevice, + tCompositeEntry *psCompEntry) +{ + tAudioInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psAudioDevice); + ASSERT(psAudioDevice->ppui8StringDescriptors); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psAudioDevice->sPrivateData; + psInst->ui32USBBase = USB0_BASE; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psAudioDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sAudioHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8AudioDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppAudioConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Initialize the device info structure for the HID device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // The Control interface is at index 0. + // + psInst->ui8InterfaceControl = AUDIO_INTERFACE_CONTROL; + + // + // The Audio interface is at index 1. + // + psInst->ui8InterfaceAudio = AUDIO_INTERFACE_OUTPUT; + + // + // Set the default Isochronous OUT endpoint. + // + psInst->ui8OUTEndpoint = ISOC_OUT_ENDPOINT; + psInst->ui8OUTDMA = 0; + + // + // Set the initial buffer to null. + // + psInst->sBuffer.pvData = 0; + + // + // Save the volume settings. + // + psInst->i16VolumeMax = psAudioDevice->i16VolumeMax; + psInst->i16VolumeMin = psAudioDevice->i16VolumeMin; + psInst->i16VolumeStep = psAudioDevice->i16VolumeStep; + + // + // No update pending to any command. + // + psInst->ui16Update = 0; + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psAudioDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psAudioDevice->ui32NumStringDescriptors; + + // + // Get the DMA instance pointer. + // + psInst->psDMAInstance = USBLibDMAInit(0); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psAudioDevice); +} + +//***************************************************************************** +// +//! Shuts down the audio device. +//! +//! \param pvAudioDevice is the pointer to the device instance structure as +//! returned by USBDAudioInit(). +//! +//! This function terminates audio interface for the instance supplied. This +//! function should not be called if the audio device is part of a composite +//! device and instead the USBDCompositeTerm() function should be called for +//! the full composite device. +//! Following this call, the \e pvAudioDevice instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDAudioTerm(void *pvAudioDevice) +{ + ASSERT(pvAudioDevice != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvAudioDevice is the instance data for this request. +// \param psUSBRequest points to the request received. +// +// This call parses the provided request structure to the type of request and +// will respond to all commands that are understood by the class. +// +// \return None. +// +//***************************************************************************** +static void +HandleRequests(void *pvAudioDevice, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Control, ui32Recipient, ui32Stall; + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // Make sure to acknowledge that the data was read, this will not send and + // ACK that has already been done at this point. This just tells the + // hardware that the data was read. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Don't stall by default. + // + ui32Stall = 0; + + // + // Get the request type. + // + ui32Recipient = psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M; + + // + // Save the request type and request value. + // + psInst->ui16RequestType = psUSBRequest->bmRequestType; + psInst->ui8Request = psUSBRequest->bRequest; + + // + // Check if this is an endpoint request to the audio streaming endpoint. + // + if((ui32Recipient == USB_RTYPE_ENDPOINT) && + (psUSBRequest->wIndex == USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Determine the type of request. + // + switch(psInst->ui8Request) + { + case USB_AC_SET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(psUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Retrieve the requested sample rate. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->ui32SampleRate, + 3); + + // + // Save what we are updating. + // + psInst->ui16Update = SAMPLING_FREQ_CONTROL; + } + break; + } + case USB_AC_GET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(psUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Send back the current sample rate. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->ui32SampleRate, + 3); + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + break; + } + } + } + else if(ui32Recipient == USB_RTYPE_INTERFACE) + { + // + // Make sure the request was for the control interface. + // + if((uint8_t)psUSBRequest->wIndex != psInst->ui8InterfaceControl) + { + return; + } + + // + // Extract the control value from the message. + // + ui32Control = psUSBRequest->wValue & USB_CS_CONTROL_M; + + // + // Handle an audio control request to the feature control unit. + // + if((AUDIO_CONTROL_ID << 8) == + (psUSBRequest->wIndex & USB_CS_CONTROL_M)) + { + // + // Determine the type of request. + // + switch(psInst->ui8Request) + { + case USB_AC_GET_MAX: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Return the maximum volume setting. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16VolumeMax, + 2); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_GET_MIN: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Return the minimum volume setting. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16VolumeMin, + 2); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_GET_RES: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Return the volume step setting. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16VolumeStep, + 2); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_GET_CUR: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Send back the current volume level. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16Volume, + 2); + } + else if(ui32Control == MUTE_CONTROL) + { + // + // Send back the current mute value. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->ui8Mute, 1); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_SET_CUR: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Read the new volume level. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->i16Volume, + 2); + + // + // Save what we are updating. + // + psInst->ui16Update = VOLUME_CONTROL; + } + else if(ui32Control == MUTE_CONTROL) + { + // + // Read the new mute setting. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->ui8Mute, + 1); + + // + // Save what we are updating. + // + psInst->ui16Update = MUTE_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_SET_RES: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Read the new volume step setting. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->i16VolumeStep, 2); + + // + // Save what we are updating. + // + psInst->ui16Update = VOLUME_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + break; + } + } + } + } + + // + // Stall on all unknown commands. + // + if(ui32Stall) + { + USBDCDStallEP0(0); + } +} + +//***************************************************************************** +// +//! This function is used to supply buffers to the audio class to be filled +//! from the USB host device. +//! +//! \param pvAudioDevice is the pointer to the device instance structure as +//! returned by USBDAudioInit() or USBDAudioCompositeInit(). +//! \param pvBuffer is a pointer to the buffer to fill with audio data. +//! \param ui32Size is the size in bytes of the buffer pointed to by the +//! \e pvBuffer +//! parameter. +//! \param pfnCallback is a callback that will provide notification when this +//! buffer has valid data. +//! +//! This function fills the buffer pointed to by the \e pvBuffer parameter with +//! at most \e ui32Size one packet of data from the host controller. The +//! \e ui32Size has a minimum value of \b ISOC_OUT_EP_MAX_SIZE since each USB +//! packet can be at most \b ISOC_OUT_EP_MAX_SIZE bytes in size. Since the +//! audio data may not be received in amounts that evenly fit in the buffer +//! provided, the buffer may not be completely filled. The \e pfnCallback +//! function will provide the amount of valid data that was actually stored in +//! the buffer provided. The function will return zero if the buffer could be +//! scheduled to be filled, otherwise the function will return a non-zero value +//! if there was some reason that the buffer could not be added. +//! +//! \return Returns 0 to indicate success any other value indicates that the +//! buffer will not be filled. +// +//***************************************************************************** +int32_t +USBAudioBufferOut(void *pvAudioDevice, void *pvBuffer, uint32_t ui32Size, + tUSBAudioBufferCallback pfnCallback) +{ + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + + // + // Make sure we were not passed NULL pointers. + // + ASSERT(pvAudioDevice != 0); + ASSERT(pvBuffer != 0); + + // + // Buffer must be at least one packet in size. + // + ASSERT(ui32Size >= ISOC_OUT_EP_MAX_SIZE); + ASSERT(pfnCallback); + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // Initialize the buffer instance. + // + psInst->sBuffer.pvData = pvBuffer; + psInst->sBuffer.ui32Size = ui32Size; + psInst->sBuffer.ui32NumBytes = 0; + psInst->sBuffer.pfnCallback = pfnCallback; + + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/device/usbdaudio.h b/usblib/device/usbdaudio.h new file mode 100644 index 0000000..e934914 --- /dev/null +++ b/usblib/device/usbdaudio.h @@ -0,0 +1,382 @@ +//***************************************************************************** +// +// usbdaudio.h - USB audio device class driver. +// +// Copyright (c) 2009-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. +// +//***************************************************************************** + +#ifndef __USBDAUDIO_H__ +#define __USBDAUDIO_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup audio_device_class_api +//! @{ +// +//***************************************************************************** + +typedef void (* tUSBAudioBufferCallback)(void *pvBuffer, uint32_t ui32Param, + uint32_t ui32Event); + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// audio device class. The memory for this structure is pointed to by +// the pi16PrivateData field in the tUSBDAudioDevice structure passed on +// USBDAudioInit() and should not be modified by any code outside of the audio +// device. +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The maximum volume expressed as an 8.8 signed value. + // + int16_t i16VolumeMax; + + // + // The minimum volume expressed as an 8.8 signed value. + // + int16_t i16VolumeMin; + + // + // The minimum volume step expressed as an 8.8 signed value. + // + int16_t i16VolumeStep; + + struct + { + // + // Pointer to a buffer provided by caller. + // + void *pvData; + + // + // Size of the data area provided in pvData in bytes. + // + uint32_t ui32Size; + + // + // Number of valid bytes copied into the pvData area. + // + uint32_t ui32NumBytes; + + // + // The buffer callback for this function. + // + tUSBAudioBufferCallback pfnCallback; + } + sBuffer; + + // + // Pending request type. + // + uint16_t ui16RequestType; + + // + // Pending request. + // + uint8_t ui8Request; + + // + // Pending update value. + // + uint16_t ui16Update; + + // + // Current Volume setting. + // + int16_t i16Volume; + + // + // Current Mute setting. + // + uint8_t ui8Mute; + + // + // Current Sample rate, this is not writable but the host will try. + // + uint32_t ui32SampleRate; + + // + // The OUT endpoint in use by this instance. + // + uint8_t ui8OUTEndpoint; + + // + // The OUT endpoint DMA channel in use by this instance. + // + uint8_t ui8OUTDMA; + + // + // The control interface number associated with this instance. + // + uint8_t ui8InterfaceControl; + + // + // The audio interface number associated with this instance. + // + uint8_t ui8InterfaceAudio; + + // + // A copy of the DMA instance data used with calls to USBLibDMA functions. + // + tUSBDMAInstance *psDMAInstance; +} +tAudioInstance; + +//***************************************************************************** +// +// This is the size of the g_pui8IADAudioDescriptor array in bytes. +// +//***************************************************************************** +#define AUDIODESCRIPTOR_SIZE (8) + +//***************************************************************************** +// +// This is the size of the g_pui8AudioControlInterface array in bytes. +// +//***************************************************************************** +#define CONTROLINTERFACE_SIZE (52) + +//***************************************************************************** +// +// This is the size of the g_pui8AudioStreamInterface array in bytes. +// +//***************************************************************************** +#define STREAMINTERFACE_SIZE (52) + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Audio Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +//***************************************************************************** +#define COMPOSITE_DAUDIO_SIZE (AUDIODESCRIPTOR_SIZE + \ + CONTROLINTERFACE_SIZE + STREAMINTERFACE_SIZE) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the device audio class. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! 8 byte vendor string. + // + const char pcVendor[8]; + + // + //! 16 byte vendor string. + // + const char pcProduct[16]; + + // + //! 4 byte vendor string. + // + const char pcVersion[4]; + + // + //! The maximum power consumption of the device, expressed in mA. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wake up. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the audio + //! device. + // + const tUSBCallback pfnCallback; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), Audio + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The maximum volume expressed as an 8.8 signed value. + // + const int16_t i16VolumeMax; + + // + //! The minimum volume expressed as an 8.8 signed value. + // + const int16_t i16VolumeMin; + + // + //! The minimum volume step expressed as an 8.8 signed value. + // + const int16_t i16VolumeStep; + + // + //! The private instance data for the audio device. + // + tAudioInstance sPrivateData; +} +tUSBDAudioDevice; + +//***************************************************************************** +// +// Audio specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This USB audio event indicates that the device is connected but not active. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_IDLE (USBD_AUDIO_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This USB audio event indicates that the device is connected and is now +//! active. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_ACTIVE (USBD_AUDIO_EVENT_BASE + 1) + +//***************************************************************************** +// +//! This USB audio event indicates that the device is returning a data buffer +//! provided by the USBAudioBufferOut() function back to the application with +//! valid audio data received from the USB host controller. The \e pvBuffer +//! parameter holds the pointer to the buffer with the new audio data and +//! the \e ui32Param value holds the amount of valid data in bytes that are +//! contained in the \e pvBuffer parameter. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_DATAOUT (USBD_AUDIO_EVENT_BASE + 2) + +//***************************************************************************** +// +//! This USB audio event indicates that a volume change has occurred. The +//! \e ui32Param value contains a signed 8.8 fixed point value that represents +//! the current volume gain/attenuation in decibels(dB). The provided message +//! handler should be prepared to handle negative and positive values with the +//! value 0x8000 indicating maximum attenuation. The \e pvBuffer parameter +//! should be ignored. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_VOLUME (USBD_AUDIO_EVENT_BASE + 4) + +//***************************************************************************** +// +//! This USB audio event indicates that a mute request has occurred. The +//! \e ui32Param value will either be a 1 to indicate that the audio is now +//! muted, and a value of 0 indicates that the audio has been unmuted. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_MUTE (USBD_AUDIO_EVENT_BASE + 5) + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDAudioInit(uint32_t ui32Index, + tUSBDAudioDevice *psAudioDevice); +extern void *USBDAudioCompositeInit(uint32_t ui32Index, + tUSBDAudioDevice *psAudioDevice, + tCompositeEntry *psCompEntry); +extern void USBDAudioTerm(void *pvAudioDevice); +extern int32_t USBAudioBufferOut(void *pvAudioDevice, void *pvBuffer, + uint32_t ui32Size, + tUSBAudioBufferCallback pfnCallback); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/usblib/device/usbdbulk.c b/usblib/device/usbdbulk.c new file mode 100644 index 0000000..1e4cf0b --- /dev/null +++ b/usblib/device/usbdbulk.c @@ -0,0 +1,1562 @@ +//***************************************************************************** +// +// usbdbulk.c - USB bulk device class 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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdbulk.h" +#include "usblib/device/usbdcomp.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup bulk_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// Flags that may appear in ui16DeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// Each deferred operation is defined as the bit number that should be set in +// tBulkInstance->ui16DeferredOpFlags to indicate that the operation is +// pending. +// +//***************************************************************************** +#define BULK_DO_PACKET_RX 5 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver.' +// +//***************************************************************************** +#define DATA_IN_ENDPOINT USB_EP_1 +#define DATA_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// Maximum packet size for the bulk endpoints used for bulk data +// transmission and reception and the associated FIFO sizes to set aside +// for each endpoint. +// +//***************************************************************************** +#define DATA_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define DATA_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 + +#define DATA_IN_EP_MAX_SIZE USBFIFOSizeToBytes(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USBFIFOSizeToBytes(DATA_OUT_EP_FIFO_SIZE) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +uint8_t g_pui8BulkDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_VEND_SPECIFIC, // USB Device Class + 0, // USB Device Sub-class + 0, // USB Device protocol + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Bulk device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +uint8_t g_pui8BulkDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(32), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const uint8_t g_pui8BulkInterface[BULKINTERFACE_SIZE] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this + // interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_VEND_SPECIFIC, // The interface class + 0, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The bulk configuration descriptor is defined as two sections, one +// containing just the 9 byte USB configuration descriptor and the other +// containing everything else that is sent to the host along with it. +// +//***************************************************************************** +const tConfigSection g_sBulkConfigSection = +{ + sizeof(g_pui8BulkDescriptor), + g_pui8BulkDescriptor +}; + +const tConfigSection g_sBulkInterfaceSection = +{ + sizeof(g_pui8BulkInterface), + g_pui8BulkInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete bulk device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psBulkSections[] = +{ + &g_sBulkConfigSection, + &g_sBulkInterfaceSection +}; + +#define NUM_BULK_SECTIONS (sizeof(g_psBulkSections) / \ + sizeof(g_psBulkSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sBulkConfigHeader = +{ + NUM_BULK_SECTIONS, + g_psBulkSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppBulkConfigDescriptors[] = +{ + &g_sBulkConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleConfigChange(void *pvBulkDevice, uint32_t ui32Info); +static void HandleDisconnect(void *pvBulkDevice); +static void HandleEndpoints(void *pvBulkDevice, uint32_t ui32Status); +static void HandleSuspend(void *pvBulkDevice); +static void HandleResume(void *pvBulkDevice); +static void HandleDevice(void *pvBulkDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// Device event handler callbacks. +// +//***************************************************************************** +const tCustomHandlers g_sBulkHandlers = +{ + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + 0, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + 0, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pui16DeferredOp points to the flags variable which is to be modified. +// \param ui16Bit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile uint16_t *pui16DeferredOp, uint16_t ui16Bit, + bool bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psBulkDevice is the device instance whose endpoint is to be +// processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the bulk OUT endpoint (in other words, whenever the +// host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +ProcessDataFromHost(tUSBDBulkDevice *psBulkDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus; + uint32_t ui32Size; + tBulkInstance *psInst; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Has a packet been received? + // + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client does not read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, BULK_DO_PACKET_RX, + true); + + // + // How big is the packet we have just received? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, + ui32Size, (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ui32EPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so allow the callback + // to handle it. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_ERROR, + (ui32EPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psBulkDevice is the device instance whose endpoint is to be +// processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the bulk IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +ProcessDataToHost(tUSBDBulkDevice *psBulkDevice, uint32_t ui32Status) +{ + tBulkInstance *psInst; + uint32_t ui32EPStatus, ui32Size; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8INEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, psInst->ui8INEndpoint, + ui32EPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->iBulkTxState = eBulkStateIdle; + + // + // Notify the client that the last transmission completed. + // + ui32Size = psInst->ui16LastTxSize; + psInst->ui16LastTxSize = 0; + psBulkDevice->pfnTxCallback(psBulkDevice->pvTxCBData, + USB_EVENT_TX_COMPLETE, ui32Size, (void *)0); + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ui32Status. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvBulkDevice, uint32_t ui32Status) +{ + tUSBDBulkDevice *psBulkDevice; + tBulkInstance *psInst; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Handler for the bulk OUT data endpoint. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(psBulkDevice, ui32Status); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) + { + ProcessDataToHost(psBulkDevice, ui32Status); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvBulkDevice, uint32_t ui32Info) +{ + tBulkInstance *psInst; + tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Set all our endpoints to idle state. + // + psInst->iBulkRxState = eBulkStateIdle; + psInst->iBulkTxState = eBulkStateIdle; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the connected event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvBulkDevice, uint32_t ui32Request, void *pvRequestData) +{ + tBulkInstance *psInst; + uint8_t *pui8Data; + tUSBDBulkDevice *psBulkDevice; + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB Bulk + // class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f); + } + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the LPM resume event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the LPM sleep event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_LPM_SLEEP, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the LPM error event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_LPM_ERROR, 0, (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvBulkDevice) +{ + tUSBDBulkDevice *psBulkDevice; + tBulkInstance *psInst; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psInst->bConnected) + { + // + // Pass the disconnected event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psInst->bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvBulkDevice) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvBulkDevice; + + // + // Pass the event on to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_SUSPEND, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvBulkDevice) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvBulkDevice; + + // + // Pass the event on to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_RESUME, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param ui32Index is the index of the USB controller for which this tick +// is being generated. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +BulkTickHandler(void *pvBulkDevice, uint32_t ui32TimemS) +{ + tBulkInstance *psInst; + uint32_t ui32Size; + tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Do we have a deferred receive waiting + // + if(psInst->ui16DeferredOpFlags & (1 << BULK_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psBulkDevice points to a structure containing parameters customizing +//! the operation of the bulk device. +//! +//! An application wishing to make use of a USB bulk communication channel +//! must call this function to initialize the USB controller and attach the +//! device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psBulkDevice +//! pointer passed to it. This must be passed on all future calls to the +//! device driver related to this device. +//! +//! The USBDBulk interface offers packet-based transmit and receive operation. +//! If the application would rather use block based communication with +//! transmit and receive buffers, USB buffers may be used above the bulk +//! transmit and receive channels to offer this functionality. +//! +//! Transmit Operation: +//! +//! Calls to USBDBulkPacketWrite() must send no more than 64 bytes of data at a +//! time and may only be made when no other transmission is currently +//! outstanding. +//! +//! Once a packet of data has been acknowledged by the USB host, a +//! \b USB_EVENT_TX_COMPLETE event is sent to the application callback to +//! inform it that another packet may be transmitted. +//! +//! Receive Operation: +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event \b USBD_EVENT_RX_AVAILABLE. The application must then +//! call USBDBulkPacketRead(), passing a buffer capable of holding 64 bytes, to +//! retrieve the data and acknowledge reception to the USB host. +//! +//! \note The application must not make any calls to the low level USB Device +//! API if interacting with USB via the USB bulk device class API. Doing so +//! will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or void pointer that should be used with +//! the remaining USB bulk class APSs. +// +//***************************************************************************** +void * +USBDBulkInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice) +{ + void *pvBulkDevice; + tDeviceDescriptor *psDevDesc; + tConfigDescriptor *psConfigDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psBulkDevice); + + pvBulkDevice = USBDBulkCompositeInit(ui32Index, psBulkDevice, 0); + + if(pvBulkDevice) + { + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8BulkDeviceDescriptor; + psDevDesc->idVendor = psBulkDevice->ui16VID; + psDevDesc->idProduct = psBulkDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psConfigDesc = (tConfigDescriptor *)g_pui8BulkDescriptor; + psConfigDesc->bmAttributes = psBulkDevice->ui8PwrAttributes; + psConfigDesc->bMaxPower = (uint8_t)(psBulkDevice->ui16MaxPowermA / 2); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psBulkDevice->sPrivateData.sDevInfo, + (void *)psBulkDevice); + } + + // + // Return the pointer to the instance indicating that everything went well. + // + return(pvBulkDevice); +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psBulkDevice points to a structure containing parameters customizing +//! the operation of the bulk device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! This call is very similar to USBDBulkInit() except that it is used for +//! initializing an instance of the bulk device for use in a composite device. +//! When this bulk device is part of a composite device, then the +//! \e psCompEntry should point to the composite device entry to initialize. +//! This is part of the array that is passed to the USBDCompositeInit() +//! function. +//! +//! \return Returns zero on failure or a non-zero value that should be +//! used with the remaining USB Bulk APIs. +// +//***************************************************************************** +void * +USBDBulkCompositeInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice, + tCompositeEntry *psCompEntry) +{ + tBulkInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psBulkDevice); + ASSERT(psBulkDevice->ppui8StringDescriptors); + ASSERT(psBulkDevice->pfnRxCallback); + ASSERT(psBulkDevice->pfnTxCallback); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psBulkDevice->sPrivateData; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psBulkDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sBulkHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8BulkDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppBulkConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Set the basic state information for the class. + // + psInst->ui32USBBase = USB0_BASE; + psInst->iBulkRxState = eBulkStateUnconfigured; + psInst->iBulkTxState = eBulkStateUnconfigured; + psInst->ui16DeferredOpFlags = 0; + psInst->bConnected = false; + + // + // Initialize the device info structure for the Bulk device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Set the default endpoint and interface assignments. + // + psInst->ui8INEndpoint = DATA_IN_ENDPOINT; + psInst->ui8OUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ui8Interface = 0; + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psBulkDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psBulkDevice->ui32NumStringDescriptors; + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(BulkTickHandler, (void *)psBulkDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psBulkDevice); +} + +//***************************************************************************** +// +//! Shut down the bulk device. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function terminates device operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the bulk device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvBulkDevice instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkTerm(void *pvBulkDevice) +{ + tBulkInstance *psInst; + + ASSERT(pvBulkDevice); + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase)); + + psInst->ui32USBBase = 0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDBulkInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the \e pvBulkDevice structure passed to USBDBulkInit() resides +//! in RAM. If this structure is in flash, callback pointer changes are not +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDBulkSetRxCBData(void *pvBulkDevice, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvBulkDevice); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvBulkDevice)->pvRxCBData; + ((tUSBDBulkDevice *)pvBulkDevice)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the transmit callback. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDBulkInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the \e pvBulkDevice structure passed to USBDBulkInit() resides +//! in RAM. If this structure is in flash, callback pointer changes are not +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDBulkSetTxCBData(void *pvBulkDevice, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvBulkDevice); + + // + // Set the callback pointer for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvBulkDevice)->pvTxCBData; + ((tUSBDBulkDevice *)pvBulkDevice)->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the bulk data interface. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pi8Data points to the first byte of data which is to be transmitted. +//! \param ui32Length is the number of bytes of data to transmit. +//! \param bLast indicates whether more data is to be written before a packet +//! should be scheduled for transmission. If \b true, the client will make +//! a further call to this function. If \b false, no further call will be +//! made and the driver should schedule transmission of a short packet. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB packet. If no transmission is currently ongoing, +//! the data is immediately copied to the relevant USB endpoint FIFO for +//! transmission. Whenever a USB packet is acknowledged by the host, a +//! \b USB_EVENT_TX_COMPLETE event will be sent to the transmit channel +//! callback indicating that more data can now be transmitted. +//! +//! The maximum value for \e ui32Length is 64 bytes (the maximum USB packet +//! size for the bulk endpoints in use by the device). Attempts to send more +//! data than this will result in a return code of 0 indicating that the data +//! cannot be sent. +//! +//! The \e bLast parameter allows a client to make multiple calls to this +//! function before scheduling transmission of the packet to the host. This +//! can be helpful if, for example, constructing a packet on the fly or +//! writing a packet which spans the wrap point in a ring buffer. +//! +//! \return Returns the number of bytes actually sent. At this level, this +//! will either be the number of bytes passed (if less than or equal to the +//! maximum packet size for the USB endpoint in use and no outstanding +//! transmission ongoing) or 0 to indicate a failure. +// +//***************************************************************************** +uint32_t +USBDBulkPacketWrite(void *pvBulkDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + tBulkInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvBulkDevice); + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Can we send the data provided? + // + if((ui32Length > DATA_IN_EP_MAX_SIZE) || + (psInst->iBulkTxState != eBulkStateIdle)) + { + // + // Either the packet was too big or we are in the middle of sending + // another packet. Return 0 to indicate that we can't send this data. + // + return(0); + } + + // + // Copy the data into the USB endpoint FIFO. + // + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8INEndpoint, + pi8Data, ui32Length); + + // + // Did we copy the data successfully? + // + if(i32Retcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->ui16LastTxSize += (uint16_t)ui32Length; + + // + // If this is the last call for this packet, schedule transmission. + // + if(bLast) + { + // + // Send the packet to the host if we have received all the data we + // can expect for this packet. + // + psInst->iBulkTxState = eBulkStateWaitData; + i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8INEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(i32Retcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ui32Length); + } + else + { + // + // Yes - tell the caller we could not send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the bulk data +//! interface. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pi8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by pi8Data. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ui32Length bytes of data received from the USB +//! host into the supplied application buffer. If the driver detects that the +//! entire packet has been read, it is acknowledged to the host. +//! +//! The \e bLast parameter is ignored in this implementation since the end of +//! a packet can be determined without relying upon the client to provide +//! this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +uint32_t +USBDBulkPacketRead(void *pvBulkDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + uint32_t ui32EPStatus, ui32Count, ui32Pkt; + tBulkInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvBulkDevice); + + // + // Get our instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Get as much data as we can. + // + ui32Count = ui32Length; + i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + pi8Data, &ui32Count); + + // + // Did we read the last of the packet data? + // + if(ui32Count == ui32Pkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, BULK_DO_PACKET_RX, + false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(i32Retcode != -1) + { + return(ui32Count); + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function returns the maximum number of bytes that can be passed on a +//! call to USBDBulkPacketWrite() and accepted for transmission. The value +//! returned will be the maximum USB packet size (64) if no transmission is +//! currently outstanding or 0 if a transmission is in progress. +//! +//! \return Returns the number of bytes available in the transmit buffer. +// +//***************************************************************************** +uint32_t +USBDBulkTxPacketAvailable(void *pvBulkDevice) +{ + tBulkInstance *psInst; + + ASSERT(pvBulkDevice); + + // + // Get our instance data pointer. + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->iBulkTxState != eBulkStateIdle) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(DATA_IN_EP_MAX_SIZE); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +uint32_t +USBDBulkRxPacketAvailable(void *pvBulkDevice) +{ + uint32_t ui32EPStatus, ui32Size; + tBulkInstance *psInst; + + ASSERT(pvBulkDevice); + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + return(ui32Size); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvBulkDevice is the pointer to the bulk device instance structure. +//! \param ui8Power indicates the current power status, either +//! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkPowerStatusSet(void *pvBulkDevice, uint8_t ui8Power) +{ + ASSERT(pvBulkDevice); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvBulkDevice is the pointer to the bulk device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +bool +USBDBulkRemoteWakeupRequest(void *pvBulkDevice) +{ + ASSERT(pvBulkDevice); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdbulk.h b/usblib/device/usbdbulk.h new file mode 100644 index 0000000..0f6887d --- /dev/null +++ b/usblib/device/usbdbulk.h @@ -0,0 +1,300 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for a generic bulk device. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBDBULK_H__ +#define __USBDBULK_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup bulk_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB Bulk example code and are here only to help with the application +// allocating the correct amount of memory for the Bulk example device code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Not configured. + // + eBulkStateUnconfigured, + + // + // No outstanding transaction remains to be completed. + // + eBulkStateIdle, + + // + // Waiting on completion of a send or receive transaction. + // + eBulkStateWaitData, + + // + // Waiting for client to process data. + // + eBulkStateWaitClient +} +tBulkState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// Bulk only example device. The memory for this structure is inlcluded in +// the sPrivateData field in the tUSBDBulkDevice structure passed on +// USBDBulkInit(). +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The state of the bulk receive channel. + // + volatile tBulkState iBulkRxState; + + // + // The state of the bulk transmit channel. + // + volatile tBulkState iBulkTxState; + + // + // State of any pending operations that could not be handled immediately + // upon receipt. + // + volatile uint16_t ui16DeferredOpFlags; + + // + // Size of the last transmit. + // + uint16_t ui16LastTxSize; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8INEndpoint; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8OUTEndpoint; + + // + // The bulk class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; +} +tBulkInstance; + +//***************************************************************************** +// +// This is the size of the g_pui8BulkInterface array in bytes. +// +//***************************************************************************** +#define BULKINTERFACE_SIZE (23) + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Bulk Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +//***************************************************************************** +#define COMPOSITE_DBULK_SIZE (BULKINTERFACE_SIZE) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the bulk device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data receive channel. + // + const tUSBCallback pfnRxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the receive channel callback, + //! pfnRxCallback. + // + void *pvRxCBData; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data transmit + //! channel. + // + const tUSBCallback pfnTxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the transmit channel callback, + //! pfnTxCallback. + // + void *pvTxCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain pointers to the following string descriptors in this + //! order. Language descriptor, Manufacturer name string (language 1), + //! Product name string (language 1), Serial number string (language 1), + //! Interface description string (language 1) and Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the strings for indices 1 through 5 + //! must be repeated for each of the other languages defined in the + //! language descriptor. + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors array. + //! This must be 1 + (5 * number of supported languages). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The private instance data for this device. This memory must + //! not be modified by any code outside the bulk class driver. + // + tBulkInstance sPrivateData; +} +tUSBDBulkDevice; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDBulkInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice); +extern void *USBDBulkCompositeInit(uint32_t ui32Index, + tUSBDBulkDevice *psBulkDevice, + tCompositeEntry *psCompEntry); +extern void USBDBulkTerm(void *pvBulkInstance); +extern void *USBDBulkSetRxCBData(void *pvBulkInstance, void *pvCBData); +extern void *USBDBulkSetTxCBData(void *pvBulkInstance, void *pvCBData); +extern uint32_t USBDBulkPacketWrite(void *pvBulkInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDBulkPacketRead(void *pvBulkInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDBulkTxPacketAvailable(void *pvBulkInstance); +extern uint32_t USBDBulkRxPacketAvailable(void *pvBulkInstance); +extern bool USBDBulkRemoteWakeupRequest(void *pvBulkInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED +extern void USBDBulkPowerStatusSet(void *pvBulkInstance, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDBULK_H__ diff --git a/usblib/device/usbdcdc.c b/usblib/device/usbdcdc.c new file mode 100644 index 0000000..a4dc73a --- /dev/null +++ b/usblib/device/usbdcdc.c @@ -0,0 +1,3043 @@ +//***************************************************************************** +// +// usbdcdc.c - USB CDC ACM (serial) device class 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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usbcdc.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdcomp.h" +#include "usblib/device/usbdcdc.h" + +//***************************************************************************** +// +//! \addtogroup cdc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Some assumptions and deviations from the CDC specification +// ---------------------------------------------------------- +// +// 1. Although the CDC specification indicates that the following requests +// should be supported by ACM CDC devices, these don't seem relevant to a +// virtual COM port implementation and are never seen when connecting to a +// Windows host and running either Hyperterminal or TeraTerm. As a result, +// this implementation does not support them and stalls endpoint 0 if they are +// received. +// - SEND_ENCAPSULATED_COMMAND +// - GET_ENCAPSULATED_RESPONSE +// - SET_COMM_FEATURE +// - GET_COMM_FEATURE +// - CLEAR_COMM_FEATURE +// +// 2. The CDC specification is very clear on the fact that an ACM device +// should offer two interfaces - a control interface offering an interrupt IN +// endpoint and a data interface offering bulk IN and OUT endpoints. Using +// this descriptor configuration, however, Windows insists on enumerating the +// device as two separate entities resulting in two virtual COM ports or one +// COM port and an Unknown Device (depending upon INF contents) appearing +// in Device Manager. This implementation, derived by experimentation and +// examination of other virtual COM and CDC solutions, uses only a single +// interface combining all three endpoints. This appears to satisfy +// Windows2000, XP and Vista and operates as intended using the Hyperterminal +// and TeraTerm terminal emulators. Your mileage may vary with other +// (untested) operating systems! +// +//***************************************************************************** + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// Size of the buffer to hold request-specific data read from the host. This +// must be sized to accommodate the largest request structure that we intend +// processing. +// +//***************************************************************************** +#define MAX_REQUEST_DATA_SIZE sizeof(tLineCoding) + +//***************************************************************************** +// +// Flags that may appear in ui16DeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// +//***************************************************************************** +#define CDC_DO_SERIAL_STATE_CHANGE \ + 0 +#define CDC_DO_SEND_BREAK 1 +#define CDC_DO_CLEAR_BREAK 2 +#define CDC_DO_LINE_CODING_CHANGE \ + 3 +#define CDC_DO_LINE_STATE_CHANGE \ + 4 +#define CDC_DO_PACKET_RX 5 + +//***************************************************************************** +// +// The subset of deferred operations which result in the receive channel +// being blocked. +// +//***************************************************************************** +#define RX_BLOCK_OPS ((1 << CDC_DO_SEND_BREAK) | \ + (1 << CDC_DO_LINE_CODING_CHANGE) | \ + (1 << CDC_DO_LINE_STATE_CHANGE)) + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define CONTROL_ENDPOINT USB_EP_1 +#define DATA_IN_ENDPOINT USB_EP_2 +#define DATA_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// The following are the USB interface numbers for the CDC serial device. +// +//***************************************************************************** +#define SERIAL_INTERFACE_CONTROL \ + 0 +#define SERIAL_INTERFACE_DATA 1 + +//***************************************************************************** +// +// Maximum packet size for the bulk endpoints used for serial data +// transmission and reception and the associated FIFO sizes to set aside +// for each endpoint. +// +//***************************************************************************** +#define DATA_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define DATA_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define CTL_IN_EP_FIFO_SIZE USB_FIFO_SZ_16 + +#define DATA_IN_EP_MAX_SIZE USBFIFOSizeToBytes(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USBFIFOSizeToBytes(DATA_IN_EP_FIFO_SIZE) +#define CTL_IN_EP_MAX_SIZE USBFIFOSizeToBytes(CTL_IN_EP_FIFO_SIZE) + +//***************************************************************************** +// +// The collection of serial state flags indicating character errors. +// +//***************************************************************************** +#define USB_CDC_SERIAL_ERRORS (USB_CDC_SERIAL_STATE_OVERRUN | \ + USB_CDC_SERIAL_STATE_PARITY | \ + USB_CDC_SERIAL_STATE_FRAMING) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +uint8_t g_pui8CDCSerDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts + // assume high-speed - see USB 2.0 spec + // 9.2.6.6) + USB_CLASS_CDC, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + USB_CDC_PROTOCOL_NONE, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during + // USBDCDCInit). + USBShort(0), // Product ID (filled in during + // USBDCDCInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// CDC Serial configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +uint8_t g_pui8CDCSerDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(9), // The total size of this full structure, + // this will be patched so it is just set + // to the size of this structure. + 2, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // The string identifier that describes + // this configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake + // up. + 250, // The maximum power in 2mA increments. +}; + +const tConfigSection g_sCDCSerConfigSection = +{ + sizeof(g_pui8CDCSerDescriptor), + g_pui8CDCSerDescriptor +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +uint8_t g_pui8IADSerDescriptor[SERDESCRIPTOR_SIZE] = +{ + + 8, // Size of the interface descriptor. + USB_DTYPE_INTERFACE_ASC, // Interface Association Type. + 0x0, // Default starting interface is 0. + 0x2, // Number of interfaces in this + // association. + USB_CLASS_CDC, // The device class for this association. + USB_CDC_SUBCLASS_ABSTRACT_MODEL, + // The device subclass for this + // association. + USB_CDC_PROTOCOL_V25TER, // The protocol for this association. + 0 // The string index for this association. +}; + +const tConfigSection g_sIADSerConfigSection = +{ + sizeof(g_pui8IADSerDescriptor), + g_pui8IADSerDescriptor +}; + +//***************************************************************************** +// +// This is the control interface for the serial device. +// +//***************************************************************************** +const uint8_t g_pui8CDCSerCommInterface[SERCOMMINTERFACE_SIZE] = +{ + // + // Communication Class Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + SERIAL_INTERFACE_CONTROL, // The index for this interface. + 0, // The alternate setting for this + // interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_CDC, // The interface class constant defined by + // USB-IF (spec 5.1.3). + USB_CDC_SUBCLASS_ABSTRACT_MODEL, + // The interface sub-class constant + // defined by USB-IF (spec 5.1.3). + USB_CDC_PROTOCOL_V25TER, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. + + // + // Communication Class Interface Functional Descriptor - Header + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_HEADER, // Header functional descriptor + USBShort(0x110), // Complies with CDC version 1.1 + + // + // Communication Class Interface Functional Descriptor - ACM + // + 4, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_ABSTRACT_CTL_MGMT, + USB_CDC_ACM_SUPPORTS_LINE_PARAMS | USB_CDC_ACM_SUPPORTS_SEND_BREAK, + + // + // Communication Class Interface Functional Descriptor - Unions + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_UNION, + SERIAL_INTERFACE_CONTROL, + SERIAL_INTERFACE_DATA, // Data interface number + + // + // Communication Class Interface Functional Descriptor - Call Management + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_CALL_MGMT, + USB_CDC_CALL_MGMT_HANDLED, + SERIAL_INTERFACE_DATA, // Data interface number + + // + // Endpoint Descriptor (interrupt, IN) + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(CONTROL_ENDPOINT), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(CTL_IN_EP_MAX_SIZE), // The maximum packet size. + 1 // The polling interval for this endpoint. +}; + +const tConfigSection g_sCDCSerCommInterfaceSection = +{ + sizeof(g_pui8CDCSerCommInterface), + g_pui8CDCSerCommInterface +}; + +//***************************************************************************** +// +// This is the Data interface for the serial device. +// +//***************************************************************************** +const uint8_t g_pui8CDCSerDataInterface[SERDATAINTERFACE_SIZE] = +{ + // + // Communication Class Data Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + SERIAL_INTERFACE_DATA, // The index for this interface. + 0, // The alternate setting for this + // interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_CDC_DATA, // The interface class constant defined by + // USB-IF (spec 5.1.3). + 0, // The interface sub-class constant + // defined by USB-IF (spec 5.1.3). + USB_CDC_PROTOCOL_NONE, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +const tConfigSection g_sCDCSerDataInterfaceSection = +{ + sizeof(g_pui8CDCSerDataInterface), + g_pui8CDCSerDataInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete CDC ACM configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psCDCSerSections[] = +{ + &g_sCDCSerConfigSection, + &g_sCDCSerCommInterfaceSection, + &g_sCDCSerDataInterfaceSection, +}; + +#define NUM_CDCSER_SECTIONS (sizeof(g_psCDCSerSections) / \ + sizeof(g_psCDCSerSections[0])) + +//***************************************************************************** +// +// The header for the single configuration. This is the root of the data +// structure that defines all the bits and pieces that are pulled together to +// generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sCDCSerConfigHeader = +{ + NUM_CDCSER_SECTIONS, + g_psCDCSerSections +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete CDC ACM configuration descriptor used in composite devices. +// The only addition is the g_sIADSerConfigSection. +// +//***************************************************************************** +const tConfigSection *g_psCDCCompSerSections[] = +{ + &g_sCDCSerConfigSection, + &g_sIADSerConfigSection, + &g_sCDCSerCommInterfaceSection, + &g_sCDCSerDataInterfaceSection, +}; + +#define NUM_COMP_CDCSER_SECTIONS (sizeof(g_psCDCCompSerSections) / \ + sizeof(g_psCDCCompSerSections[0])) + +//***************************************************************************** +// +// The header for the composite configuration. This is the root of the data +// structure that defines all the bits and pieces that are pulled together to +// generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sCDCCompSerConfigHeader = +{ + NUM_COMP_CDCSER_SECTIONS, + g_psCDCCompSerSections +}; + +//***************************************************************************** +// +// Configuration Descriptor for the CDC serial class device. +// +//***************************************************************************** +const tConfigHeader * const g_ppCDCSerConfigDescriptors[] = +{ + &g_sCDCSerConfigHeader +}; + +//***************************************************************************** +// +// Configuration Descriptor for the CDC serial class device used in a composite +// device. +// +//***************************************************************************** +const tConfigHeader * const g_pCDCCompSerConfigDescriptors[] = +{ + &g_sCDCCompSerConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleRequests(void *pvCDCDevice, tUSBRequest *pUSBRequest); +static void HandleConfigChange(void *pvCDCDevice, uint32_t ui32Info); +static void HandleEP0Data(void *pvCDCDevice, uint32_t ui32DataSize); +static void HandleDisconnect(void *pvCDCDevice); +static void HandleEndpoints(void *pvCDCDevice, uint32_t ui32Status); +static void HandleSuspend(void *pvCDCDevice); +static void HandleResume(void *pvCDCDevice); +static void HandleDevice(void *pvCDCDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB serial device. +// +//***************************************************************************** +const tCustomHandlers g_sCDCHandlers = +{ + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + HandleEP0Data, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler. + // + HandleDevice +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pui16DeferredOp points to the flags variable which is to be modified. +// \param ui16Bit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile uint16_t *pui16DeferredOp, uint16_t ui16Bit, + bool bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Determines whether or not a client has consumed all received data previously +// passed to it. +// +//! \param psCDCDevice is the pointer to the device instance structure as returned +//! by USBDCDCInit(). +// +// This function is called to determine whether or not a device has consumed +// all data previously passed to it via its receive callback. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +DeviceConsumedAllData(const tUSBDCDCDevice *psCDCDevice) +{ + uint32_t ui32Remaining; + + // + // Send the device an event requesting that it tell us how many bytes + // of data it still has to process. + // + ui32Remaining = psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, + USB_EVENT_DATA_REMAINING, 0, (void *)0); + + // + // If any data remains to be processed, return false, else return true. + // + return(ui32Remaining ? false : true); +} + +//***************************************************************************** +// +// Notifies the client that it should set or clear a break condition. +// +// \param psCDCDevice is the pointer to the device instance structure as returned +// by USBDCDCInit(). +// \param bSend is \b true if a break condition is to be set or \b false if +// it is to be cleared. +// +// This function is called to instruct the client to start or stop sending a +// break condition on its serial transmit line. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static void +SendBreak(tUSBDCDCDevice *psCDCDevice, bool bSend) +{ + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Set the break state flags as necessary. If we are turning the break on, + // set the flag to tell ourselves that we need to notify the client when + // it is time to turn it off again. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_SEND_BREAK, false); + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_CLEAR_BREAK, bSend); + + // + // Tell the client to start or stop sending the break. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + (bSend ? USBD_CDC_EVENT_SEND_BREAK : + USBD_CDC_EVENT_CLEAR_BREAK), 0, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a host request to set the serial communication +// parameters. +// +// \param psCDCDevice is the device instance whose communication parameters are to +// be set. +// +// This function is called to notify the client when the host requests a change +// in the serial communication parameters (baud rate, parity, number of bits +// per character and number of stop bits) to use. +// +// \return None. +// +//***************************************************************************** +static void +SendLineCodingChange(tUSBDCDCDevice *psCDCDevice) +{ + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_LINE_CODING_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USBD_CDC_EVENT_SET_LINE_CODING, 0, + &(psInst->sLineCoding)); +} + +//***************************************************************************** +// +// Notifies the client of a host request to set the RTS and DTR handshake line +// states. +// +// \param psCDCDevice is the device instance whose break condition is to be set or +// cleared. +// +// This function is called to notify the client when the host requests a change +// in the state of one or other of the RTS and DTR handshake lines. +// +// \return None. +// +//***************************************************************************** +static void +SendLineStateChange(tUSBDCDCDevice *psCDCDevice) +{ + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_LINE_STATE_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USBD_CDC_EVENT_SET_CONTROL_LINE_STATE, + psInst->ui16ControlLineState, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a break request if no data remains to be processed. +// +// \param psCDCDevice is the device instance that is to be commanded to send a +// break condition. +// +// This function is called when the host requests that the device set a break +// condition on the serial transmit line. If no data received from the host +// remains to be processed, the break request is passed to the control +// callback. If data is outstanding, the call is ignored (with the operation +// being retried on the next timer tick). +// +// \return Returns \b true if the break notification was sent, \b false +// otherwise. +// +//***************************************************************************** +static bool +CheckAndSendBreak(tUSBDCDCDevice *psCDCDevice, uint16_t ui16Duration) +{ + bool bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psCDCDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the break request on to the client since no data remains to be + // consumed. + // + SendBreak(psCDCDevice, (ui16Duration ? true : false)); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a request to change the serial line parameters if no +// data remains to be processed. +// +// \param psCDCDevice is the device instance whose line coding parameters are to +// be changed. +// +// This function is called when the host requests that the device change the +// serial line coding parameters. If no data received from the host remains +// to be processed, the request is passed to the control callback. If data is +// outstanding, the call is ignored (with the operation being retried on the +// next timer tick). +// +// \return Returns \b true if the notification was sent, \b false otherwise. +// +//***************************************************************************** +static bool +CheckAndSendLineCodingChange(tUSBDCDCDevice *psCDCDevice) +{ + bool bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psCDCDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineCodingChange(psCDCDevice); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a request to change the handshake line states if no +// data remains to be processed. +// +// \param psCDCDevice is the device instance whose handshake line states are to +// be changed. +// +// This function is called when the host requests that the device change the +// state of one or other of the RTS or DTR handshake lines. If no data +// received from the host remains to be processed, the request is passed to +// the control callback. If data is outstanding, the call is ignored (with +// the operation being retried on the next timer tick). +// +// \return Returns \b true if the notification was sent, \b false otherwise. +// +//***************************************************************************** +static bool +CheckAndSendLineStateChange(tUSBDCDCDevice *psCDCDevice) +{ + bool bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psCDCDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineStateChange(psCDCDevice); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a change in the serial line state. +// +// \param psInst is the instance whose serial state is to be reported. +// +// This function is called to send the current serial state information to +// the host via the the interrupt IN endpoint. This notification informs the +// host of problems or conditions such as parity errors, breaks received, +// framing errors, etc. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +SendSerialState(tUSBDCDCDevice *psCDCDevice) +{ + tUSBRequest sRequest; + uint16_t ui16SerialState; + tCDCSerInstance *psInst; + int32_t i32Retcode; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Remember that we are in the middle of sending a notification. + // + psInst->iCDCInterruptState = eCDCStateWaitData; + + // + // Clear the flag we use to indicate that a send is required. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + false); + // + // Take a snapshot of the serial state. + // + ui16SerialState = psInst->ui16SerialState; + + // + // Build the request we will use to send the notification. + // + sRequest.bmRequestType = (USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE); + sRequest.bRequest = USB_CDC_NOTIFY_SERIAL_STATE; + sRequest.wValue = 0; + sRequest.wIndex = 0; + sRequest.wLength = USB_CDC_NOTIFY_SERIAL_STATE_SIZE; + + // + // Write the request structure to the USB FIFO. + // + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, + (uint8_t *)&sRequest, + sizeof(tUSBRequest)); + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, + (uint8_t *)&ui16SerialState, + USB_CDC_NOTIFY_SERIAL_STATE_SIZE); + + // + // Did we correctly write the data to the endpoint FIFO? + // + if(i32Retcode != -1) + { + // + // We put the data into the FIFO so now schedule it to be + // sent. + // + i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, + USB_TRANS_IN); + } + + // + // If an error occurred, mark the endpoint as idle (to prevent possible + // lockup) and return an error. + // + if(i32Retcode == -1) + { + psInst->iCDCInterruptState = eCDCStateIdle; + return(false); + } + else + { + // + // Everything went fine. Clear the error bits that we just notified + // and return true. + // + psInst->ui16SerialState &= ~(ui16SerialState & USB_CDC_SERIAL_ERRORS); + return(true); + } +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psCDCDevice is the device instance whose endpoint is to be processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the bulk OUT endpoint (in other words, whenever the +// host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +ProcessDataFromHost(tUSBDCDCDevice *psCDCDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus, ui32Size; + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + ui32EPStatus); + + // + // Has a packet been received? + // + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client doesn't read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be notified later during tick processing. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_PACKET_RX, + true); + + // + // Is the receive channel currently blocked? + // + if(!psInst->bControlBlocked && !psInst->bRxBlocked) + { + // + // How big is the packet we have just been received? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ui32EPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, USB_EVENT_ERROR, + (ui32EPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to interrupt messages sent to the host. +// +// \param psCDCDevice is the device instance whose endpoint is to be processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the interrupt IN endpoint (in other words, whenever a notification has +// been transmitted to the USB host). +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +ProcessNotificationToHost(tUSBDCDCDevice *psCDCDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus; + tCDCSerInstance *psInst; + bool bRetcode; + + // + // Assume all will go well until we have reason to believe otherwise. + // + bRetcode = true; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8ControlEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, ui32EPStatus); + + // + // Did the state change while we were waiting for the previous notification + // to complete? + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_SERIAL_STATE_CHANGE)) + { + // + // The state changed while we were waiting so we need to schedule + // another notification immediately. + // + bRetcode = SendSerialState(psCDCDevice); + } + else + { + // + // Our last notification completed and we did not have any new + // notifications to make so the interrupt channel is now idle again. + // + psInst->iCDCInterruptState = eCDCStateIdle; + } + + // + // Tell the caller how things went. + // + return(bRetcode); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psCDCDevice is the device instance whose endpoint is to be processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the bulk IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +ProcessDataToHost(tUSBDCDCDevice *psCDCDevice, uint32_t ui32Status) +{ + tCDCSerInstance *psInst; + uint32_t ui32EPStatus, ui32Size; + bool bSentFullPacket; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, ui32EPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->iCDCTxState = eCDCStateIdle; + + // + // If this notification is not as a result of sending a zero-length packet, + // call back to the client to let it know we sent the last thing it passed + // us. + // + if(psInst->ui16LastTxSize) + { + // + // Have we just sent a 64 byte packet? + // + bSentFullPacket = (psInst->ui16LastTxSize == DATA_IN_EP_MAX_SIZE) ? + true : false; + + // + // Notify the client that the last transmission completed. + // + ui32Size = (uint32_t)psInst->ui16LastTxSize; + psInst->ui16LastTxSize = 0; + psCDCDevice->pfnTxCallback(psCDCDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ui32Size, (void *)0); + + // + // If we had previously sent a full packet and the callback didn't + // schedule a new transmission, send a zero length packet to indicate + // the end of the transfer. + // + if(bSentFullPacket && !psInst->ui16LastTxSize) + { + // + // We can expect another transmit complete notification after doing + // this. + // + psInst->iCDCTxState = eCDCStateWaitData; + + // + // Send the zero-length packet. + // + MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, + USB_TRANS_IN); + } + } + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ui32Status. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvCDCDevice, uint32_t ui32Status) +{ + tUSBDCDCDevice *psCDCDeviceInst; + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ui32Index is different in both cases. + // + psCDCDeviceInst = pvCDCDevice; + psInst = &psCDCDeviceInst->sPrivateData; + + // + // Handler for the interrupt IN notification endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8ControlEndpoint))) + { + // + // We have sent an interrupt notification to the host. + // + ProcessNotificationToHost(psCDCDeviceInst, ui32Status); + } + + // + // Handler for the bulk OUT data endpoint. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8BulkOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(psCDCDeviceInst, ui32Status); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8BulkINEndpoint))) + { + ProcessDataToHost(psCDCDeviceInst, ui32Status); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvCDCDevice, uint32_t ui32Info) +{ + tCDCSerInstance *psInst; + tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Set all our endpoints to idle state. + // + psInst->iCDCInterruptState = eCDCStateIdle; + psInst->iCDCRequestState = eCDCStateIdle; + psInst->iCDCRxState = eCDCStateIdle; + psInst->iCDCTxState = eCDCStateIdle; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(!psInst->bConnected) + { + // + // Pass the connected event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// USB data received callback. +// +// This function is called by the USB stack whenever any data requested from +// EP0 is received. +// +//***************************************************************************** +static void +HandleEP0Data(void *pvCDCDevice, uint32_t ui32DataSize) +{ + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + bool bRetcode; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // If we were not passed any data, just return. + // + if(ui32DataSize == 0) + { + return; + } + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Make sure we are actually expecting something. + // + if(psInst->iCDCRequestState != eCDCStateWaitData) + { + return; + } + + // + // Process the data received. This will be a request-specific data + // block associated with the last request received. + // + switch (psInst->ui8PendingRequest) + { + // + // We just got the line coding structure. Make sure the client has + // read all outstanding data then pass it back to initiate a change + // in the line state. + // + case USB_CDC_SET_LINE_CODING: + { + if(ui32DataSize != sizeof(tLineCoding)) + { + USBDCDStallEP0(0); + } + else + { + // + // Set the flag telling us that we need to send a line coding + // notification to the client. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_LINE_CODING_CHANGE, true); + + // + // See if we can send the notification immediately. + // + bRetcode = CheckAndSendLineCodingChange(psCDCDevice); + + // + // If we could not send the line coding change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + } + break; + } + + // + // Oops - we seem to be waiting on a request which has not yet been + // coded here. Flag the error and stall EP0 anyway (even though + // this would indicate a coding error). + // + default: + { + USBDCDStallEP0(0); + ASSERT(0); + break; + } + } + + // + // All is well. Set the state back to IDLE. + // + psInst->iCDCRequestState = eCDCStateIdle; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvCDCDevice, uint32_t ui32Request, void *pvRequestData) +{ + tCDCSerInstance *psInst; + uint8_t *pui8Data; + tUSBDCDCDevice *psCDCDevice; + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB CDC + // serial class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pui8Data[0] == SERIAL_INTERFACE_CONTROL) + { + psInst->ui8InterfaceControl = pui8Data[1]; + } + else if(pui8Data[0] == SERIAL_INTERFACE_DATA) + { + psInst->ui8InterfaceData = pui8Data[1]; + } + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + // + // Determine which IN endpoint to modify. + // + if((pui8Data[0] & 0x7f) == USBEPToIndex(CONTROL_ENDPOINT)) + { + psInst->ui8ControlEndpoint = + IndexToUSBEP((pui8Data[1] & 0x7f)); + } + else + { + psInst->ui8BulkINEndpoint = + IndexToUSBEP((pui8Data[1] & 0x7f)); + } + } + else + { + // + // Extract the new endpoint number. + // + psInst->ui8BulkOUTEndpoint = + IndexToUSBEP(pui8Data[1] & 0x7f); + } + break; + } + + // + // Handle class specific reconfiguring of the configuration descriptor + // once the composite class has built the full descriptor. + // + case USB_EVENT_COMP_CONFIG: + { + // + // This sets the bFirstInterface of the Interface Association + // descriptor to the first interface which is the control + // interface used by this instance. + // + pui8Data[2] = psInst->ui8InterfaceControl; + + // + // This sets the bMasterInterface of the Union descriptor to the + // Control interface and the bSlaveInterface of the Union + // Descriptor to the Data interface used by this instance. + // + pui8Data[29] = psInst->ui8InterfaceControl; + pui8Data[30] = psInst->ui8InterfaceData; + + // + // This sets the bDataInterface of the Union descriptor to the + // Data interface used by this instance. + pui8Data[35] = psInst->ui8InterfaceData; + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psCDCDevice->pfnControlCallback) + { + // + // Pass the LPM resume event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psCDCDevice->pfnControlCallback) + { + // + // Pass the LPM sleep event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_LPM_SLEEP, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psCDCDevice->pfnControlCallback) + { + // + // Pass the LPM error event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_LPM_ERROR, 0, + (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// USB non-standard request callback. +// +// This function is called by the USB stack whenever any non-standard request +// is made to the device. The handler should process any requests that it +// supports or stall EP0 in any unsupported cases. +// +//***************************************************************************** +static void +HandleRequests(void *pvCDCDevice, tUSBRequest *pUSBRequest) +{ + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + bool bRetcode; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Only handle requests meant for this interface. + // + if(pUSBRequest->wIndex != psInst->ui8InterfaceControl) + { + return; + } + + // + // Handle each of the requests that we expect from the host. + // + switch(pUSBRequest->bRequest) + { + case USB_CDC_SEND_ENCAPSULATED_COMMAND: + { + // + // This implementation makes use of no communication protocol so + // this request is meaningless. We stall endpoint 0 if we receive + // it. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_GET_ENCAPSULATED_RESPONSE: + { + // + // This implementation makes use of no communication protocol so + // this request is meaningless. We stall endpoint 0 if we receive + // it. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_SET_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_GET_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_CLEAR_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + // + // Set the serial communication parameters. + // + case USB_CDC_SET_LINE_CODING: + { + // + // Remember the request we are processing. + // + psInst->ui8PendingRequest = USB_CDC_SET_LINE_CODING; + + // + // Set the state to indicate we are waiting for data. + // + psInst->iCDCRequestState = eCDCStateWaitData; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, (uint8_t *)&psInst->sLineCoding, + sizeof(tLineCoding)); + + // + // ACK what we have already received. We must do this after + // requesting the data or we get into a race condition where the + // data may return before we have set the stack state appropriately + // to receive it. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, false); + + break; + } + + // + // Return the serial communication parameters. + // + case USB_CDC_GET_LINE_CODING: + { + tLineCoding sLineCoding; + + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, false); + + // + // Ask the client for the current line coding. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USBD_CDC_EVENT_GET_LINE_CODING, 0, + &sLineCoding); + + // + // Send the line coding information back to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&sLineCoding, sizeof(tLineCoding)); + + break; + } + + case USB_CDC_SET_CONTROL_LINE_STATE: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, false); + + // + // Set the handshake lines as required. + // + psInst->ui16ControlLineState = pUSBRequest->wValue; + + // + // Remember that we are due to notify the client of a line + // state change. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_LINE_STATE_CHANGE, true); + + // + // See if we can notify now. + // + bRetcode = CheckAndSendLineStateChange(psCDCDevice); + + // + // If we could not send the line state change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + + break; + } + + case USB_CDC_SEND_BREAK: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, false); + + // + // Keep a copy of the requested break duration. + // + psInst->ui16BreakDuration = pUSBRequest->wValue; + + // + // Remember that we need to send a break request. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_SEND_BREAK, true); + + // + // Send the break request if all outstanding receive data has been + // processed. + // + bRetcode = CheckAndSendBreak(psCDCDevice, pUSBRequest->wValue); + + // + // If we could not send the line coding change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + + break; + } + + // + // These are valid CDC requests but not ones that an ACM device should + // receive. + // + case USB_CDC_SET_AUX_LINE_STATE: + case USB_CDC_SET_HOOK_STATE: + case USB_CDC_PULSE_SETUP: + case USB_CDC_SEND_PULSE: + case USB_CDC_SET_PULSE_TIME: + case USB_CDC_RING_AUX_JACK: + case USB_CDC_SET_RINGER_PARMS: + case USB_CDC_GET_RINGER_PARMS: + case USB_CDC_SET_OPERATION_PARMS: + case USB_CDC_GET_OPERATION_PARMS: + case USB_CDC_SET_LINE_PARMS: + case USB_CDC_GET_LINE_PARMS: + case USB_CDC_DIAL_DIGITS: + case USB_CDC_SET_UNIT_PARAMETER: + case USB_CDC_GET_UNIT_PARAMETER: + case USB_CDC_CLEAR_UNIT_PARAMETER: + case USB_CDC_GET_PROFILE: + case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + case USB_CDC_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + case USB_CDC_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + case USB_CDC_SET_ETHERNET_PACKET_FILTER: + case USB_CDC_GET_ETHERNET_STATISTIC: + case USB_CDC_SET_ATM_DATA_FORMAT: + case USB_CDC_GET_ATM_DEVICE_STATISTICS: + case USB_CDC_SET_ATM_DEFAULT_VC: + case USB_CDC_GET_ATM_VC_STATISTICS: + { + USBDCDStallEP0(0); + break; + } + + default: + { + // + // This request is not part of the CDC specification. + // + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvCDCDevice) +{ + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // If we are not currently connected and we have a control callback, + // let the client know we are open for business. + // + if(psInst->bConnected) + { + // + // Pass the disconnected event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psInst->bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvCDCDevice) +{ + const tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvCDCDevice; + + // + // Pass the event on to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_SUSPEND, 0, (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvCDCDevice) +{ + tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Pass the event on to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_RESUME, 0, (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param ui32Index is the index of the USB controller for which this tick +// is being generated. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +CDCTickHandler(void *pvCDCDevice, uint32_t ui32TimemS) +{ + bool bCanSend; + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + uint32_t ui32Size; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Is there any outstanding operation that we should try to perform? + // + if(psInst->ui16DeferredOpFlags) + { + // + // Yes - we have at least one deferred operation pending. First check + // to see if it is time to turn off a break condition. + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_CLEAR_BREAK)) + { + // + // Will our break timer expire this time? + // + if(psInst->ui16BreakDuration <= ui32TimemS) + { + // + // Yes - turn off the break condition. + // + SendBreak(psCDCDevice, false); + } + else + { + // + // We have not timed out yet. Decrement the break timer. + // + psInst->ui16BreakDuration -= (uint16_t)ui32TimemS; + } + } + + // Now check to see if the client has any data remaining to be + // processed. This information is needed by the remaining deferred + // operations which are waiting for the receive pipe to be emptied + // before they can be carried out. + // + bCanSend = DeviceConsumedAllData(psCDCDevice); + + // + // Has all outstanding data been consumed? + // + if(bCanSend) + { + // + // Yes - go ahead and notify the client of the various things + // it has been asked to do while we waited for data to be + // consumed. + // + + // + // Do we need to start sending a break condition? + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_SEND_BREAK)) + { + SendBreak(psCDCDevice, true); + } + + // + // Do we need to set the RTS/DTR states? + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_LINE_STATE_CHANGE)) + { + SendLineStateChange(psCDCDevice); + } + + // + // Do we need to change the line coding parameters? + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_LINE_CODING_CHANGE)) + { + SendLineCodingChange(psCDCDevice); + } + + // + // NOTE: We do not need to handle CDC_DO_SERIAL_STATE_CHANGE here + // since this is handled in the transmission complete notification + // for the control IN endpoint (ProcessNotificationToHost()). + // + + // + // If all the deferred operations which caused the receive channel + // to be blocked are now handled, we can unblock receive and handle + // any packet that is currently waiting to be received. + // + if(!(psInst->ui16DeferredOpFlags & RX_BLOCK_OPS)) + { + // + // We can remove the receive block. + // + psInst->bRxBlocked = false; + } + } + + // + // Is the receive channel unblocked? + // + if(!psInst->bRxBlocked) + { + // + // Do we have a deferred receive waiting + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // Tell the client that there is a packet waiting for it. + // + psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + } + } + + return; +} + +//***************************************************************************** +// +//! Initializes CDC device operation when used with a composite device. +//! +//! \param ui32Index is the index of the USB controller in use. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! This call is very similar to USBDCDCInit() except that it is used for +//! initializing an instance of the serial device for use in a composite +//! device. When this CDC serial device is part of a composite device, then +//! the \e psCompEntry should point to the composite device entry to +//! initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB CDC APIs. +// +//***************************************************************************** +void * +USBDCDCCompositeInit(uint32_t ui32Index, tUSBDCDCDevice *psCDCDevice, + tCompositeEntry *psCompEntry) +{ + tCDCSerInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psCDCDevice); + ASSERT(psCDCDevice->pfnControlCallback); + ASSERT(psCDCDevice->pfnRxCallback); + ASSERT(psCDCDevice->pfnTxCallback); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psCDCDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sCDCHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8CDCSerDeviceDescriptor; + + // + // The CDC serial configuration is different for composite devices and + // stand alone devices. + // + if(psCompEntry == 0) + { + psInst->sDevInfo.ppsConfigDescriptors = g_ppCDCSerConfigDescriptors; + } + else + { + psInst->sDevInfo.ppsConfigDescriptors = g_pCDCCompSerConfigDescriptors; + } + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Set the default endpoint and interface assignments. + // + psInst->ui8BulkINEndpoint = DATA_IN_ENDPOINT; + psInst->ui8BulkOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ui8InterfaceControl = SERIAL_INTERFACE_CONTROL; + psInst->ui8InterfaceData = SERIAL_INTERFACE_DATA; + + // + // By default do not use the interrupt control endpoint. The single + // instance CDC serial device will turn this on in USBDCDCInit(); + // + psInst->ui8ControlEndpoint = CONTROL_ENDPOINT; + + // + // Initialize the workspace in the passed instance structure. + // + psInst->ui32USBBase = USB0_BASE; + psInst->iCDCRxState = eCDCStateUnconfigured; + psInst->iCDCTxState = eCDCStateUnconfigured; + psInst->iCDCInterruptState = eCDCStateUnconfigured; + psInst->iCDCRequestState = eCDCStateUnconfigured; + psInst->ui8PendingRequest = 0; + psInst->ui16BreakDuration = 0; + psInst->ui16SerialState = 0; + psInst->ui16DeferredOpFlags = 0; + psInst->ui16ControlLineState = 0; + psInst->bRxBlocked = false; + psInst->bControlBlocked = false; + psInst->bConnected = false; + + // + // Initialize the device info structure for the serial device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psCDCDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psCDCDevice->ui32NumStringDescriptors; + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(CDCTickHandler, (void *)psCDCDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psCDCDevice); +} + +//***************************************************************************** +// +//! Initializes CDC device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for CDC device operation. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC device. +//! +//! An application wishing to make use of a USB CDC communication channel and +//! appear as a virtual serial port on the host system must call this function +//! to initialize the USB controller and attach the device to the USB bus. +//! This function performs all required USB initialization. +//! +//! The value returned by this function is the \e psCDCDevice pointer passed +//! to it if successful. This pointer must be passed to all later calls to the +//! CDC class driver to identify the device instance. +//! +//! The USB CDC device class driver offers packet-based transmit and receive +//! operation. If the application would rather use block based communication +//! with transmit and receive buffers, USB buffers on the transmit and receive +//! channels may be used to offer this functionality. +//! +//! Transmit Operation: +//! +//! Calls to USBDCDCPacketWrite() must send no more than 64 bytes of data at a +//! time and may only be made when no other transmission is currently +//! outstanding. +//! +//! Once a packet of data has been acknowledged by the USB host, a +//! \b USB_EVENT_TX_COMPLETE event is sent to the application callback to +//! inform it that another packet may be transmitted. +//! +//! Receive Operation: +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event \b USB_EVENT_RX_AVAILABLE. The application must then +//! call USBDCDCPacketRead(), passing a buffer capable of holding the received +//! packet to retrieve the data and acknowledge reception to the USB host. The +//! size of the received packet may be queried by calling +//! USBDCDCRxPacketAvailable(). +//! +//! \note The application must not make any calls to the low level USB Device +//! API if interacting with USB via the CDC device class API. Doing so +//! will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the psCDCDevice pointer on success. +// +//***************************************************************************** +void * +USBDCDCInit(uint32_t ui32Index, tUSBDCDCDevice *psCDCDevice) +{ + void *pvRet; + tCDCSerInstance *psInst; + tDeviceDescriptor *psDevDesc; + tConfigDescriptor *psConfigDesc; + + // + // Initialize the internal state for this class. + // + pvRet = USBDCDCCompositeInit(ui32Index, psCDCDevice, 0); + + if(pvRet) + { + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8CDCSerDeviceDescriptor; + psDevDesc->idVendor = psCDCDevice->ui16VID; + psDevDesc->idProduct = psCDCDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psConfigDesc = (tConfigDescriptor *)g_pui8CDCSerDescriptor; + psConfigDesc->bmAttributes = psCDCDevice->ui8PwrAttributes; + psConfigDesc->bMaxPower = (uint8_t)(psCDCDevice->ui16MaxPowermA / 2); + + // + // Create an instance pointer to the private data area. + // + psInst = &psCDCDevice->sPrivateData; + + // + // Enable the default interrupt control endpoint if this class is not + // being used in a composite device. + // + psInst->ui8ControlEndpoint = CONTROL_ENDPOINT; + + // + // Use the configuration descriptor with the interrupt control + // endpoint. + // + psInst->sDevInfo.ppsConfigDescriptors = g_ppCDCSerConfigDescriptors; + + // + // All is well so now pass the descriptors to the lower layer and put + // the CDC device on the bus. + // + USBDCDInit(ui32Index, &psInst->sDevInfo, (void *)psCDCDevice); + } + + return(pvRet); +} + +//***************************************************************************** +// +//! Shuts down the CDC device instance. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! +//! This function terminates CDC operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the CDC device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvCDCDevice instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCTerm(void *pvCDCDevice) +{ + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase)); + + psInst->ui32USBBase = 0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer for the control callback. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the control channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnControlCallback function +//! passed on USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback pointer changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's control callback. +// +//***************************************************************************** +void * +USBDCDCSetControlCBData(void *pvCDCDevice, void *pvCBData) +{ + tUSBDCDCDevice *psBulkDevice; + void *pvOldValue; + + ASSERT(pvCDCDevice); + + // + // The CDC device structure pointer. + // + psBulkDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Set the callback pointer for the control channel after remembering the + // previous value. + // + pvOldValue = psBulkDevice->pvControlCBData; + psBulkDevice->pvControlCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the receive channel callback. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDCDCSetRxCBData(void *pvCDCDevice, void *pvCBData) +{ + tUSBDCDCDevice *psBulkDevice; + void *pvOldValue; + + ASSERT(pvCDCDevice); + + // + // The CDC device structure pointer. + // + psBulkDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = psBulkDevice->pvRxCBData; + psBulkDevice->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the transmit callback. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDCDCSetTxCBData(void *pvCDCDevice, void *pvCBData) +{ + tUSBDCDCDevice *psBulkDevice; + void *pvOldValue; + + ASSERT(pvCDCDevice); + + // + // The CDC device structure pointer. + // + psBulkDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = psBulkDevice->pvTxCBData; + psBulkDevice->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the CDC data interface. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pi8Data points to the first byte of data which is to be transmitted. +//! \param ui32Length is the number of bytes of data to transmit. +//! \param bLast indicates whether more data is to be written before a packet +//! should be scheduled for transmission. If \b true, the client will make +//! a further call to this function. If \b false, no further call will be +//! made and the driver should schedule transmission of a short packet. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB packet. If no transmission is currently ongoing +//! the data is immediately copied to the relevant USB endpoint FIFO. If the +//! \e bLast parameter is \b true, the newly written packet is then scheduled +//! for transmission. Whenever a USB packet is acknowledged by the host, a +//! \b USB_EVENT_TX_COMPLETE event will be sent to the application transmit +//! callback indicating that more data can now be transmitted. +//! +//! The maximum value for \e ui32Length is 64 bytes (the maximum USB packet +//! size for the bulk endpoints in use by CDC). Attempts to send more data +//! than this will result in a return code of 0 indicating that the data cannot +//! be sent. +//! +//! \return Returns the number of bytes actually sent. At this level, this +//! will either be the number of bytes passed (if less than or equal to the +//! maximum packet size for the USB endpoint in use and no outstanding +//! transmission ongoing) or 0 to indicate a failure. +// +//***************************************************************************** +uint32_t +USBDCDCPacketWrite(void *pvCDCDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + tCDCSerInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Can we send the data provided? + // + if((ui32Length > DATA_IN_EP_MAX_SIZE) || + (psInst->iCDCTxState != eCDCStateIdle)) + { + // + // Either the packet was too big or we are in the middle of sending + // another packet. Return 0 to indicate that we can't send this data. + // + return(0); + } + + // + // Copy the data into the USB endpoint FIFO. + // + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, pi8Data, + ui32Length); + + // + // Did we copy the data successfully? + // + if(i32Retcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->ui16LastTxSize += (uint16_t)ui32Length; + + // + // If this is the last call for this packet, schedule transmission. + // + if(bLast) + { + // + // Send the packet to the host if we have received all the data we + // can expect for this packet. + // + psInst->iCDCTxState = eCDCStateWaitData; + i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(i32Retcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ui32Length); + } + else + { + // + // Yes - tell the caller we could not send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the CDC data +//! interface. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pi8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by \e pi8Data. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to ui32Length bytes of data received from the USB +//! host into the supplied application buffer. +//! +//! \note The \e bLast parameter is ignored in this implementation since the +//! end of a packet can be determined without relying upon the client to +//! provide this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +uint32_t +USBDCDCPacketRead(void *pvCDCDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + uint32_t ui32EPStatus, ui32Count, ui32Pkt; + tCDCSerInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // If receive is currently blocked or the buffer we were passed is + // (potentially) too small, set the flag telling us that we have a + // packet waiting but return 0. + // + if(psInst->bRxBlocked || psInst->bControlBlocked) + { + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_PACKET_RX, + true); + return(0); + } + else + { + // + // It is OK to receive the new packet. How many bytes are + // available for us to receive? + // + ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // + // Get as much data as we can. + // + ui32Count = ui32Length; + i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + pi8Data, &ui32Count); + + // + // Did we read the last of the packet data? + // + if(ui32Count == ui32Pkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + ui32EPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_PACKET_RX, false); + + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(i32Retcode != -1) + { + return(ui32Count); + } + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! +//! This function returns the maximum number of bytes that can be passed on a +//! call to USBDCDCPacketWrite() and accepted for transmission. The value +//! returned will be the maximum USB packet size if no transmission is +//! currently outstanding or 0 if a transmission is in progress. +//! +//! \return Returns the number of bytes available in the transmit buffer. +// +//***************************************************************************** +uint32_t +USBDCDCTxPacketAvailable(void *pvCDCDevice) +{ + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->iCDCTxState != eCDCStateIdle) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(DATA_IN_EP_MAX_SIZE); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +uint32_t +USBDCDCRxPacketAvailable(void *pvCDCDevice) +{ + uint32_t ui32EPStatus, ui32Size; + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // If receive is currently blocked, return 0. + // + if(psInst->bRxBlocked || psInst->bControlBlocked) + { + return(0); + } + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + return(ui32Size); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Informs the CDC module of changes in the serial control line states or +//! receive error conditions. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param ui16State indicates the states of the various control lines and +//! any receive errors detected. Bit definitions are as for the USB CDC +//! SerialState asynchronous notification and are defined in header file +//! usbcdc.h. +//! +//! The application should call this function whenever the state of any of +//! the incoming RS232 handshake signals changes or in response to a receive +//! error or break condition. The \e ui16State parameter is the ORed +//! combination of the following flags with each flag indicating the presence +//! of that condition. +//! +//! - USB_CDC_SERIAL_STATE_OVERRUN +//! - USB_CDC_SERIAL_STATE_PARITY +//! - USB_CDC_SERIAL_STATE_FRAMING +//! - USB_CDC_SERIAL_STATE_RING_SIGNAL +//! - USB_CDC_SERIAL_STATE_BREAK +//! - USB_CDC_SERIAL_STATE_TXCARRIER +//! - USB_CDC_SERIAL_STATE_RXCARRIER +//! +//! This function should be called only when the state of any flag changes. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCSerialStateChange(void *pvCDCDevice, uint16_t ui16State) +{ + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Add the newly reported state bits to the current collection. We do this + // in case two state changes occur back-to-back before the first has been + // notified. There are two distinct types of signals that we report here + // and we deal with them differently: + // + // 1. Errors (overrun, parity, framing error) are ORed together so that + // any reported error is sent on the next notification. + // 2. Signal line states (RI, break, TX carrier, RX carrier) always + // report the last state notified to us. The implementation here will + // send an interrupt showing the last state but, if two state changes + // occur very quickly, the host may receive a notification containing + // the same state that was last reported (in other words, a short pulse + // will be lost). It would be possible to reduce the likelihood of + // this happening by building a queue of state changes and sending + // these in order but you are left with exactly the same problem if the + // queue fills up. For now, therefore, we run the risk of missing very + // short pulses on the "steady-state" signal lines. + // + psInst->ui16SerialState |= (ui16State & USB_CDC_SERIAL_ERRORS); + psInst->ui16SerialState &= ~USB_CDC_SERIAL_ERRORS; + psInst->ui16SerialState |= (ui16State & ~USB_CDC_SERIAL_ERRORS); + + // + // Set the flag indicating that a serial state change is to be sent. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + true); + + // + // Can we send the state change immediately? + // + if(psInst->iCDCInterruptState == eCDCStateIdle) + { + // + // The interrupt channel is free so send the notification immediately. + // If we can't do this, the tick timer will catch this next time + // round. + // + psInst->iCDCInterruptState = eCDCStateWaitData; + SendSerialState(pvCDCDevice); + } + + return; +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvCDCDevice is the pointer to the CDC device instance structure. +//! \param ui8Power indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCPowerStatusSet(void *pvCDCDevice, uint8_t ui8Power) +{ + ASSERT(pvCDCDevice); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wakeup to resume communication when in suspended state. +//! +//! \param pvCDCDevice is the pointer to the CDC device instance structure. +//! +//! When the bus is suspended, an application which supports remote wakeup +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wakeup signaling to the host. If the remote +//! wakeup feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wakeup, +//! \b false will be returned to indicate that the wakeup request was not +//! successful. +//! +//! \return Returns \b true if the remote wakeup is not disabled and the +//! signaling was started or \b false if remote wakeup is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +bool +USBDCDCRemoteWakeupRequest(void *pvCDCDevice) +{ + ASSERT(pvCDCDevice); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdcdc.h b/usblib/device/usbdcdc.h new file mode 100644 index 0000000..dc96086 --- /dev/null +++ b/usblib/device/usbdcdc.h @@ -0,0 +1,447 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for generic CDC ACM (serial) device. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBDCDC_H__ +#define __USBDCDC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup cdc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB CDC Serial code and are here only to help with the application +// allocating the correct amount of memory for the CDC Serial device code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + eCDCStateUnconfigured, + + // + // No outstanding transaction remains to be completed. + // + eCDCStateIdle, + + // + // Waiting on completion of a send or receive transaction. + // + eCDCStateWaitData, + + // + // Waiting for client to process data. + // + eCDCStateWaitClient +} +tCDCState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// CDC Serial device. The memory for this structure is allocated in the +// tUSBDCDCDevice structure passed on USBDCDCInit(). +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The state of the serial receive state. + // + volatile tCDCState iCDCRxState; + + // + // The state of the serial transmit state. + // + volatile tCDCState iCDCTxState; + + // + // The state of the serial request state. + // + volatile tCDCState iCDCRequestState; + + // + // The state of the serial interrupt state. + // + volatile tCDCState iCDCInterruptState; + + // + // The current pending request. + // + volatile uint8_t ui8PendingRequest; + + // + // The current break duration used during send break requests. + // + uint16_t ui16BreakDuration; + + // + // The current line control state for the serial port. + // + uint16_t ui16ControlLineState; + + // + // The general serial state. + // + uint16_t ui16SerialState; + + // + // State of any pending operations that could not be handled immediately + // upon receipt. + // + volatile uint16_t ui16DeferredOpFlags; + + // + // Size of the last transmit. + // + uint16_t ui16LastTxSize; + + // + // The current serial line coding. + // + tLineCoding sLineCoding; + + // + // Serial port receive is blocked. + // + volatile bool bRxBlocked; + + // + // Serial control port is blocked. + // + volatile bool bControlBlocked; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // The control endpoint number, this is modified in composite devices. + // + uint8_t ui8ControlEndpoint; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8BulkINEndpoint; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8BulkOUTEndpoint; + + // + // The interface number for the control interface, this is modified in + // composite devices. + // + uint8_t ui8InterfaceControl; + + // + // The interface number for the data interface, this is modified in + // composite devices. + // + uint8_t ui8InterfaceData; +} +tCDCSerInstance; + +//***************************************************************************** +// +// The following defines are used when working with composite devices. +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the size of the g_pui8IADSerDescriptor array in bytes. +// +//***************************************************************************** +#define SERDESCRIPTOR_SIZE (8) + +//***************************************************************************** +// +// This is the size of the g_pui8CDCSerCommInterface array in bytes. +// +//***************************************************************************** +#define SERCOMMINTERFACE_SIZE (35) + +//***************************************************************************** +// +// This is the size of the g_pui8CDCSerDataInterface array in bytes. +// +//***************************************************************************** +#define SERDATAINTERFACE_SIZE (23) + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Serial CDC Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +//***************************************************************************** +#define COMPOSITE_DCDC_SIZE (SERDESCRIPTOR_SIZE + SERCOMMINTERFACE_SIZE + \ + SERDATAINTERFACE_SIZE) + +//***************************************************************************** +// +// CDC-specific events These events are provided to the application in the +// \e ui32Msg parameter of the tUSBCallback function. +// +//***************************************************************************** + +// +//! The host requests that the device send a BREAK condition on its +//! serial communication channel. The BREAK should remain active until +//! a USBD_CDC_EVENT_CLEAR_BREAK event is received. +// +#define USBD_CDC_EVENT_SEND_BREAK (USBD_CDC_EVENT_BASE + 0) + +// +//! The host requests that the device stop sending a BREAK condition on its +//! serial communication channel. +// +#define USBD_CDC_EVENT_CLEAR_BREAK (USBD_CDC_EVENT_BASE + 1) + +// +//! The host requests that the device set the RS232 signaling lines to +//! a particular state. The ui32MsgValue parameter contains the RTS and +//! DTR control line states as defined in table 51 of the USB CDC class +//! definition and is a combination of the following values: +//! +//! (RTS) USB_CDC_DEACTIVATE_CARRIER or USB_CDC_ACTIVATE_CARRIER +//! (DTR) USB_CDC_DTE_NOT_PRESENT or USB_CDC_DTE_PRESENT +// +#define USBD_CDC_EVENT_SET_CONTROL_LINE_STATE (USBD_CDC_EVENT_BASE + 2) + +// +//! The host requests that the device set the RS232 communication +//! parameters. The pvMsgData parameter points to a tLineCoding structure +//! defining the required number of bits per character, parity mode, +//! number of stop bits and the baud rate. +// +#define USBD_CDC_EVENT_SET_LINE_CODING (USBD_CDC_EVENT_BASE + 3) + +// +//! The host is querying the current RS232 communication parameters. The +//! pvMsgData parameter points to a tLineCoding structure that the +//! application must fill with the current settings prior to returning +//! from the callback. +// +#define USBD_CDC_EVENT_GET_LINE_CODING (USBD_CDC_EVENT_BASE + 4) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the CDC device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of all asynchronous control events related to the + //! operation of the device. + // + const tUSBCallback pfnControlCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the control channel callback, + //! pfnControlCallback. + // + void *pvControlCBData; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data receive channel. + // + const tUSBCallback pfnRxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the receive channel callback, + //! pfnRxCallback. + // + void *pvRxCBData; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data transmit + //! channel. + // + const tUSBCallback pfnTxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the transmit channel callback, + //! pfnTxCallback. + // + void *pvTxCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), + //! Control interface description string (language 1), Configuration + //! description string (language 1). + //! + //! If supporting more than 1 language, the strings for indices 1 through 5 + //! must be repeated for each of the other languages defined in the + //! language descriptor. + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + (5 * number of supported languages). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The private instance data for this device. This memory + //! must remain accessible for as long as the CDC device is in use and + //! must not be modified by any code outside the CDC class driver. + // + tCDCSerInstance sPrivateData; +} +tUSBDCDCDevice; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDCDCCompositeInit(uint32_t ui32Index, + tUSBDCDCDevice *psCDCDevice, + tCompositeEntry *psCompEntry); +extern void *USBDCDCInit(uint32_t ui32Index, + tUSBDCDCDevice *psCDCDevice); +extern void USBDCDCTerm(void *pvCDCDevice); +extern void *USBDCDCSetControlCBData(void *pvCDCDevice, void *pvCBData); +extern void *USBDCDCSetRxCBData(void *pvCDCDevice, void *pvCBData); +extern void *USBDCDCSetTxCBData(void *pvCDCDevice, void *pvCBData); +extern uint32_t USBDCDCPacketWrite(void *pvCDCDevice, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDCDCPacketRead(void *pvCDCDevice, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDCDCTxPacketAvailable(void *pvCDCDevice); +extern uint32_t USBDCDCRxPacketAvailable(void *pvCDCDevice); +extern void USBDCDCSerialStateChange(void *pvCDCDevice, uint16_t ui16State); +extern bool USBDCDCRemoteWakeupRequest(void *pvCDCDevice); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED + +// +// Use USBDCDFeatureSet() or USBHCDFeatureSet() with \b USBLIB_FEATURE_POWER +// configuration option. +// +extern void USBDCDCPowerStatusSet(void *pvCDCDevice, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDCDC_H__ diff --git a/usblib/device/usbdcdesc.c b/usblib/device/usbdcdesc.c new file mode 100644 index 0000000..6260d42 --- /dev/null +++ b/usblib/device/usbdcdesc.c @@ -0,0 +1,643 @@ +//***************************************************************************** +// +// usbcdesc.c - Config descriptor parsing functions. +// +// Copyright (c) 2007-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/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" + +//***************************************************************************** +// +// The functions in this file mirror the descriptor parsing APIs available +// in usblib.h but parse configuration descriptors defined in terms of a list +// of sections rather than as a single block of descriptor data. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! \internal +//! +//! Walk to the next descriptor after the supplied one within a section-based +//! config descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor which contains \e pi16Desc. +//! \param pui32Sec points to a variable containing the section within +//! \e psConfig which contains \e pi16Desc. +//! \param pi16Desc points to the descriptor that we want to step past. +//! +//! This function walks forward one descriptor within a configuration +//! descriptor. The value returned is a pointer to the header of the next +//! descriptor after the descriptor supplied in \e pi16Desc. If the next +//! descriptor is in the next section, \e *pui32Sec will be incremented +//! accordingly. +//! +//! \return Returns a pointer to the next descriptor in the configuration +//! descriptor. +// +//***************************************************************************** +static tDescriptorHeader * +NextConfigDescGet(const tConfigHeader *psConfig, uint32_t *pui32Sec, + tDescriptorHeader *psDesc) +{ + // + // Determine where the next descriptor after the supplied one should be + // assuming it is within the current section. + // + psDesc = NEXT_USB_DESCRIPTOR(psDesc); + + // + // Did we run off the end of the section? + // + if((uint8_t *)psDesc >= (psConfig->psSections[*pui32Sec]->pui8Data + + psConfig->psSections[*pui32Sec]->ui16Size)) + { + // + // Yes - move to the next section. + // + (*pui32Sec)++; + + // + // Are we still within the configuration descriptor? + // + if(*pui32Sec < psConfig->ui8NumSections) + { + // + // Yes - the new descriptor is at the start of the new section. + // + psDesc = + (tDescriptorHeader *)psConfig->psSections[*pui32Sec]->pui8Data; + } + else + { + // + // No - we ran off the end of the descriptor so return NULL. + // + psDesc = (tDescriptorHeader *)0; + } + } + + // + // Return the new descriptor pointer. + // + return(psDesc); +} + +//***************************************************************************** +// +//! \internal +//! +//! Returns a pointer to the n-th interface descriptor in a configuration +//! descriptor with the supplied interface number. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor to search. +//! \param ui8InterfaceNumber is the interface number of the descriptor to +//! query. +//! \param ui32Index is the zero based index of the descriptor. +//! \param pui32Section points to storage which is written with the index +//! of the section containing the returned descriptor. +//! +//! This function returns a pointer to the n-th interface descriptor in the +//! supplied configuration which has the requested interface number. It may be +//! used by a client to retrieve the descriptors for each alternate setting +//! of a given interface within the configuration passed. +//! +//! \return Returns a pointer to the n-th interface descriptor with interface +//! number as specified or NULL of this descriptor does not exist. +// +//***************************************************************************** +static tInterfaceDescriptor * +ConfigAlternateInterfaceGet(const tConfigHeader *psConfig, + uint8_t ui8InterfaceNumber, uint32_t ui32Index, + uint32_t *pui32Section) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32Count, ui32Sec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pui8Data; + ui32Count = 0; + ui32Sec = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(psDescCheck) + { + // + // Does this descriptor match the type passed (if a specific type + // has been specified)? + // + if((psDescCheck->bDescriptorType == USB_DTYPE_INTERFACE) && + (((tInterfaceDescriptor *)psDescCheck)->bInterfaceNumber == + ui8InterfaceNumber)) + { + // + // This is an interface descriptor for interface + // ui8InterfaceNumber. Determine if this is the n-th one we have + // found and, if so, return its pointer. + // + if(ui32Count == ui32Index) + { + // + // Found it - return the pointer and section number. + // + *pui32Section = ui32Sec; + return((tInterfaceDescriptor *)psDescCheck); + } + + // + // Increment our count of matching descriptors found and go back + // to look for another since we have not yet reached the n-th + // match. + // + ui32Count++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ui32Sec, psDescCheck); + } + + // + // If we drop out the end of the loop, we did not find the requested + // descriptor so return NULL. + // + return((tInterfaceDescriptor *)0); +} + +//***************************************************************************** +// +//! \internal +//! +//! Determines the total length of a configuration descriptor defined in terms +//! of a collection of concatenated sections. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor whose size is to be determined. +//! +//! \return Returns the number of bytes in the configuration descriptor will +//! result from concatenating the required sections. +// +//***************************************************************************** +uint32_t +USBDCDConfigDescGetSize(const tConfigHeader *psConfig) +{ + uint32_t ui32Loop, ui32Len; + + ui32Len = 0; + + // + // Determine the size of the whole descriptor by adding the sizes of + // each section which will be concatenated to produce it. + // + for(ui32Loop = 0; ui32Loop < psConfig->ui8NumSections; ui32Loop++) + { + ui32Len += psConfig->psSections[ui32Loop]->ui16Size; + } + + return(ui32Len); +} + +//***************************************************************************** +// +//! \internal +//! +//! Determines the number of individual descriptors of a particular type within +//! a supplied configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ui32Type identifies the type of descriptor that is to be counted. +//! If the value is \b USB_DESC_ANY, the function returns the total number of +//! descriptors regardless of type. +//! +//! This function can be used to count the number of descriptors of a +//! particular type within a configuration descriptor. The caller can provide +//! a specific type value which the function matches against the second byte +//! of each descriptor or, alternatively, can specify \b USB_DESC_ANY to have +//! the function count all descriptors regardless of their type. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetNum(), searches through a single, contiguous +//! block of data to perform the same enumeration. +//! +//! \return Returns the number of descriptors found in the supplied block of +//! data. +// +//***************************************************************************** +uint32_t +USBDCDConfigDescGetNum(const tConfigHeader *psConfig, uint32_t ui32Type) +{ + uint32_t ui32Section, ui32NumDescs; + + // + // Initialize our counts. + // + ui32NumDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the configuration descriptor. Note that this + // assumes each section contains only whole descriptors! + // + for(ui32Section = 0; ui32Section < (uint32_t)psConfig->ui8NumSections; + ui32Section++) + { + ui32NumDescs += USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ui32Section]->pui8Data, + psConfig->psSections[ui32Section]->ui16Size, ui32Type); + } + + return(ui32NumDescs); +} + +//***************************************************************************** +// +//! \internal +//! +//! Finds the n-th descriptor of a particular type within the supplied +//! configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ui32Type identifies the type of descriptor that is to be found. If +//! the value is \b USB_DESC_ANY, the function returns a pointer to the n-th +//! descriptor regardless of type. +//! \param ui32Index is the zero based index of the descriptor whose pointer is +//! to be returned. For example, passing value 1 in \e ui32Index returns the +//! second matching descriptor. +//! \param pui32Section points to storage which will receive the section index +//! containing the requested descriptor. +//! +//! Return a pointer to the n-th descriptor of a particular type found in the +//! configuration descriptor passed. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGet(), searches through a single, contiguous +//! block of data to perform the same enumeration. +//! +//! \return Returns a pointer to the header of the required descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tDescriptorHeader * +USBDCDConfigDescGet(const tConfigHeader *psConfig, uint32_t ui32Type, + uint32_t ui32Index, uint32_t *pui32Section) +{ + uint32_t ui32Section, ui32TotalDescs, ui32NumDescs; + + // + // Initialize our counts. + // + ui32TotalDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the configuration descriptor. This allows us to + // determine which section contains the descriptor we are being asked for. + // + for(ui32Section = 0; ui32Section < (uint32_t)psConfig->ui8NumSections; + ui32Section++) + { + // + // How many descriptors of the requested type exist in this section? + // + ui32NumDescs = USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ui32Section]->pui8Data, + psConfig->psSections[ui32Section]->ui16Size, ui32Type); + + // + // Does this section contain the descriptor whose index we are looking + // for? + // + if((ui32TotalDescs + ui32NumDescs) > ui32Index) + { + // + // We know the requested descriptor exists in the current + // block so write the section number to the caller's storage. + // + *pui32Section = ui32Section; + + // + // Now find the actual descriptor requested and return its pointer. + // + return(USBDescGet( + (tDescriptorHeader *)psConfig->psSections[ui32Section]->pui8Data, + psConfig->psSections[ui32Section]->ui16Size, + ui32Type, ui32Index - ui32TotalDescs)); + } + + // + // We have not found the required descriptor yet. Update our running + // count of the number of type matches found so far then move on to + // the next section. + // + ui32TotalDescs += ui32NumDescs; + } + + // + // If we drop out of the loop, we can't find the requested descriptor + // so return NULL. + // + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +//! \internal +//! +//! Determines the number of different alternate configurations for a given +//! interface within a configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ui8InterfaceNumber is the interface number for which the number of +//! alternate configurations is to be counted. +//! +//! This function can be used to count the number of alternate settings for a +//! specific interface within a configuration. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetNumAlternateInterfaces(), searches through +//! a single, contiguous block of data to perform the same enumeration. +//! +//! \return Returns the number of alternate versions of the specified interface +//! or 0 if the interface number supplied cannot be found in the configuration +//! descriptor. +// +//***************************************************************************** +uint32_t +USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + uint8_t ui8InterfaceNumber) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32Count, ui32Sec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pui8Data; + ui32Sec = 0; + ui32Count = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(psDescCheck) + { + // + // Is this an interface descriptor with the required interface number? + // + if((psDescCheck->bDescriptorType == USB_DTYPE_INTERFACE) && + (((tInterfaceDescriptor *)psDescCheck)->bInterfaceNumber == + ui8InterfaceNumber)) + { + // + // Yes - increment our count. + // + ui32Count++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ui32Sec, psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ui32Count); +} + +//***************************************************************************** +// +//! \internal +//! +//! Returns a pointer to the n-th interface descriptor in a configuration +//! descriptor that applies to the supplied alternate setting number. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ui32Index is the zero based index of the interface that is to be +//! found. If \e ui32Alt is set to a value other than \b USB_DESC_ANY, this +//! is equivalent to the interface number being searched for. +//! \param ui32Alt is the alternate setting number which is to be +//! searched for. If this value is \b USB_DESC_ANY, the alternate setting +//! is ignored and all interface descriptors are considered in the search. +//! \param pui32Section points to storage which will receive the index of the +//! config descriptor section which contains the requested interface +//! descriptor. +//! +//! Return a pointer to the n-th interface descriptor found in the supplied +//! configuration descriptor. If \e ui32Alt is not \b USB_DESC_ANY, only +//! interface descriptors which are part of the supplied alternate setting are +//! considered in the search otherwise all interface descriptors are +//! considered. +//! +//! Note that, although alternate settings can be applied on an interface-by- +//! interface basis, the number of interfaces offered is fixed for a given +//! config descriptor. Hence, this function will correctly find the unique +//! interface descriptor for that interface's alternate setting number \e +//! ui32Alt if \e ui32Index is set to the required interface number and +//! \e ui32Alt is set to a valid alternate setting number for that interface. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetInterface(), searches through a single, +//! contiguous block of data to perform the same enumeration. +//! +//! \return Returns a pointer to the required interface descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tInterfaceDescriptor * +USBDCDConfigGetInterface(const tConfigHeader *psConfig, uint32_t ui32Index, + uint32_t ui32Alt, uint32_t *pui32Section) +{ + // + // If we are being told to ignore the alternate configuration, this boils + // down to a very simple query. + // + if(ui32Alt == USB_DESC_ANY) + { + // + // Return the ui32Index-th interface descriptor we find in the + // configuration descriptor. + // + return((tInterfaceDescriptor *)USBDCDConfigDescGet(psConfig, + USB_DTYPE_INTERFACE, + ui32Index, + pui32Section)); + } + else + { + // + // In this case, a specific alternate setting number is required. + // Given that interface numbers are zero based indices, we can + // pass the supplied ui32Index parameter directly as the interface + // number to USBDescGetAlternateInterface() to retrieve the requested + // interface descriptor pointer. + // + return(ConfigAlternateInterfaceGet(psConfig, ui32Index, ui32Alt, + pui32Section)); + } +} + +//***************************************************************************** +// +//! \internal +//! +//! Return a pointer to the n-th endpoint descriptor in a particular interface +//! within a configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ui32InterfaceNumber is the interface number whose endpoint is to be +//! found. +//! \param ui32AltCfg is the alternate setting number which is to be searched +//! for. This must be a valid alternate setting number for the requested +//! interface. +//! \param ui32Index is the zero based index of the endpoint that is to be +//! found within the appropriate alternate setting for the interface. +//! +//! Return a pointer to the n-th endpoint descriptor found in the supplied +//! interface descriptor. If the \e ui32Index parameter is invalid (greater +//! than or equal to the bNumEndpoints field of the interface descriptor) or +//! the endpoint descriptor cannot be found, the function will return NULL. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetInterfaceEndpoint(), searches through a +//! single, contiguous block of data to perform the same enumeration. +//! +//! \return Returns a pointer to the requested endpoint descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tEndpointDescriptor * +USBDCDConfigGetInterfaceEndpoint(const tConfigHeader *psConfig, + uint32_t ui32InterfaceNumber, + uint32_t ui32AltCfg, uint32_t ui32Index) +{ + tInterfaceDescriptor *psInterface; + tDescriptorHeader *psEndpoint; + uint32_t ui32Section, ui32Count; + + // + // Find the requested interface descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32InterfaceNumber, + ui32AltCfg, &ui32Section); + + // + // Did we find the requested interface? + // + if(psInterface) + { + // + // Is the index passed valid? + // + if(ui32Index >= psInterface->bNumEndpoints) + { + // + // It's out of bounds so return a NULL. + // + return((tEndpointDescriptor *)0); + } + else + { + // + // Endpoint index is valid so find the descriptor. We start from + // the interface descriptor and look for following endpoint + // descriptors. + // + ui32Count = 0; + psEndpoint = (tDescriptorHeader *)psInterface; + + while(psEndpoint) + { + if(psEndpoint->bDescriptorType == USB_DTYPE_ENDPOINT) + { + // + // We found an endpoint descriptor. Have we reached the + // one we want? + // + if(ui32Count == ui32Index) + { + // + // Yes - return the descriptor pointer to the caller. + // + return((tEndpointDescriptor *)psEndpoint); + } + + // + // Move on to look for the next endpoint. + // + ui32Count++; + } + + // + // Move to the next descriptor. + // + psEndpoint = NextConfigDescGet(psConfig, &ui32Section, + psEndpoint); + } + } + } + + // + // We could not find the requested interface or we got to the end of the + // descriptor without finding the requested endpoint. + // + return((tEndpointDescriptor *)0); + +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdcomp.c b/usblib/device/usbdcomp.c new file mode 100644 index 0000000..727ed42 --- /dev/null +++ b/usblib/device/usbdcomp.c @@ -0,0 +1,1544 @@ +//**************************************************************************** +// +// usbdcomp.c - USB composite device class driver. +// +// Copyright (c) 2010-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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/sysctl.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usb-ids.h" +#include "usblib/usbcdc.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdcdc.h" +#include "usblib/device/usbdcomp.h" + +//**************************************************************************** +// +//! \addtogroup composite_device_class_api +//! @{ +// +//**************************************************************************** + +//**************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//**************************************************************************** +static uint8_t g_pui8CompDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_MISC, // USB Device Class (spec 5.1.1) + USB_MISC_SUBCLASS_COMMON, // USB Device Sub-class (spec 5.1.1) + USB_MISC_PROTOCOL_IAD, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during USBDCompositeInit). + USBShort(0), // Product ID (filled in during USBDCompositeInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//**************************************************************************** +// +// Composite class device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//**************************************************************************** +static const uint8_t g_pui8CompConfigDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(0), // The total size of this full structure. + 0, // The number of interfaces in this + // configuration, this will be filled by + // the class as it discovers all classes + // supported. + 1, // The unique value for this configuration. + 0, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_BUS_PWR, // . + 250, // The maximum power in 2mA increments. +}; + +//**************************************************************************** +// +// Byte offsets used to access various fields in our index/interface/endpoint +// lookup table (tUSBDCompositeDevice.pui32DeviceWorkspace). This workspace +// contains one 4 byte entry per device. The LSB is the device index, next byte +// is the number of the first interface not within this device, next byte is +// the number of the first IN endpoint not within this device and the final +// byte is the number of the first OUT endpoint not within this device. Using +// this simple table we can reasonably quickly cross-reference index with +// interface and endpoint numbers. +// +//**************************************************************************** +#define LOOKUP_INDEX_BYTE 0 +#define LOOKUP_INTERFACE_BYTE 1 +#define LOOKUP_IN_END_BYTE 2 +#define LOOKUP_OUT_END_BYTE 3 + +//**************************************************************************** +// +// A marker used to indicate an invalid index into the device table. +// +//**************************************************************************** +#define INVALID_DEVICE_INDEX 0xFFFFFFFF + +//**************************************************************************** +// +// Various internal handlers needed by this class. +// +//**************************************************************************** +static void HandleDisconnect(void *pvCompositeInstance); +static void InterfaceChange(void *pvCompositeInstance, uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting); +static void ConfigChangeHandler(void *pvCompositeInstance, uint32_t ui32Value); +static void DataSent(void *pvCompositeInstance, uint32_t ui32Info); +static void DataReceived(void *pvCompositeInstance, uint32_t ui32Info); +static void HandleEndpoints(void *pvCompositeInstance, uint32_t ui32Status); +static void HandleRequests(void *pvCompositeInstance, tUSBRequest *psUSBRequest); +static void SuspendHandler(void *pvCompositeInstance); +static void ResumeHandler(void *pvCompositeInstance); +static void ResetHandler(void *pvCompositeInstance); +static void HandleDevice(void *pvCompositeInstance, uint32_t ui32Request, + void *pvRequestData); +static void GetDescriptor(void *pvCompositeInstance, tUSBRequest *psUSBRequest); + +//**************************************************************************** +// +// Configuration Descriptor. +// +//**************************************************************************** +tConfigHeader *g_ppCompConfigDescriptors[1]; + +//**************************************************************************** +// +// The device information structure for the USB Composite device. +// +//**************************************************************************** +const tCustomHandlers g_sCompHandlers = +{ + // + // GetDescriptor + // + GetDescriptor, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + DataSent, + + // + // ResetHandler + // + ResetHandler, + + // + // SuspendHandler + // + SuspendHandler, + + // + // ResumeHandler + // + ResumeHandler, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // DeviceHandler + // + HandleDevice, +}; + +//**************************************************************************** +// +// Use the lookup table from the field pui32DeviceWorkspace in the +// tUSBDCompositeDevice structure to determine which device to call given a +// particular composite device interface number. +// +// The returned value is the index into psDevice->tCompositeEntry indicating +// the device which contains this interface or INVALID_DEVICE_INDEX if no +// device contains the passed interface number. +// +//**************************************************************************** +static uint32_t +InterfaceToIndex(tUSBDCompositeDevice *psDevice, uint32_t ui32Interface) +{ + uint32_t ui32Loop; + uint32_t ui32Lookup; + + // + // Check each lookup entry in turn. + // + for(ui32Loop = 0; ui32Loop < psDevice->ui32NumDevices; ui32Loop++) + { + // + // Get the look up value from the device. + // + ui32Lookup = psDevice->psDevices[ui32Loop].ui32DeviceWorkspace; + ui32Lookup = (ui32Lookup >> (8 * LOOKUP_INTERFACE_BYTE)) & 0xff; + + // + // If the desired interface number is lower than the value in the + // current lookup table entry, we have found the desired device so + // return its index. + // + if(ui32Interface < ui32Lookup) + { + return(ui32Loop); + } + } + + // + // If we get here, an invalid interface number was passed so return a + // marker to indicate this. + // + return(INVALID_DEVICE_INDEX); +} + +//**************************************************************************** +// +// Use the lookup table from the field pui32DeviceWorkspace in the +// tUSBDCompositeDevice structure to determine which device to call given a +// particular composite device endpoint number. +// +// The returned value is the index into psDevice->tCompositeEntry indicating +// the device which contains this endpoint or INVALID_DEVICE_INDEX if no +// device contains the passed endpoint number. +// +//**************************************************************************** +static uint32_t +EndpointToIndex(tUSBDCompositeDevice *psDevice, uint32_t ui32Endpoint, + bool bInEndpoint) +{ + uint32_t ui32Loop, ui32EndpointByte, ui32Lookup; + + // + // Are we considering an IN or OUT endpoint? + // + ui32EndpointByte = bInEndpoint ? LOOKUP_IN_END_BYTE : LOOKUP_OUT_END_BYTE; + + // + // Check each lookup entry in turn. + // + for(ui32Loop = 0; ui32Loop < psDevice->ui32NumDevices; ui32Loop++) + { + // + // Get the look up byte from the device. + // + ui32Lookup = psDevice->psDevices[ui32Loop].ui32DeviceWorkspace; + ui32Lookup = (ui32Lookup >> (ui32EndpointByte * 8)) & 0xff; + + // + // If the desired endpoint number is lower than the value in the + // current lookup table entry, we have found the desired device so + // return its index. + // + if(ui32Endpoint < ui32Lookup) + { + return(ui32Loop); + } + } + + // + // If we get here, an invalid endpoint number was passed so return a + // marker to indicate this. + // + return(INVALID_DEVICE_INDEX); +} + + +//**************************************************************************** +// +// This function will check if any device classes need a get descriptor +// handler called. +// +//**************************************************************************** +static void +GetDescriptor(void *pvCompositeInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the composite device pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Determine which device this request is intended for. We have to be + // careful here to send this to the callback for the correct device + // depending upon whether it is a request sent to the device, the interface + // or the endpoint. + // + switch(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ui32Idx = InterfaceToIndex(psCompDevice, + (psUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ui32Idx = EndpointToIndex(psCompDevice, + (psUSBRequest->wIndex & 0x0F), + (psUSBRequest->wIndex & 0x80) ? true : false); + break; + } + + // + // Requests sent to the device or any other recipient can't be + // handled here since we have no way of telling where they are + // supposed to be handled. As a result, we just stall them. + // + // If your composite device has some device-specific descriptors, + // you should add code here to handle them. + // + case USB_RTYPE_DEVICE: + case USB_RTYPE_OTHER: + default: + { + ui32Idx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ui32Idx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + // + // Does this device have a GetDescriptor callback? + // + if(psDeviceInfo->psCallbacks->pfnGetDescriptor) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psCompDevice->sPrivateData.ui32EP0Owner = ui32Idx; + + // + // Call the device to retrieve the descriptor. + // + psDeviceInfo->psCallbacks->pfnGetDescriptor( + psCompDevice->psDevices[ui32Idx].pvInstance, psUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an suspend handler +// called. +// +//**************************************************************************** +static void +SuspendHandler(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has resumed. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, USB_EVENT_SUSPEND, + 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance; + + if(psDeviceInfo->psCallbacks->pfnSuspendHandler) + { + psDeviceInfo->psCallbacks->pfnSuspendHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an resume handler +// called. +// +//**************************************************************************** +static void +ResumeHandler(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has resumed. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, USB_EVENT_RESUME, + 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance; + + if(psDeviceInfo->psCallbacks->pfnResumeHandler) + { + psDeviceInfo->psCallbacks->pfnResumeHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an reset handler +// called. +// +//**************************************************************************** +static void +ResetHandler(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has been connected. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, + USB_EVENT_CONNECTED, 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance; + + if(psDeviceInfo->psCallbacks->pfnResetHandler) + { + psDeviceInfo->psCallbacks->pfnResetHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function is called to handle data being set to the host so that the +// application callback can be called when the data has been transferred. +// +//**************************************************************************** +static void +DataSent(void *pvCompositeInstance, uint32_t ui32Info) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ui32Idx = psCompDevice->sPrivateData.ui32EP0Owner; + + if(ui32Idx != INVALID_DEVICE_INDEX) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDataSent) + { + psDeviceInfo->psCallbacks->pfnDataSent( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Info); + } + } +} + +//**************************************************************************** +// +// This function is called to handle data being received back from the host so +// that the application callback can be called when the new data is ready. +// +//**************************************************************************** +static void +DataReceived(void *pvCompositeInstance, uint32_t ui32Info) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ui32Idx = psCompDevice->sPrivateData.ui32EP0Owner; + + if(ui32Idx != INVALID_DEVICE_INDEX) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDataReceived) + { + psDeviceInfo->psCallbacks->pfnDataReceived( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Info); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an endpoint handler +// called. +// +//**************************************************************************** +static void +HandleEndpoints(void *pvCompositeInstance, uint32_t ui32Status) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Call each of the endpoint handlers. This may seem odd since we should + // only call the handler whose endpoint needs service. Unfortunately, if + // the device class driver is using uDMA, we have no way of knowing which + // handler to call (since ui32Status will be 0). Since the handlers are + // set up to ignore any callback that is not for them, this is safe. + // + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnEndpointHandler) + { + psDeviceInfo->psCallbacks->pfnEndpointHandler( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Status); + } + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvCompositeInstance, uint32_t ui32Request, + void *pvRequestData) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDeviceHandler) + { + psDeviceInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Request, + pvRequestData); + } + } + + if(psCompDevice->pfnCallback) + { + switch(ui32Request) + { + case USB_EVENT_LPM_RESUME: + { + // + // Pass the LPM resume event to the client. + // + psCompDevice->pfnCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + break; + } + case USB_EVENT_LPM_SLEEP: + { + // + // Pass the LPM sleep event to the client. + // + psCompDevice->pfnCallback(0, USB_EVENT_LPM_SLEEP, 0, + (void *)0); + break; + } + case USB_EVENT_LPM_ERROR: + { + // + // Pass the LPM error event to the client. + // + psCompDevice->pfnCallback(0, USB_EVENT_LPM_ERROR, 0, + (void *)0); + break; + } + default: + { + break; + } + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//**************************************************************************** +static void +HandleDisconnect(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has been disconnected. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, + USB_EVENT_DISCONNECTED, 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDisconnectHandler) + { + psDeviceInfo->psCallbacks->pfnDisconnectHandler( + psCompDevice->psDevices[ui32Idx].pvInstance); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device +// interface changes. It will be passed on to the device classes if they have +// a handler for this function. +// +//**************************************************************************** +static void +InterfaceChange(void *pvCompositeInstance, uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnInterfaceChange) + { + psDeviceInfo->psCallbacks->pfnInterfaceChange( + psCompDevice->psDevices[ui32Idx].pvInstance, + ui8InterfaceNum, ui8AlternateSetting); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. It will be passed on to the device classes if they +// have a handler for this function. +// +//**************************************************************************** +static void +ConfigChangeHandler(void *pvCompositeInstance, uint32_t ui32Value) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnConfigChange) + { + psDeviceInfo->psCallbacks->pfnConfigChange( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Value); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvCompositeInstance +// \param psUSBRequest points to the request received. +// +// This call will be passed on to the device classes if they have a handler +// for this function. +// +// \return None. +// +//**************************************************************************** +static void +HandleRequests(void *pvCompositeInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Determine which device this request is intended for. We have to be + // careful here to send this to the callback for the correct device + // depending upon whether it is a request sent to the device, the interface + // or the endpoint. + // + switch(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ui32Idx = InterfaceToIndex(psCompDevice, + (psUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ui32Idx = EndpointToIndex(psCompDevice, + (psUSBRequest->wIndex & 0x0F), + (psUSBRequest->wIndex & 0x80) ? true : false); + break; + } + + // + // Requests sent to the device or any other recipient can't be + // handled here since we have no way of telling where they are + // supposed to be handled. As a result, we just stall them. + // + // If your composite device has some device-specific requests that need + // to be handled at the device (rather than interface or endpoint) + // level, you should add code here to handle them. + // + case USB_RTYPE_DEVICE: + case USB_RTYPE_OTHER: + default: + { + ui32Idx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ui32Idx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + // + // Does this device have a RequestHandler callback? + // + if(psDeviceInfo->psCallbacks->pfnRequestHandler) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psCompDevice->sPrivateData.ui32EP0Owner = ui32Idx; + + // + // Yes - call the device to retrieve the descriptor. + // + psDeviceInfo->psCallbacks->pfnRequestHandler( + psCompDevice->psDevices[ui32Idx].pvInstance, + psUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } +} + +//**************************************************************************** +// +// This function handles sending interface number changes to device instances. +// +//**************************************************************************** +static void +CompositeIfaceChange(tCompositeEntry *psCompDevice, uint8_t ui8Old, + uint8_t ui8New) +{ + uint8_t pui8Interfaces[2]; + + if(psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pui8Interfaces[0] = ui8Old; + pui8Interfaces[1] = ui8New; + + // + // Call the device handler to inform the class of the interface number + // change. + // + psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->pvInstance, USB_EVENT_COMP_IFACE_CHANGE, + (void *)pui8Interfaces); + } +} + +//**************************************************************************** +// +// This function handles sending endpoint number changes to device instances. +// +//**************************************************************************** +static void +CompositeEPChange(tCompositeEntry *psCompDevice, uint8_t ui8Old, + uint8_t ui8New) +{ + uint8_t pui8Interfaces[2]; + + if(psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pui8Interfaces[0] = ui8Old; + pui8Interfaces[1] = ui8New; + + ui8New--; + + // + // Call the device handler to inform the class of the interface number + // change. + // + psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->pvInstance, USB_EVENT_COMP_EP_CHANGE, + (void *)pui8Interfaces); + } +} + +//**************************************************************************** +// +// This function merges the configuration descriptors into a single multiple +// instance device. +// +//**************************************************************************** +uint32_t +BuildCompositeDescriptor(tUSBDCompositeDevice *psCompDevice) +{ + uint32_t ui32Idx, ui32Offset, ui32CPIdx, ui32FixINT, ui32Dev; + uint16_t ui16TotalLength, ui16Bytes; + uint8_t ui8Interface, ui8INEndpoint, ui8OUTEndpoint; + uint8_t *pui8Data, *pui8Config; + const tConfigHeader *psConfigHeader; + tDescriptorHeader *psHeader; + const uint8_t *pui8Descriptor; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + const tDeviceInfo *psDevice; + + // + // Save the number of devices to look through. + // + ui32Dev = 0; + ui32Idx = 0; + ui8Interface = 0; + ui8INEndpoint = 1; + ui8OUTEndpoint = 1; + ui32Offset = 0; + ui32FixINT = 0; + + // + // This puts the first section pointer in the first entry in the list + // of sections. + // + psCompDevice->sPrivateData.ppsCompSections[0] = + &psCompDevice->sPrivateData.psCompSections[0]; + + // + // Put the pointer to this instances configuration descriptor into the + // front of the list. + // + psCompDevice->sPrivateData.ppsCompSections[0]->pui8Data = + (uint8_t *)&psCompDevice->sPrivateData.sConfigDescriptor; + + psCompDevice->sPrivateData.ppsCompSections[0]->ui16Size = + psCompDevice->sPrivateData.sConfigDescriptor.bLength; + + // + // The configuration descriptor is 9 bytes so initialize the total length + // to 9 bytes. + // + ui16TotalLength = 9; + + // + // Copy the section pointer into the section array for the composite + // device. This is awkward but is required given the definition + // of the structures. + // + psCompDevice->sPrivateData.ppsCompSections[1] = + &psCompDevice->sPrivateData.psCompSections[1]; + + // + // Copy the pointer to the application supplied space into the section + // list. + // + psCompDevice->sPrivateData.ppsCompSections[1]->ui16Size = 0; + psCompDevice->sPrivateData.ppsCompSections[1]->pui8Data = + psCompDevice->sPrivateData.pui8Data; + + // + // Create a local pointer to the data that is used to copy data from + // the other devices into the composite descriptor. + // + pui8Data = psCompDevice->sPrivateData.pui8Data; + + // + // Consider each device in turn. + // + while(ui32Dev < psCompDevice->ui32NumDevices) + { + // + // Save the current starting address of this descriptor. + // + pui8Config = pui8Data + ui32Offset; + + // + // Create a local pointer to the configuration header. + // + psDevice = psCompDevice->psDevices[ui32Dev].psDevInfo; + psConfigHeader = psDevice->ppsConfigDescriptors[0]; + + // + // Loop through each of the sections in this device's configuration + // descriptor. + // + for(ui32Idx = 0; ui32Idx < psConfigHeader->ui8NumSections; ui32Idx++) + { + // + // Initialize the local offset in this descriptor. We include + // a special case here to ignore the initial 9 byte configuration + // descriptor since this has already been handled. + // + if(ui32Idx) + { + // + // This is not the first section so we handle everything in + // it. + // + ui16Bytes = 0; + } + else + { + // + // This is the first section for this device so skip the 9 + // byte configuration descriptor since we've already handled + // this. + // + ui16Bytes = 9; + + // + // If this section includes only the configuration descriptor, + // skip it entirely. + // + if(psConfigHeader->psSections[ui32Idx]->ui16Size <= ui16Bytes) + { + continue; + } + } + + // + // Get a pointer to the configuration descriptor. + // + pui8Descriptor = psConfigHeader->psSections[ui32Idx]->pui8Data; + + // + // Bounds check the allocated space and return if there is not + // enough space. + // + if(ui32Offset > psCompDevice->sPrivateData.ui32DataSize) + { + return(1); + } + + // + // Copy the descriptor from the device into the descriptor list. + // + for(ui32CPIdx = 0; + ui32CPIdx < psConfigHeader->psSections[ui32Idx]->ui16Size; + ui32CPIdx++) + { + pui8Data[ui32CPIdx + ui32Offset] = pui8Descriptor[ui32CPIdx]; + } + + // + // Read out the descriptors in this section. + // + while(ui16Bytes < psConfigHeader->psSections[ui32Idx]->ui16Size) + { + // + // Create a descriptor header pointer. + // + psHeader = (tDescriptorHeader *)&pui8Data[ui32Offset + + ui16Bytes]; + + // + // Check for interface descriptors and modify the numbering to + // match the composite device. + // + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // See if this is an alternate setting or the initial + // setting. + // + if(psInterface->bAlternateSetting != 0) + { + // + // If this is an alternate setting then use the + // previous interface number because the current one + // has already been incremented. + // + psInterface->bInterfaceNumber = ui8Interface - 1; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeIfaceChange( + &psCompDevice->psDevices[ui32Dev], + psInterface->bInterfaceNumber, + ui8Interface); + // + // This was the non-alternate setting so save the + // value and move to the next interface number. + // + psInterface->bInterfaceNumber = ui8Interface; + + // + // No strings allowed on interface descriptors for + // composite devices. + // + psInterface->iInterface = 0; + + ui8Interface++; + } + } + // + // Check for endpoint descriptors and modify the numbering to + // match the composite device. + // + else if(psHeader->bDescriptorType == USB_DTYPE_ENDPOINT) + { + psEndpoint = (tEndpointDescriptor *)psHeader; + + // + // Check if this is an IN or OUT endpoint. + // + if(psEndpoint->bEndpointAddress & USB_RTYPE_DIR_IN) + { + // + // Check if this is the special Fixed Interrupt class + // and this is the interrupt endpoint. + // + if(((psEndpoint->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) && + (psCompDevice->ui16PID == USB_PID_COMP_SERIAL)) + { + // + // Check if the Fixed Interrupt endpoint has been + // set yet. + // + if(ui32FixINT == 0) + { + // + // Allocate the fixed interrupt endpoint and + // save its number. + // + ui32FixINT = ui8INEndpoint++; + } + + CompositeEPChange( + &psCompDevice->psDevices[ui32Dev], + psEndpoint->bEndpointAddress, + ui32FixINT); + + psEndpoint->bEndpointAddress = ui32FixINT | + USB_RTYPE_DIR_IN; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange( + &psCompDevice->psDevices[ui32Dev], + psEndpoint->bEndpointAddress, + ui8INEndpoint); + + psEndpoint->bEndpointAddress = ui8INEndpoint++ | + USB_RTYPE_DIR_IN; + } + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ui32Dev], + psEndpoint->bEndpointAddress, + ui8OUTEndpoint); + psEndpoint->bEndpointAddress = ui8OUTEndpoint++; + } + } + + // + // Move on to the next descriptor. + // + ui16Bytes += psHeader->bLength; + } + + ui32Offset += psConfigHeader->psSections[ui32Idx]->ui16Size; + + ui16TotalLength += ui16Bytes; + } + + // + // Allow the device class to make adjustments to the configuration + // descriptor. + // + psCompDevice->psDevices[ui32Dev].psDevInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->psDevices[ui32Dev].pvInstance, + USB_EVENT_COMP_CONFIG, (void *)pui8Config); + + // + // Add an entry into the device workspace array to allow us to quickly + // map interface and endpoint numbers to device instances later. + // + psCompDevice->psDevices[ui32Dev].ui32DeviceWorkspace = + (ui32Dev << (LOOKUP_INDEX_BYTE * 8)) | + (ui8Interface << (LOOKUP_INTERFACE_BYTE * 8)) | + (ui8OUTEndpoint << (LOOKUP_OUT_END_BYTE * 8)) | + (ui8INEndpoint << (LOOKUP_IN_END_BYTE * 8)); + + // + // Move on to the next device. + // + ui32Dev++; + } + + // + // Modify the configuration descriptor to match the number of interfaces + // and the new total size. + // + psCompDevice->sPrivateData.sCompConfigHeader.ui8NumSections = 2; + psCompDevice->sPrivateData.ppsCompSections[1]->ui16Size = ui32Offset; + psCompDevice->sPrivateData.sConfigDescriptor.bNumInterfaces = + ui8Interface; + psCompDevice->sPrivateData.sConfigDescriptor.wTotalLength = + ui16TotalLength; + + + return(0); +} + +//**************************************************************************** +// +//! This function should be called once for the composite class device to +//! initialize basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! composite device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the composite device. +//! \param ui32Size is the size in bytes of the data pointed to by the +//! \e pui8Data parameter. +//! \param pui8Data is the data area that the composite class can use to build +//! up descriptors. +//! +//! In order for an application to initialize the USB composite device class, +//! it must first call this function with the a valid composite device class +//! structure in the \e psDevice parameter. This allows this function to +//! initialize the USB controller and device code to be prepared to enumerate +//! and function as a USB composite device. The \e ui32Size and \e pui8Data +//! parameters should be large enough to hold all of the class instances +//! passed in via the \e psDevice structure. This is typically the full size +//! of the configuration descriptor for a device minus its configuration +//! header(9 bytes). +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the composite class. +//! +//! See the documentation on the tUSBDCompositeDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return This function returns 0 on failure or a non-zero void pointer on +//! success. +// +//**************************************************************************** +void * +USBDCompositeInit(uint32_t ui32Index, tUSBDCompositeDevice *psDevice, + uint32_t ui32Size, uint8_t *pui8Data) +{ + tCompositeInstance *psInst; + int32_t i32Idx; + uint8_t *pui8Temp; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppui8StringDescriptors); + + // + // Initialize the work space in the passed instance structure. + // + psInst = &psDevice->sPrivateData; + psInst->ui32DataSize = ui32Size; + psInst->pui8Data = pui8Data; + + // + // Save the base address of the USB controller. + // + psInst->ui32USBBase = USBIndexToBase(ui32Index); + + // + // No device is currently transferring data on EP0. + // + psInst->ui32EP0Owner = INVALID_DEVICE_INDEX; + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sCompHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8CompDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = + (const tConfigHeader * const *)g_ppCompConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Initialize the device info structure for the composite device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + g_ppCompConfigDescriptors[0] = &psInst->sCompConfigHeader; + g_ppCompConfigDescriptors[0]->ui8NumSections = 0; + g_ppCompConfigDescriptors[0]->psSections = + (const tConfigSection * const *)psDevice->sPrivateData.ppsCompSections; + + // + // Create a byte pointer to use with the copy. + // + pui8Temp = (uint8_t *)&psInst->sConfigDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(i32Idx = 0; i32Idx < g_pui8CompConfigDescriptor[0]; i32Idx++) + { + pui8Temp[i32Idx] = g_pui8CompConfigDescriptor[i32Idx]; + } + + // + // Create a byte pointer to use with the copy. + // + pui8Temp = (uint8_t *)&psInst->sDeviceDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(i32Idx = 0; i32Idx < g_pui8CompDeviceDescriptor[0]; i32Idx++) + { + pui8Temp[i32Idx] = g_pui8CompDeviceDescriptor[i32Idx]; + } + + // + // Fix up the device descriptor with the client-supplied values. + // + psInst->sDeviceDescriptor.idVendor = psDevice->ui16VID; + psInst->sDeviceDescriptor.idProduct = psDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->sConfigDescriptor.bmAttributes = psDevice->ui8PwrAttributes; + psInst->sConfigDescriptor.bMaxPower = + (uint8_t)(psDevice->ui16MaxPowermA>>1); + + psInst->sDevInfo.pui8DeviceDescriptor = + (const uint8_t *)&psInst->sDeviceDescriptor; + + // + // Plug in the client's string table to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psDevice->ui32NumStringDescriptors; + + // + // Enable Clocking to the USB controller so that changes to the USB + // controller can be made in the BuildCompositeDescriptor() function. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Create the combined descriptors. + // + if(BuildCompositeDescriptor(psDevice)) + { + return(0); + } + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psInst->sDevInfo, (void *)psDevice); + + // + // Return the pointer to the instance indicating that everything went + // well. + // + return((void *)psDevice); +} + +//**************************************************************************** +// +//! Shuts down the composite device. +//! +//! \param pvCompositeInstance is the pointer to the device instance structure +//! as returned by USBDCompositeInit(). +//! +//! This function terminates composite device interface for the instance +//! not me supplied. Following this call, the \e pvCompositeInstance instance +//! should not be used in any other calls. +//! +//! \return None. +// +//**************************************************************************** +void +USBDCompositeTerm(void *pvCompositeInstance) +{ + ASSERT(pvCompositeInstance != 0); + +} + +//**************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//**************************************************************************** + diff --git a/usblib/device/usbdcomp.h b/usblib/device/usbdcomp.h new file mode 100644 index 0000000..e064193 --- /dev/null +++ b/usblib/device/usbdcomp.h @@ -0,0 +1,261 @@ +//***************************************************************************** +// +// usbdcomp.h - USB composite device class driver. +// +// Copyright (c) 2010-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. +// +//***************************************************************************** + +#ifndef __USBDCOMP_H__ +#define __USBDCOMP_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +//! \addtogroup composite_device_class_api +//! @{ +// +//***************************************************************************** + +// +// Defines a single entry in a table of device types supported by the composite +// device. +// +typedef struct +{ + // + // This is set internally by the composite class so it can be left + // uninitialized by the application. + // + const tDeviceInfo *psDeviceInfo; + + // + // This should be the header to the configuration header for a class. + // + const tConfigHeader *psConfigHeader; + + // + // The offset to this devices interface, filled in by the composite class. + // + uint8_t ui8IfaceOffset; +} +tUSBDCompositeEntry; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// composite device class. The memory for this structure is included in +// the sPrivateData field in the tUSBDCompositeDevice structure passed on +// USBDCompositeInit() and should not be modified by any code outside of the +// composite device code. +// +//***************************************************************************** +typedef struct +{ + // + // Saves which USB controller is in use. + // + uint32_t ui32USBBase; + + // + // The device information pointer. + // + tDeviceInfo sDevInfo; + + // + // This is the configuration descriptor for this instance. + // + tConfigDescriptor sConfigDescriptor; + + // + // This is the device descriptor for this instance. + // + tDeviceDescriptor sDeviceDescriptor; + + // + // The configuration header for this instance. + // + tConfigHeader sCompConfigHeader; + + // + // These are the configuration sections that will be built from the + // Configuration Descriptor header and the descriptors from the devices + // that are part of this composite device. + // + tConfigSection psCompSections[2]; + tConfigSection *ppsCompSections[2]; + + // + // The size and pointer to the data used by the instance. + // + uint32_t ui32DataSize; + uint8_t *pui8Data; + + // + // The current "owner" of endpoint 0. This is used to track the device + // class which is currently transferring data on EP0. + // + uint32_t ui32EP0Owner; +} +tCompositeInstance; + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the composite device class. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in mA. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wake up. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the composite + //! device. + // + const tUSBCallback pfnCallback; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), Composite + //! device interface description string (language 1), Configuration + //! description string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The number of devices in the psDevices array. + // + const uint32_t ui32NumDevices; + + // + //! This application supplied array holds the the top level device class + //! information as well as the Instance data for that class. + // + tCompositeEntry * const psDevices; + + // + //! The private data for this device instance. This memory must remain + //! accessible for as long as the composite device is in use and must + //! not be modified by any code outside the composite class driver. + // + tCompositeInstance sPrivateData; +} +tUSBDCompositeDevice; + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// Composite specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDCompositeInit(uint32_t ui32Index, + tUSBDCompositeDevice *psCompDevice, + uint32_t ui32Size, uint8_t *pui8Data); +extern void USBDCompositeTerm(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/usblib/device/usbdconfig.c b/usblib/device/usbdconfig.c new file mode 100644 index 0000000..0a100cc --- /dev/null +++ b/usblib/device/usbdconfig.c @@ -0,0 +1,555 @@ +//***************************************************************************** +// +// usbdconfig.c - High level USB device configuration function. +// +// 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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Structure used in compiling FIFO size and endpoint properties from a +// configuration descriptor. +// +//***************************************************************************** +typedef struct +{ + uint32_t pui32Size[2]; +} +tUSBEndpointInfo; + +//***************************************************************************** +// +// Indices used when accessing the tUSBEndpointInfo structure. +// +//***************************************************************************** +#define EP_INFO_IN 0 +#define EP_INFO_OUT 1 + +//***************************************************************************** +// +// Given a maximum packet size and the user's FIFO scaling requirements, +// determine the flags to use to configure the endpoint FIFO and the number +// of bytes of FIFO space occupied. +// +//***************************************************************************** +static uint32_t +GetEndpointFIFOSize(uint32_t ui32MaxPktSize, uint32_t *pupBytesUsed) +{ + uint32_t ui32Loop, ui32FIFOSize; + + // + // Now we need to find the nearest supported size that accommodates the + // requested size. Step through each of the supported sizes until we + // find one that will do. + // + for(ui32Loop = USB_FIFO_SZ_8; ui32Loop <= USB_FIFO_SZ_2048; ui32Loop++) + { + // + // How many bytes does this FIFO value represent? + // + ui32FIFOSize = USBFIFOSizeToBytes(ui32Loop); + + // + // Is this large enough to hold one packet. + // + if(ui32FIFOSize >= ui32MaxPktSize) + { + // + // Return the FIFO size setting and the USB_FIFO_SZ_ value. + // + *pupBytesUsed = ui32FIFOSize; + + return(ui32Loop); + } + } + + // + // If we drop out, we can't support the FIFO size requested. Signal a + // problem by returning 0 in the pBytesUsed + // + *pupBytesUsed = 0; + + return(USB_FIFO_SZ_8); +} + +//***************************************************************************** +// +// Translate a USB endpoint descriptor into the values we need to pass to the +// USBDevEndpointConfigSet() API. +// +//***************************************************************************** +static void +GetEPDescriptorType(tEndpointDescriptor *psEndpoint, uint32_t *pui32EPIndex, + uint32_t *pui32MaxPktSize, uint32_t *pui32Flags) +{ + // + // Get the endpoint index. + // + *pui32EPIndex = psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + + // + // Extract the maximum packet size. + // + *pui32MaxPktSize = psEndpoint->wMaxPacketSize & USB_EP_MAX_PACKET_COUNT_M; + + // + // Is this an IN or an OUT endpoint? + // + *pui32Flags = (psEndpoint->bEndpointAddress & USB_EP_DESC_IN) ? + USB_EP_DEV_IN : USB_EP_DEV_OUT; + + // + // Set the endpoint mode. + // + switch(psEndpoint->bmAttributes & USB_EP_ATTR_TYPE_M) + { + case USB_EP_ATTR_CONTROL: + { + *pui32Flags |= USB_EP_MODE_CTRL; + break; + } + case USB_EP_ATTR_BULK: + { + *pui32Flags |= USB_EP_MODE_BULK; + break; + } + case USB_EP_ATTR_INT: + { + *pui32Flags |= USB_EP_MODE_INT; + break; + } + case USB_EP_ATTR_ISOC: + { + *pui32Flags |= USB_EP_MODE_ISOC; + break; + } + } +} + +//***************************************************************************** +// +//! Configure the USB controller appropriately for the device whose +//! configuration descriptor is passed. +//! +//! \param psDevInst is a pointer to the device instance being configured. +//! \param psConfig is a pointer to the configuration descriptor that the +//! USB controller is to be set up to support. +//! +//! This function may be used to initialize a USB controller to operate as +//! the device whose configuration descriptor is passed. The function +//! enables the USB controller, partitions the FIFO appropriately and +//! configures each endpoint required by the configuration. If the supplied +//! configuration supports multiple alternate settings for any interface, +//! the USB FIFO is set up assuming the worst case use (largest packet size +//! for a given endpoint in any alternate setting using that endpoint) to +//! allow for on-the-fly alternate setting changes later. On return from this +//! function, the USB controller is configured for correct operation of +//! the default configuration of the device described by the descriptor passed. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +USBDeviceConfig(tDCDInstance *psDevInst, const tConfigHeader *psConfig) +{ + uint32_t ui32Loop, ui32Count, ui32NumInterfaces, ui32EpIndex, ui32EpType, + ui32MaxPkt, ui32NumEndpoints, ui32Flags, ui32BytesUsed, + ui32Section; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + tUSBEndpointInfo psEPInfo[NUM_USB_EP - 1]; + + // + // A valid device instance is required. + // + ASSERT(psDevInst != 0); + + // + // Catch bad pointers in a debug build. + // + ASSERT(psConfig); + + // + // Clear out our endpoint info. + // + for(ui32Loop = 0; ui32Loop < (NUM_USB_EP - 1); ui32Loop++) + { + psEPInfo[ui32Loop].pui32Size[EP_INFO_IN] = 0; + psEPInfo[ui32Loop].pui32Size[EP_INFO_OUT] = 0; + } + + // + // How many (total) endpoints does this configuration describe? + // + ui32NumEndpoints = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_ENDPOINT); + + // + // How many interfaces are included? + // + ui32NumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + // + // Look at each endpoint and determine the largest max packet size for + // each endpoint. This will determine how we partition the USB FIFO. + // + for(ui32Loop = 0; ui32Loop < ui32NumEndpoints; ui32Loop++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = (tEndpointDescriptor *)USBDCDConfigDescGet( + psConfig, USB_DTYPE_ENDPOINT, ui32Loop, + &ui32Section); + + // + // Extract the endpoint number and whether it is an IN or OUT + // endpoint. + // + ui32EpIndex = (uint32_t) + psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + ui32EpType = (psEndpoint->bEndpointAddress & USB_EP_DESC_IN) ? + EP_INFO_IN : EP_INFO_OUT; + + // + // Make sure the endpoint number is valid for our controller. If not, + // return false to indicate an error. Note that 0 is invalid since + // you shouldn't reference endpoint 0 in the config descriptor. + // + if((ui32EpIndex >= NUM_USB_EP) || (ui32EpIndex == 0)) + { + return(false); + } + + // + // Does this endpoint have a max packet size requirement larger than + // any previous use we have seen? + // + if(psEndpoint->wMaxPacketSize > + psEPInfo[ui32EpIndex - 1].pui32Size[ui32EpType]) + { + // + // Yes - remember the new maximum packet size. + // + psEPInfo[ui32EpIndex - 1].pui32Size[ui32EpType] = + psEndpoint->wMaxPacketSize; + } + } + + // + // At this point, we have determined the maximum packet size required + // for each endpoint by any possible alternate setting of any interface + // in this configuration. Now determine the endpoint settings required + // for the interface setting we are actually going to use. + // + for(ui32Loop = 0; ui32Loop < ui32NumInterfaces; ui32Loop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32Loop, + USB_DESC_ANY, &ui32Section); + + // + // Is this the default interface (bAlternateSetting set to 0)? + // + if(psInterface && (psInterface->bAlternateSetting == 0)) + { + // + // This is an interface we are interested in so gather the + // information on its endpoints. + // + ui32NumEndpoints = (uint32_t)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in this interface and configure + // it appropriately. + // + for(ui32Count = 0; ui32Count < ui32NumEndpoints; ui32Count++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ui32Count); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ui32EpIndex, &ui32MaxPkt, + &ui32Flags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ui32EpIndex) + { + return(false); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + IndexToUSBEP(ui32EpIndex), + ui32MaxPkt, ui32Flags); + } + } + } + } + + // + // At this point, we have configured all the endpoints that are to be + // used by this configuration's alternate setting 0. Now we go on and + // partition the FIFO based on the maximum packet size information we + // extracted earlier. Endpoint 0 is automatically configured to use the + // first MAX_PACKET_SIZE_EP0 bytes of the FIFO so we start from there. + // + ui32Count = MAX_PACKET_SIZE_EP0; + for(ui32Loop = 1; ui32Loop < NUM_USB_EP; ui32Loop++) + { + // + // Configure the IN endpoint at this index if it is referred to + // anywhere. + // + if(psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_IN]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ui32MaxPkt = GetEndpointFIFOSize( + psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_IN], + &ui32BytesUsed); + + // + // The FIFO space could not be allocated. + // + if(ui32BytesUsed == 0) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, IndexToUSBEP(ui32Loop), ui32Count, + ui32MaxPkt, USB_EP_DEV_IN); + ui32Count += ui32BytesUsed; + } + + // + // Configure the OUT endpoint at this index. + // + if(psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_OUT]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ui32MaxPkt = GetEndpointFIFOSize( + psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_OUT], + &ui32BytesUsed); + + // + // The FIFO space could not be allocated. + // + if(ui32BytesUsed == 0) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, IndexToUSBEP(ui32Loop), ui32Count, + ui32MaxPkt, USB_EP_DEV_OUT); + ui32Count += ui32BytesUsed; + } + + } + + // + // If we get to the end, all is well. + // + return(true); +} + +//***************************************************************************** +// +//! Configure the affected USB endpoints appropriately for one alternate +//! interface setting. +//! +//! \param psDevInst is a pointer to the device instance being configured. +//! \param psConfig is a pointer to the configuration descriptor that contains +//! the interface whose alternate settings is to be configured. +//! \param ui8InterfaceNum is the number of the interface whose alternate +//! setting is to be configured. This number corresponds to the +//! bInterfaceNumber field in the desired interface descriptor. +//! \param ui8AlternateSetting is the alternate setting number for the desired +//! interface. This number corresponds to the bAlternateSetting field in the +//! desired interface descriptor. +//! +//! This function may be used to reconfigure the endpoints of an interface +//! for operation in one of the interface's alternate settings. Note that this +//! function assumes that the endpoint FIFO settings will not need to change +//! and only the endpoint mode is changed. This assumption is valid if the +//! USB controller was initialized using a previous call to USBDCDConfig(). +//! +//! In reconfiguring the interface endpoints, any additional configuration +//! bits set in the endpoint configuration other than the direction (\b +//! USB_EP_DEV_IN or \b USB_EP_DEV_OUT) and mode (\b USB_EP_MODE_MASK) are +//! preserved. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +USBDeviceConfigAlternate(tDCDInstance *psDevInst, + const tConfigHeader *psConfig, + uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting) +{ + uint32_t ui32NumInterfaces, ui32NumEndpoints, ui32Loop, ui32Count, + ui32MaxPkt, ui32Flags, ui32Section, ui32EpIndex; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + + // + // How many interfaces are included in the descriptor? + // + ui32NumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + + for(ui32Loop = 0; ui32Loop < ui32NumInterfaces; ui32Loop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32Loop, + USB_DESC_ANY, &ui32Section); + + // + // Is this the default interface (bAlternateSetting set to 0)? + // + if(psInterface && + (psInterface->bInterfaceNumber == ui8InterfaceNum) && + (psInterface->bAlternateSetting == ui8AlternateSetting)) + { + // + // This is an interface we are interested in and the descriptor + // representing the alternate setting we want so go ahead and + // reconfigure the endpoints. + // + + // + // How many endpoints does this interface have? + // + ui32NumEndpoints = (uint32_t)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in turn. + // + for(ui32Count = 0; ui32Count < ui32NumEndpoints; ui32Count++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ui32Count); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ui32EpIndex, &ui32MaxPkt, + &ui32Flags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ui32EpIndex) + { + return(false); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + IndexToUSBEP(ui32EpIndex), + ui32MaxPkt, ui32Flags); + } + } + + // + // At this point, we have reconfigured the desired interface so + // return indicating all is well. + // + return(true); + } + } + + return(false); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbddfu-rt.c b/usblib/device/usbddfu-rt.c new file mode 100644 index 0000000..24adb26 --- /dev/null +++ b/usblib/device/usbddfu-rt.c @@ -0,0 +1,661 @@ +//***************************************************************************** +// +// usbddfu-rt.c - USB Device Firmware Update runtime device class driver. +// +// Copyright (c) 2010-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_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_nvic.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "driverlib/sysctl.h" +#include "driverlib/systick.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbdfu.h" +#include "usblib/usb-ids.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbddfu-rt.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup dfu_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// DFU Device Descriptor. This is a dummy structure since runtime DFU must be +// a part of a composite device and cannot be instantiated on its own. +// +//***************************************************************************** +const uint8_t g_pui8DFUDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts + // assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_VEND_SPECIFIC, // USB Device Class + 0, // USB Device Sub-class + 0, // USB Device protocol + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0), // Device Release Number BCD. + 0, // Manufacturer string identifier. + 0, // Product string identifier. + 0, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// DFU device runtime configuration descriptor. This is also a dummy structure +// since the primary device class configuration will be used when DFU is added +// to the composite device. +// +//***************************************************************************** +uint8_t g_pui8DFUConfigDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(27), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 0, // The string identifier that describes + // this configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake + // up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The DFU runtime interface descriptor. +// +//***************************************************************************** +uint8_t g_pui8DFUInterface[DFUINTERFACE_SIZE] = +{ + // + // Interface descriptor for runtime DFU operation. + // + 9, // Length of this descriptor. + USB_DTYPE_INTERFACE, // This is an interface descriptor. + 0, // Interface number . + 0, // Alternate setting number. + 0, // Number of endpoints (only endpoint 0 + // used) + USB_CLASS_APP_SPECIFIC, // Application specific interface class + USB_DFU_SUBCLASS, // Device Firmware Upgrade subclass + USB_DFU_RUNTIME_PROTOCOL, // DFU runtime protocol + 0, // No string descriptor for this interface. +}; + +//***************************************************************************** +// +// The DFU functional descriptor. +// +//***************************************************************************** +uint8_t g_pui8DFUFunctionalDesc[DFUFUNCTIONALDESC_SIZE] = +{ + // + // Device Firmware Upgrade functional descriptor. + // + 9, // Length of this descriptor. + USB_DFU_FUNC_DESCRIPTOR_TYPE, // DFU Functional descriptor type + (DFU_ATTR_CAN_DOWNLOAD | // DFU attributes. + DFU_ATTR_CAN_UPLOAD | + DFU_ATTR_WILL_DETACH | + DFU_ATTR_MANIFEST_TOLERANT), + USBShort(0xFFFF), // Detach timeout (set to maximum). + USBShort(DFU_TRANSFER_SIZE), // Transfer size 1KB. + USBShort(0x0110) // DFU Version 1.1 +}; + +//***************************************************************************** +// +// The DFU runtime configuration descriptor is defined as two sections. +// These sections are: +// +// 1. The 9 byte configuration descriptor. +// 2. The interface descriptor + DFU functional descriptor. +// +//***************************************************************************** +const tConfigSection g_sDFUConfigSection = +{ + sizeof(g_pui8DFUConfigDescriptor), + g_pui8DFUConfigDescriptor +}; + +const tConfigSection g_sDFUInterfaceSection = +{ + sizeof(g_pui8DFUInterface), + g_pui8DFUInterface +}; + +const tConfigSection g_sDFUFunctionalDescSection = +{ + sizeof(g_pui8DFUFunctionalDesc), + g_pui8DFUFunctionalDesc +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete DFU runtime configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psDFUSections[] = +{ + &g_sDFUConfigSection, + &g_sDFUInterfaceSection, + &g_sDFUFunctionalDescSection +}; + +#define NUM_DFU_SECTIONS (sizeof(g_psDFUSections) / \ + sizeof(g_psDFUSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +tConfigHeader g_sDFUConfigHeader = +{ + NUM_DFU_SECTIONS, + g_psDFUSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppsDFUConfigDescriptors[] = +{ + &g_sDFUConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvDFUInstance, tUSBRequest *psUSBRequest); +static void HandleRequest(void *pvDFUInstance, tUSBRequest *psUSBRequest); +static void HandleDevice(void *pvDFUInstance, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB DFU devices. +// +//***************************************************************************** +static const tCustomHandlers g_sDFUHandlers = +{ + // + // GetDescriptor + // + HandleGetDescriptor, + + // + // RequestHandler + // + HandleRequest, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + 0, + + // + // DataReceived + // + 0, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + 0, + + // + // EndpointHandler + // + 0, + + // + // Device handler. + // + HandleDevice, +}; + +//***************************************************************************** +// +// Device instance specific handler. This callback received notifications of +// events related to handling interface, endpoint and string identifiers when +// a device is part of a composite device. In this case, the only resource we +// need which may be renumbered is the DFU runtime interface. +// +//***************************************************************************** +static void +HandleDevice(void *pvDFUInstance, uint32_t ui32Request, void *pvRequestData) +{ + tDFUInstance *psInst; + uint8_t *pui8Data; + + // + // Get a pointer to the DFU device instance data pointer + // + psInst = &((tUSBDDFUDevice *)pvDFUInstance)->sPrivateData; + + // + // Get a byte pointer to the data. + // + pui8Data = (uint8_t *)pvRequestData; + + // + // Which request event have we been passed? + // + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the interface number. + // + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // We are not interested in any other event. + // + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a request for a +// non-standard descriptor is received. +// +// \param pvDFUInstance is the instance data for this request. +// \param psUSBRequest points to the request received. +// +// This call parses the provided request structure and determines which +// descriptor is being requested. Assuming the descriptor can be found, it is +// scheduled for transmission via endpoint zero. If the descriptor cannot be +// found, the endpoint is stalled to indicate an error to the host. +// +//***************************************************************************** +static void +HandleGetDescriptor(void *pvDFUInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Size; + + ASSERT(pvDFUInstance != 0); + + // + // Which type of class descriptor are we being asked for? We only support + // 1 type - the DFU functional descriptor. + // + if(((psUSBRequest->wValue >> 8) == USB_DFU_FUNC_DESCRIPTOR_TYPE) && + ((psUSBRequest->wValue & 0xFF) == 0)) + { + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if((uint16_t)g_pui8DFUFunctionalDesc[0] > psUSBRequest->wLength) + { + ui32Size = (uint32_t)psUSBRequest->wLength; + } + else + { + ui32Size = (uint32_t)g_pui8DFUFunctionalDesc[0]; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, g_pui8DFUFunctionalDesc, ui32Size); + } + else + { + // + // This was an unknown or invalid request so stall. + // + USBDCDStallEP0(0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvDFUInstance is the instance data for this HID device. +// \param psUSBRequest points to the request received. +// +// This call parses the provided request structure. Assuming the request is +// understood, it is handled and any required response generated. If the +// request cannot be handled by this device class, endpoint zero is stalled to +// indicate an error to the host. +// +//***************************************************************************** +static void +HandleRequest(void *pvDFUInstance, tUSBRequest *psUSBRequest) +{ + tDFUInstance *psInst; + tUSBDDFUDevice *psDevice; + + ASSERT(pvDFUInstance != 0); + + // + // Get a pointer to the DFU device structure + // + psDevice = pvDFUInstance; + + // + // Get a pointer to the DFU device instance data pointer + // + psInst = &psDevice->sPrivateData; + + // + // Make sure the request was for this interface. + // + if(psUSBRequest->wIndex != psInst->ui8Interface) + { + return; + } + + // + // Determine the type of request. + // + switch(psUSBRequest->bRequest) + { + // + // We have been asked to detach. In this case, we call back to the + // application telling it to tidy up and re-enter the boot loader. We + // rely upon it doing this on our behalf since this must be done from a + // non-interrupt context and this call is most likely in interrupt + // context. + // + case USBD_DFU_REQUEST_DETACH: + { + // + // Tell the application it's time to reenter the boot loader. + // + psDevice->pfnCallback(psDevice->pvCBData, USBD_DFU_EVENT_DETACH, + 0, (void *)0); + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +//! Initializes DFU device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for DFU runtime device operation. +//! \param psDFUDevice points to a structure containing parameters customizing +//! the operation of the DFU device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! The \e psCompEntry should point to the composite device entry to +//! initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB DFU APIs. +// +//***************************************************************************** +void * +USBDDFUCompositeInit(uint32_t ui32Index, tUSBDDFUDevice *psDFUDevice, + tCompositeEntry *psCompEntry) +{ + tDFUInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psDFUDevice); + ASSERT(psCompEntry != 0); + + // + // Get a pointer to the DFU device instance data pointer + // + psInst = &psDFUDevice->sPrivateData; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psDFUDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sDFUHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8DFUDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppsDFUConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + psInst->ui32USBBase = USB0_BASE; + psInst->bConnected = false; + psInst->ui8Interface = 0; + + // + // Initialize the device info structure for the DFU device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDFUDevice); +} + +//***************************************************************************** +// +//! Shuts down the DFU device. +//! +//! \param pvDFUInstance is the pointer to the device instance structure as +//! returned by USBDDFUCompositeInit(). +//! +//! This function terminates DFU operation for the instance supplied and +//! removes the device from the USB bus. +//! +//! Following this call, the \e pvDFUInstance instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDDFUCompositeTerm(void *pvDFUInstance) +{ + tDFUInstance *psInst; + + ASSERT(pvDFUInstance); + + // + // Get a pointer to our instance data. + // + psInst = &((tUSBDDFUDevice *)pvDFUInstance)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(0); + + psInst->ui32USBBase = 0; +} + +//***************************************************************************** +// +//! Removes the current USB device from the bus and transfers control to the +//! DFU boot loader. +//! +//! This function should be called from the application's main loop (i.e. not +//! in interrupt context) following a callback to the USB DFU callback function +//! notifying the application of a DETACH request from the host. The function +//! will prepare the system to switch to DFU mode and transfer control to the +//! boot loader in preparation for a firmware upgrade from the host. +//! +//! The application must ensure that it has completed all necessary shutdown +//! activities (saved any required data, etc.) before making this call since +//! the function will not return. +//! +//! \return This function does not return. +// +//***************************************************************************** +void +USBDDFUUpdateBegin(void) +{ + // + // Terminate the USB device and take us off the bus. + // + USBDCDTerm(0); + + // + // Disable all interrupts. + // + MAP_IntMasterDisable(); + + // + // We must make sure we turn off SysTick and its interrupt + // before entering the boot loader! + // + MAP_SysTickIntDisable(); + MAP_SysTickDisable(); + + // + // Disable all processor interrupts. Instead of disabling them + // one at a time, a direct write to NVIC is done to disable all + // peripheral interrupts. + // + HWREG(NVIC_DIS0) = 0xffffffff; + HWREG(NVIC_DIS1) = 0xffffffff; + + // + // Reset the USB peripheral + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_USB0); + + // + // Wait for about a second. + // + MAP_SysCtlDelay(MAP_SysCtlClockGet() / 3); + + // + // Re-enable interrupts at the NVIC level. + // + MAP_IntMasterEnable(); + + // + // Return control to the boot loader. This is a call to the SVC + // handler in the boot loader. + // + (*((void (*)(void))(*(uint32_t *)0x2c)))(); + + // + // Should never get here, but just in case. + // + while(1) + { + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbddfu-rt.h b/usblib/device/usbddfu-rt.h new file mode 100644 index 0000000..c344207 --- /dev/null +++ b/usblib/device/usbddfu-rt.h @@ -0,0 +1,184 @@ +//***************************************************************************** +// +// usbddfu-rt.h - Definitions used by runtime DFU class devices. +// +// Copyright (c) 2010-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. +// +//***************************************************************************** + +#ifndef __USBDDFURT_H__ +#define __USBDDFURT_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup dfu_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the size of the g_pui8DFUInterface array in bytes. +// +//***************************************************************************** +#define DFUINTERFACE_SIZE (9) + +//***************************************************************************** +// +// This is the size of the g_pui8DFUFunctionalDesc array in bytes. +// +//***************************************************************************** +#define DFUFUNCTIONALDESC_SIZE (9) + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the DFU runtime device. This does not +//! include the configuration descriptor which is automatically ignored by the +//! composite device class. +//! +//! This label is used to compute the value which will be passed to the +//! USBDCompositeInit function in the ui32Size parameter. +// +//***************************************************************************** +#define COMPOSITE_DDFU_SIZE (DFUINTERFACE_SIZE + DFUFUNCTIONALDESC_SIZE) + +//***************************************************************************** +// +//! This value is passed to the client via the callback function provided in +//! the tUSBDDFUDevice structure and indicates that the host has sent a DETACH +//! request to the DFU interface. This request indicates that the device detach +//! from the USB bus and reattach in DFU mode in preparation for a firmware +//! upgrade. Currently, this is the only event that the DFU runtime class +//! reports to the client. +//! +//! When this event is received, the client should call USBDDFUUpdateBegin() +//! from a non-interrupt context at its earliest opportunity. +// +//***************************************************************************** +#define USBD_DFU_EVENT_DETACH (USBD_DFU_EVENT_BASE + 0) + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for +// DFU devices. The memory for this structure is included in the +// sPrivateData field in the tUSBDDFUDevice structure passed in the +// USBDDFUCompositeInit() function. +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The DFU class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; + + // + // The connection status of the device. + // + bool bConnected; +} +tDFUInstance; + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the DFU device. Note that, unlike all other devices, this structure does +//! not contain any fields which configure the device descriptor sent back to +//! the host. The DFU runtime device class must be used as part of a composite +//! device since all it provides is the capability to signal the device to +//! switch into DFU mode in preparation for a firmware upgrade. Creating a +//! device with nothing but DFU runtime mode capability is rather pointless +//! so this is not supported. +// +//***************************************************************************** +typedef struct +{ + // + //! A pointer to the callback function which will be called to notify + //! the application of DETACH requests. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the pfnCallback function. + // + void * const pvCBData; + + // + //! The private instance data for this device class. This + //! memory must remain accessible for as long as the DFU device is in use + //! and must not be modified by any code outside the DFU class driver. + // + tDFUInstance sPrivateData; +} +tUSBDDFUDevice; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDDFUCompositeInit(uint32_t ui32Index, + tUSBDDFUDevice *psDFUDevice, + tCompositeEntry *psCompEntry); +extern void USBDDFUCompositeTerm(void *pvDFUInstance); +extern void USBDDFUUpdateBegin(void); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDDFURT_H__ diff --git a/usblib/device/usbdenum.c b/usblib/device/usbdenum.c new file mode 100644 index 0000000..94e4cce --- /dev/null +++ b/usblib/device/usbdenum.c @@ -0,0 +1,3190 @@ +//***************************************************************************** +// +// usbenum.c - Enumeration code to handle all endpoint zero traffic. +// +// Copyright (c) 2007-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_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_sysctl.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbulpi.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_iUSBMode; + +//***************************************************************************** +// +// Local functions prototypes. +// +//***************************************************************************** +static void USBDGetStatus(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDClearFeature(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetFeature(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetAddress(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDGetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDGetConfiguration(void *pvInstance, + tUSBRequest *psUSBRequest); +static void USBDSetConfiguration(void *pvInstance, + tUSBRequest *psUSBRequest); +static void USBDGetInterface(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetInterface(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSyncFrame(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDEP0StateTx(uint32_t ui32Index); +static void USBDEP0StateTxConfig(uint32_t ui32Index); +static int32_t USBDStringIndexFromRequest(uint16_t ui16Lang, + uint16_t ui16Index); + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Indices into the ppui8Halt array to select the IN or OUT endpoint group. +// +//***************************************************************************** +#define HALT_EP_IN 0 +#define HALT_EP_OUT 1 + +//***************************************************************************** +// +// Define the max packet size for endpoint zero. +// +//***************************************************************************** +#define EP0_MAX_PACKET_SIZE 64 + +//***************************************************************************** +// +// This is a flag used with g_sUSBDeviceState.ui32DevAddress to indicate that a +// device address change is pending. +// +//***************************************************************************** +#define DEV_ADDR_PENDING 0x80000000 + +//***************************************************************************** +// +// This label defines the default configuration number to use after a bus +// reset. This may be overridden by calling USBDCDSetDefaultConfiguration() +// during processing of the device reset handler if required. +// +//***************************************************************************** +#define DEFAULT_CONFIG_ID 1 + +//***************************************************************************** +// +// This label defines the number of milliseconds that the remote wake up signal +// must remain asserted before removing it. Section 7.1.7.7 of the USB 2.0 spec +// states that "the remote wake up device must hold the resume signaling for at +// least 1ms but for no more than 15ms" so 10mS seems a reasonable choice. +// +//***************************************************************************** +#define REMOTE_WAKEUP_PULSE_MS 10 + +//***************************************************************************** +// +// This label defines the number of milliseconds between the point where we +// assert the remote wake up signal and calling the client back to tell it that +// bus operation has been resumed. This value is based on the timings provided +// in section 7.1.7.7 of the USB 2.0 specification which indicates that the +// host (which takes over resume signaling when the device's initial signal is +// detected) must hold the resume signaling for at least 20mS. +// +//***************************************************************************** +#define REMOTE_WAKEUP_READY_MS 20 + +//***************************************************************************** +// +// The LPM states. +// +//***************************************************************************** +#define USBLIB_LPM_STATE_DISABLED 0x00000000 +#define USBLIB_LPM_STATE_AWAKE 0x00000001 +#define USBLIB_LPM_STATE_SLEEP 0x00000002 + +//***************************************************************************** +// +// The buffer for reading data coming into EP0 +// +//***************************************************************************** +static uint8_t g_pui8DataBufferIn[EP0_MAX_PACKET_SIZE]; + +//***************************************************************************** +// +// This is 480000000/60000000 or a PLL Divide of 8. +// +//***************************************************************************** +static uint32_t g_ui32PLLDiv = 8; + +//***************************************************************************** +// +// Holds the ULPI configuration. +// +//***************************************************************************** +static uint32_t g_ui32ULPISupport; + +//***************************************************************************** +// +// This is the instance data for the USB controller itself and not a USB +// device class. +// +//***************************************************************************** +tDCDInstance g_psDCDInst[1]; + +//***************************************************************************** +// +// This is the currently active class in use by USBLib. There is only one +// of these per USB controller and no device has more than one controller. +// +//***************************************************************************** +tDeviceInfo *g_ppsDevInfo[1]; + +//***************************************************************************** +// +// Function table to handle standard requests. +// +//***************************************************************************** +static const tStdRequest g_psUSBDStdRequests[] = +{ + USBDGetStatus, + USBDClearFeature, + 0, + USBDSetFeature, + 0, + USBDSetAddress, + USBDGetDescriptor, + USBDSetDescriptor, + USBDGetConfiguration, + USBDSetConfiguration, + USBDGetInterface, + USBDSetInterface, + USBDSyncFrame +}; + +//***************************************************************************** +// +// Functions accessible by USBLIB clients. +// +//***************************************************************************** + +//***************************************************************************** +// +//! Initialize an instance of the tDeviceInfo structure. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized. +//! \param psDeviceInfo is a pointer to the tDeviceInfo structure that needs +//! to be initialized. +//! +//! This function must be called by a USB device class +//! instance to initialize the basic tDeviceInfo required for all USB device +//! class modules. This is typically called in the initialization routine for +//! USB device class. For example in usbdaudio.c that supports USB device +//! audio classes, this function is called in the USBDAudioCompositeInit() +//! function which is used for both composite and non-composites instances of +//! the USB audio class. +//! +//! \note This function should not be called directly by applications. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDDeviceInfoInit(uint32_t ui32Index, tDeviceInfo *psDeviceInfo) +{ + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + ASSERT(psDeviceInfo != 0); + + // + // Save the USB interrupt number. + // + g_psDCDInst[0].ui32IntNum = INT_USB0_TM4C123; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_psDCDInst[0].ui32IntNum = INT_USB0_TM4C129; + } + + // + // Disable LPM support by default. + // + g_psDCDInst[0].ui32LPMState = 0; + + // + // Initialize a couple of fields in the device state structure. + // + g_psDCDInst[0].ui32Configuration = DEFAULT_CONFIG_ID; + g_psDCDInst[0].ui32DefaultConfiguration = DEFAULT_CONFIG_ID; + + g_psDCDInst[0].iEP0State = eUSBStateIdle; + + // + // Default to the state where remote wake up is disabled. + // + g_psDCDInst[0].ui8Status = 0; + g_psDCDInst[0].bRemoteWakeup = false; + + // + // Determine the self- or bus-powered state based on the flags the + // user provided. + // + g_psDCDInst[0].bPwrSrcSet = false; +} + +//***************************************************************************** +// +//! Initialize the USB library device control driver for a given hardware +//! controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized. +//! \param psDevice is a pointer to a structure containing information that +//! the USB library requires to support operation of this application's +//! device. The structure contains event handler callbacks and pointers to the +//! various standard descriptors that the device wishes to publish to the +//! host. +//! \param pvDCDCBData is the callback data for any device callbacks. +//! +//! This function must be called by a device class which wishes to operate +//! as a USB device and is not typically called by an application. This +//! function initializes the USB device control driver for the given +//! controller and saves the device information for future use. Prior to +//! returning from this function, the device is connected to the USB bus. +//! Following return, the caller can expect to receive a callback to the +//! supplied pfnResetHandler function when a host connects to the +//! device. The \e pvDCDCBData contains a pointer to data that is returned +//! with the DCD calls back to the function in the psDevice->psCallbacks() +//! functions. +//! +//! The device information structure passed in \e psDevice must remain +//! unchanged between this call and any matching call to USBDCDTerm() because +//! it is not copied by the USB library. +//! +//! The USBStackModeSet() function can be called with eUSBModeForceDevice in +//! order to cause the USB library to force the USB operating mode to a device +//! controller. This allows the application to used the USBVBUS and USBID pins +//! as GPIOs on devices that support forcing OTG to operate as a device only +//! controller. By default the USB library will assume that the USBVBUS and +//! USBID pins are configured as USB pins and not GPIOs. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDInit(uint32_t ui32Index, tDeviceInfo *psDevice, void *pvDCDCBData) +{ + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + ASSERT(psDevice != 0); + + g_ppsDevInfo[0] = psDevice; + g_psDCDInst[0].pvCBData = pvDCDCBData; + + // + // Initialize the Device Info structure for a USB device instance. + // + USBDCDDeviceInfoInit(ui32Index, psDevice); + + // + // Should not call this if the stack is in host mode. + // + ASSERT(g_iUSBMode != eUSBModeHost); + ASSERT(g_iUSBMode != eUSBModeForceHost); + + // + // Default to device mode if no mode was set. + // + if(g_iUSBMode == eUSBModeNone) + { + g_iUSBMode = eUSBModeDevice; + } + + // + // Only do hardware update if the stack is in not in OTG mode. + // + if(g_iUSBMode != eUSBModeOTG) + { + // + // Reset the USB controller. + // + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Set the PLL to USB clock divider. + // + USBClockEnable(USB0_BASE, g_ui32PLLDiv, USB_CLOCK_INTERNAL); + + // + // Configure ULPI support. + // + if(g_ui32ULPISupport != USBLIB_FEATURE_ULPI_NONE) + { + USBULPIEnable(USB0_BASE); + + if(g_ui32ULPISupport & USBLIB_FEATURE_ULPI_HS) + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_HS); + } + else + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_FS); + } + } + else + { + USBULPIDisable(USB0_BASE); + } + + // + // Force device mode if requested. + // + if(g_iUSBMode == eUSBModeForceDevice) + { + MAP_USBDevMode(USB0_BASE); + } + else if(g_iUSBMode == eUSBModeDevice) + { + // + // To run in active device mode the OTG signals must be active. + // This allows disconnect to be detected by the controller. + // + MAP_USBOTGMode(USB0_BASE); + } + + // + // In all other cases, set the mode to device this function should not + // be called in OTG mode. + // + g_iUSBMode = eUSBModeDevice; + + // + // Enable or disable LPM functionality. + // + if(g_psDCDInst[0].ui32Features & USBLIB_FEATURE_LPM_EN) + { + // + // Enable full LPM support and all LPM related interrupts. + // USB_INTLPM_ERROR is not enabled since there is no response to + // this interrupt. + // + USBDevLPMConfig(USB0_BASE, USB_DEV_LPM_EN); + USBLPMIntEnable(USB0_BASE, USB_INTLPM_RESUME | USB_INTLPM_ERROR | + USB_INTLPM_ACK | USB_INTLPM_NYET); + USBDevLPMEnable(USB0_BASE); + + // + // Awake by default. + // + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_AWAKE; + } + else + { + USBDevLPMDisable(USB0_BASE); + USBDevLPMConfig(USB0_BASE, USB_DEV_LPM_NONE); + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_DISABLED; + } + } + + // + // Initialize the USB DMA interface. + // + g_psDCDInst[0].psDMAInstance = USBLibDMAInit(0); + + // + // Initialize the USB tick module. + // + InternalUSBTickInit(); + + // + // Get a pointer to the default configuration descriptor. + // + psHdr = psDevice->ppsConfigDescriptors[ + g_psDCDInst[0].ui32DefaultConfiguration - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pui8Data); + + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == USB_CONF_ATTR_SELF_PWR) + { + g_psDCDInst[0].ui8Status |= USB_STATUS_SELF_PWR; + } + else + { + g_psDCDInst[0].ui8Status &= ~USB_STATUS_SELF_PWR; + } + + // + // Only do hardware update if the stack is not in OTG mode. + // + if(g_iUSBMode != eUSBModeOTG) + { + // + // Get the current interrupt status.to clear all pending USB + // interrupts. + // + MAP_USBIntStatusControl(USB0_BASE); + MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Enable USB Interrupts. + // + MAP_USBIntEnableControl(USB0_BASE, USB_INTCTRL_RESET | + USB_INTCTRL_DISCONNECT | + USB_INTCTRL_RESUME | + USB_INTCTRL_SUSPEND | + USB_INTCTRL_SOF); + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Attach the device using the soft connect. + // + MAP_USBDevConnect(USB0_BASE); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_psDCDInst[0].ui32IntNum); + } +} + +//***************************************************************************** +// +//! Free the USB library device control driver for a given hardware controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! freed. +//! +//! This function should be called by an application if it no longer requires +//! the use of a given USB controller to support its operation as a USB device. +//! It frees the controller for use by another client. +//! +//! It is the caller's responsibility to remove its device from the USB bus +//! prior to calling this function. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDTerm(uint32_t ui32Index) +{ + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + + // + // Disable the USB interrupts. + // + OS_INT_DISABLE(g_psDCDInst[0].ui32IntNum); + + // + // Reset the tick handlers so that they can be reconfigured when and if + // USBDCDInit() is called. + // + InternalUSBTickReset(); + + // + // No active device. + // + g_ppsDevInfo[0] = 0; + + MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL); + MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Detach the device using the soft connect. + // + MAP_USBDevDisconnect(USB0_BASE); + + // + // Clear any pending interrupts. + // + MAP_USBIntStatusControl(USB0_BASE); + MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Turn off USB Phy clock. + // + MAP_SysCtlUSBPLLDisable(); + + // + // Disable the USB peripheral + // + MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_USB0); +} + +//***************************************************************************** +// +//! This function starts the request for data from the host on endpoint zero. +//! +//! \param ui32Index is the index of the USB controller from which the data +//! is being requested. +//! \param pui8Data is a pointer to the buffer to fill with data from the USB +//! host. +//! \param ui32Size is the size of the buffer or data to return from the USB +//! host. +//! +//! This function handles retrieving data from the host when a custom command +//! has been issued on endpoint zero. If the application needs notification +//! when the data has been received, +//! psCallbacks->pfnDataReceived() in the tDeviceInfo structure +//! must contain valid function pointer. In nearly all cases this is necessary +//! because the caller of this function would likely need to know that the data +//! requested was received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDRequestDataEP0(uint32_t ui32Index, uint8_t *pui8Data, uint32_t ui32Size) +{ + ASSERT(ui32Index == 0); + + // + // Enter the RX state on end point 0. + // + g_psDCDInst[0].iEP0State = eUSBStateRx; + + // + // Save the pointer to the data. + // + g_psDCDInst[0].pui8EP0Data = pui8Data; + + // + // Location to save the current number of bytes received. + // + g_psDCDInst[0].ui32OUTDataSize = ui32Size; + + // + // Bytes remaining to be received. + // + g_psDCDInst[0].ui32EP0DataRemain = ui32Size; +} + +//***************************************************************************** +// +//! This function requests transfer of data to the host on endpoint zero. +//! +//! \param ui32Index is the index of the USB controller which is to be used to +//! send the data. +//! \param pui8Data is a pointer to the buffer to send via endpoint zero. +//! \param ui32Size is the amount of data to send in bytes. +//! +//! This function handles sending data to the host when a custom command is +//! issued or non-standard descriptor has been requested on endpoint zero. If +//! the application needs notification when this is complete, +//! psCallbacks->pfnDataSent in the tDeviceInfo structure must +//! contain a valid function pointer. This callback could be used to free up +//! the buffer passed into this function in the \e pui8Data parameter. The +//! contents of the \e pui8Data buffer must remain unchanged until the +//! pfnDataSent callback is received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSendDataEP0(uint32_t ui32Index, uint8_t *pui8Data, uint32_t ui32Size) +{ + ASSERT(ui32Index == 0); + + // + // Return the externally provided device descriptor. + // + g_psDCDInst[0].pui8EP0Data = pui8Data; + + // + // The size of the device descriptor is in the first byte. + // + g_psDCDInst[0].ui32EP0DataRemain = ui32Size; + + // + // Save the total size of the data sent. + // + g_psDCDInst[0].ui32OUTDataSize = ui32Size; + + // + // Now in the transmit data state. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +//! This function sets the default configuration for the device. +//! +//! \param ui32Index is the index of the USB controller whose default +//! configuration is to be set. +//! \param ui32DefaultConfig is the configuration identifier (byte 6 of the +//! standard configuration descriptor) which is to be presented to the host +//! as the default configuration in cases where the configuration descriptor is +//! queried prior to any specific configuration being set. +//! +//! This function allows a device to override the default configuration +//! descriptor that will be returned to a host whenever it is queried prior +//! to a specific configuration having been set. The parameter passed must +//! equal one of the configuration identifiers found in the +//! ppsConfigDescriptors array for the device. +//! +//! If this function is not called, the USB library will return the first +//! configuration in the ppsConfigDescriptors array as the default +//! configuration. +//! +//! \note The USB device stack assumes that the configuration IDs (byte 6 of +//! the configuration descriptor, bConfigurationValue) stored within +//! the configuration descriptor array, ppsConfigDescriptors, +//! are equal to the array index + 1. In other words, the first entry in the +//! array must contain a descriptor with bConfigurationValue 1, the +//! second must have bConfigurationValue 2 and so on. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSetDefaultConfiguration(uint32_t ui32Index, uint32_t ui32DefaultConfig) +{ + ASSERT(ui32Index == 0); + + g_psDCDInst[0].ui32DefaultConfiguration = ui32DefaultConfig; +} + +//***************************************************************************** +// +//! This function generates a stall condition on endpoint zero. +//! +//! \param ui32Index is the index of the USB controller whose endpoint zero is +//! to be stalled. +//! +//! This function is typically called to signal an error condition to the host +//! when an unsupported request is received by the device. It should be +//! called from within the callback itself (in interrupt context) and not +//! deferred until later since it affects the operation of the endpoint zero +//! state machine in the USB library. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDStallEP0(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Stall the endpoint in question. + // + MAP_USBDevEndpointStall(USB0_BASE, USB_EP_0, USB_EP_DEV_OUT); + + // + // Enter the stalled state. + // + g_psDCDInst[0].iEP0State = eUSBStateStall; +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the library. +//! +//! \param ui32Index is the index of the USB controller whose device power +//! status is being reported. +//! \param ui8Power indicates the current power status, either +//! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the library to allow correct responses to be provided when +//! the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDPowerStatusSet(uint32_t ui32Index, uint8_t ui8Power) +{ + // + // Check for valid parameters. + // + ASSERT((ui8Power == USB_STATUS_BUS_PWR) || + (ui8Power == USB_STATUS_SELF_PWR)); + ASSERT(ui32Index == 0); + + // + // Update the device status with the new power status flag. + // + g_psDCDInst[0].bPwrSrcSet = true; + g_psDCDInst[0].ui8Status &= ~USB_STATUS_PWR_M; + g_psDCDInst[0].ui8Status |= ui8Power; +} +#endif + +//***************************************************************************** +// +//! This function is used to enable/disable features of the USB library. +//! +//! \param ui32Index is the index of the USB controller whose device power +//! status is being reported. +//! \param ui32Feature indicates which feature is being changed. +//! \param pvFeature holds the data that controls the feature request. +//! +//! Applications can change the support levels of some USB library features by +//! calling this function to enable/disable certain features. This function +//! should normally be called before class initialization functions since the +//! settings need to be in place before enumeration starts. This allows the +//! USB library to properly respond to all enumeration requests. The +//! \e ui32Feature value is one of the \b USBLIB_FEATURE_* defines which +//! controls the type of request being made. The \e pvFeature is a feature +//! specific data structure that is determined by the value passed in the +//! \e ui32Feature parameter. +//! +//! \return Returns \b true if the feature was successfully changed and returns +//! \b false if the feature was not able to be changed or is not supported. +// +//***************************************************************************** +bool +USBDCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, void *pvFeature) +{ + bool bRetCode; + tLPMFeature *psLPMFeature; + + bRetCode = true; + + switch(ui32Feature) + { + case USBLIB_FEATURE_LPM: + { + // + // Save the LPM setting. + // + psLPMFeature = (tLPMFeature *)pvFeature; + + if(psLPMFeature->ui32Features & USBLIB_FEATURE_LPM_EN) + { + g_psDCDInst[0].ui32Features |= USBLIB_FEATURE_LPM_EN; + } + else + { + g_psDCDInst[0].ui32Features &= ~USBLIB_FEATURE_LPM_EN; + } + + break; + } + case USBLIB_FEATURE_USBPLL: + { + // + // If the PLL rate is not evenly divisible by 60MHz then + // do not set it. + // + if((*(uint32_t *)pvFeature % 60000000) != 0) + { + bRetCode = false; + } + else + { + // + // Save the new PLL rate. + // + g_ui32PLLDiv = (*(uint32_t *)pvFeature / 60000000); + } + break; + } + case USBLIB_FEATURE_USBULPI: + { + // + // Save the ULPI support level. + // + g_ui32ULPISupport = *(uint32_t *)pvFeature; + + break; + } + case USBLIB_FEATURE_POWER: + { + // + // Update the device status with the new power status flag. + // + g_psDCDInst[0].bPwrSrcSet = true; + g_psDCDInst[0].ui8Status &= ~USBLIB_FEATURE_POWER_SELF; + g_psDCDInst[0].ui8Status |= (uint8_t)(*(uint32_t *)pvFeature); + + break; + } + default: + { + bRetCode = false; + break; + } + } + return(bRetCode); +} + +//***************************************************************************** +// +//! Requests an LPM remote wake up to resume communication when in an LPM sleep +//! state. +//! +//! \param ui32Index is the index of the USB controller that will request +//! a bus wake up. +//! +//! When the host controller puts the device into an LPM sleep state, the +//! device can call this function to initiate LPM remote wake up signaling to +//! the host. If the remote wake up feature has been enabled by the host, this +//! will cause the host to respond to the LPM remote wake request and resume +//! normal operation. If the host has disabled remote wake up, \b false is +//! returned to indicate that the wake up request was not successful. +//! +//! \return Returns \b true if the remote wake up request has been sent or +//!\b false if LPM remote wake up is disabled. +// +//***************************************************************************** +bool +USBDCDRemoteWakeLPM(uint32_t ui32Index) +{ + if(USBLPMRemoteWakeEnabled(USB0_BASE)) + { + USBDevLPMRemoteWake(USB0_BASE); + return(true); + } + return(false); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param ui32Index is the index of the USB controller that will request +//! a bus wake up. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +bool +USBDCDRemoteWakeupRequest(uint32_t ui32Index) +{ + // + // Check for parameter validity. + // + ASSERT(ui32Index == 0); + + // + // Is remote wake up signaling currently enabled? + // + if(g_psDCDInst[0].ui8Status & USB_STATUS_REMOTE_WAKE) + { + // + // The host has not disabled remote wake up. Are we still in the + // middle of a previous wake up sequence? + // + if(!g_psDCDInst[0].bRemoteWakeup) + { + // + // No - we are not in the middle of a wake up sequence so start + // one here. + // + g_psDCDInst[0].ui8RemoteWakeupCount = 0; + g_psDCDInst[0].bRemoteWakeup = true; + MAP_USBHostResume(USB0_BASE, true); + return(true); + } + } + + // + // If we drop through to here, signaling was not initiated so return + // false. + return(false); +} + +//***************************************************************************** +// +// Internal Functions, not to be called by applications +// +//***************************************************************************** + +//***************************************************************************** +// +// This internal function is called on the SOF interrupt to process any +// outstanding remote wake up requests. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceResumeTickHandler(tDCDInstance *psDevInst) +{ + if(g_psDCDInst[0].bRemoteWakeup) + { + // + // Increment the millisecond counter we use to time the resume + // signaling. + // + g_psDCDInst[0].ui8RemoteWakeupCount++; + + // + // Have we reached the 10mS mark? If so, we need to turn the signaling + // off again. + // + if(g_psDCDInst[0].ui8RemoteWakeupCount == REMOTE_WAKEUP_PULSE_MS) + { + MAP_USBHostResume(USB0_BASE, false); + } + + // + // Have we reached the point at which we can tell the client that the + // bus has resumed? The controller does not give us an interrupt if we + // initiated the wake up signaling so we just wait until 20mS have + // passed then tell the client all is well. + // + if(g_psDCDInst[0].ui8RemoteWakeupCount == REMOTE_WAKEUP_READY_MS) + { + // + // We are now finished with the remote wake up signaling. + // + g_psDCDInst[0].bRemoteWakeup = false; + + // + // If the client has registered a resume callback, call it. In the + // case of a remote wake up request, we do not get a resume + // interrupt from the controller so we need to fake it here. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler( + g_psDCDInst[0].pvCBData); + } + } + } +} + +//***************************************************************************** +// +// This internal function reads a request data packet and dispatches it to +// either a standard request handler or the registered device request +// callback depending upon the request type. +// +// \return None. +// +//***************************************************************************** +static void +USBDReadAndDispatchRequest(uint32_t ui32Index) +{ + uint32_t ui32Size; + tUSBRequest *psRequest; + + // + // Cast the buffer to a request structure. + // + psRequest = (tUSBRequest *)g_pui8DataBufferIn; + + // + // Set the buffer size. + // + ui32Size = EP0_MAX_PACKET_SIZE; + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, g_pui8DataBufferIn, + &ui32Size); + + // + // If there was a null setup packet then just return. + // + if(!ui32Size) + { + return; + } + + // + // See if this is a standard request or not. + // + if((psRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD) + { + // + // Since this is not a standard request, see if there is + // an external handler present. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnRequestHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnRequestHandler( + g_psDCDInst[0].pvCBData, + psRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } + else + { + // + // Assure that the jump table is not out of bounds. + // + if((psRequest->bRequest < + (sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) && + (g_psUSBDStdRequests[psRequest->bRequest] != 0)) + { + // + // Jump table to the appropriate handler. + // + g_psUSBDStdRequests[psRequest->bRequest](&g_psDCDInst[0], + psRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } +} + +//***************************************************************************** +// +// This is interrupt handler for endpoint zero. +// +// This function handles all interrupts on endpoint zero in order to maintain +// the state needed for the control endpoint on endpoint zero. In order to +// successfully enumerate and handle all USB standard requests, all requests +// on endpoint zero must pass through this function. The endpoint has the +// following states: \b eUSBStateIdle, \b eUSBStateTx, \b eUSBStateRx, +// \b eUSBStateStall, and \b eUSBStateStatus. In the \b eUSBStateIdle +// state the USB controller has not received the start of a request, and once +// it does receive the data for the request it will either enter the +// \b eUSBStateTx, \b eUSBStateRx, or \b eUSBStateStall depending on the +// command. If the controller enters the \b eUSBStateTx or \b eUSBStateRx +// then once all data has been sent or received, it must pass through the +// \b eUSBStateStatus state to allow the host to acknowledge completion of +// the request. The \b eUSBStateStall is entered from \b eUSBStateIdle in +// the event that the USB request was not valid. Both the \b eUSBStateStall +// and \b eUSBStateStatus are transitional states that return to the +// \b eUSBStateIdle state. +// +// \return None. +// +// eUSBStateIdle -*--> eUSBStateTx -*-> eUSBStateStatus -*->eUSBStateIdle +// | | | +// |--> eUSBStateRx | +// | | +// |--> eUSBStateStall ---------->-------- +// +// ---------------------------------------------------------------- +// | Current State | State 0 | State 1 | +// | --------------------|-------------------|---------------------- +// | eUSBStateIdle | eUSBStateTx/RX | eUSBStateStall | +// | eUSBStateTx | eUSBStateStatus | | +// | eUSBStateRx | eUSBStateStatus | | +// | eUSBStateStatus | eUSBStateIdle | | +// | eUSBStateStall | eUSBStateIdle | | +// ---------------------------------------------------------------- +// +//***************************************************************************** +void +USBDeviceEnumHandler(tDCDInstance *pDevInstance) +{ + uint32_t ui32EPStatus, ui32DataSize; + + // + // Get the end point 0 status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + switch(pDevInstance->iEP0State) + { + // + // Handle the status state, this is a transitory state from + // eUSBStateTx or eUSBStateRx back to eUSBStateIdle. + // + case eUSBStateStatus: + { + // + // Just go back to the idle state. + // + pDevInstance->iEP0State = eUSBStateIdle; + + // + // If there is a pending address change then set the address. + // + if(pDevInstance->ui32DevAddress & DEV_ADDR_PENDING) + { + // + // Clear the pending address change and set the address. + // + pDevInstance->ui32DevAddress &= ~DEV_ADDR_PENDING; + MAP_USBDevAddrSet(USB0_BASE, pDevInstance->ui32DevAddress); + } + + // + // If a new packet is already pending, we need to read it + // and handle whatever request it contains. + // + if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Process the newly arrived packet. + // + USBDReadAndDispatchRequest(0); + } + break; + } + + // + // In the IDLE state the code is waiting to receive data from the host. + // + case eUSBStateIdle: + { + // + // Is there a packet waiting for us? + // + if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Yes - process it. + // + USBDReadAndDispatchRequest(0); + } + break; + } + + // + // Data is still being sent to the host so handle this in the + // EP0StateTx() function. + // + case eUSBStateTx: + { + USBDEP0StateTx(0); + break; + } + + // + // We are still in the middle of sending the configuration descriptor + // so handle this in the EP0StateTxConfig() function. + // + case eUSBStateTxConfig: + { + USBDEP0StateTxConfig(0); + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint zero. + // + case eUSBStateRx: + { + // + // Set the number of bytes to get out of this next packet. + // + if(pDevInstance->ui32EP0DataRemain > EP0_MAX_PACKET_SIZE) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ui32DataSize = EP0_MAX_PACKET_SIZE; + } + else + { + // + // There was space so send the remaining bytes. + // + ui32DataSize = pDevInstance->ui32EP0DataRemain; + } + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, + pDevInstance->pui8EP0Data, &ui32DataSize); + + // + // If there we not more that EP0_MAX_PACKET_SIZE or more bytes + // remaining then this transfer is complete. If there were exactly + // EP0_MAX_PACKET_SIZE remaining then there still needs to be + // null packet sent before this is complete. + // + if(pDevInstance->ui32EP0DataRemain < EP0_MAX_PACKET_SIZE) + { + // + // Return to the idle state. + // + pDevInstance->iEP0State = eUSBStateStatus; + + // + // If there is a receive callback then call it. + // + if((g_ppsDevInfo[0]->psCallbacks->pfnDataReceived) && + (pDevInstance->ui32OUTDataSize != 0)) + { + // + // Call the custom receive handler to handle the data + // that was received. + // + g_ppsDevInfo[0]->psCallbacks->pfnDataReceived( + g_psDCDInst[0].pvCBData, + pDevInstance->ui32OUTDataSize); + + // + // Indicate that there is no longer any data being waited + // on. + // + pDevInstance->ui32OUTDataSize = 0; + } + + // + // Need to ACK the data on end point 0 in this case and set the + // data end as this is the last of the data. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + } + else + { + // + // Need to ACK the data on end point 0 in this case + // without setting data end because more data is coming. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + } + + // + // Advance the pointer. + // + pDevInstance->pui8EP0Data += ui32DataSize; + + // + // Decrement the number of bytes that are being waited on. + // + pDevInstance->ui32EP0DataRemain -= ui32DataSize; + + break; + } + // + // The device stalled endpoint zero so check if the stall needs to be + // cleared once it has been successfully sent. + // + case eUSBStateStall: + { + // + // If we sent a stall then acknowledge this interrupt. + // + if(ui32EPStatus & USB_DEV_EP0_SENT_STALL) + { + // + // Clear the Setup End condition. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, USB_EP_0, + USB_DEV_EP0_SENT_STALL); + + // + // Reset the global end point 0 state to IDLE. + // + pDevInstance->iEP0State = eUSBStateIdle; + + } + break; + } + // + // Halt on an unknown state, but only in DEBUG mode builds. + // + default: + { + ASSERT(0); + break; + } + } +} + +//***************************************************************************** +// +// This function handles bus reset notifications. +// +// This function is called from the low level USB interrupt handler whenever +// a bus reset is detected. It performs tidy-up as required and resets the +// configuration back to defaults in preparation for descriptor queries from +// the host. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceEnumResetHandler(tDCDInstance *pDevInstance) +{ + uint32_t ui32Loop; + + // + // Disable remote wake up signaling (as per USB 2.0 spec 9.1.1.6). + // + pDevInstance->ui8Status &= ~USB_STATUS_REMOTE_WAKE; + pDevInstance->bRemoteWakeup = false; + + // + // Call the device dependent code to indicate a bus reset has occurred. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnResetHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnResetHandler(g_psDCDInst[0].pvCBData); + } + + // + // Reset the default configuration identifier and alternate function + // selections. + // + pDevInstance->ui32Configuration = pDevInstance->ui32DefaultConfiguration; + + for(ui32Loop = 0; ui32Loop < USB_MAX_INTERFACES_PER_DEVICE; ui32Loop++) + { + pDevInstance->pui8AltSetting[ui32Loop] = (uint8_t)0; + } +} + +//***************************************************************************** +// +// This function handles the GET_STATUS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the request type and endpoint number if endpoint +// status is requested. +// +// This function handles responses to a Get Status request from the host +// controller. A status request can be for the device, an interface or an +// endpoint. If any other type of request is made this function will cause +// a stall condition to indicate that the command is not supported. The +// \e psUSBRequest structure holds the type of the request in the +// bmRequestType field. If the type indicates that this is a request for an +// endpoint's status, then the wIndex field holds the endpoint number. +// +// \return None. +// +//***************************************************************************** +static void +USBDGetStatus(void *pvInstance, tUSBRequest *psUSBRequest) +{ + uint16_t ui16Data, ui16Index; + uint32_t ui32Dir; + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Determine what type of status was requested. + // + switch(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This was a Device Status request. + // + case USB_RTYPE_DEVICE: + { + // + // Return the current status for the device. + // + ui16Data = (uint16_t)psUSBControl->ui8Status; + + break; + } + + // + // This was a Interface status request. + // + case USB_RTYPE_INTERFACE: + { + // + // Interface status always returns 0. + // + ui16Data = (uint16_t)0; + + break; + } + + // + // This was an endpoint status request. + // + case USB_RTYPE_ENDPOINT: + { + // + // Which endpoint are we dealing with? + // + ui16Index = psUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Check if this was a valid endpoint request. + // + if((ui16Index == 0) || (ui16Index >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + return; + } + else + { + // + // Are we dealing with an IN or OUT endpoint? + // + ui32Dir = ((psUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Get the current halt status for this endpoint. + // + ui16Data = + (uint16_t)psUSBControl->ppui8Halt[ui32Dir][ui16Index - 1]; + } + break; + } + + // + // This was an unknown request. + // + default: + { + // + // Anything else causes a stall condition to indicate that the + // command was not supported. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the two byte status response. + // + psUSBControl->ui32EP0DataRemain = 2; + psUSBControl->pui8EP0Data = (uint8_t *)&ui16Data; + + // + // Send the response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the CLEAR_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the options for the Clear Feature USB request. +// +// This function handles device or endpoint clear feature requests. The +// \e psUSBRequest structure holds the type of the request in the bmRequestType +// field and the feature is held in the wValue field. The device can only +// clear the Remote Wake feature. This device request should only be made if +// the descriptor indicates that Remote Wake is implemented by the device. +// Endpoints can only clear a halt on a given endpoint. If any other +// requests are made, then the device will stall the request to indicate to +// the host that the command was not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDClearFeature(void *pvInstance, tUSBRequest *psUSBRequest) +{ + tDCDInstance *psUSBControl; + uint32_t ui32Dir; + uint16_t ui16Index; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Determine what type of status was requested. + // + switch(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This is a clear feature request at the device level. + // + case USB_RTYPE_DEVICE: + { + // + // Only remote wake is can be cleared by this function. + // + if(USB_FEATURE_REMOTE_WAKE & psUSBRequest->wValue) + { + // + // Clear the remote wake up state. + // + psUSBControl->ui8Status &= ~USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a clear feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + // + // Which endpoint are we dealing with? + // + ui16Index = psUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint. + // + if((ui16Index == 0) || (ui16Index > NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the halt feature is supported. + // + if(USB_FEATURE_EP_HALT == psUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ui32Dir = ((psUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Clear the halt condition on this endpoint. + // + psUSBControl->ppui8Halt[ui32Dir][ui16Index - 1] = 0; + + if(ui32Dir == HALT_EP_IN) + { + MAP_USBDevEndpointStallClear(USB0_BASE, + IndexToUSBEP(ui16Index), + USB_EP_DEV_IN); + } + else + { + MAP_USBDevEndpointStallClear(USB0_BASE, + IndexToUSBEP(ui16Index), + USB_EP_DEV_OUT); + } + } + else + { + // + // If any other feature is requested, this is an error. + // + USBDCDStallEP0(0); + return; + } + } + break; + } + + // + // This is an unknown request. + // + default: + { + USBDCDStallEP0(0); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the feature in the wValue field of the USB +// request. +// +// This function handles device or endpoint set feature requests. The +// \e psUSBRequest structure holds the type of the request in the bmRequestType +// field and the feature is held in the wValue field. The device can only +// set the Remote Wake feature. This device request should only be made if the +// descriptor indicates that Remote Wake is implemented by the device. +// Endpoint requests can only issue a halt on a given endpoint. If any other +// requests are made, then the device will stall the request to indicate to the +// host that the command was not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetFeature(void *pvInstance, tUSBRequest *psUSBRequest) +{ + tDCDInstance *psUSBControl; + uint16_t ui16Index; + uint32_t ui32Dir; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Determine what type of status was requested. + // + switch(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This is a set feature request at the device level. + // + case USB_RTYPE_DEVICE: + { + // + // Only remote wake is the only feature that can be set by this + // function. + // + if(USB_FEATURE_REMOTE_WAKE & psUSBRequest->wValue) + { + // + // Set the remote wake up state. + // + psUSBControl->ui8Status |= USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a set feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + // + // Which endpoint are we dealing with? + // + ui16Index = psUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint? + // + if((ui16Index == 0) || (ui16Index >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the Halt feature can be set. + // + if(USB_FEATURE_EP_HALT == psUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ui32Dir = ((psUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Clear the halt condition on this endpoint. + // + psUSBControl->ppui8Halt[ui32Dir][ui16Index - 1] = 1; + } + else + { + // + // No other requests are supported. + // + USBDCDStallEP0(0); + return; + } + } + break; + } + + // + // This is an unknown request. + // + default: + { + USBDCDStallEP0(0); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_ADDRESS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the new address to use in the wValue field of the +// USB request. +// +// This function is called to handle the change of address request from the +// host controller. This can only start the sequence as the host must +// acknowledge that the device has changed address. Thus this function sets +// the address change as pending until the status phase of the request has +// been completed successfully. This prevents the devices address from +// changing and not properly responding to the status phase. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetAddress(void *pvInstance, tUSBRequest *psUSBRequest) +{ + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Save the device address as we cannot change address until the status + // phase is complete. + // + psUSBControl->ui32DevAddress = psUSBRequest->wValue | DEV_ADDR_PENDING; + + // + // Transition directly to the status state since there is no data phase + // for this request. + // + psUSBControl->iEP0State = eUSBStateStatus; +} + +//***************************************************************************** +// +// This function handles the GET_DESCRIPTOR standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the data for this request. +// +// This function will return most of the descriptors requested by the host +// controller. The descriptor specified by \e +// pvInstance->psInfo->pui8DeviceDescriptor will be returned when the device +// descriptor is requested. If a request for a specific configuration +// descriptor is made, then the appropriate descriptor from the \e +// g_pConfigDescriptors will be returned. When a request for a string +// descriptor is made, the appropriate string from the +// \e pvInstance->psInfo->pStringDescriptors will be returned. If the +// \e pvInstance->psInfo->psCallbacks->GetDescriptor is specified it will be +// called to handle the request. In this case it must call the +// USBDCDSendDataEP0() function to send the data to the host controller. If +// the callback is not specified, and the descriptor request is not for a +// device, configuration, or string descriptor then this function will stall +// the request to indicate that the request was not supported by the device. +// +// \return None. +// +//***************************************************************************** +static void +USBDGetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest) +{ + bool bConfig; + tDCDInstance *psUSBControl; + tDeviceInfo *psDevice; + const tConfigHeader *psConfig; + const tDeviceDescriptor *psDeviceDesc; + uint8_t ui8Index; + int32_t i32Index; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + psDevice = g_ppsDevInfo[0]; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Assume we are not sending the configuration descriptor until we + // determine otherwise. + // + bConfig = false; + + // + // Which descriptor are we being asked for? + // + switch(psUSBRequest->wValue >> 8) + { + // + // This request was for a device descriptor. + // + case USB_DTYPE_DEVICE: + { + // + // Return the externally provided device descriptor. + // + psUSBControl->pui8EP0Data = + (uint8_t *)psDevice->pui8DeviceDescriptor; + + // + // The size of the device descriptor is in the first byte. + // + psUSBControl->ui32EP0DataRemain = + psDevice->pui8DeviceDescriptor[0]; + + break; + } + + // + // This request was for a configuration descriptor. + // + case USB_DTYPE_CONFIGURATION: + { + // + // Which configuration are we being asked for? + // + ui8Index = (uint8_t)(psUSBRequest->wValue & 0xFF); + + // + // Is this valid? + // + psDeviceDesc = + (const tDeviceDescriptor *)psDevice->pui8DeviceDescriptor; + + if(ui8Index >= psDeviceDesc->bNumConfigurations) + { + // + // This is an invalid configuration index. Stall EP0 to + // indicate a request error. + // + USBDCDStallEP0(0); + psUSBControl->pui8EP0Data = 0; + psUSBControl->ui32EP0DataRemain = 0; + } + else + { + // + // Return the externally specified configuration descriptor. + // + psConfig = psDevice->ppsConfigDescriptors[ui8Index]; + + // + // Start by sending data from the beginning of the first + // descriptor. + // + psUSBControl->ui8ConfigSection = 0; + psUSBControl->ui16SectionOffset = 0; + psUSBControl->pui8EP0Data = + (uint8_t *)psConfig->psSections[0]->pui8Data; + + // + // Determine the total size of the configuration descriptor + // by counting the sizes of the sections comprising it. + // + psUSBControl->ui32EP0DataRemain = + USBDCDConfigDescGetSize(psConfig); + + // + // Remember that we need to send the configuration descriptor + // and which descriptor we need to send. + // + psUSBControl->ui8ConfigIndex = ui8Index; + + bConfig = true; + } + break; + } + + // + // This request was for a string descriptor. + // + case USB_DTYPE_STRING: + { + // + // Determine the correct descriptor index based on the requested + // language ID and index. + // + i32Index = USBDStringIndexFromRequest(psUSBRequest->wIndex, + psUSBRequest->wValue & 0xFF); + + // + // If the mapping function returned -1 then stall the request to + // indicate that the request was not valid. + // + if(i32Index == -1) + { + USBDCDStallEP0(0); + break; + } + + // + // Return the externally specified configuration descriptor. + // + psUSBControl->pui8EP0Data = + (uint8_t *)psDevice->ppui8StringDescriptors[i32Index]; + + // + // The total size of a string descriptor is in byte 0. + // + psUSBControl->ui32EP0DataRemain = + psDevice->ppui8StringDescriptors[i32Index][0]; + + break; + } + + // + // Any other request is not handled by the default enumeration handler + // so see if it needs to be passed on to another handler. + // + default: + { + // + // If there is a handler for requests that are not handled then + // call it. + // + if(psDevice->psCallbacks->pfnGetDescriptor) + { + psDevice->psCallbacks->pfnGetDescriptor(g_psDCDInst[0].pvCBData, + psUSBRequest); + } + else + { + // + // Whatever this was this handler does not understand it so + // just stall the request. + // + USBDCDStallEP0(0); + } + + return; + } + } + + // + // If this request has data to send, then send it. + // + if(psUSBControl->pui8EP0Data) + { + // + // If there is more data to send than is requested then just + // send the requested amount of data. + // + if(psUSBControl->ui32EP0DataRemain > psUSBRequest->wLength) + { + psUSBControl->ui32EP0DataRemain = psUSBRequest->wLength; + } + + // + // Now in the transmit data state. Be careful to call the correct + // function since we need to handle the configuration descriptor + // differently from the others. + // + if(!bConfig) + { + USBDEP0StateTx(0); + } + else + { + USBDEP0StateTxConfig(0); + } + } +} + +//***************************************************************************** +// +// This function determines which string descriptor to send to satisfy a +// request for a given index and language. +// +// \param ui16Lang is the requested string language ID. +// \param ui16Index is the requested string descriptor index. +// +// When a string descriptor is requested, the host provides a language ID and +// index to identify the string ("give me string number 5 in French"). This +// function maps these two parameters to an index within our device's string +// descriptor array which is arranged as multiple groups of strings with +// one group for each language advertised via string descriptor 0. +// +// We assume that there are an equal number of strings per language and +// that the first descriptor is the language descriptor and use this fact to +// perform the mapping. +// +// \return The index of the string descriptor to return or -1 if the string +// could not be found. +// +//***************************************************************************** +static int32_t +USBDStringIndexFromRequest(uint16_t ui16Lang, uint16_t ui16Index) +{ + tString0Descriptor *pLang; + uint32_t ui32NumLangs, ui32NumStringi16PerLang, ui32Loop; + + // + // Make sure we have a string table at all. + // + if((g_ppsDevInfo[0] == 0) || + (g_ppsDevInfo[0]->ppui8StringDescriptors == 0)) + { + return(-1); + } + + // + // First look for the trivial case where descriptor 0 is being + // requested. This is the special case since descriptor 0 contains the + // language codes supported by the device. + // + if(ui16Index == 0) + { + return(0); + } + + // + // How many languages does this device support? This is determined by + // looking at the length of the first descriptor in the string table, + // subtracting 2 for the header and dividing by two (the size of each + // language code). + // + ui32NumLangs = + (g_ppsDevInfo[0]->ppui8StringDescriptors[0][0] - 2) / 2; + + // + // We assume that the table includes the same number of strings for each + // supported language. We know the number of entries in the string table, + // so how many are there for each language? This may seem an odd way to + // do this (why not just have the application tell us in the device info + // structure?) but it's needed since we didn't want to change the API + // after the first release which did not support multiple languages. + // + ui32NumStringi16PerLang = + ((g_ppsDevInfo[0]->ui32NumStringDescriptors - 1) /ui32NumLangs); + + // + // Just to be sure, make sure that the calculation indicates an equal + // number of strings per language. We expect the string table to contain + // (1 + (strings_per_language * languages)) entries. + // + if((1 + (ui32NumStringi16PerLang * ui32NumLangs)) != + g_ppsDevInfo[0]->ui32NumStringDescriptors) + { + return(-1); + } + + // + // Now determine which language we are looking for. It is assumed that + // the order of the groups of strings per language in the table is the + // same as the order of the language IDs listed in the first descriptor. + // + pLang = (tString0Descriptor *) + (g_ppsDevInfo[0]->ppui8StringDescriptors[0]); + + // + // Look through the supported languages looking for the one we were asked + // for. + // + for(ui32Loop = 0; ui32Loop < ui32NumLangs; ui32Loop++) + { + // + // Have we found the requested language? + // + if(pLang->wLANGID[ui32Loop] == ui16Lang) + { + // + // Yes - calculate the index of the descriptor to send. + // + return((ui32NumStringi16PerLang * ui32Loop) + ui16Index); + } + } + + // + // If we drop out of the loop, the requested language was not found so + // return -1 to indicate the error. + // + return(-1); +} + +//***************************************************************************** +// +// This function handles the SET_DESCRIPTOR standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the data for this request. +// +// This function currently is not supported and will respond with a Stall +// to indicate that this command is not supported by the device. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest) +{ + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // This function is not handled by default. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This function handles the GET_CONFIGURATION standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the data for this request. +// +// This function responds to a host request to return the current +// configuration of the USB device. The function will send the configuration +// response to the host and return. This value will either be 0 or the last +// value received from a call to SetConfiguration(). +// +// \return None. +// +//***************************************************************************** +static void +USBDGetConfiguration(void *pvInstance, tUSBRequest *psUSBRequest) +{ + uint8_t ui8Value; + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // If we still have an address pending then the device is still not + // configured. + // + if(psUSBControl->ui32DevAddress & DEV_ADDR_PENDING) + { + ui8Value = 0; + } + else + { + ui8Value = (uint8_t)psUSBControl->ui32Configuration; + } + + psUSBControl->ui32EP0DataRemain = 1; + psUSBControl->pui8EP0Data = &ui8Value; + + // + // Send the single byte response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the SET_CONFIGURATION standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the data for this request. +// +// This function responds to a host request to change the current +// configuration of the USB device. The actual configuration number is taken +// from the structure passed in via \e psUSBRequest. This number should be one +// of the configurations that was specified in the descriptors. If the +// \e ConfigChange callback is specified in \e pvInstance->psInfo->psCallbacks-> +// it will be called so that the application can respond to a change in +// configuration. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetConfiguration(void *pvInstance, tUSBRequest *psUSBRequest) +{ + tDCDInstance *psUSBControl; + tDeviceInfo *psDevice; + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + psDevice = g_ppsDevInfo[0]; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Cannot set the configuration to one that does not exist so check the + // enumeration structure to see how many valid configurations are present. + // + if(psUSBRequest->wValue > psDevice->pui8DeviceDescriptor[17]) + { + // + // The passed configuration number is not valid. Stall the endpoint to + // signal the error to the host. + // + USBDCDStallEP0(0); + } + else + { + // + // Save the configuration. + // + psUSBControl->ui32Configuration = psUSBRequest->wValue; + + // + // If passed a configuration other than 0 (which tells us that we are + // not currently configured), configure the endpoints (other than EP0) + // appropriately. + // + if(psUSBControl->ui32Configuration) + { + // + // Get a pointer to the configuration descriptor. This will always + // be the first section in the current configuration. + // + psHdr = psDevice->ppsConfigDescriptors[psUSBRequest->wValue - 1]; + psDesc = + (const tConfigDescriptor *)(psHdr->psSections[0]->pui8Data); + + // + // Remember the new self- or bus-powered state if the user has not + // already called us to tell us the state to report. + // + if(!psUSBControl->bPwrSrcSet) + { + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == + USB_CONF_ATTR_SELF_PWR) + { + psUSBControl->ui8Status |= USB_STATUS_SELF_PWR; + } + else + { + psUSBControl->ui8Status &= ~USB_STATUS_SELF_PWR; + } + } + + // + // Configure endpoints for the new configuration. + // + USBDeviceConfig(psUSBControl, + psDevice->ppsConfigDescriptors[psUSBRequest->wValue - 1]); + } + + // + // If there is a configuration change callback then call it. + // + if(psDevice->psCallbacks->pfnConfigChange) + { + psDevice->psCallbacks->pfnConfigChange(g_psDCDInst[0].pvCBData, + psUSBControl->ui32Configuration); + } + } +} + +//***************************************************************************** +// +// This function handles the GET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the data for this request. +// +// This function is called when the host controller request the current +// interface that is in use by the device. This simply returns the value set +// by the last call to SetInterface(). +// +// \return None. +// +//***************************************************************************** +static void +USBDGetInterface(void *pvInstance, tUSBRequest *psUSBRequest) +{ + uint8_t ui8Value; + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // If we still have an address pending then the device is still not + // configured. + // + if(psUSBControl->ui32DevAddress & DEV_ADDR_PENDING) + { + ui8Value = (uint8_t)0; + } + else + { + // + // Is the interface number valid? + // + if(psUSBRequest->wIndex < USB_MAX_INTERFACES_PER_DEVICE) + { + // + // Read the current alternate setting for the required interface. + // + ui8Value = psUSBControl->pui8AltSetting[psUSBRequest->wIndex]; + } + else + { + // + // An invalid interface number was specified. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the single byte response. + // + psUSBControl->ui32EP0DataRemain = 1; + psUSBControl->pui8EP0Data = &ui8Value; + + // + // Send the single byte response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the SET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the data for this request. +// +// This function is called when a standard request for changing the interface +// is received from the host controller. If this is a valid request the +// function will call the function specified by the InterfaceChange in the +// \e pvInstance->psInfo->psCallbacks->variable to notify the application that +// the interface has changed and will pass it the new alternate interface +// number. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetInterface(void *pvInstance, tUSBRequest *psUSBRequest) +{ + const tConfigHeader *psConfig; + tInterfaceDescriptor *psInterface; + uint32_t ui32Loop, ui32Section, ui32NumInterfaces; + uint8_t ui8Interface; + bool bRetcode; + tDCDInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + psDevice = g_ppsDevInfo[0]; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Use the current configuration. + // + psConfig = + psDevice->ppsConfigDescriptors[psUSBControl->ui32Configuration - 1]; + + // + // How many interfaces are included in the descriptor? + // + ui32NumInterfaces = USBDCDConfigDescGetNum(psConfig, USB_DTYPE_INTERFACE); + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + for(ui32Loop = 0; ui32Loop < ui32NumInterfaces; ui32Loop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32Loop, + USB_DESC_ANY, &ui32Section); + + // + // Is this the required interface with the correct alternate setting? + // + if(psInterface && + (psInterface->bInterfaceNumber == psUSBRequest->wIndex) && + (psInterface->bAlternateSetting == psUSBRequest->wValue)) + { + ui8Interface = psInterface->bInterfaceNumber; + + // + // Make sure we don't write outside the bounds of the + // pui8AltSetting array (in a debug build, anyway, since this + // indicates an error in the device descriptor). + // + ASSERT(ui8Interface < USB_MAX_INTERFACES_PER_DEVICE); + + // + // This is the correct interface descriptor so save the + // setting. + // + psUSBControl->pui8AltSetting[ui8Interface] = + psInterface->bAlternateSetting; + + // + // Reconfigure the endpoints to match the requirements of the + // new alternate setting for the interface. + // + bRetcode = USBDeviceConfigAlternate(psUSBControl, psConfig, + ui8Interface, + psInterface->bAlternateSetting); + + // + // If there is a callback then notify the application of the + // change to the alternate interface. + // + if(bRetcode && psDevice->psCallbacks->pfnInterfaceChange) + { + psDevice->psCallbacks->pfnInterfaceChange( + g_psDCDInst[0].pvCBData, + psUSBRequest->wIndex, + psUSBRequest->wValue); + } + + // + // All done. + // + return; + } + } + + // + // If we drop out of the loop, we didn't find an interface descriptor + // matching the requested number and alternate setting or there was an + // error while trying to set up for the new alternate setting. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This function handles the SYNC_FRAME standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the data for this request. +// +// This is currently a stub function that will stall indicating that the +// command is not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDSyncFrame(void *pvInstance, tUSBRequest *psUSBRequest) +{ + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Not handled yet so stall this request. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This internal function handles sending data on endpoint zero. +// +// \param ui32Index is the index of the USB controller which is to be +// initialized. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTx(uint32_t ui32Index) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + + ASSERT(ui32Index == 0); + + // + // In the TX state on endpoint zero. + // + g_psDCDInst[0].iEP0State = eUSBStateTx; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = g_psDCDInst[0].ui32EP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ui32NumBytes > EP0_MAX_PACKET_SIZE) + { + ui32NumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pui8Data = (uint8_t *)g_psDCDInst[0].pui8EP0Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_psDCDInst[0].ui32EP0DataRemain -= ui32NumBytes; + g_psDCDInst[0].pui8EP0Data += ui32NumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pui8Data, ui32NumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ui32NumBytes == EP0_MAX_PACKET_SIZE) + { + // + // There is more data to send or exactly 64 bytes were sent, this + // means that there is either more data coming or a null packet needs + // to be sent to complete the transaction. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN); + } + else + { + // + // Now go to the status state and wait for the transmit to complete. + // + g_psDCDInst[0].iEP0State = eUSBStateStatus; + + // + // Send the last bit of data. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN_LAST); + + // + // If there is a sent callback then call it. + // + if((g_ppsDevInfo[0]->psCallbacks->pfnDataSent) && + (g_psDCDInst[0].ui32OUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_ppsDevInfo[0]->psCallbacks->pfnDataSent( + g_psDCDInst[0].pvCBData, + g_psDCDInst[0].ui32OUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psDCDInst[0].ui32OUTDataSize = 0; + } + } +} + +//***************************************************************************** +// +// This internal function handles sending the configuration descriptor on +// endpoint zero. +// +// \param ui32Index is the index of the USB controller. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTxConfig(uint32_t ui32Index) +{ + uint32_t ui32NumBytes, ui32SecBytes, ui32ToSend; + uint8_t *pui8Data; + tConfigDescriptor sConfDesc; + const tConfigHeader *psConfig; + const tConfigSection *psSection; + + ASSERT(ui32Index == 0); + + // + // In the TX state on endpoint zero. + // + g_psDCDInst[0].iEP0State = eUSBStateTxConfig; + + // + // Find the current configuration descriptor definition. + // + psConfig = g_ppsDevInfo[0]->ppsConfigDescriptors[ + g_psDCDInst[0].ui8ConfigIndex]; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = g_psDCDInst[0].ui32EP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ui32NumBytes > EP0_MAX_PACKET_SIZE) + { + ui32NumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // If this is the first call, we need to fix up the total length of the + // configuration descriptor. This has already been determined and set in + // g_sUSBDeviceState.ui32EP0DataRemain. + // + if((g_psDCDInst[0].ui16SectionOffset == 0) && + (g_psDCDInst[0].ui8ConfigSection == 0)) + { + // + // Copy the USB configuration descriptor from the beginning of the + // first section of the current configuration. + // + sConfDesc = *(tConfigDescriptor *)g_psDCDInst[0].pui8EP0Data; + + // + // Update the total size. + // + sConfDesc.wTotalLength = (uint16_t)USBDCDConfigDescGetSize(psConfig); + + // + // Write the descriptor to the USB FIFO. + // + ui32ToSend = (ui32NumBytes < sizeof(tConfigDescriptor)) ? ui32NumBytes: + sizeof(tConfigDescriptor); + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (uint8_t *)&sConfDesc, + ui32ToSend); + + // + // Did we reach the end of the first section? + // + if(psConfig->psSections[0]->ui16Size == ui32ToSend) + { + // + // Update our tracking indices to point to the start of the next + // section. + // + g_psDCDInst[0].ui16SectionOffset = 0; + g_psDCDInst[0].ui8ConfigSection = 1; + } + else + { + // + // Note that we have sent the first few bytes of the descriptor. + // + g_psDCDInst[0].ui16SectionOffset = (uint8_t)ui32ToSend; + } + + // + // How many bytes do we have remaining to send on this iteration? + // + ui32ToSend = ui32NumBytes - ui32ToSend; + } + else + { + // + // Set the number of bytes we still have to send on this call. + // + ui32ToSend = ui32NumBytes; + } + + // + // Add the relevant number of bytes to the USB FIFO + // + while(ui32ToSend) + { + // + // Get a pointer to the current configuration section. + // + psSection = psConfig->psSections[g_psDCDInst[0].ui8ConfigSection]; + + // + // Calculate bytes are available in the current configuration section. + // + ui32SecBytes = (uint32_t)(psSection->ui16Size - + g_psDCDInst[0].ui16SectionOffset); + + // + // Save the pointer so that it can be passed to the + // USBEndpointDataPut() function. + // + pui8Data = (uint8_t *)psSection->pui8Data + + g_psDCDInst[0].ui16SectionOffset; + + // + // Are there more bytes in this section that we still have to send? + // + if(ui32SecBytes > ui32ToSend) + { + // + // Yes - send only the remaining bytes in the transfer. + // + ui32SecBytes = ui32ToSend; + } + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pui8Data, ui32SecBytes); + + // + // Fix up our pointers for the next iteration. + // + ui32ToSend -= ui32SecBytes; + g_psDCDInst[0].ui16SectionOffset += (uint8_t)ui32SecBytes; + + // + // Have we reached the end of a section? + // + if(g_psDCDInst[0].ui16SectionOffset == psSection->ui16Size) + { + // + // Yes - move to the next one. + // + g_psDCDInst[0].ui8ConfigSection++; + g_psDCDInst[0].ui16SectionOffset = 0; + } + } + + // + // Fix up the number of bytes remaining to be sent and the start pointer. + // + g_psDCDInst[0].ui32EP0DataRemain -= ui32NumBytes; + + // + // If we ran out of bytes in the configuration section, bail and just + // send out what we have. + // + if(psConfig->ui8NumSections <= g_psDCDInst[0].ui8ConfigSection) + { + g_psDCDInst[0].ui32EP0DataRemain = 0; + } + + // + // If there is no more data don't keep looking or ui8ConfigSection might + // overrun the available space. + // + if(g_psDCDInst[0].ui32EP0DataRemain != 0) + { + pui8Data =(uint8_t *) + psConfig->psSections[g_psDCDInst[0].ui8ConfigSection]->pui8Data; + ui32ToSend = g_psDCDInst[0].ui16SectionOffset; + g_psDCDInst[0].pui8EP0Data = (pui8Data + ui32ToSend); + } + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ui32NumBytes == EP0_MAX_PACKET_SIZE) + { + // + // There is more data to send or exactly 64 bytes were sent, this + // means that there is either more data coming or a null packet needs + // to be sent to complete the transaction. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN); + } + else + { + // + // Send the last bit of data. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN_LAST); + + // + // If there is a sent callback then call it. + // + if((g_ppsDevInfo[0]->psCallbacks->pfnDataSent) && + (g_psDCDInst[0].ui32OUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_ppsDevInfo[0]->psCallbacks->pfnDataSent(g_psDCDInst[0].pvCBData, + g_psDCDInst[0].ui32OUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psDCDInst[0].ui32OUTDataSize = 0; + } + + // + // Now go to the status state and wait for the transmit to complete. + // + g_psDCDInst[0].iEP0State = eUSBStateStatus; + } +} + +//***************************************************************************** +// +// The internal USB device interrupt handler. +// +// \param ui32Index is the USB controller associated with this interrupt. +// \param ui32Status is the current interrupt status as read via a call to +// USBIntStatusControl(). +// +// This function is called from either \e USB0DualModeIntHandler() or +// \e USB0DeviceIntHandler() to process USB interrupts when in device mode. +// This handler will branch the interrupt off to the appropriate application or +// stack handlers depending on the current status of the USB controller. +// +// The two-tiered structure for the interrupt handler ensures that it is +// possible to use the same handler code in both device and OTG modes and +// means that host code can be excluded from applications that only require +// support for USB device mode operation. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceIntHandlerInternal(uint32_t ui32Index, uint32_t ui32Status) +{ + static uint32_t ui32SOFDivide = 0; + void *pvInstance; + uint32_t ui32DMAIntStatus; + uint32_t ui32LPMStatus; + + // + // If device initialization has not been performed then just disconnect + // from the USB bus and return from the handler. + // + if(g_ppsDevInfo[0] == 0) + { + MAP_USBDevDisconnect(USB0_BASE); + return; + } + + pvInstance = g_psDCDInst[0].pvCBData; + + // + // Received a reset from the host. + // + if(ui32Status & USB_INTCTRL_RESET) + { + USBDeviceEnumResetHandler(&g_psDCDInst[0]); + } + + // + // Suspend was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_SUSPEND) + { + // + // Call the SuspendHandler() if it was specified. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnSuspendHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnSuspendHandler(pvInstance); + } + } + + // + // Resume was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_RESUME) + { + // + // Call the ResumeHandler() if it was specified. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler(pvInstance); + } + } + + // + // USB device was disconnected. + // + if(ui32Status & USB_INTCTRL_DISCONNECT) + { + // + // Call the DisconnectHandler() if it was specified. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnDisconnectHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDisconnectHandler(pvInstance); + } + } + + // + // Start of Frame was received. + // + if(ui32Status & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ui32USBSOFCount++; + + // + // Increment our SOF divider. + // + ui32SOFDivide++; + + // + // Handle resume signaling if required. + // + USBDeviceResumeTickHandler(&g_psDCDInst[0]); + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ui32SOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ui32SOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Handle LPM interrupts. + // + ui32LPMStatus = USBLPMIntStatus(USB0_BASE); + + // + // The host LPM resume request has been acknowledged, allow the device + // class to handle the sleep state. + // + if((g_psDCDInst[0].ui32LPMState == USBLIB_LPM_STATE_SLEEP) && + ((ui32LPMStatus & (USB_INTLPM_ACK | USB_INTLPM_RESUME)) == + USB_INTLPM_RESUME)) + { + // + // Notify the class of the wake from LPM L1. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, + USB_EVENT_LPM_RESUME, + (void *)0); + } + + // + // Now back in the awake state. + // + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_AWAKE; + + // + // Enable receiving of LPM packet. + // + USBDevLPMEnable(USB0_BASE); + } + // + // The host LPM sleep request has been acknowledged, allow the device + // class to handle the sleep state. + // + else if((g_psDCDInst[0].ui32LPMState == USBLIB_LPM_STATE_AWAKE) && + ((ui32LPMStatus & (USB_INTLPM_ACK | USB_INTLPM_RESUME)) == + USB_INTLPM_ACK)) + { + if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, + USB_EVENT_LPM_SLEEP, + (void *)0); + } + + // + // Now back in the sleep state. + // + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_SLEEP; + } + else if(ui32LPMStatus & USB_INTLPM_NYET) + { + // + // The device has held off the sleep state because LPM + // responses are disabled. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, + USB_EVENT_LPM_ERROR, + (void *)0); + } + } + + // + // Get the controller interrupt status. + // + ui32Status = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ui32Status & USB_INTEP_0) + { + USBDeviceEnumHandler(&g_psDCDInst[0]); + ui32Status &= ~USB_INTEP_0; + } + + // + // Check to see if any DMA transfers are pending + // + ui32DMAIntStatus = USBLibDMAIntStatus(g_psDCDInst[0].psDMAInstance); + + if(ui32DMAIntStatus) + { + // + // Handle any DMA interrupt processing. + // + USBLibDMAIntHandler(g_psDCDInst[0].psDMAInstance, ui32DMAIntStatus); + } + + // + // Because there is no way to detect if a uDMA interrupt has occurred, + // check for an endpoint callback and call it if it is available. + // + if((g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler) && + ((ui32Status != 0) || (ui32DMAIntStatus != 0))) + { + g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler(pvInstance, ui32Status); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdevice.h b/usblib/device/usbdevice.h new file mode 100644 index 0000000..d4df6a6 --- /dev/null +++ b/usblib/device/usbdevice.h @@ -0,0 +1,232 @@ +//***************************************************************************** +// +// usbdevice.h - types and definitions used during USB enumeration. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBDEVICE_H__ +#define __USBDEVICE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The maximum number of independent interfaces that any single device +//! implementation can support. Independent interfaces means interface +//! descriptors with different \e bInterfaceNumber values - several interface +//! descriptors offering different alternative settings but the same interface +//! number count as a single interface. +// +//***************************************************************************** +#define USB_MAX_INTERFACES_PER_DEVICE 8 + +#include "usbdevicepriv.h" + +//***************************************************************************** +// +//! This structure is passed to the USB library on a call to USBDCDInit and +//! provides the library with information about the device that the +//! application is implementing. It contains functions pointers for the +//! various USB event handlers and pointers to each of the standard device +//! descriptors. +// +//***************************************************************************** +struct tDeviceInfo +{ + // + //! A pointer to a structure containing pointers to event handler functions + //! provided by the client to support the operation of this device. + // + const tCustomHandlers * psCallbacks; + + // + //! A pointer to the device descriptor for this device. + // + const uint8_t *pui8DeviceDescriptor; + + // + //! A pointer to an array of configuration descriptor pointers. Each entry + //! in the array corresponds to one configuration that the device may be + //! set to use by the USB host. The number of entries in the array must + //! match the bNumConfigurations value in the device descriptor + //! array, \e pui8DeviceDescriptor. + // + const tConfigHeader * const *ppsConfigDescriptors; + + // + //! A pointer to the string descriptor array for this device. This array + //! must be arranged as follows: + //! + //! [0] - Standard descriptor containing supported language codes. + //! + //! [1] - String 1 for the first language listed in descriptor 0. + //! + //! [2] - String 2 for the first language listed in descriptor 0. + //! + //! ... + //! + //! [n] - String n for the first language listed in descriptor 0. + //! + //! [n+1] - String 1 for the second language listed in descriptor 0. + //! + //! ... + //! + //! [2n] - String n for the second language listed in descriptor 0. + //! + //! [2n+1]- String 1 for the third language listed in descriptor 0. + //! + //! ... + //! + //! [3n] - String n for the third language listed in descriptor 0. + //! + //! and so on. + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The total number of descriptors provided in the ppStringDescriptors + //! array. + // + uint32_t ui32NumStringDescriptors; +}; + +//***************************************************************************** +// +//! This type is used by an application to describe and instance of a device +//! and an instance data pointer for that class. The psDevice pointer should +//! be a pointer to a valid device class to include in the composite device. +//! The pvInstance pointer should be a pointer to an instance pointer for the +//! device in the psDevice pointer. +//! +// +//***************************************************************************** +typedef struct +{ + // + //! This is the top level device information structure. + // + const tDeviceInfo *psDevInfo; + + // + //! This is the instance data for the device structure. + // + void *pvInstance; + + // + //! A per-device workspace used by the composite device. + // + uint32_t ui32DeviceWorkspace; +} +tCompositeEntry; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public APIs offered by the USB library device control driver. +// +//***************************************************************************** +extern void USBDCDInit(uint32_t ui32Index, tDeviceInfo *psDevice, + void *pvDCDCBData); +extern void USBDCDTerm(uint32_t ui32Index); +extern void USBDCDStallEP0(uint32_t ui32Index); +extern void USBDCDRequestDataEP0(uint32_t ui32Index, uint8_t *pui8Data, + uint32_t ui32Size); +extern void USBDCDSendDataEP0(uint32_t ui32Index, uint8_t *pui8Data, + uint32_t ui32Size); +extern void USBDCDSetDefaultConfiguration(uint32_t ui32Index, + uint32_t ui32DefaultConfig); +extern uint32_t USBDCDConfigDescGetSize(const tConfigHeader *psConfig); +extern uint32_t USBDCDConfigDescGetNum(const tConfigHeader *psConfig, + uint32_t ui32Type); +extern tDescriptorHeader *USBDCDConfigDescGet(const tConfigHeader *psConfig, + uint32_t ui32Type, + uint32_t ui32Index, + uint32_t *pui32Section); +extern uint32_t + USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + uint8_t ui8InterfaceNumber); +extern tInterfaceDescriptor * + USBDCDConfigGetInterface(const tConfigHeader *psConfig, + uint32_t ui32Index, uint32_t ui32AltCfg, + uint32_t *pui32Section); +extern tEndpointDescriptor * + USBDCDConfigGetInterfaceEndpoint(const tConfigHeader *psConfig, + uint32_t ui32InterfaceNumber, + uint32_t ui32AltCfg, + uint32_t ui32Index); +extern bool USBDCDRemoteWakeupRequest(uint32_t ui32Index); +extern bool USBDCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, + void *pvFeature); +extern bool USBDCDRemoteWakeLPM(uint32_t ui32Index); + +//***************************************************************************** +// +// Device mode interrupt handler for controller index 0. +// +//***************************************************************************** +extern void USB0DeviceIntHandler(void); + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED + +// +// Use USBDCDFeatureSet() or USBHCDFeatureSet() with \b USBLIB_FEATURE_POWER +// configuration option. +// +extern void USBDCDPowerStatusSet(uint32_t ui32Index, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBENUM_H__ diff --git a/usblib/device/usbdevicepriv.h b/usblib/device/usbdevicepriv.h new file mode 100644 index 0000000..4eef230 --- /dev/null +++ b/usblib/device/usbdevicepriv.h @@ -0,0 +1,248 @@ +//***************************************************************************** +// +// usbdevicepriv.h - Private header file used to share internal variables and +// function prototypes between the various device-related +// modules in the USB library. This header MUST NOT be +// used by application code. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBDEVICEPRIV_H__ +#define __USBDEVICEPRIV_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The states for endpoint zero during enumeration. +// +//***************************************************************************** +typedef enum +{ + // + // The USB device is waiting on a request from the host controller on + // endpoint zero. + // + eUSBStateIdle, + + // + // The USB device is sending data back to the host due to an IN request. + // + eUSBStateTx, + + // + // The USB device is sending the configuration descriptor back to the host + // due to an IN request. + // + eUSBStateTxConfig, + + // + // The USB device is receiving data from the host due to an OUT + // request from the host. + // + eUSBStateRx, + + // + // The USB device has completed the IN or OUT request and is now waiting + // for the host to acknowledge the end of the IN/OUT transaction. This + // is the status phase for a USB control transaction. + // + eUSBStateStatus, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + eUSBStateStall +} +tEP0State; + +typedef struct tDeviceInfo tDeviceInfo; + +//***************************************************************************** +// +// The USB controller device information. +// +//***************************************************************************** +typedef struct +{ + // + // The current state of endpoint zero. + // + volatile tEP0State iEP0State; + + // + // The devices current address, this also has a change pending bit in the + // MSB of this value specified by DEV_ADDR_PENDING. + // + volatile uint32_t ui32DevAddress; + + // + // This holds the current active configuration for this device. + // + uint32_t ui32Configuration; + + // + // This holds the configuration id that will take effect after a reset. + // + uint32_t ui32DefaultConfiguration; + + // + // This holds the current alternate interface for this device. + // + uint8_t pui8AltSetting[USB_MAX_INTERFACES_PER_DEVICE]; + + // + // This is the pointer to the current data being sent out or received + // on endpoint zero. + // + uint8_t *pui8EP0Data; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_sUSBDeviceState.pui8EP0Data data buffer. + // + volatile uint32_t ui32EP0DataRemain; + + // + // The amount of data being sent/received due to a custom request. + // + uint32_t ui32OUTDataSize; + + // + // Holds the current device status. + // + uint8_t ui8Status; + + // + // Holds the endpoint status for the HALT condition. This array is sized + // to hold halt status for all IN and OUT endpoints. + // + uint8_t ppui8Halt[2][USBLIB_NUM_EP - 1]; + + // + // Holds the configuration descriptor section number currently being sent + // to the host. + // + uint8_t ui8ConfigSection; + + // + // Holds the offset within the configuration descriptor section currently + // being sent to the host. + // + uint16_t ui16SectionOffset; + + // + // Holds the index of the configuration that we are currently sending back + // to the host. + // + uint8_t ui8ConfigIndex; + + // + // This flag is set to true if the client has called USBDPowerStatusSet() + // and tells the USB library not to try to determine the current power + // status from the configuration descriptor. + // + bool bPwrSrcSet; + + // + // This flag indicates whether or not remote wake up signaling is in + // progress. + // + bool bRemoteWakeup; + + // + // During remote wake up signaling, this counter is used to track the + // number of milliseconds since the signaling was initiated. + // + uint8_t ui8RemoteWakeupCount; + + // + // The DMA instance information for this USB controller. + // + tUSBDMAInstance *psDMAInstance; + + // + // The interrupt number for this instance. + // + uint32_t ui32IntNum; + + // + // Pointer to the device supplied call back data. + // + void *pvCBData; + + // + // This holds the state of the LPM support for the device. + // + uint32_t ui32LPMState; + + // + // Device feature flags. + // + uint32_t ui32Features; +} +tDCDInstance; + +extern tDCDInstance g_psDCDInst[]; +extern tDeviceInfo *g_ppsDevInfo[]; + +//***************************************************************************** +// +// Device enumeration functions provided by device/usbenum.c and called from +// the interrupt handler in device/usbhandler.c +// +//***************************************************************************** +extern bool USBDeviceConfig(tDCDInstance *psDevInst, + const tConfigHeader *psConfig); +extern bool USBDeviceConfigAlternate(tDCDInstance *psDevInst, + const tConfigHeader *psConfig, + uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting); + +extern void USBDCDDeviceInfoInit(uint32_t ui32Index, tDeviceInfo *psDevice); + +//***************************************************************************** +// +// Macro access function to device information. +// +//***************************************************************************** +#define DCDGetDMAInstance(psDevInfo) (&(psDevInfo->psDCDInst->sDMAInstance)) + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDEVICEPRIV_H__ diff --git a/usblib/device/usbdhandler.c b/usblib/device/usbdhandler.c new file mode 100644 index 0000000..751cc44 --- /dev/null +++ b/usblib/device/usbdhandler.c @@ -0,0 +1,85 @@ +//***************************************************************************** +// +// usbhandler.c - General USB handling routines. +// +// Copyright (c) 2007-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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The USB device interrupt handler. +//! +//! This the main USB interrupt handler entry point for use in USB device +//! applications. This top-level handler will branch the interrupt off to the +//! appropriate application or stack handlers depending on the current status +//! of the USB controller. +//! +//! Applications which operate purely as USB devices (rather than dual mode +//! applications which can operate in either device or host mode at different +//! times) must ensure that a pointer to this function is installed in the +//! interrupt vector table entry for the USB0 interrupt. For dual mode +//! operation, the vector should be set to point to \e USB0DualModeIntHandler() +//! instead. +//! +//! \return None. +// +//***************************************************************************** +void +USB0DeviceIntHandler(void) +{ + uint32_t ui32Status; + + // + // Get the controller interrupt status. + // + ui32Status = MAP_USBIntStatusControl(USB0_BASE); + + // + // Call the internal handler. + // + USBDeviceIntHandlerInternal(0, ui32Status); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhid.c b/usblib/device/usbdhid.c new file mode 100644 index 0000000..c708f62 --- /dev/null +++ b/usblib/device/usbdhid.c @@ -0,0 +1,2502 @@ +//***************************************************************************** +// +// usbdhid.c - USB HID device class 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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdhid.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup hid_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// Marker used to indicate that a given HID descriptor cannot be found in the +// client-supplied list. +// +//***************************************************************************** +#define HID_NOT_FOUND 0xFFFFFFFF + +//***************************************************************************** +// +// Flags that may appear in ui16DeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// Each deferred operation is defined as the bit number that should be set in +// tHIDInstance->ui16DeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define HID_DO_PACKET_RX 5 +#define HID_DO_SEND_IDLE_REPORT 6 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define INT_IN_ENDPOINT USB_EP_3 +#define INT_OUT_ENDPOINT USB_EP_3 + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +uint8_t g_pui8HIDDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_DEVICE, // USB Device Class + 0, // USB Device Sub-class + USB_HID_PROTOCOL_NONE, // USB Device protocol + USBDHID_MAX_PACKET, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvHIDInstance, tUSBRequest *psUSBRequest); +static void HandleRequest(void *pvHIDInstance, tUSBRequest *psUSBRequest); +static void HandleConfigChange(void *pvHIDInstance, uint32_t ui32Info); +static void HandleEP0DataReceived(void *pvHIDInstance, uint32_t ui32Info); +static void HandleEP0DataSent(void *pvHIDInstance, uint32_t ui32Info); +static void HandleReset(void *pvHIDInstance); +static void HandleSuspend(void *pvHIDInstance); +static void HandleResume(void *pvHIDInstance); +static void HandleDisconnect(void *pvHIDInstance); +static void HandleEndpoints(void *pvHIDInstance, uint32_t ui32Status); +static void HandleDevice(void *pvHIDInstance, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB HID devices. +// +//***************************************************************************** +const tCustomHandlers g_sHIDHandlers = +{ + // + // GetDescriptor + // + HandleGetDescriptor, + + // + // RequestHandler + // + HandleRequest, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + HandleEP0DataReceived, + + // + // DataSentCallback + // + HandleEP0DataSent, + + // + // ResetHandler + // + HandleReset, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler. + // + HandleDevice +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pui16DeferredOp points to the flags variable which is to be modified. +// \param ui16Bit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile uint16_t *pui16DeferredOp, uint16_t ui16Bit, + bool bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// This function is called to clear the counter used to keep track of the time +// elapsed since a given report was last sent. +// +// \param psHIDDevice points to the HID device structure whose report timer is +// to be cleared. +// \param ui8ReportID is the first byte of the report to be sent. If this +// device offers more than one input report, this value is used to find the +// relevant report timer structure in the psHIDDevice structure. +// +// \return None. +// +//***************************************************************************** +static void +ClearReportTimer(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID) +{ + uint32_t ui32Loop; + + if(psHIDDevice->ui8NumInputReports > 1) + { + // + // We have more than 1 input report so the report must begin with a + // byte containing the report ID. Scan the table we were provided + // when the device was initialized to find the entry for this report. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; + ui32Loop++) + { + if(psHIDDevice->psReportIdle[ui32Loop].ui8ReportID == ui8ReportID) + { + break; + } + } + } + else + { + ui32Loop = 0; + } + + // + // If we drop out of the loop with an index less than ui8NumInputReports, + // we found the relevant report so clear its timer. + // + if(ui32Loop < psHIDDevice->ui8NumInputReports) + { + psHIDDevice->psReportIdle[ui32Loop].ui32TimeSinceReportmS = 0; + } +} + +//***************************************************************************** +// +// This function is called to clear the idle period timers for each input +// report supported by the device. +// +// \param psHIDDevice points to the HID device structure whose timers are to be +// cleared. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +ClearIdleTimers(const tUSBDHIDDevice *psHIDDevice) +{ + uint32_t ui32Loop; + + // + // Clear the "time till next report" counters for each input report. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = + psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS * 4; + } +} + +//***************************************************************************** +// +// This function is called periodically to allow us to process the report idle +// timers. +// +// \param psHIDDevice points to the HID device structure whose timers are to be +// updated. +// \param ui32ElapsedmS indicates the number of milliseconds that have elapsed +// since the last call to this function. +// +// \return None. +// +//***************************************************************************** +static void +ProcessIdleTimers(const tUSBDHIDDevice *psHIDDevice, uint32_t ui32ElapsedmS) +{ + uint32_t ui32Loop, ui32SizeReport; + void *pvReport; + tHIDInstance *psInst; + bool bDeferred; + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)psHIDDevice)->sPrivateData; + + // + // We have not had to defer any report transmissions yet. + // + bDeferred = false; + + // + // Look at each of the input report idle timers in turn. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + // + // Update the time since the last report was sent. + // + psHIDDevice->psReportIdle[ui32Loop].ui32TimeSinceReportmS += + ui32ElapsedmS; + + // + // Is this timer running? + // + if(psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS) + { + // + // Yes - is it about to expire? + // + if(psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS <= + ui32ElapsedmS) + { + // + // The timer is about to expire. Can we send a report right + // now? + // + if((psInst->iHIDTxState == eHIDStateIdle) && + (psInst->bSendInProgress == false)) + { + // + // We can send a report so send a message to the + // application to retrieve its latest report for + // transmission to the host. + // + ui32SizeReport = psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, + USBD_HID_EVENT_IDLE_TIMEOUT, + psHIDDevice->psReportIdle[ui32Loop].ui8ReportID, + &pvReport); + + // + // Schedule the report for transmission. + // + USBDHIDReportWrite((void *)psHIDDevice, pvReport, + ui32SizeReport, true); + + // + // Reload the timer for the next period. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = + psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS * 4; + } + else + { + // + // We can't send the report straight away so flag it for + // transmission as soon as the previous transmission ends. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = 0; + bDeferred = true; + } + } + else + { + // + // The timer is not about to expire. Update the time till the + // next report transmission. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS -= + ui32ElapsedmS; + } + } + } + + // + // If we had to defer transmission of any report, remember this so that we + // will process it as soon as possible. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, HID_DO_SEND_IDLE_REPORT, + bDeferred); +} + +static void +SetIdleTimeout(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID, + uint8_t ui8Timeout4mS) +{ + uint32_t ui32Loop; + bool bReportNeeded; + tHIDReportIdle *psIdle; + + // + // Remember that we have not found any report that needs to be sent + // immediately. + // + bReportNeeded = false; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psIdle = &psHIDDevice->psReportIdle[ui32Loop]; + + // + // If the report ID passed matches the report ID in the idle timer + // control structure or we were passed a report ID of zero, which + // indicates that all timers are to be set... + // + if(!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID)) + { + // + // Save the new duration for the idle timer. + // + psIdle->ui8Duration4mS = ui8Timeout4mS; + + // + // Are we enabling the idle timer? If so, fix up the time until it + // needs to fire. + // + if(ui8Timeout4mS) + { + // + // Determine what the timeout is for this report given the time + // since the last report of this type was sent. + // + if(psIdle->ui32TimeSinceReportmS >= + ((uint32_t)ui8Timeout4mS * 4)) + { + psIdle->ui16TimeTillNextmS = 0; + bReportNeeded = true; + } + else + { + psIdle->ui16TimeTillNextmS = + (((uint16_t)ui8Timeout4mS * 4) - + psIdle->ui32TimeSinceReportmS); + } + } + } + } + + // + // If we get to here and bReportNeeded is true, this means we need to + // send back at least one of the input reports as soon as possible. Try + // to do this immediately. + // + if(bReportNeeded) + { + ProcessIdleTimers(psHIDDevice, 0); + } +} + +//***************************************************************************** +// +// Find the idle timeout for a given HID input report. +// +// \param psHIDDevice points to the HID device whose report idle timeout is to +// be found. +// \param ui8ReportID identifies the report whose timeout is requested. If 0, +// the timeout for the first report is returns, regardless of its ID (or +// whether it has one). +// +// This function returns the current idle timeout for a given HID input report. +// The value returned is expressed in terms of 4mS intervals. Convert to +// milliseconds by multiplying by 4. If the return value is 0, this indicates +// that an infinite timeout is currently set and the device will not send the +// report unless a state change occurs. +// +// \return Returns the current idle timeout for the given report. +// +//***************************************************************************** +static uint32_t +GetIdleTimeout(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID) +{ + uint32_t ui32Loop; + tHIDReportIdle *psIdle; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psIdle = &psHIDDevice->psReportIdle[ui32Loop]; + + // + // If the report ID passed matches the report ID in the idle timer + // control structure or we were passed a report ID of zero, which + // indicates that all timers are to be set... + // + if(!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID)) + { + // + // We found a report matching the required ID or we were not passed + // an ID and we are looking at the first report information. + // + return((uint32_t)psIdle->ui8Duration4mS); + } + } + + // + // If we drop out, the report could not be found so we need to indicate + // an error. + // + return(HID_NOT_FOUND); +} + +//***************************************************************************** +// +// Find the n-th HID class descriptor of a given type in the client-provided +// descriptor table. +// +// \param psHIDDevice points to the HID device which is to be searched for the +// required class descriptor. +// \param ui8Type is the type of class descriptor being requested. This will +// be either USB_HID_DTYPE_REPORT or USB_HID_DTYPE_PHYSICAL. +// \param ui32Index is the zero-based index of the descriptor that is being +// requested. +// +// This function parses the supplied HID descriptor to find the index into the +// sClassDescriptor array that corresponds to the requested descriptor. If +// a descriptor with the requested index does not exist, HID_NOT_FOUND will be +// returned unless the request is for a physical descriptor and at least one +// such descriptor exists. In this case, the index returned will be for the +// last physical descriptor (as required by the HID spec 7.1.1). +// +// \return Returns the index of the descriptor within the sClassDescriptor +// of the tHIDDevice structure if found or HID_NOT_FOUND otherwise. +// +//***************************************************************************** +static uint32_t +FindHIDDescriptor(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8Type, + uint32_t ui32Index, uint32_t *pui32Len) +{ + bool bFoundType; + uint32_t ui32Loop, ui32Count, ui32LastFound; + const tHIDClassDescriptorInfo *psDesc; + + // + // Remember that we have not found any descriptor with a matching type yet. + // + bFoundType = false; + ui32Count = 0; + ui32LastFound = 0; + + // + // Walk through all the class descriptors looking for the one which + // matches the requested index and type. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->psHIDDescriptor->bNumDescriptors; + ui32Loop++) + { + psDesc = &(psHIDDevice->psHIDDescriptor->sClassDescriptor[ui32Loop]); + if(psDesc->bDescriptorType == ui8Type) + { + // + // We found a descriptor of the correct type. Is this the + // correct index? + // + bFoundType = true; + + // + // Is this the descriptor we are looking for? + // + if(ui32Count == ui32Index) + { + // + // Yes - we found it so return the index and size to the + // caller. + // + *pui32Len = (uint32_t)psDesc->wDescriptorLength; + return(ui32Loop); + } + else + { + // + // Update our count and keep looking. Remember where we were + // when we found this descriptor in case we need to return the + // last physical descriptor. + // + ui32Count++; + ui32LastFound = ui32Loop; + } + } + } + + // + // If we drop out, we did not find the requested descriptor. Now handle + // the special case of a physical descriptor - if we found any physical + // descriptors, return the last one. + // + if((ui8Type == USB_HID_DTYPE_PHYSICAL) && bFoundType) + { + // + // Get the length of the last descriptor we found. + // + psDesc = + &(psHIDDevice->psHIDDescriptor->sClassDescriptor[ui32LastFound]); + *pui32Len = (uint32_t)psDesc->wDescriptorLength; + + // + // Return the index to the caller. + // + return(ui32LastFound); + } + else + { + // + // We could not find the descriptor so return an appropriate error. + // + return(HID_NOT_FOUND); + } +} + +//***************************************************************************** +// +// Schedule transmission of the next packet forming part of an input report. +// +// \param psHIDInst points to the HID device instance whose input report is to +// be sent. +// +// This function is called to transmit the next packet of an input report +// passed to the driver via a call to USBDHIDReportWrite. If any data remains +// to be sent, a USB packet is written to the FIFO and scheduled for +// transmission to the host. The function ensures that reports are sent as +// a sequence of full packets followed by either a single int16_t packet or a +// packet with no data to indicate the end of the transaction. +// +//***************************************************************************** +static int32_t +ScheduleReportTransmission(tHIDInstance *psHIDInst) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + int32_t i32Retcode; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = (uint32_t)(psHIDInst->ui16InReportSize - + psHIDInst->ui16InReportIndex); + + // + // Limit individual transfers to the maximum packet size for the endpoint. + // + if(ui32NumBytes > USBDHID_MAX_PACKET) + { + ui32NumBytes = USBDHID_MAX_PACKET; + } + + // + // Where are we sending this data from? + // + pui8Data = psHIDInst->pui8InReportData + psHIDInst->ui16InReportIndex; + + // + // Put the data in the correct FIFO. + // + i32Retcode = MAP_USBEndpointDataPut(psHIDInst->ui32USBBase, + psHIDInst->ui8INEndpoint, + pui8Data, ui32NumBytes); + + if(i32Retcode != -1) + { + // + // Update the count and index ready for the next time round. + // + psHIDInst->ui16InReportIndex += ui32NumBytes; + + // + // Send out the current data. + // + i32Retcode = MAP_USBEndpointDataSend(psHIDInst->ui32USBBase, + psHIDInst->ui8INEndpoint, + USB_TRANS_IN); + } + + // + // Tell the caller how we got on. + // + return(i32Retcode); +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psHIDDevice is the device instance whose endpoint is to be processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the interrupt OUT endpoint (in other words, whenever +// the host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +ProcessDataFromHost(tUSBDHIDDevice *psHIDDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus, ui32Size; + tHIDInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Has a packet been received? + // + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client does not read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, HID_DO_PACKET_RX, + true); + + // + // How big is the packet we have just been sent? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ui32EPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_ERROR, + (ui32EPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psHIDDevice is the device instance whose endpoint is to be processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the interrupt IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +ProcessDataToHost(tUSBDHIDDevice *psHIDDevice, uint32_t ui32Status) +{ + tHIDInstance *psInst; + uint32_t ui32EPStatus; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8INEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, psInst->ui8INEndpoint, + ui32EPStatus); + + // + // Our last packet was transmitted successfully. Is there any more data to + // send or have we finished sending the whole report? We know we finished + // if the ui16InReportIndex has reached the ui16InReportSize value. + // + if(psInst->ui16InReportSize == psInst->ui16InReportIndex) + { + // + // We finished sending the last report so are idle once again. + // + psInst->iHIDTxState = eHIDStateIdle; + + // + // Notify the client that the report transmission completed. + // + psHIDDevice->pfnTxCallback(psHIDDevice->pvTxCBData, + USB_EVENT_TX_COMPLETE, + psInst->ui16InReportSize, (void *)0); + + // + // Do we have any reports to send as a result of idle timer timeouts? + // + if(psInst->ui16DeferredOpFlags & (1 << HID_DO_SEND_IDLE_REPORT)) + { + // + // Yes - send reports for any timers that expired recently. + // + ProcessIdleTimers(psHIDDevice, 0); + } + } + else + { + // + // There must be more data or a zero length packet waiting to be sent + // so go ahead and do this. + // + ScheduleReportTransmission(psInst); + } + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ui32Status. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvHIDInstance, uint32_t ui32Status) +{ + tUSBDHIDDevice *psHIDInst; + tHIDInstance *psInst; + + ASSERT(pvHIDInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ui32Index is different in both cases. + // + psHIDInst = (tUSBDHIDDevice *)pvHIDInstance; + psInst = &psHIDInst->sPrivateData; + + // + // Handler for the interrupt OUT data endpoint. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvHIDInstance, ui32Status); + } + + // + // Handler for the interrupt IN data endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) + { + ProcessDataToHost(pvHIDInstance, ui32Status); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvHIDInstance, uint32_t ui32Info) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = pvHIDInstance; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Set all our endpoints to idle state. + // + psInst->iHIDRxState = eHIDStateIdle; + psInst->iHIDTxState = eHIDStateIdle; + + // + // If we are not currently connected let the client know we are open for + // business. + // + if(!psInst->bConnected) + { + // + // Pass the connected event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Clear the idle timers for each input report. + // + ClearIdleTimers(psHIDDevice); + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvHIDInstance, uint32_t ui32Request, void *pvRequestData) +{ + tHIDInstance *psInst; + uint8_t *pui8Data; + tUSBDHIDDevice *psHIDDevice; + + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // Create the serial instance data. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Create the int8_t array used by the events supported by the USB CDC + // serial class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f); + } + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM resume event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_RESUME, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM sleep event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_SLEEP, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM error event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_ERROR, 0, (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvHIDInstance) +{ + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psHIDDevice->sPrivateData.bConnected) + { + // + // Pass the disconnected event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psHIDDevice->sPrivateData.bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a request for a +// non-standard descriptor is received. +// +// \param pvHIDInstance is the instance data for this request. +// \param psUSBRequest points to the request received. +// +// This call parses the provided request structure and determines which +// descriptor is being requested. Assuming the descriptor can be found, it is +// scheduled for transmission via endpoint zero. If the descriptor cannot be +// found, the endpoint is stalled to indicate an error to the host. +// +//***************************************************************************** +static void +HandleGetDescriptor(void *pvHIDInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Size, ui32Desc; + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Which type of class descriptor are we being asked for? + // + switch(psUSBRequest->wValue >> 8) + { + // + // This is a request for a HID report or physical descriptor. + // + case USB_HID_DTYPE_REPORT: + case USB_HID_DTYPE_PHYSICAL: + { + // + // Find the index to the descriptor that is being queried. + // + ui32Size = 0; + ui32Desc = FindHIDDescriptor(psHIDDevice, + psUSBRequest->wValue >> 8, + psUSBRequest->wValue & 0xFF, + &ui32Size); + + // + // Did we find the descriptor? + // + if(ui32Desc == HID_NOT_FOUND) + { + // + // No - stall the endpoint and return. + // + USBDCDStallEP0(0); + return; + } + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ui32Size > psUSBRequest->wLength) + { + ui32Size = psUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, + (uint8_t *)psHIDDevice->ppui8ClassDescriptors[ui32Desc], + ui32Size); + + break; + } + + // + // This is a request for the HID descriptor (as found in the + // configuration descriptor following the relevant interface). + // + case USB_HID_DTYPE_HID: + { + // + // How big is the HID descriptor? + // + ui32Size = (uint32_t)psHIDDevice->psHIDDescriptor->bLength; + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ui32Size > psUSBRequest->wLength) + { + ui32Size = psUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, (uint8_t *)psHIDDevice->psHIDDescriptor, + ui32Size); + break; + } + + // + // This was an unknown request so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvHIDInstance is the instance data for this HID device. +// \param psUSBRequest points to the request received. +// +// This call parses the provided request structure. Assuming the request is +// understood, it is handled and any required response generated. If the +// request cannot be handled by this device class, endpoint zero is stalled to +// indicate an error to the host. +// +//***************************************************************************** +static void +HandleRequest(void *pvHIDInstance, tUSBRequest *psUSBRequest) +{ + tHIDInstance *psInst; + uint8_t ui8Protocol; + uint32_t ui32Size, ui32Timeout; + uint8_t *pui8Report; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Make sure the request was for this interface. + // + if(psUSBRequest->wIndex != psInst->ui8Interface) + { + return; + } + + // + // Determine the type of request. + // + switch(psUSBRequest->bRequest) + { + // + // A Set Report request is received from the host when it sends an + // Output report via endpoint 0. + // + case USBREQ_SET_REPORT: + { + // + // Ask the application for a buffer large enough to hold the + // report we are to be sent. + // + psInst->ui16OutReportSize = psUSBRequest->wLength; + psInst->pui8OutReportData = + (uint8_t *)psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT_BUFFER, + psUSBRequest->wValue, + (void *)(uint32_t)(psUSBRequest->wLength)); + + // + // Did the client provide us a buffer? + // + if(!psInst->pui8OutReportData) + { + // + // The application could not provide us a buffer so stall the + // request. + // + USBDCDStallEP0(0); + } + else + { + // + // The client provided us a buffer to read the report into + // so request the data from the host. + // + + // + // Set the state to indicate we are waiting for data. + // + psInst->iHIDRxState = eHIDStateWaitData; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, psInst->pui8OutReportData, + (uint32_t)psUSBRequest->wLength); + + // + // Need to ACK the data on end point 0 in this case. Do this + // after requesting the data to prevent race conditions that + // occur if you acknowledge before setting up to receive the + // request data. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, false); + } + + break; + } + + // + // A Get Report request is used by the host to poll a device for its + // current state. + // + case USBREQ_GET_REPORT: + { + // + // Get the latest report from the application. + // + ui32Size = psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT, + psUSBRequest->wValue, &pui8Report); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // ..then send back the requested report. + // + psInst->bGetRequestPending = true; + USBDCDSendDataEP0(0, pui8Report, ui32Size); + + break; + } + + // + // A set IDLE request has been made. This indicates to us how often a + // given report should be sent back to the host in the absence of any + // change in state of the device. + // + case USBREQ_SET_IDLE: + { + // + // Set the idle timeout for the requested report(s). + // + SetIdleTimeout(psHIDDevice, psUSBRequest->wValue & 0xFF, + (psUSBRequest->wValue >> 8) & 0xFF); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + break; + } + + // + // A get IDLE request has been made. This request queries the current + // idle timeout for a given report. + // + case USBREQ_GET_IDLE: + { + // + // Determine the timeout for the requested report. + // + ui32Timeout = GetIdleTimeout(psHIDDevice, psUSBRequest->wValue); + + if(ui32Timeout != HID_NOT_FOUND) + { + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui32Timeout, 1); + } + else + { + // + // The report ID was not found so stall the endpoint. + // + USBDCDStallEP0(0); + } + break; + } + + // + // Set either boot or report protocol for reports sent from the device. + // This is only supported by devices in the boot subclass. + // + case USBREQ_SET_PROTOCOL: + { + if(psHIDDevice->ui8Subclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_SET_PROTOCOL, + psUSBRequest->wValue, + (void *)0); + } + else + { + // + // This is not a boot subclass device so stall the endpoint to + // show that we don't support this request. + // + USBDCDStallEP0(0); + } + break; + } + + // + // Inform the host of the protocol, boot or report, that is currently + // in use. This is only supported by devices in the boot subclass. + // + case USBREQ_GET_PROTOCOL: + { + if(psHIDDevice->ui8Subclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application callback to get the answer. + // + ui8Protocol = (uint8_t)psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, USBD_HID_EVENT_GET_PROTOCOL, 0, + (void *)0); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui8Protocol, 1); + } + else + { + // + // This is not a boot subclass device so stall the endpoint to + // show that we don't support this request. + // + USBDCDStallEP0(0); + } + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the data requested +// on endpoint zero is received. +// +//***************************************************************************** +static void +HandleEP0DataReceived(void *pvHIDInstance, uint32_t ui32DataSize) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // If we were not passed any data, just return. + // + if(ui32DataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Make sure we are actually expecting something. + // + if(psInst->iHIDRxState != eHIDStateWaitData) + { + return; + } + + // + // Change the endpoint state back to idle now that we have been passed + // the data we were waiting for. + // + psInst->iHIDRxState = eHIDStateIdle; + + // + // The only things we ever request via endpoint zero are reports sent to + // us via a Set_Report request. Pass the newly received report on to + // the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_SET_REPORT, + psInst->ui16OutReportSize, + psInst->pui8OutReportData); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the data sent on +// endpoint zero is received and acknowledged by the host. +// +//***************************************************************************** +static void +HandleEP0DataSent(void *pvHIDInstance, uint32_t ui32Info) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // If we just sent a report in response to a Get_Report request, send an + // event to the application telling it that the transmission completed. + // + if(psInst->bGetRequestPending) + { + // + // Clear the flag now that we are sending the application callback. + // + psInst->bGetRequestPending = false; + + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_REPORT_SENT, 0, (void *)0); + } + + return; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// reset. If we are currently connected, send a disconnect event at this +// point. +// +//***************************************************************************** +static void +HandleReset(void *pvHIDInstance) +{ + ASSERT(pvHIDInstance != 0); + + // + // Merely call the disconnect handler. This causes a disconnect message to + // be sent to the client if we think we are currently connected. + // + HandleDisconnect(pvHIDInstance); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvHIDInstance) +{ + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (const tUSBDHIDDevice *)pvHIDInstance; + + // + // Pass the event on to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, USB_EVENT_SUSPEND, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvHIDInstance) +{ + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (const tUSBDHIDDevice *)pvHIDInstance; + + // + // Pass the event on to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, USB_EVENT_RESUME, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param pvHIDInstance is the instance data for this request. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +HIDTickHandler(void *pvHIDInstance, uint32_t ui32TimemS) +{ + tHIDInstance *psInst; + uint32_t ui32Size; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // If we are connected, process our idle timers. + // + if(psInst->bConnected) + { + ProcessIdleTimers(psHIDDevice, ui32TimemS); + } + + // + // Do we have a deferred receive waiting + // + if(psInst->ui16DeferredOpFlags & (1 << HID_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ui32Size = MAP_USBEndpointDataAvail(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psHIDDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! +//! An application wishing to offer a USB HID interface to a host system +//! must call this function to initialize the USB controller and attach the +//! device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psHIDDevice +//! pointer passed to it. This must be passed on all future calls from the +//! application to the HID device class driver. +//! +//! The USB HID device class API offers the application a report-based transmit +//! interface for Input reports. Output reports may be received via the +//! control endpoint or via a dedicated Interrupt OUT endpoint. If using the +//! dedicated endpoint, report data is delivered to the application packet-by- +//! packet. If the application uses reports longer than \b USBDHID_MAX_PACKET +//! bytes and would rather receive full reports, it may use a USB buffer above +//! the receive channel to allow full reports to be read. +//! +//! Transmit Operation: +//! +//! Calls to USBDHIDReportWrite() pass complete reports to the driver for +//! transmission. These will be transmitted to the host using as many USB +//! packets as are necessary to complete the transmission. +//! +//! Once a full Input report has been acknowledged by the USB host, a +//! \b USB_EVENT_TX_COMPLETE event is sent to the application transmit callback +//! to inform it that another report may be transmitted. +//! +//! Receive Operation (when using a dedicated interrupt OUT endpoint): +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event \b USB_EVENT_RX_AVAILABLE. The application must then +//! call USBDHIDPacketRead(), passing a buffer capable of holding the received +//! packet. The size of the packet may be determined by calling function +//! USBDHIDRxPacketAvailable() prior to reading the packet. +//! +//! Receive Operation (when not using a dedicated OUT endpoint): +//! +//! If no dedicated OUT endpoint is used, Output and Feature reports are sent +//! from the host using the control endpoint, endpoint zero. When such a +//! report is received, \b USBD_HID_EVENT_GET_REPORT_BUFFER is sent to the +//! application which must respond with a buffer large enough to hold the +//! report. The device class driver will then copy the received report into +//! the supplied buffer before sending \b USBD_HID_EVENT_SET_REPORT to indicate +//! that the report is now available. +//! +//! \note The application must not make any calls to the low level USB device +//! interface if interacting with USB via the USB HID device class API. Doing +//! so will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the \e psHIDDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice) +{ + tDeviceDescriptor *pi16DevDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psHIDDevice); + ASSERT(psHIDDevice->ppui8StringDescriptors); + ASSERT(psHIDDevice->pfnRxCallback); + ASSERT(psHIDDevice->pfnTxCallback); + ASSERT(psHIDDevice->ppui8ClassDescriptors); + ASSERT(psHIDDevice->psHIDDescriptor); + ASSERT((psHIDDevice->ui8NumInputReports == 0) || psHIDDevice->psReportIdle); + + USBDHIDCompositeInit(ui32Index, psHIDDevice, 0); + + // + // Fix up the device descriptor with the client-supplied values. + // + pi16DevDesc = (tDeviceDescriptor *)psHIDDevice->sPrivateData.sDevInfo.pui8DeviceDescriptor; + pi16DevDesc->idVendor = psHIDDevice->ui16VID; + pi16DevDesc->idProduct = psHIDDevice->ui16PID; + + // + // All is well so now pass the descriptors to the lower layer and put + // the HID device on the bus. + // + USBDCDInit(ui32Index, &psHIDDevice->sPrivateData.sDevInfo, + (void *)psHIDDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psHIDDevice); +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psHIDDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! USB HID device classes call this function to initialize the lower level +//! HID interface in the USB controller. If this HID device device is part of +//! a composite device, then the \e psCompEntry should point to the composite +//! device entry to initialize. This is part of the array that is passed to +//! the USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID APIs. +// +//***************************************************************************** +void * +USBDHIDCompositeInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice, + tCompositeEntry *psCompEntry) +{ + tHIDInstance *psInst; + tEndpointDescriptor *psEndpoint; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psHIDDevice); + ASSERT(psHIDDevice->ppsConfigDescriptor); + ASSERT(psHIDDevice->ppui8StringDescriptors); + ASSERT(psHIDDevice->pfnRxCallback); + ASSERT(psHIDDevice->pfnTxCallback); + ASSERT(psHIDDevice->ppui8ClassDescriptors); + ASSERT(psHIDDevice->psHIDDescriptor); + ASSERT((psHIDDevice->ui8NumInputReports == 0) || psHIDDevice->psReportIdle); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sHIDHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8HIDDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = psHIDDevice->ppsConfigDescriptor; + psInst->sDevInfo.ppui8StringDescriptors = + psHIDDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psHIDDevice->ui32NumStringDescriptors; + + // + // Default the endpoints zero before looking for them in the configuration + // descriptor. + // + psInst->ui8Interface = 0; + psInst->ui8INEndpoint = 0; + psInst->ui8OUTEndpoint = 0; + + // + // Get the first endpoint descriptor on interface 0. + // + psEndpoint = + USBDCDConfigGetInterfaceEndpoint(psHIDDevice->ppsConfigDescriptor[0], + psInst->ui8Interface, 0, 0); + + if(psEndpoint) + { + if(psEndpoint->bEndpointAddress & 0x80) + { + psInst->ui8INEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + else + { + psInst->ui8OUTEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + } + + // + // Get the second endpoint descriptor on interface 0. + // + psEndpoint = + USBDCDConfigGetInterfaceEndpoint(psHIDDevice->ppsConfigDescriptor[0], + psInst->ui8Interface, 0, 1); + if(psEndpoint) + { + if(psEndpoint->bEndpointAddress & 0x80) + { + psInst->ui8INEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + else + { + psInst->ui8OUTEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + } + + // + // Must have at least an IN endpoint. + // + if(psInst->ui8INEndpoint == 0) + { + return((void *)0); + } + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psHIDDevice; + } + + psInst->ui32USBBase = USB0_BASE; + psInst->iHIDRxState = eHIDStateUnconfigured; + psInst->iHIDTxState = eHIDStateUnconfigured; + psInst->ui16DeferredOpFlags = 0; + psInst->bConnected = false; + psInst->bGetRequestPending = false; + psInst->bSendInProgress = false; + psInst->ui16InReportIndex = 0; + psInst->ui16InReportSize = 0; + psInst->pui8InReportData = (uint8_t *)0; + psInst->ui16OutReportSize = 0; + psInst->pui8OutReportData = (uint8_t *)0; + + // + // Initialize the device info structure for the HID device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Initialize the input report idle timers if any input reports exist. + // + ClearIdleTimers(psHIDDevice); + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(HIDTickHandler, (void *)psHIDDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psHIDDevice); +} + +//***************************************************************************** +// +//! Shuts down the HID device. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function terminates HID operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the HID device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvHIDInstance instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDTerm(void *pvHIDInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get a pointer to our instance data. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase)); + + psInst->ui32USBBase = 0; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDHIDInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the pvHIDInstance structure passed to USBDHIDInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDHIDSetRxCBData(void *pvHIDInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvHIDInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvHIDInstance)->pvRxCBData; + ((tUSBDHIDDevice *)pvHIDInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data pointer for the transmit callback. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback data pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDHIDInit(). +//! +//! If a client wants to make runtime changes in the callback data, it must +//! ensure that the pvHIDInstance structure passed to USBDHIDInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback data pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDHIDSetTxCBData(void *pvHIDInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvHIDInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvHIDInstance)->pvTxCBData; + ((tUSBDHIDDevice *)pvHIDInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a HID device report to the USB host via the HID interrupt IN +//! endpoint. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pi8Data points to the first byte of data which is to be transmitted. +//! \param ui32Length is the number of bytes of data to transmit. +//! \param bLast is ignored in this implementation. This parameter is required +//! to ensure compatibility with other device class drivers and USB buffers. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB transaction using as many packets as it takes to send +//! all the data in the report. If no transmission is currently ongoing, +//! the first packet of data is immediately copied to the relevant USB endpoint +//! FIFO for transmission. Whenever all the report data has been acknowledged +//! by the host, a \b USB_EVENT_TX_COMPLETE event will be sent to the +//! application transmit callback indicating that another report can now be +//! transmitted. +//! +//! The caller must ensure that the data pointed to by \e pui8Data remains +//! accessible and unaltered until the \b USB_EVENT_TX_COMPLETE is received. +//! +//! \return Returns the number of bytes actually scheduled for transmission. +//! At this level, this will either be the number of bytes passed or 0 to +//! indicate a failure. +// +//***************************************************************************** +uint32_t +USBDHIDReportWrite(void *pvHIDInstance, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + tHIDInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Set a flag indicating that we are currently in the process of sending + // a packet. + // + psInst->bSendInProgress = true; + + // + // Can we send the data provided? + // + if(psInst->iHIDTxState != eHIDStateIdle) + { + // + // We are in the middle of sending another report. Return 0 to + // indicate that we can't send this report until the previous one + // finishes. + // + psInst->bSendInProgress = false; + return(0); + } + + // + // Clear the elapsed time since this report was last sent. + // + if(ui32Length) + { + ClearReportTimer(pvHIDInstance, *pi8Data); + } + + // + // Keep track of the whereabouts of the report so that we can send it in + // multiple packets if necessary. + // + psInst->pui8InReportData = pi8Data; + psInst->ui16InReportIndex = 0; + psInst->ui16InReportSize = ui32Length; + + // + // Schedule transmission of the first packet of the report. + // + psInst->iHIDTxState = eHIDStateWaitData; + i32Retcode = ScheduleReportTransmission(psInst); + + // + // Clear the flag we use to indicate that we are in the midst of sending + // a packet. + // + psInst->bSendInProgress = false; + + // + // Did an error occur while trying to send the data? + // + if(i32Retcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ui32Length); + } + else + { + // + // Yes - tell the caller we could not send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the interrupt OUT +//! endpoint (if in use). +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pi8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by pi8Data. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ui32Length bytes of data received from the USB +//! host into the supplied application buffer. If the driver detects that the +//! entire packet has been read, it is acknowledged to the host. +//! +//! The \e bLast parameter is ignored in this implementation since the end of +//! a packet can be determined without relying upon the client to provide +//! this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +uint32_t +USBDHIDPacketRead(void *pvHIDInstance, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + uint32_t ui32EPStatus, ui32Count, ui32Pkt; + tHIDInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Get as much data as we can. + // + ui32Count = ui32Length; + i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + pi8Data, &ui32Count); + + // + // Did we read the last of the packet data? + // + if(ui32Count == ui32Pkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + HID_DO_PACKET_RX, false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(i32Retcode != -1) + { + return(ui32Count); + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function indicates to the caller whether or not it is safe to send a +//! new report using a call to USBDHIDReportWrite(). The value returned will +//! be the maximum USB packet size (\b USBDHID_MAX_PACKET) if no transmission +//! is currently outstanding or 0 if a transmission is in progress. Since the +//! function USBDHIDReportWrite() can accept full reports longer than a single +//! USB packet, the caller should be aware that the returned value from this +//! class driver, unlike others, does not indicate the maximum size of report +//! that can be written but is merely an indication that another report can be +//! written. +//! +//! \return Returns 0 if an outgoing report is still being transmitted or +//! \b USBDHID_MAX_PACKET if no transmission is currently in progress. +// +//***************************************************************************** +uint32_t +USBDHIDTxPacketAvailable(void *pvHIDInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->iHIDTxState != eHIDStateIdle) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(USBDHID_MAX_PACKET); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +uint32_t +USBDHIDRxPacketAvailable(void *pvHIDInstance) +{ + uint32_t ui32EPStatus, ui32Size; + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + return(ui32Size); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvHIDInstance is the pointer to the HID device instance structure. +//! \param ui8Power indicates the current power status, either +//! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDPowerStatusSet(void *pvHIDInstance, uint8_t ui8Power) +{ + ASSERT(pvHIDInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvHIDInstance is the pointer to the HID device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +bool +USBDHIDRemoteWakeupRequest(void *pvHIDInstance) +{ + ASSERT(pvHIDInstance); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhid.h b/usblib/device/usbdhid.h new file mode 100644 index 0000000..14830fa --- /dev/null +++ b/usblib/device/usbdhid.h @@ -0,0 +1,1102 @@ +//***************************************************************************** +// +// usbdhid.h - Definitions used by HID class devices. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBDHID_H__ +#define __USBDHID_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the size of the g_pui8HIDInterface array in bytes. +// +//***************************************************************************** +#define HIDINTERFACE_SIZE (9) + +//***************************************************************************** +// +// This is the size of the g_pui8HIDInEndpoint array in bytes. +// +//***************************************************************************** +#define HIDINENDPOINT_SIZE (7) + +//***************************************************************************** +// +// This is the size of the g_pui8HIDOutEndpoint array in bytes. +// +//***************************************************************************** +#define HIDOUTENDPOINT_SIZE (7) + +//***************************************************************************** +// +// This is the size of the tHIDDescriptor in bytes. +// +//***************************************************************************** +#define HIDDESCRIPTOR_SIZE (9) + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB HID Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +//***************************************************************************** +#define COMPOSITE_DHID_SIZE (HIDINTERFACE_SIZE + HIDINENDPOINT_SIZE + \ + HIDOUTENDPOINT_SIZE + HIDDESCRIPTOR_SIZE) + +//***************************************************************************** +// +// Macros used to create the static Report Descriptors. +// +//***************************************************************************** + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Page entries in HID report +//! descriptors. +//! +//! \param ui8Value is the Usage Page value. +//! +//! This macro takes a value and prepares it to be placed as a Usage Page entry +//! into a HID report structure. These are defined by the USB HID +//! specification. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsagePage(ui8Value) 0x05, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Page entries in HID report +//! descriptors when a vendor-specific value is to be used. +//! +//! \param ui16Value is the Usage Page value. +//! +//! This macro takes a value and prepares it to be placed as a Usage Page entry +//! into a HID report structure. These are defined by the USB HID +//! specification. Vendor-specific values must lie in the range 0xFF00 to +//! 0xFFFF inclusive. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsagePageVendor(ui16Value) 0x06, ((ui16Value) & 0xFF), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage entries in HID report descriptors. +//! +//! \param ui8Value is the Usage value. +//! +//! This macro takes a value and prepares it to be placed as a Usage entry into +//! a HID report structure. These are defined by the USB HID specification. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Usage(ui8Value) 0x09, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding vendor-specific Usage entries in HID +//! report descriptors. +//! +//! \param ui16Value is the vendor-specific Usage value in the range 0xFF00 to +//! 0xFFFF. +//! +//! This macro takes a value and prepares it to be placed as a Usage entry into +//! a HID report structure. These are defined by the USB HID specification. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsageVendor(ui16Value) 0x0A, ((ui16Value) & 0xFF), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Minimum entries in HID report +//! descriptors. +//! +//! \param ui8Value is the Usage Minimum value. +//! +//! This macro takes a value and prepares it to be placed as a Usage Minimum +//! entry into a HID report structure. This is the first or minimum value +//! associated with a usage value. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsageMinimum(ui8Value) 0x19, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Maximum entries in HID report +//! descriptors. +//! +//! \param ui8Value is the Usage Maximum value. +//! +//! This macro takes a value and prepares it to be placed as a Usage Maximum +//! entry into a HID report structure. This is the last or maximum value +//! associated with a usage value. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsageMaximum(ui8Value) 0x29, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Minimum entries in HID report +//! descriptors. +//! +//! \param i8Value is the Logical Minimum value. +//! +//! This macro takes a value and prepares it to be placed as a Logical Minimum +//! entry into a HID report structure. This is the actual minimum value for a +//! range of values associated with a field. +//! +//! \return Not a function. +// +//***************************************************************************** +#define LogicalMinimum(i8Value) 0x15, ((i8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Maximum entries in HID report +//! descriptors. +//! +//! \param i8Value is the Logical Maximum value. +//! +//! This macro takes a value and prepares it to be placed as a Logical Maximum +//! entry into a HID report structure. This is the actual maximum value for a +//! range of values associated with a field. +//! +//! \return Not a function. +// +//***************************************************************************** +#define LogicalMaximum(i8Value) 0x25, ((i8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Minimum entries in HID report +//! descriptors. +//! +//! \param i16Value is the Physical Minimum value. It is a signed, 16 bit +//! number. +//! +//! This macro takes a value and prepares it to be placed as a Physical Minimum +//! entry into a HID report structure. This is value is used in conversion of +//! the control logical value, as returned to the host in the relevant report, +//! to a physical measurement in the appropriate units. +//! +//! \return Not a function. +// +//***************************************************************************** +#define PhysicalMinimum(i16Value) \ + 0x36, ((i16Value) & 0xFF), \ + (((i16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Maximum entries in HID report +//! descriptors. +//! +//! \param i16Value is the Physical Maximum value. It is a signed, 16 bit +//! number. +//! +//! This macro takes a value and prepares it to be placed as a Physical Maximum +//! entry into a HID report structure. This is value is used in conversion of +//! the control logical value, as returned to the host in the relevant report, +//! to a physical measurement in the appropriate units. +//! +//! \return Not a function. +// +//***************************************************************************** +#define PhysicalMaximum(i16Value) \ + 0x46, ((i16Value) & 0xFF), \ + (((i16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Collection entries in HID report +//! descriptors. +//! +//! \param ui8Value is the type of Collection. +//! +//! This macro takes a value and prepares it to be placed as a Collection +//! entry into a HID report structure. This is the type of values that are +//! being grouped together, for instance input, output or features can be +//! grouped together as a collection. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Collection(ui8Value) 0xa1, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding End Collection entries in HID report +//! descriptors. +//! +//! This macro can be used to place an End Collection entry into a HID report +//! structure. This is a tag to indicate that a collection of entries has +//! ended in the HID report structure. This terminates a previous Collection() +//! entry. +//! +//! \return Not a function. +// +//***************************************************************************** +#define EndCollection 0xc0 + +//***************************************************************************** +// +//! This is a macro to assist adding Report Count entries in HID report +//! descriptors. +//! +//! \param ui8Value is the number of items in a report item. +//! +//! This macro takes a value and prepares it to be placed as a Report Count +//! entry into a HID report structure. This is number of entries of Report +//! Size for a given item. +//! +//! \return Not a function. +// +//***************************************************************************** +#define ReportCount(ui8Value) 0x95, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report ID entries in HID report +//! descriptors. +//! +//! \param ui8Value is the identifier prefix for the current report. +//! +//! This macro takes a value and prepares it to be placed as a Report ID +//! entry into a HID report structure. This value is used as a 1 byte prefix +//! for the report it is contained within. +//! +//! \return Not a function. +// +//***************************************************************************** +#define ReportID(ui8Value) 0x85, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report Size entries in HID report +//! descriptors. +//! +//! \param ui8Value is the size, in bits, of items in a report item. +//! +//! This macro takes a value and prepares it to be placed as a Report Size +//! entry into a HID report structure. This is size in bits of the entries of +//! of a report entry. The Report Count specifies how many entries of Report +//! Size are in a given item. These can be individual bits or bit fields. +//! +//! \return Not a function. +// +//***************************************************************************** +#define ReportSize(ui8Value) 0x75, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param ui8Value is bit mask to specify the type of a set of input report +//! items. Note that if the USB_HID_INPUT_BITF flag is required, the Input2 +//! macro (which uses a 2 byte version of the Input item tag) must be used +//! instead of this macro. +//! +//! This macro takes a value and prepares it to be placed as an Input entry +//! into a HID report structure. This specifies the type of an input item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of input for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Input(ui8Value) 0x81, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param ui16Value is bit mask to specify the type of a set of input report +//! items. Note that this macro uses a version of the Input item tag with a +//! two byte payload and allows any of the 8 possible data bits for the tag to +//! be used. If USB_HID_INPUT_BITF (bit 8) is not required, the Input macro +//! may be used instead. +//! +//! This macro takes a value and prepares it to be placed as an Input entry +//! into a HID report structure. This specifies the type of an input item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of input for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Input2(ui16Value) 0x82, ((ui16Value) & 0xff), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param ui8Value is bit mask to specify the type of a set of feature report +//! items. Note that if the \b USB_HID_FEATURE_BITF flag is required, the +//! Feature2 macro (which uses a 2 byte version of the Feature item tag) must +//! be used instead of this macro. +//! +//! This macro takes a value and prepares it to be placed as a Feature entry +//! into a HID report structure. This specifies the type of a feature item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of feature for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Feature(ui8Value) 0xB1, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param ui16Value is bit mask to specify the type of a set of feature report +//! items. Note that this macro uses a version of the Feature item tag with a +//! two byte payload and allows any of the 8 possible data bits for the tag to +//! be used. If \b USB_HID_FEATURE_BITF (bit 8) is not required, the Feature +//! macro may be used instead. +//! +//! This macro takes a value and prepares it to be placed as a Feature entry +//! into a HID report structure. This specifies the type of a feature item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of feature for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Feature2(ui16Value) 0xB2, ((ui16Value) & 0xff), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param ui8Value is bit mask to specify the type of a set of output report +//! items. Note that if the \b USB_HID_OUTPUT_BITF flag is required, the +//! Output2 macro (which uses a 2 byte version of the Output item tag) must be +//! used instead of this macro. +//! +//! This macro takes a value and prepares it to be placed as an Output entry +//! into a HID report structure. This specifies the type of an output item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of output for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Output(ui8Value) 0x91, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param ui16Value is bit mask to specify the type of a set of output report +//! items. Note that this macro uses a version of the Output item tag with a +//! two byte payload and allows any of the 8 possible data bits for the tag to +//! be used. If \b USB_HID_OUTPUT_BITF is not required, the Output macro +//! may be used instead. +//! +//! This macro takes a value and prepares it to be placed as an Output entry +//! into a HID report structure. This specifies the type of an output item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of output for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Output2(ui16Value) 0x92, ((ui16Value) & 0xff), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit Exponent entries in HID report +//! descriptors. +//! +//! \param i8Value is the required exponent in the range [-8, 7]. +//! +//! This macro takes a value and prepares it to be placed as a Unit Exponent +//! entry into a HID report structure. This is the exponent applied to +//! PhysicalMinimum and PhysicalMaximum when scaling and converting control +//! values to "real" units. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UnitExponent(i8Value) 0x55, ((i8Value) & 0x0f) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit entries for uncommon units in HID +//! report descriptors. +//! +//! \param ui32Value is the definition of the unit required as defined in +//! section 6.2.2.7 of the USB HID device class definition document. +//! +//! This macro takes a value and prepares it to be placed as a Unit entry into +//! a HID report structure. Note that individual macros are defined for common +//! units and this macro is intended for use when a complex or uncommon unit +//! is needed. It allows entry of a 5 nibble unit definition into the report +//! descriptor. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Unit(ui32Value) 0x67, (ui32Value) & 0x0f), \ + (((ui32Value) >> 8) & 0xFF), \ + (((ui32Value) >> 16) & 0xFF), \ + (((ui32Value) >> 24) & 0xFF) + +//***************************************************************************** +// +//! This macro inserts a Unit entry for centimeters into a report descriptor. +//! +//***************************************************************************** +#define UnitDistance_cm 0x66, 0x11, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for inches into a report descriptor. +//! +//***************************************************************************** +#define UnitDistance_i 0x66, 0x13, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for degrees into a report descriptor. +//! +//***************************************************************************** +#define UnitRotation_deg 0x66, 0x14, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for radians into a report descriptor. +//! +//***************************************************************************** +#define UnitRotation_rad 0x66, 0x12, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for grams into a report descriptor. +//! +//***************************************************************************** +#define UnitMass_g 0x66, 0x01, 0x01 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for seconds into a report descriptor. +//! +//***************************************************************************** +#define UnitTime_s 0x66, 0x01, 0x10 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for temperature in Kelvin into a report +//! descriptor. +//! +//***************************************************************************** +#define UnitTemp_K 0x67, 0x01, 0x00, 0x01, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for temperature in Fahrenheit into a report +//! descriptor. +//! +//***************************************************************************** +#define UnitTemp_F 0x67, 0x03, 0x00, 0x01, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for velocity in cm/s into a report +//! descriptor. +//! +//***************************************************************************** +#define UnitVelocitySI 0x66, 0x11, 0xF0 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for momentum in (grams * cm)/s into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitMomentumSI 0x66, 0x11, 0xF1 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for acceleration in cm/s**2 into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitAccelerationSI 0x66, 0x11, 0xE0 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for force in (cm * grams)/s**2 into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitForceSI 0x66, 0x11, 0xE1 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for energy in (grams * cm^2)/(s^2) into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitEnergySI 0x66, 0x21, 0xE1 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for angular acceleration in degrees/(s^2) +//! into a report descriptor. +//! +//***************************************************************************** +#define UnitAngAccelerationSI 0x66, 0x12, 0xE0 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for voltage into a a report descriptor. +//! +//***************************************************************************** +#define UnitVoltage 0x67, 0x21, 0xD1, 0xF0, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for voltage into a a report descriptor. +//! +//***************************************************************************** +#define UnitCurrent_A 0x67, 0x01, 0x00, 0x10, 0x00 + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB HID code and are here only to help with the application +// allocating the correct amount of memory for the HID device code. +// +//***************************************************************************** +#define USBDHID_MAX_PACKET 64 + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + eHIDStateUnconfigured, + + // + // No outstanding transaction remains to be completed. + // + eHIDStateIdle, + + // + // Waiting on completion of a send or receive transaction. + // + eHIDStateWaitData +} +tHIDState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for +// HID devices. The memory for this structure is included in the +// sPrivateData field in the tUSBDHIDDevice structure passed in the +// USBDHIDInit() function. +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The state of the HID receive channel. + // + volatile tHIDState iHIDRxState; + + // + // The state of the HID transmit channel. + // + volatile tHIDState iHIDTxState; + + // + // State of any pending operations that could not be handled immediately + // upon receipt. + // + volatile uint16_t ui16DeferredOpFlags; + + // + // Size of the HID IN report. + // + uint16_t ui16InReportSize; + + // + // . + // + uint16_t ui16InReportIndex; + + // + // Size of the HID OUT report. + // + uint16_t ui16OutReportSize; + + // + // Pointer to the current HID IN report data. + // + uint8_t *pui8InReportData; + + // + // Pointer to the current HID OUT report data. + // + uint8_t *pui8OutReportData; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // Whether an IN transaction is in process. + // + volatile bool bSendInProgress; + + // + // An HID request transaction is in process(Endpoint 0). + // + bool bGetRequestPending; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8INEndpoint; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8OUTEndpoint; + + // + // The bulk class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; +} +tHIDInstance; + +//***************************************************************************** +// +//! The structure used to track idle time for reports. An array of these +//! structures is passed to the HID device class driver during USBDHIDInit and +//! is used to track automatic resending of each report (if not disabled by +//! the host). +// +//***************************************************************************** +typedef struct +{ + // + //! The idle duration for the report expressed in units of 4mS. 0 + //! indicates infinite and informs the class driver not to send the report + //! unless a state change occurs. + // + uint8_t ui8Duration4mS; + + // + //! The ID of the report which this structure applies to. This is the + //! report ID as specified using a ReportID tag in the report descriptor + //! rather than the index of the report in the HID class descriptor array. + //! If only a single Input report is supported and, thus, no ReportID tag + //! is present, this field should be set to 0. + // + uint8_t ui8ReportID; + + // + //! The number of milliseconds before we need to send a copy of a given + //! report back to the host. This field is updated by the HID driver and + //! used to time sending of \b USBD_HID_EVENT_IDLE_TIMEOUT. + // + uint16_t ui16TimeTillNextmS; + + // + //! The number of milliseconds that have passed since the last time this + //! report was sent. The HID class driver needs to track this since + //! Set_Idle requests are required to take effect as if issued immediately + //! after the last transmission of the report to which they refer. + // + uint32_t ui32TimeSinceReportmS; +} +tHIDReportIdle; + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the HID device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + uint8_t ui8PwrAttributes; + + // + //! The interface subclass to publish to the server for this HID device. + // + uint8_t ui8Subclass; + + // + //! The interface protocol to publish to the server for this HID device. + // + uint8_t ui8Protocol; + + // + //! The number of Input reports that this device supports. This field + //! must equal the number of reports published in the HID class descriptors + //! for the device and also the number of entries in the array whose first + //! element is pointed to by field \e pi16ReportIdle below. + // + uint8_t ui8NumInputReports; + + // + //! A pointer to the first element in an array of structures used to track + //! idle time for each Input report. When USBDHIDInit() is called, the + //! ui8Duration4mS and ui8ReportID fields of each of these array members + //! should be initialized to indicate the default idle timeout for each + //! input report. This array must be in RAM since the HID device class + //! driver updates values in it in response to requests from the host + //! and to track elapsed time. The number of elements in the array must + //! match the number supplied in the ui8NumInputReports field above. + // + tHIDReportIdle *psReportIdle; + + //! A pointer to the callback function which is called to notify + //! the application of general events, events related to report transfers + //! on endpoint zero and events related to reception of Output and Feature + //! reports via the (optional) interrupt OUT endpoint. + // + tUSBCallback pfnRxCallback; + + // + //! A client-supplied pointer which is sent as the first + //! parameter in all calls made to the receive channel callback, + //! pfnRxCallback. + // + void *pvRxCBData; + + // + //! A pointer to the callback function which is called to notify + //! the application of events related to transmission of Input reports + //! via the interrupt IN endpoint. + // + tUSBCallback pfnTxCallback; + + // + //! A client-supplied pointer which is sent as the first + //! parameter in all calls made to the transmit channel callback, + //! pfnTxCallback. + // + void *pvTxCBData; + + // + //! If set to true, this field indicates that the device should use a + //! dedicated interrupt OUT endpoint to receive reports from the host. In + //! this case, reports from the host are passed to the application via the + //! receive callback using \b USB_EVENT_RX_AVAILABLE events. If false, + //! reports from the host are received via endpoint zero and passed to the + //! application via \b USBD_HID_EVENT_REPORT_SENT events. + // + bool bUseOutEndpoint; + + // + //! The HID descriptor that the device is to publish (following the + //! standard interface descriptor and prior to the endpoint descriptors for + //! the interface). + // + const tHIDDescriptor *psHIDDescriptor; + + // + //! The HID class descriptors offered by the device are defined in an + //! array of byte pointers and this field points to that array. The + //! order and number of elements in the array must match the associated + //! information provided in the HID descriptor in field by + //! \e pi16HIDDescriptor. + // + const uint8_t * const *ppui8ClassDescriptors; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1),HID + //! Interface description string (language 1), Configuration description + //! string (language 1), (optionally) First HID device-specific string + //! (language 1), (optionally) Second HID device-specific string (language + //! 1), etc. + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + //! The number of HID device-specific strings is dependent upon the content + //! of the report descriptor passed to the interface and is, thus, + //! application controlled. + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the \e ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + uint32_t ui32NumStringDescriptors; + + // + // ! The configuration descriptor for this HID device. + // + const tConfigHeader * const *ppsConfigDescriptor; + + // + //! The private instance data for this device instance. This + //! memory must remain accessible for as long as the HID device is in + //! use and must not be modified by any code outside the HID class driver. + // + tHIDInstance sPrivateData; +} +tUSBDHIDDevice; + +//***************************************************************************** +// +// HID-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the host is requesting a particular report be +//! returned via endpoint 0, the control endpoint. The ui32MsgValue parameter +//! contains the requested report type in the high byte and report ID in the +//! low byte (as passed in the wValue field of the USB request structure). +//! The pvMsgData parameter contains a pointer which must be written with the +//! address of the first byte of the requested report. The callback must +//! return the size in bytes of the report pointed to by *pvMsgData. The +//! memory returned in response to this event must remain unaltered until +//! \b USBD_HID_EVENT_REPORT_SENT is sent. +// +//***************************************************************************** +#define USBD_HID_EVENT_GET_REPORT \ + (USBD_HID_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This event indicates that a report previously requested via a +//! \b USBD_HID_EVENT_GET_REPORT has been successfully transmitted to the host. +//! The application may now free or reuse the report memory passed on the +//! previous event. Although this would seem to be an event that would be +//! passed to the transmit channel callback, it is actually passed to the +//! receive channel callback. This ensures that all events related to the +//! request and transmission of reports via endpoint zero can be handled in +//! a single function. +// +//***************************************************************************** +#define USBD_HID_EVENT_REPORT_SENT \ + (USBD_HID_EVENT_BASE + 1) + +//***************************************************************************** +// +//! This event indicates that the host has sent a Set_Report request to +//! the device and requests that the device provide a buffer into which the +//! report can be written. The ui32MsgValue parameter contains the received +//! report type in the high byte and report ID in the low byte (as passed in +//! the wValue field of the USB request structure). The pvMsgData parameter +//! contains the length of buffer requested. Note that this is the actual +//! length value cast to a "void *" type and not a pointer in this case. +//! The callback must return a pointer to a suitable buffer (cast to the +//! standard "uint32_t" return type for the callback). +// +//***************************************************************************** +#define USBD_HID_EVENT_GET_REPORT_BUFFER \ + (USBD_HID_EVENT_BASE + 2) + +//***************************************************************************** +// +//! This event indicates that the host has sent the device a report via +//! endpoint 0, the control endpoint. The ui32MsgValue field indicates the +//! size of the report and pvMsgData points to the first byte of the report. +//! The report buffer was previously returned in response to an +//! earlier \b USBD_HID_EVENT_GET_REPORT_BUFFER callback. The HID device class +//! driver does not access the memory pointed to by pvMsgData after this +//! callback is made so the application is free to reuse or free it at this +//! point. +// +//***************************************************************************** +#define USBD_HID_EVENT_SET_REPORT \ + (USBD_HID_EVENT_BASE + 3) + +//***************************************************************************** +// +//! This event is sent in response to a Get_Protocol request from the host. +//! The callback should provide the current protocol via the return code, +//! \b USB_HID_PROTOCOL_BOOT or \b USB_HID_PROTOCOL_REPORT. +// +//***************************************************************************** +#define USBD_HID_EVENT_GET_PROTOCOL \ + (USBD_HID_EVENT_BASE + 4) + +//***************************************************************************** +// +//! This event is sent in response to a Set_Protocol request from the host. +//! The ui32MsgData value contains the requested protocol, +//! \b USB_HID_PROTOCOL_BOOT or \b USB_HID_PROTOCOL_REPORT. +// +//***************************************************************************** +#define USBD_HID_EVENT_SET_PROTOCOL \ + (USBD_HID_EVENT_BASE + 5) + +//***************************************************************************** +// +//! This event indicates to an application that a report idle timeout has +//! occurred and requests a pointer to the report that must be sent back to +//! the host. The ui32MsgData value contains the requested report ID and +//! pvMsgData contains a pointer that must be written with a pointer to the +//! report data that is to be sent. The callback must return the number of +//! bytes in the report pointed to by *pvMsgData. +// +//***************************************************************************** +#define USBD_HID_EVENT_IDLE_TIMEOUT \ + (USBD_HID_EVENT_BASE + 6) + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice); +extern void *USBDHIDCompositeInit(uint32_t ui32Index, + tUSBDHIDDevice *psDevice, + tCompositeEntry *psCompEntry); +extern void USBDHIDTerm(void *pvHIDInstance); +extern void *USBDHIDSetRxCBData(void *pvHIDInstance, void *pvCBData); +extern void *USBDHIDSetTxCBData(void *pvHIDInstance, void *pvCBData); +extern uint32_t USBDHIDReportWrite(void *pvHIDInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDHIDPacketRead(void *pvHIDInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDHIDTxPacketAvailable(void *pvHIDInstance); +extern uint32_t USBDHIDRxPacketAvailable(void *pvHIDInstance); +extern bool USBDHIDRemoteWakeupRequest(void *pvHIDInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED + +// +// Use USBDCDFeatureSet() or USBHCDFeatureSet() with \b USBLIB_FEATURE_POWER +// configuration option. +// +extern void USBDHIDPowerStatusSet(void *pvHIDInstance, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHID_H__ diff --git a/usblib/device/usbdhidgamepad.c b/usblib/device/usbdhidgamepad.c new file mode 100644 index 0000000..a0945cc --- /dev/null +++ b/usblib/device/usbdhidgamepad.c @@ -0,0 +1,845 @@ +//***************************************************************************** +// +// usbdhidgame.c - USB HID Gamepad device class driver +// +// Copyright (c) 2013-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/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdhid.h" +#include "usblib/device/usbdhidgamepad.h" + +//***************************************************************************** +// +//! \addtogroup hid_gamepad_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// HID device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +//***************************************************************************** +static uint8_t g_pui8GameDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(24), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_SELF_PWR, // Self Powered. + 0, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// This is the HID interface descriptor for the gamepad device. +// +//***************************************************************************** +static uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] = +{ + // + // HID Device Class Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_HID, // The interface class + 0, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. +}; + +//***************************************************************************** +// +// This is the HID IN endpoint descriptor for the gamepad device. +// +//***************************************************************************** +static const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(USB_EP_1), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 1, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The following is the HID report structure definition that is passed back +// to the host. +// +//***************************************************************************** +static const uint8_t g_pui8GameReportDescriptor[] = +{ + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_JOYSTICK), + Collection(USB_HID_APPLICATION), + // + // The axis for the controller. + // + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage (USB_HID_POINTER), + Collection (USB_HID_PHYSICAL), + + // + // The X, Y and Z values which are specified as 8-bit absolute + // position values. + // + Usage (USB_HID_X), + Usage (USB_HID_Y), + Usage (USB_HID_Z), + + // + // 3 8-bit absolute values. + // + ReportSize(8), + ReportCount(3), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_ABS), + + // + // The 8 buttons. + // + UsagePage(USB_HID_BUTTONS), + UsageMinimum(1), + UsageMaximum(8), + LogicalMinimum(0), + LogicalMaximum(1), + PhysicalMinimum(0), + PhysicalMaximum(1), + + // + // 8 - 1 bit values for the buttons. + // + ReportSize(1), + ReportCount(8), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_ABS), + + EndCollection, + EndCollection +}; + +//***************************************************************************** +// +// The HID descriptor for the gamepad device. +// +//***************************************************************************** +static tHIDDescriptor g_sGameHIDDescriptor = +{ + 9, // bLength + USB_HID_DTYPE_HID, // bDescriptorType + 0x111, // bcdHID (version 1.11 compliant) + 0, // bCountryCode (not localized) + 1, // bNumDescriptors + { + { + USB_HID_DTYPE_REPORT, // Report descriptor + sizeof(g_pui8GameReportDescriptor) + // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// The HID configuration descriptor is defined as four sections. +// These sections are: +// +// 1. The 9 byte configuration descriptor. +// 2. The interface descriptor. +// 3. The HID report and physical descriptors, provided by the application +// or the default can be used. +// 4. The mandatory interrupt IN endpoint descriptor. +// +//***************************************************************************** +static const tConfigSection g_sHIDConfigSection = +{ + sizeof(g_pui8GameDescriptor), + g_pui8GameDescriptor +}; + +static const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pui8HIDInterface), + g_pui8HIDInterface +}; + +static const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pui8HIDInEndpoint), + g_pui8HIDInEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +static tConfigSection g_sHIDDescriptorSection = +{ + sizeof(g_sGameHIDDescriptor), + (const uint8_t *)&g_sGameHIDDescriptor +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete HID configuration descriptor. +// +//***************************************************************************** +static const tConfigSection *g_psHIDSections[] = +{ + &g_sHIDConfigSection, + &g_sHIDInterfaceSection, + &g_sHIDDescriptorSection, + &g_sHIDInEndpointSection, +}; + +#define NUM_HID_SECTIONS ((sizeof(g_psHIDSections) / \ + sizeof(tConfigSection *))) + +//***************************************************************************** +// +// The header for the single configuration supported. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. Note that this must be +// in RAM since we need to include or exclude the final section based on +// client supplied initialization parameters. +// +//***************************************************************************** +static tConfigHeader g_sHIDConfigHeader = +{ + NUM_HID_SECTIONS, + g_psHIDSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +static const tConfigHeader * const g_ppsHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the gamepad class there is only a +// single report descriptor. +// +//***************************************************************************** +static const uint8_t *g_ppui8GameClassDescriptors[] = +{ + g_pui8GameReportDescriptor +}; + +//***************************************************************************** +// +// HID gamepad transmit channel event handler function. +// +// \param pvGameDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to the HID gamepad device structure +// of the type tUSBDHIDGamepadDevice. +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the lower level HID device class driver to inform +// the application of particular asynchronous events related to report events +// related to using the interrupt IN endpoint. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDGamepadTxHandler(void *pvGameDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tUSBDGamepadInstance *psInst; + tUSBDHIDGamepadDevice *psGamepad; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvGameDevice); + + // + // Get a pointer to our instance data + // + psGamepad = (tUSBDHIDGamepadDevice *)pvGameDevice; + psInst = &psGamepad->sPrivateData; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // The last transmission is complete so return to the idle state. + // + psInst->iState = eHIDGamepadStateIdle; + + // + // Pass the event on to the application. + // + psGamepad->pfnCallback(psGamepad->pvCBData, USB_EVENT_TX_COMPLETE, + ui32MsgData, (void *)0); + + break; + } + + // + // Ignore all other events related to transmission of reports via + // the interrupt IN endpoint. + // + default: + { + break; + } + } + + return(0); +} + +//***************************************************************************** +// +// Main HID device class event receive handler function. +// +// \param pvGameDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to the HID gamepad device structure +// of the type tUSBDHIDGamepadDevice. +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the lower level HID device class driver to inform +// the application of particular asynchronous events related to operation of +// the gamepad HID device. +// +// \note This function also receive all generic events as well such as +// \b USB_EVENT_CONNECTED and USB_EVENT_DISCONNECTED. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDGamepadRxHandler(void *pvGamepad, uint32_t ui32Event, uint32_t ui32MsgData, + void *pvMsgData) +{ + tUSBDGamepadInstance *psInst; + tUSBDHIDGamepadDevice *psGamepad; + uint32_t ui32Ret; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvGamepad); + + // + // Return zero by default. + // + ui32Ret = 0; + + // + // Get a pointer to our instance data + // + psGamepad = (tUSBDHIDGamepadDevice *)pvGamepad; + psInst = &psGamepad->sPrivateData; + + // + // Which event were we sent? + // + switch(ui32Event) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + // + // Now in the idle state. + // + psInst->iState = eHIDGamepadStateIdle; + + // + // Pass the information on to the application. + // + psGamepad->pfnCallback(psGamepad->pvCBData, USB_EVENT_CONNECTED, 0, + (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->iState = eHIDGamepadStateNotConnected; + + // + // Pass the information on to the application. + // + ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, + USB_EVENT_DISCONNECTED, 0, + (void *)0); + + break; + } + + // + // This handles the Set Idle command. + // + case USBD_HID_EVENT_IDLE_TIMEOUT: + { + // + // Give the pointer to the idle report structure. + // + *(void **)pvMsgData = (void *)&psInst->sReportIdle; + + ui32Ret = sizeof(psInst->sReportIdle); + + break; + } + + // + // The host is polling for a particular report and the HID driver + // is asking for the latest version to transmit. + // + case USBD_HID_EVENT_GET_REPORT: + { + // + // If this is an IN request then pass the request on to the + // application. All other requests are ignored. + // + if(ui32MsgData == USB_HID_REPORT_IN) + { + ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, + USBD_HID_EVENT_GET_REPORT, 0, + pvMsgData); + } + + break; + } + + // + // The device class driver has completed sending a report to the + // host in response to a Get_Report request. + // + case USBD_HID_EVENT_REPORT_SENT: + { + // + // We have nothing to do here. + // + break; + } + + // + // Pass these events to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + case USB_EVENT_LPM_RESUME: + case USB_EVENT_LPM_SLEEP: + case USB_EVENT_LPM_ERROR: + { + ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, ui32Event, + ui32MsgData, pvMsgData); + + break; + } + + // + // This event is sent in response to a host Set_Report request which + // is not supported for gamepads. + // + case USBD_HID_EVENT_GET_REPORT_BUFFER: + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(ui32Ret); +} + +//***************************************************************************** +// +//! Initializes HID gamepad device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller that is to be +//! initialized for HID gamepad device operation. +//! \param psGamepad points to a structure containing parameters +//! customizing the operation of the HID gamepad device. +//! +//! An application that enables a USB HID gamepad interface to a USB host +//! must call this function to initialize the USB controller and attach the +//! gamepad device to the USB bus. This function performs all required USB +//! initialization, and the device is ready for operation on the function +//! return. +//! +//! On successful completion, this function returns the modified \e psGamepad +//! pointer passed to it or returns a NULL pointer if there was a problem. +//! This pointer must be passed on all future calls to the HID gamepad device +//! driver. +//! +//! When a host connects and configures the device, the application callback +//! receives \b USB_EVENT_CONNECTED, after which calls can be made to +//! USBDHIDGamepadSendReport() to report changes to the gamepad interface to +//! the USB host when it requests them. +//! +//! \note The application must not make any calls to the lower level USB device +//! interfaces if interacting with USB via the USB HID gamepad device class +//! API. +//! +//! \return Returns NULL on failure or the \e psGamepad pointer on success. +// +//***************************************************************************** +tUSBDHIDGamepadDevice * +USBDHIDGamepadInit(uint32_t ui32Index, tUSBDHIDGamepadDevice *psGamepad) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + tConfigDescriptor *pConfigDesc; + + // + // Check basic parameter validity. + // + ASSERT(psGamepad); + ASSERT(psGamepad->ppui8StringDescriptors); + ASSERT(psGamepad->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psGamepad->sPrivateData.sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDGamepadCompositeInit(ui32Index, psGamepad, 0); + + pConfigDesc = (tConfigDescriptor *)g_pui8GameDescriptor; + pConfigDesc->bmAttributes = psGamepad->ui8PwrAttributes; + pConfigDesc->bMaxPower = (uint8_t)(psGamepad->ui16MaxPowermA / 2); + + // + // If we initialized the HID layer successfully, pass our device pointer + // back as the return code, otherwise return NULL to indicate an error. + // + if(pvRetcode) + { + // + // Initialize the lower layer HID driver and pass it the various + // structures and descriptors necessary to declare that we are a + // gamepad. + // + pvRetcode = USBDHIDInit(ui32Index, psHIDDevice); + + return(psGamepad); + } + else + { + return((tUSBDHIDGamepadDevice *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID gamepad device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller that is to be +//! initialized for HID gamepad device operation. +//! \param psGamepad points to a structure containing parameters +//! customizing the operation of the HID gamepad device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! This call is very similar to USBDHIDGamepadInit() except that it is used +//! for initializing an instance of the HID gamepad device for use in a +//! composite device. If this HID gamepad is part of a composite device, then +//! the \e psCompEntry should point to the composite device entry to +//! initialize. This entry is part of the array that is passed to the +//! USBDCompositeInit() function to start up and complete configuration of a +//! composite USB device. +//! +//! \return Returns NULL on failure or the \e psGamepad value that should be +//! used with the remaining USB HID gamepad APIs. +// +//***************************************************************************** +tUSBDHIDGamepadDevice * +USBDHIDGamepadCompositeInit(uint32_t ui32Index, + tUSBDHIDGamepadDevice *psGamepad, + tCompositeEntry *psCompEntry) +{ + tUSBDGamepadInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psGamepad); + ASSERT(psGamepad->ppui8StringDescriptors); + ASSERT(psGamepad->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = &psGamepad->sPrivateData; + + // + // Initialize the various fields in our instance structure. + // + psInst->iState = eHIDGamepadStateNotConnected; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psInst->sHIDDevice; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->ui16PID = psGamepad->ui16PID; + psHIDDevice->ui16VID = psGamepad->ui16VID; + psHIDDevice->ui16MaxPowermA = psGamepad->ui16MaxPowermA; + psHIDDevice->ui8PwrAttributes = psGamepad->ui8PwrAttributes; + psHIDDevice->ui8Subclass = 0; + psHIDDevice->ui8Protocol = 0; + psHIDDevice->ui8NumInputReports = 1; + psHIDDevice->psReportIdle = &psInst->sReportIdle; + psInst->sReportIdle.ui8Duration4mS = 125; + psInst->sReportIdle.ui8ReportID = 0; + psInst->sReportIdle.ui32TimeSinceReportmS = 0; + psInst->sReportIdle.ui16TimeTillNextmS = 0; + psHIDDevice->pfnTxCallback = HIDGamepadTxHandler; + psHIDDevice->pvRxCBData = (void *)psGamepad; + psHIDDevice->pfnRxCallback = HIDGamepadRxHandler; + psHIDDevice->pvTxCBData = (void *)psGamepad; + psHIDDevice->bUseOutEndpoint = false, + psHIDDevice->psHIDDescriptor = &g_sGameHIDDescriptor; + psHIDDevice->ppui8ClassDescriptors = g_ppui8GameClassDescriptors; + psHIDDevice->ppui8StringDescriptors = psGamepad->ppui8StringDescriptors; + psHIDDevice->ui32NumStringDescriptors = + psGamepad->ui32NumStringDescriptors; + psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors; + + // + // If there was an override for the report descriptor then use it. + // + if(psGamepad->pui8ReportDescriptor) + { + // + // Save the report descriptor in the list of report descriptors. + // + g_ppui8GameClassDescriptors[0] = psGamepad->pui8ReportDescriptor; + + // + // Override the report descriptor size. + // + g_sGameHIDDescriptor.sClassDescriptor[0].wDescriptorLength = + psGamepad->ui32ReportSize; + } + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a gamepad. + // + return(USBDHIDCompositeInit(ui32Index, psHIDDevice, psCompEntry)); +} + +//***************************************************************************** +// +//! Schedules a report to be sent once the host requests more data. +//! +//! \param psHIDGamepad is the structure pointer that is returned from the +//! USBDHIDGamepadCompositeInit() or USBDHIDGamepadInit() functions. +//! \param pvReport is the data to send to the host. +//! \param ui32Size is the number of bytes in the \e pvReport buffer. +//! +//! This call is made by an application to schedule data to be sent to the +//! host when the host requests an update from the device. The application +//! must then wait for a \b USB_EVENT_TX_COMPLETE event in the function +//! provided in the \e pfnCallback pointer in the tUSBDHIDGamepadDevice +//! structure before being able to send more data with this function. The +//! pointer passed in the \e pvReport can be updated once this call returns as +//! the data has been copied from the buffer. The function returns +//! \b USBDGAMEPAD_SUCCESS if the transmission was successfully scheduled or +//! \b USBDGAMEPAD_TX_ERROR if the report could not be sent at this time. +//! If the call is made before the device is connected or ready to communicate +//! with the host, then the function can return \b USBDGAMEPAD_NOT_CONFIGURED. +//! +//! \return The function returns one of the \b USBDGAMEPAD_* values. +// +//***************************************************************************** +uint32_t +USBDHIDGamepadSendReport(tUSBDHIDGamepadDevice *psHIDGamepad, void *pvReport, + uint32_t ui32Size) +{ + uint32_t ui32Retcode, ui32Count; + tUSBDGamepadInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDGamepad->sPrivateData.sHIDDevice; + + // + // Get a pointer to our instance data + // + psInst = &psHIDGamepad->sPrivateData; + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(psInst->iState == eHIDGamepadStateNotConnected) + { + return(USBDGAMEPAD_NOT_CONFIGURED); + } + + // + // Only send a report if the transmitter is currently free. + // + if(USBDHIDTxPacketAvailable((void *)psHIDDevice)) + { + // + // Send the report to the host. + // + psInst->iState = eHIDGamepadStateSending; + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, pvReport, ui32Size, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(ui32Count == 0) + { + // + // No - report the error to the caller. + // + ui32Retcode = USBDGAMEPAD_TX_ERROR; + } + else + { + ui32Retcode = USBDGAMEPAD_SUCCESS; + } + } + else + { + ui32Retcode = USBDGAMEPAD_TX_ERROR; + } + + // + // Return the relevant error code to the caller. + // + return(ui32Retcode); +} + +//***************************************************************************** +// +//! Shuts down the HID gamepad device. +//! +//! \param psGamepad is the pointer to the device instance structure +//! as returned by USBDHIDGamepadInit() or USBDHIDGamepadCompositeInit(). +//! +//! This function terminates HID gamepad operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e psGamepad instance may not me used in any other call to the HID +//! gamepad device other than to reinitialize by calling USBDHIDGamepadInit() +//! or USBDHIDGamepadCompositeInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDGamepadTerm(tUSBDHIDGamepadDevice *psGamepad) +{ + tUSBDHIDDevice *psHIDDevice; + + ASSERT(psGamepad); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psGamepad->sPrivateData.sHIDDevice; + + // + // Mark the device as no longer connected. + // + psGamepad->sPrivateData.iState = eHIDGamepadStateNotConnected; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhidgamepad.h b/usblib/device/usbdhidgamepad.h new file mode 100644 index 0000000..3610680 --- /dev/null +++ b/usblib/device/usbdhidgamepad.h @@ -0,0 +1,274 @@ +//***************************************************************************** +// +// usbdhidgame.h - The header information for using the USB libraries game pad +// device class. +// +// Copyright (c) 2013-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. +// +//***************************************************************************** + +#ifndef __USBDHIDGAME_H__ +#define __USBDHIDGAME_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_gamepad_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the game pad can be in during +// normal operation. This should not be used by applications and is only +// here for memory allocation purposes. +// +//***************************************************************************** +typedef enum +{ + // + // Not yet configured. + // + eHIDGamepadStateNotConnected, + + // + // Nothing to transmit and not waiting on data to be sent. + // + eHIDGamepadStateIdle, + + // + // Waiting on data to be sent. + // + eHIDGamepadStateSending +} +tGamepadState; + +//***************************************************************************** +// +// PRIVATE +// +// This is the structure for an instance of a USB game pad device. This should +// not be used by applications and is only here for memory allocation purposes. +// +//***************************************************************************** +typedef struct +{ + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; + + // + // The current state of the game pad device. + // + tGamepadState iState; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; +} tUSBDGamepadInstance; + +//***************************************************************************** +// +//! This structure is used by the application to define operating parameters +//! for the HID game device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wake up. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function that is called to notify + //! the application of general events. This pointer must point to a valid + //! function. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer that is sent as the first parameter in all + //! calls made to the pfnCallback gamedevice callback function. + // + void *pvCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order: + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1),HID + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the \e ppStringDescriptors + //! array, which must be (1 + (5 * (number of languages))). + // + const uint32_t ui32NumStringDescriptors; + + // + //! Optional report descriptor if the application wants to use a custom + //! descriptor. + // + const uint8_t *pui8ReportDescriptor; + + // + //! The size of the optional report descriptor define in + //! pui8ReportDescriptor. + // + const uint32_t ui32ReportSize; + + // + //! The private instance data for this device. This memory must + //! remain accessible for as long as the game device is in use and + //! must not be modified by any code outside the HID game device driver. + // + tUSBDGamepadInstance sPrivateData; +} +tUSBDHIDGamepadDevice; + +//***************************************************************************** +// +//! The USBDHIDGamepadSendReport() call successfully scheduled the report. +// +//***************************************************************************** +#define USBDGAMEPAD_SUCCESS 0 + +//***************************************************************************** +// +//! The USBDHIDGamepadSendReport() function could not send the report at this +//! time. +// +//***************************************************************************** +#define USBDGAMEPAD_TX_ERROR 1 + +//***************************************************************************** +// +//! The device is not currently configured and cannot perform any operations. +// +//***************************************************************************** +#define USBDGAMEPAD_NOT_CONFIGURED \ + 2 + +//***************************************************************************** +// +//! This structure is the default packed report structure that is sent to the +//! host. The application can provide its own structure if the default report +//! descriptor is overridden by the application. This structure or an +//! application-defined structure is passed to the USBDHIDGamepadSendReport +//! function to send gamepad updates to the host. +// +//***************************************************************************** +typedef struct +{ + // + //! Signed 8-bit value (-128 to 127). + // + int8_t i8XPos; + + // + //! Signed 8-bit value (-128 to 127). + // + int8_t i8YPos; + + // + //! Signed 8-bit value (-128 to 127). + // + int8_t i8ZPos; + + // + //! 8-bit button mapping with button 1 in the LSB. + // + uint8_t ui8Buttons; +} +PACKED tGamepadReport; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern tUSBDHIDGamepadDevice *USBDHIDGamepadInit(uint32_t ui32Index, + tUSBDHIDGamepadDevice *psHIDGamepad); +extern tUSBDHIDGamepadDevice *USBDHIDGamepadCompositeInit(uint32_t ui32Index, + tUSBDHIDGamepadDevice *psHIDGamepad, + tCompositeEntry *psCompEntry); +extern void USBDHIDGamepadTerm(tUSBDHIDGamepadDevice *psCompEntry); + +extern uint32_t USBDHIDGamepadSendReport(tUSBDHIDGamepadDevice *psHIDGamepad, + void *pvReport, uint32_t ui32Size); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usblib/device/usbdhidkeyb.c b/usblib/device/usbdhidkeyb.c new file mode 100644 index 0000000..619714c --- /dev/null +++ b/usblib/device/usbdhidkeyb.c @@ -0,0 +1,1321 @@ +//***************************************************************************** +// +// usbdhidkeyb.c - USB HID Keyboard device class 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/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdhid.h" +#include "usblib/device/usbdhidkeyb.h" + +//***************************************************************************** +// +//! \addtogroup hid_keyboard_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// HID device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +static uint8_t g_pui8KeybDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(34), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +static uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] = +{ + // + // HID Device Class Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_HID, // The interface class + USB_HID_SCLASS_BOOT, // The interface sub-class. + USB_HID_PROTOCOL_KEYB, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. +}; + +static const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(USB_EP_1), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +static const uint8_t g_pui8HIDOutEndpoint[HIDOUTENDPOINT_SIZE] = +{ + // + // Interrupt OUT endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(USB_EP_2), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The following is the HID report structure definition that is passed back +// to the host. +// +//***************************************************************************** +static const uint8_t g_pui8KeybReportDescriptor[] = +{ + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_KEYBOARD), + Collection(USB_HID_APPLICATION), + + // + // Modifier keys. + // 8 - 1 bit values indicating the modifier keys (ctrl, shift...) + // + ReportSize(1), + ReportCount(8), + UsagePage(USB_HID_USAGE_KEYCODES), + UsageMinimum(224), + UsageMaximum(231), + LogicalMinimum(0), + LogicalMaximum(1), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | USB_HID_INPUT_ABS), + + // + // One byte of rsvd data required by HID spec. + // + ReportCount(1), + ReportSize(8), + Input(USB_HID_INPUT_CONSTANT), + + // + // Keyboard LEDs. + // 5 - 1 bit values. + // + ReportCount(5), + ReportSize(1), + UsagePage(USB_HID_USAGE_LEDS), + UsageMinimum(1), + UsageMaximum(5), + Output(USB_HID_OUTPUT_DATA | USB_HID_OUTPUT_VARIABLE | + USB_HID_OUTPUT_ABS), + // + // 1 - 3 bit value to pad out to a full byte. + // + ReportCount(1), + ReportSize(3), + Output(USB_HID_OUTPUT_CONSTANT), //LED report padding + + // + // The Key buffer. + // 6 - 8 bit values to store the current key state. + // + ReportCount(6), + ReportSize(8), + LogicalMinimum(0), + LogicalMaximum(101), + UsagePage(USB_HID_USAGE_KEYCODES), + UsageMinimum (0), + UsageMaximum (101), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_ARRAY), + EndCollection +}; + +//***************************************************************************** +// +// The HID descriptor for the keyboard device. +// +//***************************************************************************** +static const tHIDDescriptor g_sKeybHIDDescriptor = +{ + 9, // bLength + USB_HID_DTYPE_HID, // bDescriptorType + 0x111, // bcdHID (version 1.11 compliant) + 0, // bCountryCode (not localized) + 1, // bNumDescriptors + { + { + USB_HID_DTYPE_REPORT, // Report descriptor + sizeof(g_pui8KeybReportDescriptor) + // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// The HID configuration descriptor is defined as four or five sections +// depending upon the client's configuration choice. These sections are: +// +// 1. The 9 byte configuration descriptor (RAM). +// 2. The interface descriptor (RAM). +// 3. The HID report and physical descriptors (provided by the client) +// (FLASH). +// 4. The mandatory interrupt IN endpoint descriptor (FLASH). +// 5. The optional interrupt OUT endpoint descriptor (FLASH). +// +//***************************************************************************** +static const tConfigSection g_sHIDConfigSection = +{ + sizeof(g_pui8KeybDescriptor), + g_pui8KeybDescriptor +}; + +static const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pui8HIDInterface), + g_pui8HIDInterface +}; + +static const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pui8HIDInEndpoint), + g_pui8HIDInEndpoint +}; + +static const tConfigSection g_sHIDOutEndpointSection = +{ + sizeof(g_pui8HIDOutEndpoint), + g_pui8HIDOutEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +static tConfigSection g_sHIDDescriptorSection = +{ + sizeof(g_sKeybHIDDescriptor), + (const uint8_t *)&g_sKeybHIDDescriptor +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete HID configuration descriptor. +// +//***************************************************************************** +static const tConfigSection *g_psHIDSections[] = +{ + &g_sHIDConfigSection, + &g_sHIDInterfaceSection, + &g_sHIDDescriptorSection, + &g_sHIDInEndpointSection, + &g_sHIDOutEndpointSection +}; + +#define NUM_HID_SECTIONS ((sizeof(g_psHIDSections) / \ + sizeof(g_psHIDSections[0])) - 1) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. Note that this must be +// in RAM since we need to include or exclude the final section based on +// client supplied initialization parameters. +// +//***************************************************************************** +static tConfigHeader g_sHIDConfigHeader = +{ + NUM_HID_SECTIONS, + g_psHIDSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +static const tConfigHeader * const g_ppsHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the keyboard class, we have only a +// single report descriptor. +// +//***************************************************************************** +static const uint8_t * const g_pui8KeybClassDescriptors[] = +{ + g_pui8KeybReportDescriptor +}; + +//***************************************************************************** +// +// Forward references for keyboard device callback functions. +// +//***************************************************************************** +static uint32_t HIDKeyboardRxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData); +static uint32_t HIDKeyboardTxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData); + +//***************************************************************************** +// +// Main HID device class event handler function. +// +// \param pvKeyboardDevice is the event callback pointer provided during +// USBDHIDInit().This is a pointer to our HID device structure +// (&g_sHIDKeybDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to operation of the +// keyboard HID device. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDKeyboardRxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psKeyboardDevice; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to our instance data + // + psKeyboardDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + psInst = &psKeyboardDevice->sPrivateData; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ui8USBConfigured = true; + + // + // Pass the information on to the client. + // + psKeyboardDevice->pfnCallback(psKeyboardDevice->pvCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ui8USBConfigured = false; + + // + // Pass the information on to the client. + // + psKeyboardDevice->pfnCallback(psKeyboardDevice->pvCBData, + USB_EVENT_DISCONNECTED, 0, + (void *)0); + + break; + } + + // + // The host is polling us for a particular report and the HID driver + // is asking for the latest version to transmit. + // + case USBD_HID_EVENT_IDLE_TIMEOUT: + case USBD_HID_EVENT_GET_REPORT: + { + // + // We only support a single input report so we don't need to check + // the ui32MsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(uint8_t **)pvMsgData = psInst->pui8Report; + return(KEYB_IN_REPORT_SIZE); + } + + // + // The device class driver has completed sending a report to the + // host in response to a Get_Report request. + // + case USBD_HID_EVENT_REPORT_SENT: + { + // + // We have nothing to do here. + // + break; + } + + // + // This event is sent in response to a host Set_Report request. We + // must return a pointer to a buffer large enough to receive the + // report into. + // + case USBD_HID_EVENT_GET_REPORT_BUFFER: + { + // + // Are we being asked for a report that is shorter than the storage + // we have set aside for this? The only output report we define is + // 8 bits long so we really expect to see a length of 1 passed. + // + if((uint32_t)pvMsgData == KEYB_OUT_REPORT_SIZE ) + { + // + // Yes - return our pointer. + // + return((uint32_t)psInst->pui8DataBuffer); + } + else + { + // + // We are being passed a report that is longer than the + // only report we expect so return NULL. This causes the + // device class driver to stall the request. + // + return(0); + } + } + + // + // This event indicates that the host has sent us an Output or + // Feature report and that the report is now in the buffer we provided + // on the previous USBD_HID_EVENT_GET_REPORT_BUFFER callback. + // + case USBD_HID_EVENT_SET_REPORT: + { + // + // Inform the application if the keyboard LEDs have changed. + // + if(psInst->ui8LEDStates != psInst->pui8DataBuffer[0]) + { + // + // Note the new LED states. + // + psInst->ui8LEDStates = psInst->pui8DataBuffer[0]; + + // + // Pass the information on to the client. + // + psKeyboardDevice->pfnCallback( + psKeyboardDevice->pvCBData, + USBD_HID_KEYB_EVENT_SET_LEDS, + psInst->pui8DataBuffer[0], + (void *)0); + } + break; + } + + // + // The host is asking us to set either boot or report protocol (not + // that it makes any difference to this particular mouse). + // + case USBD_HID_EVENT_SET_PROTOCOL: + { + psInst->ui8Protocol = ui32MsgData; + break; + } + + // + // The host is asking us to tell it which protocol we are currently + // using, boot or request. + // + case USBD_HID_EVENT_GET_PROTOCOL: + { + return(psInst->ui8Protocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + case USB_EVENT_LPM_RESUME: + case USB_EVENT_LPM_SLEEP: + case USB_EVENT_LPM_ERROR: + { + return(psKeyboardDevice->pfnCallback( + psKeyboardDevice->pvCBData, + ui32Event, ui32MsgData, pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvKeyboardDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to our HID device structure +// (&g_sHIDKeybDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to report +// transmissions made using the interrupt IN endpoint. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDKeyboardTxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + uint32_t ui32Count; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to our instance data + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + psInst = &psHIDKbDevice->sPrivateData; + psHIDDevice = &psInst->sHIDDevice; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // Do we have any pending changes needing transmitted? + // + if(psInst->bChangeMade) + { + // + // Yes - go ahead and send another report immediately. + // + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pui8Report, + KEYB_IN_REPORT_SIZE, true); + + // + // If we scheduled the report for transmission, clear the + // change flag. + // + if(ui32Count != 0) + { + psInst->bChangeMade = false; + } + } + else + { + // + // Our last transmission is complete and we have nothing more + // to send. + // + psInst->eKeyboardState = HID_KEYBOARD_STATE_IDLE; + } + + // + // Pass the event on to the client. + // + psHIDKbDevice->pfnCallback(psHIDKbDevice->pvCBData, + USB_EVENT_TX_COMPLETE, ui32MsgData, + (void *)0); + + break; + } + + // + // We ignore all other events related to transmission of reports via + // the interrupt IN endpoint. + // + default: + { + break; + } + } + + return(0); +} + +//***************************************************************************** +// +// Add the supplied usage code to the list of keys currently in the pressed +// state. +// +// \param ui8UsageCode is the HID usage code of the newly pressed key. +// +// This function adds the supplied usage code to the global list of keys which +// are currently pressed (assuming it is not already noted as pressed and that +// there is space in the list to hold the new information). The return code +// indicates success if the list did not overflow and failure if the list +// already contains as many pressed keys as can be reported. +// +// \return Returns \b true if the usage code was successfully added to the +// list or \b false if there was insufficient space to hold the new key +// press (in which case the caller should report a roll over error to the +// host). +// +//***************************************************************************** +static bool +AddKeyToPressedList(tHIDKeyboardInstance *psInst, uint8_t ui8UsageCode) +{ + uint32_t ui32Loop; + bool bRetcode; + + // + // Assume all is well until we determine otherwise. + // + bRetcode = true; + + // + // Look through the list of existing pressed keys to see if the new one + // is already there. + // + for(ui32Loop = 0; ui32Loop < (uint32_t)psInst->ui8KeyCount; ui32Loop++) + { + // + // Is this key already included in the list of keys in the pressed + // state? + // + if(ui8UsageCode == psInst->pui8KeysPressed[ui32Loop]) + { + // + // Yes - drop out. + // + break; + } + } + + // + // If we exited the loop at the end of the existing key presses, this + // key does not exist already so add it if space exists. + // + if(ui32Loop >= psInst->ui8KeyCount) + { + if(psInst->ui8KeyCount < KEYB_MAX_CHARS_PER_REPORT) + { + // + // We have room so store the new key press in the list. + // + psInst->pui8KeysPressed[psInst->ui8KeyCount] = ui8UsageCode; + psInst->ui8KeyCount++; + bRetcode = true; + } + else + { + // + // We have no room for the new key - declare a rollover error. + // + bRetcode = false; + } + } + + return(bRetcode); +} + +//***************************************************************************** +// +// Remove the supplied usage code from the list of keys currently in the +// pressed state. +// +// \param ui8UsageCode is the HID usage code of the newly released key. +// +// This function removes the supplied usage code from the global list of keys +// which are currently pressed. The return code indicates whether the key was +// found in the list. On exit, the list has been cleaned up to ensure +// that all key presses are contiguous starting at the first entry. +// +// \return Returns \b true if the usage code was found and removed from the +// list or \b false if the code was not found. The caller need not pass a new +// report to the host if \b false is returned since the key list has not +// changed. +// +//***************************************************************************** +static bool +RemoveKeyFromPressedList(tHIDKeyboardInstance *psInst, + uint8_t ui8UsageCode) +{ + uint32_t ui32Loop; + uint32_t ui32Pos; + + // + // Keep the compiler happy by setting ui32Pos to something. + // + ui32Pos = 0; + + // + // Find the usage code in the current list. + // + for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++) + { + if(psInst->pui8KeysPressed[ui32Loop] == ui8UsageCode) + { + ui32Pos = ui32Loop; + break; + } + } + + // + // If we dropped out at the end of the loop, we could not find the code so + // just return false. + // + if(ui32Loop == KEYB_MAX_CHARS_PER_REPORT) + { + return(false); + } + + // + // Now shuffle all the values to the right of the usage code we found + // down one position to fill the gap left by removing it. + // + for(ui32Loop = (ui32Pos + 1); ui32Loop < KEYB_MAX_CHARS_PER_REPORT; + ui32Loop++) + { + psInst->pui8KeysPressed[ui32Loop - 1] = + psInst->pui8KeysPressed[ui32Loop]; + } + + // + // Clear the last entry in the array and adjust the number of keys in the + // array. + // + psInst->pui8KeysPressed[KEYB_MAX_CHARS_PER_REPORT - 1] = + HID_KEYB_USAGE_RESERVED; + psInst->ui8KeyCount--; + + // + // Tell the caller we were successful. + // + return(true); +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psHIDKbDevice points to a structure containing parameters +//! customizing the operation of the HID keyboard device. +//! +//! An application wishing to offer a USB HID keyboard interface to a USB host +//! must call this function to initialize the USB controller and attach the +//! keyboard device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function returns the \e psHIDKbDevice +//! pointer passed to it. This must be passed on all future calls to the HID +//! keyboard device driver. +//! +//! When a host connects and configures the device, the application callback +//! receives \b USB_EVENT_CONNECTED after which calls can be made to +//! USBDHIDKeyboardKeyStateChange() to report key presses and releases to the +//! USB host. +//! +//! \note The application must not make any calls to the lower level USB device +//! interfaces if interacting with USB via the USB HID keyboard device class +//! API. Doing so causes unpredictable (though almost certainly +//! unpleasant) behavior. +//! +//! \return Returns NULL on failure or the \e psHIDKbDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDKeyboardInit(uint32_t ui32Index, tUSBDHIDKeyboardDevice *psHIDKbDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + tConfigDescriptor *pConfigDesc; + + // + // Check parameter validity. + // + ASSERT(psHIDKbDevice); + ASSERT(psHIDKbDevice->ppui8StringDescriptors); + ASSERT(psHIDKbDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDKeyboardCompositeInit(ui32Index, psHIDKbDevice, 0); + + pConfigDesc = (tConfigDescriptor *)g_pui8KeybDescriptor; + pConfigDesc->bmAttributes = psHIDKbDevice->ui8PwrAttributes; + pConfigDesc->bMaxPower = (uint8_t)(psHIDKbDevice->ui16MaxPowermA / 2); + + // + // If we initialized the HID layer successfully, pass our device pointer + // back as the return code, otherwise return NULL to indicate an error. + // + if(pvRetcode) + { + // + // Initialize the lower layer HID driver and pass it the various + // structures and descriptors necessary to declare that we are a + // keyboard. + // + pvRetcode = USBDHIDInit(ui32Index, psHIDDevice); + + return((void *)psHIDKbDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psHIDKbDevice points to a structure containing parameters +//! customizing the operation of the HID keyboard device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! This call is very similar to USBDHIDKeyboardInit() except that it is used +//! for initializing an instance of the HID keyboard device for use in a +//! composite device. If this HID keyboard is part of a composite device, then +//! the \e psCompEntry should point to the composite device entry to +//! initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Keyboard APIs. +// +//***************************************************************************** +void * +USBDHIDKeyboardCompositeInit(uint32_t ui32Index, + tUSBDHIDKeyboardDevice *psHIDKbDevice, + tCompositeEntry *psCompEntry) +{ + tHIDKeyboardInstance *psInst; + uint32_t ui32Loop; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psHIDKbDevice); + ASSERT(psHIDKbDevice->ppui8StringDescriptors); + ASSERT(psHIDKbDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = &psHIDKbDevice->sPrivateData; + + // + // Initialize the various fields in our instance structure. + // + psInst->ui8USBConfigured = 0; + psInst->ui8Protocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ui8Duration4mS = 125; + psInst->sReportIdle.ui8ReportID = 0; + psInst->sReportIdle.ui32TimeSinceReportmS = 0; + psInst->sReportIdle.ui16TimeTillNextmS = 0; + psInst->ui8LEDStates = 0; + psInst->ui8KeyCount = 0; + for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++) + { + psInst->pui8KeysPressed[ui32Loop] = HID_KEYB_USAGE_RESERVED; + } + + psInst->eKeyboardState = HID_KEYBOARD_STATE_UNCONFIGURED; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psInst->sHIDDevice; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->ui16PID = psHIDKbDevice->ui16PID; + psHIDDevice->ui16VID = psHIDKbDevice->ui16VID; + psHIDDevice->ui16MaxPowermA = psHIDKbDevice->ui16MaxPowermA; + psHIDDevice->ui8PwrAttributes = psHIDKbDevice->ui8PwrAttributes; + psHIDDevice->ui8Subclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ui8Protocol = USB_HID_PROTOCOL_KEYB; + psHIDDevice->ui8NumInputReports = 1; + psHIDDevice->psReportIdle = 0; + psHIDDevice->pfnRxCallback = HIDKeyboardRxHandler; + psHIDDevice->pvRxCBData = (void *)psHIDKbDevice; + psHIDDevice->pfnTxCallback = HIDKeyboardTxHandler; + psHIDDevice->pvTxCBData = (void *)psHIDKbDevice; + psHIDDevice->bUseOutEndpoint = false, + + psHIDDevice->psHIDDescriptor = &g_sKeybHIDDescriptor; + psHIDDevice->ppui8ClassDescriptors = g_pui8KeybClassDescriptors; + psHIDDevice->ppui8StringDescriptors = + psHIDKbDevice->ppui8StringDescriptors; + psHIDDevice->ui32NumStringDescriptors = + psHIDKbDevice->ui32NumStringDescriptors; + psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors; + + psHIDDevice->psReportIdle = &psInst->sReportIdle; + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a keyboard. + // + return(USBDHIDCompositeInit(ui32Index, psHIDDevice, psCompEntry)); +} + +//***************************************************************************** +// +//! Shuts down the HID keyboard device. +//! +//! \param pvKeyboardDevice is the pointer to the device instance structure +//! as returned by USBDHIDKeyboardInit(). +//! +//! This function terminates HID keyboard operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e pvKeyboardDevice instance may not me used in any other call to the HID +//! keyboard device other than USBDHIDKeyboardInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDKeyboardTerm(void *pvKeyboardDevice) +{ + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to the device. + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Mark the device as no longer configured. + // + psHIDKbDevice->sPrivateData.ui8USBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the keyboard callback. +//! +//! \param pvKeyboardDevice is the pointer to the device instance structure +//! as returned by USBDHIDKeyboardInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the keyboard callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnCallback function +//! passed on USBDHIDKeyboardInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the \e pvKeyboardDevice structure passed to +//! USBDHIDKeyboardInit() resides in RAM. If this structure is in flash, +//! callback data changes is not possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDKeyboardSetCBData(void *pvKeyboardDevice, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDKeyboardDevice *psKeyboard; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to our keyboard device. + // + psKeyboard = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Save the old callback pointer and replace it with the new value. + // + pvOldCBData = psKeyboard->pvCBData; + psKeyboard->pvCBData = pvCBData; + + // + // Pass the old callback pointer back to the caller. + // + return(pvOldCBData); +} + +//***************************************************************************** +// +//! Reports a key state change to the USB host. +//! +//! \param pvKeyboardDevice is the pointer to the device instance structure +//! as returned by USBDHIDKeyboardInit(). +//! \param ui8Modifiers contains the states of each of the keyboard modifiers +//! (left/right shift, ctrl, alt or GUI keys). Valid values are logical OR +//! combinations of the labels \b HID_KEYB_LEFT_CTRL, \b HID_KEYB_LEFT_SHIFT, +//! \b HID_KEYB_LEFT_ALT, \b HID_KEYB_LEFT_GUI, \b HID_KEYB_RIGHT_CTRL, \b +//! HID_KEYB_RIGHT_SHIFT, \b HID_KEYB_RIGHT_ALT and \b HID_KEYB_RIGHT_GUI. +//! Presence of one of these bit flags indicates that the relevant modifier +//! key is pressed and absence indicates that it is released. +//! \param ui8UsageCode is the usage code of the key whose state has changed. +//! If only modifier keys have changed, \b HID_KEYB_USAGE_RESERVED should be +//! passed in this parameter. +//! \param bPress is \b true if the key has been pressed or \b false if it has +//! been released. If only modifier keys have changed state, this parameter is +//! ignored. +//! +//! This function adds or removes a key usage code from the list of keys +//! currently pressed and schedules a report transmission to the host to +//! inform it of the new keyboard state. If the maximum number of simultaneous +//! key presses are already recorded, the report to the host contains the +//! rollover error code, \b HID_KEYB_USAGE_ROLLOVER instead of key usage codes +//! and the caller receives return code \b KEYB_ERR_TOO_MANY_KEYS. +//! +//! \return Returns \b KEYB_SUCCESS if the key usage code was added to or +//! removed from the current list successfully. \b KEYB_ERR_TOO_MANY_KEYS is +//! returned if an attempt is made to press a 7th key (the BIOS keyboard +//! protocol can report no more than 6 simultaneously pressed keys). If called +//! before the USB host has configured the device, \b KEYB_ERR_NOT_CONFIGURED +//! is returned and, if an error is reported while attempting to transmit the +//! report, \b KEYB_ERR_TX_ERROR is returned. If an attempt is made to remove +//! a key from the pressed list (by setting parameter \e bPressed to \b false) +//! but the key usage code is not found, \b KEYB_ERR_NOT_FOUND is returned. +// +//***************************************************************************** +uint32_t +USBDHIDKeyboardKeyStateChange(void *pvKeyboardDevice, uint8_t ui8Modifiers, + uint8_t ui8UsageCode, bool bPress) +{ + bool bRetcode; + uint32_t ui32Loop, ui32Count; + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Assume all is well until we determine otherwise. + // + bRetcode = true; + + // + // Get a pointer to our instance data + // + psInst = &psHIDKbDevice->sPrivateData; + + // + // Update the global keyboard report with the information passed. + // + psInst->pui8Report[0] = ui8Modifiers; + psInst->pui8Report[1] = 0; + + // + // Were we passed a usage code for a new key press or release or was + // this call just telling us about a modifier change? + // + if(ui8UsageCode != HID_KEYB_USAGE_RESERVED) + { + // + // Has a key been pressed or released? + // + if(bPress) + { + // + // A key has been pressed - add it to the list if there is space an + // and the key is not already in the list. + // + bRetcode = AddKeyToPressedList(psInst, ui8UsageCode); + } + else + { + // + // A key has been released - remove it from the list. + // + bRetcode = RemoveKeyFromPressedList(psInst, ui8UsageCode); + + // + // The return code here indicates whether the key was found. If it + // wasn't, the list has not changes so merely exit at this point + // without sending anything to the host. + // + if(!bRetcode) + { + return(KEYB_ERR_NOT_FOUND); + } + } + + // + // Build the report from the current list of keys. If we added a key + // and got a bad return code indicating a roll over error, we need to + // send a roll over report + // + for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++) + { + psInst->pui8Report[2 + ui32Loop] = (bRetcode ? + psInst->pui8KeysPressed[ui32Loop] : HID_KEYB_USAGE_ROLLOVER); + } + } + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ui8USBConfigured) + { + return(KEYB_ERR_NOT_CONFIGURED); + } + + // + // Only send a report if the transmitter is currently free. + // + if(USBDHIDTxPacketAvailable((void *)psHIDDevice)) + { + // + // Send the report to the host. + // + psInst->eKeyboardState = HID_KEYBOARD_STATE_SEND; + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pui8Report, KEYB_IN_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ui32Count) + { + // + // No - report the error to the caller. + // + return(KEYB_ERR_TX_ERROR); + } + } + else + { + // + // We can't send the report immediately so mark the instance so that + // it is sent next time the transmitter is free. + // + psInst->bChangeMade = true; + } + + // + // If we get this far, the key information was sent successfully. Are + // too many keys currently pressed, though? + // + return(bRetcode ? KEYB_SUCCESS : KEYB_ERR_TOO_MANY_KEYS); +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus or self powered) to the USB library. +//! +//! \param pvKeyboardDevice is the pointer to the keyboard device instance +//! structure. +//! \param ui8Power indicates the current power status, either +//! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus or self powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDKeyboardPowerStatusSet(void *pvKeyboardDevice, uint8_t ui8Power) +{ + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvKeyboardDevice); + + // + // Get the keyboard device pointer. + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvKeyboardDevice is the pointer to the keyboard device instance +//! structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this causes the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false is returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +bool +USBDHIDKeyboardRemoteWakeupRequest(void *pvKeyboardDevice) +{ + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvKeyboardDevice); + + // + // Get the keyboard device pointer. + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Pass the request through to the lower layer. + // + return(USBDHIDRemoteWakeupRequest((void *)psHIDDevice)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhidkeyb.h b/usblib/device/usbdhidkeyb.h new file mode 100644 index 0000000..c509b9f --- /dev/null +++ b/usblib/device/usbdhidkeyb.h @@ -0,0 +1,365 @@ +//***************************************************************************** +// +// usbdhidkeyb.h - Definitions used by HID keyboard class devices. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBDHIDKEYB_H__ +#define __USBDHIDKEYB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_keyboard_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The maximum number of simultaneously-pressed, non-modifier keys that the +//! HID BIOS keyboard protocol can send at once. Attempts to send more pressed +//! keys than this results in a rollover error being reported to the host +//! and KEYB_ERR_TOO_MANY_KEYS being returned from +//! USBDHIDKeyboardKeyStateChange(). +// +//***************************************************************************** +#define KEYB_MAX_CHARS_PER_REPORT \ + 6 + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB HID keyboard code and are here only to help with the application +// allocating the correct amount of memory for the USB HID Keyboard device +// code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the keyboard can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + HID_KEYBOARD_STATE_UNCONFIGURED, + + // + // No keys to send and not waiting on data. + // + HID_KEYBOARD_STATE_IDLE, + + // + // Waiting on report data from the host. + // + HID_KEYBOARD_STATE_WAIT_DATA, + + // + // Waiting on data to be sent out. + // + HID_KEYBOARD_STATE_SEND +} +tKeyboardState; + +//***************************************************************************** +// +// PRIVATE +// +// The size of the keyboard input and output reports. +// +//***************************************************************************** +#define KEYB_IN_REPORT_SIZE 8 +#define KEYB_OUT_REPORT_SIZE 1 + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data structure for the USB HID +// keyboard device. This structure forms the RAM workspace used by each +// instance of the keyboard. +// +//***************************************************************************** +typedef struct +{ + // + // The USB configuration number set by the host or 0 of the device is + // currently unconfigured. + // + uint8_t ui8USBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + uint8_t ui8Protocol; + + // + // The current states that the keyboard LEDs are to be set to. + // + volatile uint8_t ui8LEDStates; + + // + // The total number of keys currently pressed. This indicates the number + // of key press entries in the pui8KeysPressed array. + // + uint8_t ui8KeyCount; + + // + // The current state of the keyboard interrupt IN endpoint. + // + volatile tKeyboardState eKeyboardState; + + // + // A flag to indicate that the application pressed or released a key + // but that we couldn't send the report immediately. + // + volatile bool bChangeMade; + + // + // A buffer used to receive output reports from the host. + // + uint8_t pui8DataBuffer[KEYB_OUT_REPORT_SIZE]; + + // + // A buffer used to hold the last input report sent to the host. + // + uint8_t pui8Report[KEYB_IN_REPORT_SIZE]; + + // + // A buffer containing the usage codes of all non-modifier keys currently + // in the pressed state. + // + uint8_t pui8KeysPressed[KEYB_MAX_CHARS_PER_REPORT]; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDKeyboardInstance; + +//***************************************************************************** +// +//! This structure is used by the application to define operating parameters +//! for the HID keyboard device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + //! A pointer to the callback function which is called to notify + //! the application of general events and those related to reception of + //! Output and Feature reports via the (optional) interrupt OUT endpoint. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which is sent as the first + //! parameter in all calls made to the keyboard callback, + //! pfnCallback. + // + void *pvCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1),HID + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The private instance data for this device. This memory must + //! remain accessible for as long as the keyboard device is in use and + //! must not be modified by any code outside the HID keyboard driver. + // + tHIDKeyboardInstance sPrivateData; +} +tUSBDHIDKeyboardDevice; + +//***************************************************************************** +// +// Keyboard-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the keyboard LED states are to be set. The +//! ui32MsgValue parameter contains the requested state for each of the LEDs +//! defined as a collection of ORed bits where a 1 indicates that the LED is +//! to be turned on and a 0 indicates that it should be turned off. The +//! individual LED bits are defined using labels \b HID_KEYB_NUM_LOCK, +//! \b HID_KEYB_CAPS_LOCK, \b HID_KEYB_SCROLL_LOCK, \b HID_KEYB_COMPOSE and +//! \b HID_KEYB_KANA. +// +//***************************************************************************** +#define USBD_HID_KEYB_EVENT_SET_LEDS \ + USBD_HID_KEYB_EVENT_BASE + +//***************************************************************************** +// +//! This return code from USBDHIDKeyboardKeyStateChange() indicates success. +// +//***************************************************************************** +#define KEYB_SUCCESS 0 + +//***************************************************************************** +// +//! This return code from USBDHIDKeyboardKeyStateChange() indicates that an +//! attempt has been made to record more than 6 simultaneously pressed, +//! non-modifier keys. The USB HID BIOS keyboard protocol allows no more than +//! 6 pressed keys to be reported at one time. Until at least one key is +//! released, the device reports a roll over error to the host each time it +//! is asked for the keyboard input report. +// +//***************************************************************************** +#define KEYB_ERR_TOO_MANY_KEYS 1 + +//***************************************************************************** +// +//! This return code from USBDHIDKeyboardKeyStateChange() indicates that an +//! error was reported while attempting to send a report to the host. A client +//! should assume that the host has disconnected if this return code is seen. +// +//***************************************************************************** +#define KEYB_ERR_TX_ERROR 2 + +//***************************************************************************** +// +//! USBDHIDKeyboardKeyStateChange() returns this value if it is called with the +//! bPress parameter set to false but with a ui8UsageCode parameter which does +//! does not indicate a key that is currently recorded as being pressed. This +//! may occur if an attempt was previously made to report more than 6 pressed +//! keys and the earlier pressed keys are released before the later ones. This +//! condition is benign and should not be used to indicate a host disconnection +//! or serious error. +// +//***************************************************************************** +#define KEYB_ERR_NOT_FOUND 3 + +//***************************************************************************** +// +//! USBDHIDKeyboardKeyStateChange() returns this value if it is called before +//! the USB host has connected and configured the device. Any key usage code +//! passed is stored and passed to the host once configuration completes. +// +//***************************************************************************** +#define KEYB_ERR_NOT_CONFIGURED 4 + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDKeyboardInit(uint32_t ui32Index, + tUSBDHIDKeyboardDevice *psHIDKbDevice); +extern void *USBDHIDKeyboardCompositeInit(uint32_t ui32Index, + tUSBDHIDKeyboardDevice *psHIDKbDevice, + tCompositeEntry *psCompEntry); +extern void USBDHIDKeyboardTerm(void *pvKeyboardInstance); +extern void *USBDHIDKeyboardSetCBData(void *pvKeyboardInstance, + void *pvCBData); +extern uint32_t USBDHIDKeyboardKeyStateChange(void *pvKeyboardInstance, + uint8_t ui8Modifiers, + uint8_t ui8UsageCode, + bool bPressed); +extern void USBDHIDKeyboardPowerStatusSet(void *pvKeyboardInstance, + uint8_t ui8Power); +extern bool USBDHIDKeyboardRemoteWakeupRequest(void *pvKeyboardInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHIDKEYB_H__ diff --git a/usblib/device/usbdhidmouse.c b/usblib/device/usbdhidmouse.c new file mode 100644 index 0000000..8b8a95a --- /dev/null +++ b/usblib/device/usbdhidmouse.c @@ -0,0 +1,1008 @@ +//***************************************************************************** +// +// usbdhidmouse.c - USB HID Mouse device class 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/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdhid.h" +#include "usblib/device/usbdhidmouse.h" + +//***************************************************************************** +// +//! \addtogroup hid_mouse_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// HID device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +uint8_t g_pui8MouseDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(34), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] = +{ + // + // HID Device Class Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_HID, // The interface class + USB_HID_SCLASS_BOOT, // The interface sub-class. + USB_HID_PROTOCOL_MOUSE, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. +}; + +const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(USB_EP_1), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The report descriptor for the mouse class device. +// +//***************************************************************************** +static const uint8_t g_pui8MouseReportDescriptor[] = +{ + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_MOUSE), + Collection(USB_HID_APPLICATION), + Usage(USB_HID_POINTER), + Collection(USB_HID_PHYSICAL), + + // + // The buttons. + // + UsagePage(USB_HID_BUTTONS), + UsageMinimum(1), + UsageMaximum(3), + LogicalMinimum(0), + LogicalMaximum(1), + + // + // 3 - 1 bit values for the buttons. + // + ReportSize(1), + ReportCount(3), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_ABS), + + // + // 1 - 5 bit unused constant value to fill the 8 bits. + // + ReportSize(5), + ReportCount(1), + Input(USB_HID_INPUT_CONSTANT | USB_HID_INPUT_ARRAY | + USB_HID_INPUT_ABS), + + // + // The X and Y axis. + // + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_X), + Usage(USB_HID_Y), + LogicalMinimum(-127), + LogicalMaximum(127), + + // + // 2 - 8 bit Values for x and y. + // + ReportSize(8), + ReportCount(2), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_RELATIVE), + + EndCollection, + EndCollection, +}; + +//***************************************************************************** +// +// The HID descriptor for the mouse device. +// +//***************************************************************************** +static const tHIDDescriptor g_sMouseHIDDescriptor = +{ + 9, // bLength + USB_HID_DTYPE_HID, // bDescriptorType + 0x111, // bcdHID (version 1.11 compliant) + 0, // bCountryCode (not localized) + 1, // bNumDescriptors + { + { + USB_HID_DTYPE_REPORT, // Report descriptor + sizeof(g_pui8MouseReportDescriptor) + // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// The HID configuration descriptor is defined as four or five sections +// depending upon the client's configuration choice. These sections are: +// +// 1. The 9 byte configuration descriptor (RAM). +// 2. The interface descriptor (RAM). +// 3. The HID report and physical descriptors (provided by the client) +// (FLASH). +// 4. The mandatory interrupt IN endpoint descriptor (FLASH). +// 5. The optional interrupt OUT endpoint descriptor (FLASH). +// +//***************************************************************************** +const tConfigSection g_sHIDConfigSection = +{ + sizeof(g_pui8MouseDescriptor), + g_pui8MouseDescriptor +}; + +const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pui8HIDInterface), + g_pui8HIDInterface +}; + +const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pui8HIDInEndpoint), + g_pui8HIDInEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +tConfigSection g_sHIDDescriptorSection = +{ + sizeof(g_sMouseHIDDescriptor), + (const uint8_t *)&g_sMouseHIDDescriptor +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete HID configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psHIDSections[] = +{ + &g_sHIDConfigSection, + &g_sHIDInterfaceSection, + &g_sHIDDescriptorSection, + &g_sHIDInEndpointSection, +}; + +#define NUM_HID_SECTIONS (sizeof(g_psHIDSections) / \ + sizeof(g_psHIDSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. Note that this must be +// in RAM since we need to include or exclude the final section based on +// client supplied initialization parameters. +// +//***************************************************************************** +tConfigHeader g_sHIDConfigHeader = +{ + NUM_HID_SECTIONS, + g_psHIDSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppsHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the mouse class, we have only a single +// report descriptor. +// +//***************************************************************************** +static const uint8_t * const g_pui8MouseClassDescriptors[] = +{ + g_pui8MouseReportDescriptor +}; + +//***************************************************************************** +// +// Forward references for mouse device callback functions. +// +//***************************************************************************** +static uint32_t HIDMouseRxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData); +static uint32_t HIDMouseTxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData); + +//***************************************************************************** +// +// The HID mouse report offsets for this mouse application. +// +//***************************************************************************** +#define HID_REPORT_BUTTONS 0 +#define HID_REPORT_X 1 +#define HID_REPORT_Y 2 + +//***************************************************************************** +// +// Main HID device class event handler function. +// +// \param pvMouseDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to our HID device structure +// (&g_sHIDMouseDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to operation of the +// mouse HID device. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDMouseRxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psMouseDevice; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvMouseDevice); + + // + // Get a pointer to our instance data + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + psInst = &psMouseDevice->sPrivateData; + + // + // Which event were we sent? + // + switch(ui32Event) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ui8USBConfigured = true; + + // + // Pass the information on to the client. + // + psMouseDevice->pfnCallback(psMouseDevice->pvCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ui8USBConfigured = false; + + // + // Pass the information on to the client. + // + psMouseDevice->pfnCallback(psMouseDevice->pvCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + + break; + } + + // + // The host is polling us for a particular report and the HID driver + // is asking for the latest version to transmit. + // + case USBD_HID_EVENT_IDLE_TIMEOUT: + case USBD_HID_EVENT_GET_REPORT: + { + // + // We only support a single input report so we don't need to check + // the ui32MsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(uint8_t **)pvMsgData = psInst->pui8Report; + return(8); + } + + // + // The device class driver has completed sending a report to the + // host in response to a Get_Report request. + // + case USBD_HID_EVENT_REPORT_SENT: + { + // + // We have nothing to do here. + // + break; + } + + // + // This event is sent in response to a host Set_Report request. The + // mouse device has no output reports so we return a NULL pointer and + // zero length to cause this request to be stalled. + // + case USBD_HID_EVENT_GET_REPORT_BUFFER: + { + // + // We are being asked for a report that does not exist for + // this device. Return 0 to indicate that we are not providing + // a buffer. + // + return(0); + } + + // + // The host is asking us to set either boot or report protocol (not + // that it makes any difference to this particular mouse). + // + case USBD_HID_EVENT_SET_PROTOCOL: + { + psInst->ui8Protocol = ui32MsgData; + break; + } + + // + // The host is asking us to tell it which protocol we are currently + // using, boot or request. + // + case USBD_HID_EVENT_GET_PROTOCOL: + { + return(psInst->ui8Protocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + case USB_EVENT_LPM_RESUME: + case USB_EVENT_LPM_SLEEP: + case USB_EVENT_LPM_ERROR: + { + return(psMouseDevice->pfnCallback(psMouseDevice->pvCBData, + ui32Event, ui32MsgData, + pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvMouseDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to our HID device structure +// (&g_sHIDMouseDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to report +// transmissions made using the interrupt IN endpoint. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDMouseTxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psMouseDevice; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvMouseDevice); + + // + // Get a pointer to our instance data + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + psInst = &psMouseDevice->sPrivateData; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // Our last transmission is complete. + // + psInst->iMouseState = eHIDMouseStateIdle; + + // + // Pass the event on to the client. + // + psMouseDevice->pfnCallback(psMouseDevice->pvCBData, + USB_EVENT_TX_COMPLETE, ui32MsgData, + (void *)0); + + break; + } + + // + // We ignore all other events related to transmission of reports via + // the interrupt IN endpoint. + // + default: + { + break; + } + } + + return(0); +} + +//***************************************************************************** +// +//! Initializes HID mouse device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psMouseDevice points to a structure containing parameters +//! customizing the operation of the HID mouse device. +//! +//! An application wishing to offer a USB HID mouse interface to a USB host +//! must call this function to initialize the USB controller and attach the +//! mouse device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function returns the \e psMouseDevice +//! pointer passed to it. This must be passed on all future calls to the HID +//! mouse device driver. +//! +//! When a host connects and configures the device, the application callback +//! receives \b USB_EVENT_CONNECTED after which calls can be made to +//! USBDHIDMouseStateChange() to report pointer movement and button presses +//! to the host. +//! +//! \note The application must not make any calls to the lower level USB device +//! interfaces if interacting with USB via the USB HID mouse device API. +//! Doing so causes unpredictable (though almost certainly unpleasant) +//! behavior. +//! +//! \return Returns NULL on failure or the psMouseDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDMouseInit(uint32_t ui32Index, tUSBDHIDMouseDevice *psMouseDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + tConfigDescriptor *pConfigDesc; + + // + // Check parameter validity. + // + ASSERT(psMouseDevice); + ASSERT(psMouseDevice->ppui8StringDescriptors); + ASSERT(psMouseDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + pConfigDesc = (tConfigDescriptor *)g_pui8MouseDescriptor; + pConfigDesc->bmAttributes = psMouseDevice->ui8PwrAttributes; + pConfigDesc->bMaxPower = (uint8_t)(psMouseDevice->ui16MaxPowermA / 2); + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDMouseCompositeInit(ui32Index, psMouseDevice, 0); + + // + // If we initialized the HID layer successfully, pass our device pointer + // back as the return code, otherwise return NULL to indicate an error. + // + if(pvRetcode) + { + // + // Initialize the lower layer HID driver and pass it the various + // structures and descriptors necessary to declare that we are a + // keyboard. + // + pvRetcode = USBDHIDInit(ui32Index, psHIDDevice); + + return((void *)psMouseDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID mouse device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psMouseDevice points to a structure containing parameters +//! customizing the operation of the HID mouse device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! This call is very similar to USBDHIDMouseInit() except that it is used for +//! initializing an instance of the HID mouse device for use in a composite +//! device. If this HID mouse is part of a composite device, then the +//! \e psCompEntry should point to the composite device entry to initialize. +//! This is part of the array that is passed to the USBDCompositeInit() +//! function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Mouse APIs. +// +//***************************************************************************** +void * +USBDHIDMouseCompositeInit(uint32_t ui32Index, + tUSBDHIDMouseDevice *psMouseDevice, + tCompositeEntry *psCompEntry) +{ + tHIDMouseInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + + // + // Check parameter validity. + // + ASSERT(psMouseDevice); + ASSERT(psMouseDevice->ppui8StringDescriptors); + ASSERT(psMouseDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = &psMouseDevice->sPrivateData; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Initialize the various fields in our instance structure. + // + psInst->ui8USBConfigured = 0; + psInst->ui8Protocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ui8Duration4mS = 0; + psInst->sReportIdle.ui8ReportID = 0; + psInst->sReportIdle.ui32TimeSinceReportmS = 0; + psInst->sReportIdle.ui16TimeTillNextmS = 0; + psInst->iMouseState = eHIDMouseStateUnconfigured; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->ui16PID = psMouseDevice->ui16PID; + psHIDDevice->ui16VID = psMouseDevice->ui16VID; + psHIDDevice->ui16MaxPowermA = psMouseDevice->ui16MaxPowermA; + psHIDDevice->ui8PwrAttributes = psMouseDevice->ui8PwrAttributes; + psHIDDevice->ui8Subclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ui8Protocol = USB_HID_PROTOCOL_MOUSE; + psHIDDevice->ui8NumInputReports = 1; + psHIDDevice->psReportIdle = &psInst->sReportIdle; + psHIDDevice->pfnRxCallback = HIDMouseRxHandler; + psHIDDevice->pvRxCBData = (void *)psMouseDevice; + psHIDDevice->pfnTxCallback = HIDMouseTxHandler; + psHIDDevice->pvTxCBData = (void *)psMouseDevice; + psHIDDevice->bUseOutEndpoint = false; + psHIDDevice->psHIDDescriptor = &g_sMouseHIDDescriptor; + psHIDDevice->ppui8ClassDescriptors = g_pui8MouseClassDescriptors; + psHIDDevice->ppui8StringDescriptors = + psMouseDevice->ppui8StringDescriptors; + psHIDDevice->ui32NumStringDescriptors = + psMouseDevice->ui32NumStringDescriptors; + psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors; + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a keyboard. + // + return(USBDHIDCompositeInit(ui32Index, psHIDDevice, psCompEntry)); +} + +//***************************************************************************** +// +//! Shuts down the HID mouse device. +//! +//! \param pvMouseDevice is the pointer to the device instance structure. +//! +//! This function terminates HID mouse operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e pvMouseDevice instance may not me used in any other call to the HID +//! mouse device other than USBDHIDMouseInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDMouseTerm(void *pvMouseDevice) +{ + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvMouseDevice); + + // + // Get a pointer to the device. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Mark our device as no longer configured. + // + psMouseDevice->sPrivateData.ui8USBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the mouse callback. +//! +//! \param pvMouseDevice is the pointer to the mouse device instance structure. +//! \param pvCBData is the pointer that client wishes to be provided on +//! each event sent to the mouse callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnCallback function +//! passed on USBDHIDMouseInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the pvMouseDevice structure passed to USBDHIDMouseInit() +//! resides in RAM. If this structure is in flash, callback data changes are +//! not possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDMouseSetCBData(void *pvMouseDevice, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDMouseDevice *psMouse; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvMouseDevice); + + // + // Get a pointer to our mouse device. + // + psMouse = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Save the old callback pointer and replace it with the new value. + // + pvOldCBData = psMouse->pvCBData; + psMouse->pvCBData = pvCBData; + + // + // Pass the old callback pointer back to the caller. + // + return(pvOldCBData); +} + +//***************************************************************************** +// +//! Reports a mouse state change, pointer movement or button press, to the USB +//! host. +//! +//! \param pvMouseDevice is the pointer to the mouse device instance structure. +//! \param i8DeltaX is the relative horizontal pointer movement that the +//! application wishes to report. Valid values are in the range [-127, 127] +//! with positive values indicating movement to the right. +//! \param i8DeltaY is the relative vertical pointer movement that the +//! application wishes to report. Valid values are in the range [-127, 127] +//! with positive values indicating downward movement. +//! \param ui8Buttons is a bit mask indicating which (if any) of the three +//! mouse buttons is pressed. Valid values are logical OR combinations of +//! \b MOUSE_REPORT_BUTTON_1, \b MOUSE_REPORT_BUTTON_2 and +//! \b MOUSE_REPORT_BUTTON_3. +//! +//! This function is called to report changes in the mouse state to the USB +//! host. These changes can be movement of the pointer, reported relative to +//! its previous position, or changes in the states of up to 3 buttons that +//! the mouse may support. The return code indicates whether or not the +//! mouse report could be sent to the host. In cases where a previous +//! report is still being transmitted, \b MOUSE_ERR_TX_ERROR is returned +//! and the state change is ignored. +//! +//! \return Returns \b MOUSE_SUCCESS on success, \b MOUSE_ERR_TX_ERROR if an +//! error occurred while attempting to schedule transmission of the mouse +//! report to the host (typically due to a previous report which has not yet +//! completed transmission or due to disconnection of the host) or \b +//! MOUSE_ERR_NOT_CONFIGURED if called before a host has connected to and +//! configured the device. +// +//***************************************************************************** +uint32_t +USBDHIDMouseStateChange(void *pvMouseDevice, int8_t i8DeltaX, int8_t i8DeltaY, + uint8_t ui8Buttons) +{ + uint32_t ui32Retcode, ui32Count; + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + // + // Get a pointer to the device. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Get a pointer to our instance data + // + psInst = &psMouseDevice->sPrivateData; + + // + // Update the global mouse report with the information passed. + // + psInst->pui8Report[HID_REPORT_BUTTONS] = ui8Buttons; + psInst->pui8Report[HID_REPORT_X] = (uint8_t)i8DeltaX; + psInst->pui8Report[HID_REPORT_Y] = (uint8_t)i8DeltaY; + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ui8USBConfigured) + { + return(MOUSE_ERR_NOT_CONFIGURED); + } + + // + // Only send a report if the transmitter is currently free. + // + if(USBDHIDTxPacketAvailable((void *)psHIDDevice)) + { + // + // Send the report to the host. + // + psInst->iMouseState = eHIDMouseStateSend; + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pui8Report, MOUSE_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ui32Count) + { + // + // No - report the error to the caller. + // + ui32Retcode = MOUSE_ERR_TX_ERROR; + } + else + { + ui32Retcode = MOUSE_SUCCESS; + } + } + else + { + ui32Retcode = MOUSE_ERR_TX_ERROR; + } + // + // Return the relevant error code to the caller. + // + return(ui32Retcode); +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvMouseDevice is the pointer to the mouse device instance structure. +//! \param ui8Power indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDMousePowerStatusSet(void *pvMouseDevice, uint8_t ui8Power) +{ + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvMouseDevice); + + // + // Get the keyboard device pointer. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvMouseDevice is the pointer to the mouse device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this causes the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false is returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +bool +USBDHIDMouseRemoteWakeupRequest(void *pvMouseDevice) +{ + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvMouseDevice); + + // + // Get the keyboard device pointer. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Pass the request through to the lower layer. + // + return(USBDHIDRemoteWakeupRequest((void *)&psHIDDevice)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhidmouse.h b/usblib/device/usbdhidmouse.h new file mode 100644 index 0000000..6978ca7 --- /dev/null +++ b/usblib/device/usbdhidmouse.h @@ -0,0 +1,299 @@ +//***************************************************************************** +// +// usbdhidmouse.h - Public header file for the USB HID Mouse device class +// 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. +// +//***************************************************************************** + +#ifndef __USBDHIDMOUSE_H__ +#define __USBDHIDMOUSE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_mouse_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB HID mouse code and are here only to help with the application +// allocating the correct amount of memory for the HID mouse device code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The size of the mouse input report sent to the host. +// +//***************************************************************************** +#define MOUSE_REPORT_SIZE 3 + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the mouse can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + eHIDMouseStateUnconfigured, + + // + // No keys to send and not waiting on data. + // + eHIDMouseStateIdle, + + // + // Waiting on report data from the host. + // + eHIDMouseStateWaitData, + + // + // Waiting on data to be sent out. + // + eHIDMouseStateSend +} +tMouseState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure provides the private instance data structure for the USB +// HID Mouse device. This structure forms the RAM workspace used by each +// instance of the mouse. +// +//***************************************************************************** +typedef struct +{ + // + // The USB configuration number set by the host or 0 of the device is + // currently unconfigured. + // + uint8_t ui8USBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + uint8_t ui8Protocol; + + // + // A buffer used to hold the last input report sent to the host. + // + uint8_t pui8Report[MOUSE_REPORT_SIZE]; + + // + // The current state of the mouse interrupt IN endpoint. + // + volatile tMouseState iMouseState; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDMouseInstance; + +//***************************************************************************** +// +//! This structure is used by the application to define operating parameters +//! for the HID mouse device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which is called to notify + //! the application of events relating to the operation of the mouse. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which is sent as the first + //! parameter in all calls made to the mouse callback, pfnCallback. + // + void *pvCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1),HID + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The private instance data for this device. This memory must + //! remain accessible for as long as the mouse device is in use and must + //! not be modified by any code outside the HID mouse driver. + // + tHIDMouseInstance sPrivateData; +} +tUSBDHIDMouseDevice; + +//***************************************************************************** +// +//! This return code from USBDHIDMouseStateChange() indicates success. +// +//***************************************************************************** +#define MOUSE_SUCCESS 0 + +//***************************************************************************** +// +//! This return code from USBDHIDMouseStateChange() indicates that an error was +//! reported while attempting to send a report to the host. A client should +//! assume that the host has disconnected if this return code is seen. +// +//***************************************************************************** +#define MOUSE_ERR_TX_ERROR 2 + +//***************************************************************************** +// +//! USBDHIDMouseStateChange() returns this value if it is called before the +//! USB host has connected and configured the device. All mouse state +//! information passed on the call is been ignored. +// +//***************************************************************************** +#define MOUSE_ERR_NOT_CONFIGURED \ + 4 + +//***************************************************************************** +// +//! Setting this bit in the ui8Buttons parameter to USBDHIDMouseStateChange() +//! indicates to the USB host that button 1 on the mouse is pressed. +// +//***************************************************************************** +#define MOUSE_REPORT_BUTTON_1 0x01 + +//***************************************************************************** +// +//! Setting this bit in the ui8Buttons parameter to USBDHIDMouseStateChange() +//! indicates to the USB host that button 2 on the mouse is pressed. +// +//***************************************************************************** +#define MOUSE_REPORT_BUTTON_2 0x02 + +//***************************************************************************** +// +//! Setting this bit in the ui8Buttons parameter to USBDHIDMouseStateChange() +//! indicates to the USB host that button 3 on the mouse is pressed. +// +//***************************************************************************** +#define MOUSE_REPORT_BUTTON_3 0x04 + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDMouseInit(uint32_t ui32Index, + tUSBDHIDMouseDevice *psMouseDevice); +extern void *USBDHIDMouseCompositeInit(uint32_t ui32Index, + tUSBDHIDMouseDevice *psMouseDevice, + tCompositeEntry *psCompEntry); +extern void USBDHIDMouseTerm(void *pvMouseDevice); +extern void *USBDHIDMouseSetCBData(void *pvMouseDevice, void *pvCBData); +extern uint32_t USBDHIDMouseStateChange(void *pvMouseDevice, int8_t i8DeltaX, + int8_t i8DeltaY, uint8_t ui8Buttons); +extern void USBDHIDMousePowerStatusSet(void *pvMouseDevice, + uint8_t ui8Power); +extern bool USBDHIDMouseRemoteWakeupRequest(void *pvMouseDevice); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHIDMOUSE_H__ diff --git a/usblib/device/usbdmsc.c b/usblib/device/usbdmsc.c new file mode 100644 index 0000000..daea1f0 --- /dev/null +++ b/usblib/device/usbdmsc.c @@ -0,0 +1,2441 @@ +//***************************************************************************** +// +// usbdmsc.c - USB mass storage device class driver. +// +// Copyright (c) 2009-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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbmsc.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdmsc.h" + +//***************************************************************************** +// +//! \addtogroup msc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These are the internal flags used with the ui32Flags member variable. +// +//***************************************************************************** +#define USBD_FLAG_DMA_IN 0x00000001 +#define USBD_FLAG_DMA_OUT 0x00000002 +#define USBD_FLAG_ALLOW_REMOVAL 0x00000004 + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// These are fields that are used by the USB descriptors for the Mass Storage +// Class. +// +//***************************************************************************** +#define USB_MSC_SUBCLASS_SCSI 0x6 +#define USB_MSC_PROTO_BULKONLY 0x50 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define DATA_IN_ENDPOINT USB_EP_1 +#define DATA_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// Maximum packet size for the bulk endpoints is 64 bytes. +// +//***************************************************************************** +#define DATA_IN_EP_MAX_SIZE 64 +#define DATA_OUT_EP_MAX_SIZE 64 + +//***************************************************************************** +// +// These defines control the size of USB transfers for commands. +// +//***************************************************************************** +#define COMMAND_BUFFER_SIZE 64 + +//***************************************************************************** +// +// The block size of a device. It defaults to DEVICE_BLOCK_SIZE +// +//***************************************************************************** +static uint32_t g_pui32BlockSize = DEVICE_BLOCK_SIZE; + +//***************************************************************************** +// +// The local buffer used to read in commands and process them. +// +//***************************************************************************** +static uint8_t g_pui8Command[COMMAND_BUFFER_SIZE]; + +//***************************************************************************** +// +// The current transfer state is held in these variables. +// +//***************************************************************************** +static tMSCCSW g_sSCSICSW; + +//***************************************************************************** +// +// The current state for the SCSI commands that are being handled and are +// stored in the tMSCInstance.ui8SCSIState structure member. +// +//***************************************************************************** + +// +// No command in process. +// +#define STATE_SCSI_IDLE 0x00 + +// +// Sending and reading logical blocks. +// +#define STATE_SCSI_SEND_BLOCKS 0x01 + +// +// Receiving and writing logical blocks. +// +#define STATE_SCSI_RECEIVE_BLOCKS 0x02 + +// +// Send the status once the previous transfer is complete. +// +#define STATE_SCSI_SEND_STATUS 0x03 + +// +// Status was prepared to be sent and now waiting for it to have gone out. +// +#define STATE_SCSI_SENT_STATUS 0x04 + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +static uint8_t g_pui8MSCDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts + // assume + // high-speed - see USB 2.0 spec 9.2.6.6) + 0, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + 0, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during + // USBDCDCInit). + USBShort(0), // Product ID (filled in during + // USBDCDCInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Mass storage device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +static uint8_t g_pui8MSCDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(32), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 0, // The string identifier that describes + // this configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake + // up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const uint8_t g_pui8MSCInterface[MSCINTERFACE_SIZE] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this + // interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_MASS_STORAGE, // The interface class + USB_MSC_SUBCLASS_SCSI, // The interface sub-class. + USB_MSC_PROTO_BULKONLY, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The mass storage configuration descriptor is defined as two sections, +// one containing just the 9 byte USB configuration descriptor and the other +// containing everything else that is sent to the host along with it. +// +//***************************************************************************** +const tConfigSection g_sMSCConfigSection = +{ + sizeof(g_pui8MSCDescriptor), + g_pui8MSCDescriptor +}; + +const tConfigSection g_sMSCInterfaceSection = +{ + sizeof(g_pui8MSCInterface), + g_pui8MSCInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete bulk device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psMSCSections[] = +{ + &g_sMSCConfigSection, + &g_sMSCInterfaceSection +}; + +#define NUM_MSC_SECTIONS (sizeof(g_psMSCSections) / \ + sizeof(g_psMSCSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sMSCConfigHeader = +{ + NUM_MSC_SECTIONS, + g_psMSCSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppsMSCConfigDescriptors[] = +{ + &g_sMSCConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvMSCDevice); +static void ConfigChangeHandler(void *pvMSCDevice, uint32_t ui32Value); +static void HandleEndpoints(void *pvMSCDevice, uint32_t ui32Status); +static void HandleRequests(void *pvMSCDevice, tUSBRequest *psUSBRequest); +static void USBDSCSISendStatus(tUSBDMSCDevice *psMSCDevice); +uint32_t USBDSCSICommand(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW); +static void HandleDevice(void *pvMSCDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB MSC device. +// +//***************************************************************************** +const tCustomHandlers g_sMSCHandlers = +{ + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + 0, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice +}; + +//***************************************************************************** +// +//! This function is used by an application if it can detect insertion or +//! removal of the media. +//! +//! \param pvMSCDevice is the mass storage device instance that had a media +//! change. +//! \param iMediaStatus is the updated status for the media. +//! +//! This function should be called by an application when it detects a change +//! in the status of the media in use by the USB mass storage class. The +//! \e iMediaStatus parameter will indicate the new status of the media and +//! can also indicate that the application has no knowledge of the media state. +//! +//! There are currently the three following values for the \e iMediaStatus +//! parameter: +//! - \b eUSBDMSCMediaPresent indicates that the media is present or has been +//! added. +//! - \b eUSBDMSCMediaNotPresent indicates that the media is not present or was +//! removed. +//! - \b eUSBDMSCMediaUnknown indicates that the application has no knowledge +//! of the media state and the USB mass storage class. +//! +//! It will be left up to the application to call this function whenever it +//! detects a change or simply call it once with \b eUSBDMSCMediaUnknown and +//! allow the mass storage class to infer the state from the remaining device +//! APIs. +//! +//! \note It is recommended that the application use this function to inform +//! the mass storage class of media state changes as it will lead to a more +//! responsive system. +//! +//! \return None. +// +//***************************************************************************** +void +USBDMSCMediaChange(void *pvMSCDevice, tUSBDMSCMediaStatus iMediaStatus) +{ + tUSBDMSCDevice *psMSCDevice; + + // + // Create a device instance pointer. + // + psMSCDevice = pvMSCDevice; + + // + // Save the current media status. + // + psMSCDevice->sPrivateData.iMediaStatus = iMediaStatus; +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the Bulk endpoints for +// the mass storage class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvMSCDevice, uint32_t ui32Status) +{ + tUSBDMSCDevice *psMSCDevice; + tMSCInstance *psInst; + tMSCCBW *psSCSICBW; + uint32_t ui32EPStatus, ui32Size; + + ASSERT(pvMSCDevice != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ui32Index is different in both cases. + // + psMSCDevice = pvMSCDevice; + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Get the endpoints status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Handler for the bulk IN data endpoint. + // + if((ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) || + ((psInst->ui32Flags & USBD_FLAG_DMA_IN) && + (USBLibDMAChannelStatus(psInst->psDMAInstance, psInst->ui8INDMA) & + USBLIBSTATUS_DMA_COMPLETE))) + { + switch(psInst->ui8SCSIState) + { + // + // Handle the case where we are sending out data due to a read + // command. + // + case STATE_SCSI_SEND_BLOCKS: + { + // + // Decrement the number of bytes left to send. + // + psInst->ui32BytesToTransfer -= g_pui32BlockSize; + + // + // If we are done then move on to the status phase. + // + if(psInst->ui32BytesToTransfer == 0) + { + // + // Set the status so that it can be sent when this + // response has has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // DMA has completed for the IN endpoint. + // + psInst->ui32Flags &= ~USBD_FLAG_DMA_IN; + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, + 0, 0); + } + + // + // Make sure that the transfer has actually finished. If + // it has not there will be another interrupt to send + // out the status. + // + if(USBEndpointStatus(USB0_BASE,psInst->ui8INEndpoint) & + USB_DEV_TX_TXPKTRDY) + { + // + // Send back the status once this transfer is complete. + // + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; + } + else + { + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psMSCDevice); + } + + // + // The transfer is complete so don't read anymore data. + // + break; + } + + // + // Move on to the next Logical Block. + // + psInst->ui32CurrentLBA++; + + // + // Read the new data and send it out. + // + if(psMSCDevice->sMediaFunctions.pfnBlockRead(psInst->pvMedia, + (uint8_t *)psInst->pui32Buffer, + psInst->ui32CurrentLBA, 1) == 0) + { + } + + // + // Configure and enable DMA for the IN transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, + psInst->ui8INDMA, psInst->pui32Buffer, + g_pui32BlockSize); + + // + // Start the DMA transfer. + // + USBLibDMAChannelEnable(psInst->psDMAInstance, + psInst->ui8INDMA); + + break; + } + + // + // Handle sending status. + // + case STATE_SCSI_SEND_STATUS: + { + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psMSCDevice); + + break; + } + + // + // Handle completing sending status. + // + case STATE_SCSI_SENT_STATUS: + { + psInst->ui8SCSIState = STATE_SCSI_IDLE; + + break; + } + + // + // These cases should not occur as the being in the IDLE state due + // to an IN interrupt is invalid. + // + case STATE_SCSI_IDLE: + default: + { + break; + } + } + } + + // + // Handler for the bulk OUT data endpoint. + // + if((ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) || + ((psInst->ui32Flags & USBD_FLAG_DMA_OUT) && + (USBLibDMAChannelStatus(psInst->psDMAInstance, psInst->ui8OUTDMA) & + USBLIBSTATUS_DMA_COMPLETE))) + { + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, + psInst->ui8OUTEndpoint); + + switch(psInst->ui8SCSIState) + { + // + // Receiving and writing bytes to the storage device. + // + case STATE_SCSI_RECEIVE_BLOCKS: + { + // + // Update the current status for the buffer. + // + psInst->ui32BytesToTransfer -= g_pui32BlockSize; + + // + // Write the new data. + // + psMSCDevice->sMediaFunctions.pfnBlockWrite(psInst->pvMedia, + (uint8_t *)psInst->pui32Buffer, + psInst->ui32CurrentLBA, 1); + + // + // Move on to the next Logical Block. + // + psInst->ui32CurrentLBA++; + + // + // Check if all bytes have been received. + // + if(psInst->ui32BytesToTransfer == 0) + { + // + // Set the status so that it can be sent when this response + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // DMA has completed for the OUT endpoint. + // + psInst->ui32Flags &= ~USBD_FLAG_DMA_OUT; + + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psMSCDevice); + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, + psInst->ui8OUTEndpoint, + USB_EP_DEV_OUT); + + // + // If there is an event callback then call it to notify + // that last operation has completed. + // + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, + 0, 0); + } + } + else + { + // + // Configure and enable DMA for the OUT transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, + psInst->ui8OUTDMA, psInst->pui32Buffer, + g_pui32BlockSize); + } + + break; + } + + // + // If there is an OUT transfer in idle state then it was a new + // command. + // + case STATE_SCSI_IDLE: + { + // + // Attempt to handle the new command. + // + + // + // Receive the command. + // + ui32Size = COMMAND_BUFFER_SIZE; + MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + g_pui8Command, &ui32Size); + psSCSICBW = (tMSCCBW *)g_pui8Command; + + // + // Acknowledge the OUT data packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, false); + + // + // If this is a valid CBW then handle it. + // + if(psSCSICBW->dCBWSignature == CBW_SIGNATURE) + { + g_sSCSICSW.dCSWSignature = CSW_SIGNATURE; + g_sSCSICSW.dCSWTag = psSCSICBW->dCBWTag; + g_sSCSICSW.dCSWDataResidue = 0; + g_sSCSICSW.bCSWStatus = 0; + + USBDSCSICommand(psMSCDevice, psSCSICBW); + } + else + { + // + // Just return to the idle state since we are now out of + // sync with the host. This should not happen, but this + // should allow the device to synchronize with the host + // controller. + // + psInst->ui8SCSIState = STATE_SCSI_IDLE; + } + + break; + } + default: + { + break; + } + } + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvMSCDevice, uint32_t ui32Request, void *pvRequestData) +{ + tMSCInstance *psInst; + uint8_t *pui8Data; + tUSBDMSCDevice *psMSCDevice; + + psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice; + + // + // Get the instance data pointers. + // + psInst = &((tUSBDMSCDevice *)pvMSCDevice)->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB MSC + // class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f)); + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psInst->ui8INDMA != 0) + { + USBLibDMAChannelRelease(psInst->psDMAInstance, + psInst->ui8INDMA); + } + + // + // Allocate a DMA channel to the endpoint. + // + psInst->ui8INDMA = + USBLibDMAChannelAllocate(psInst->psDMAInstance, + psInst->ui8INEndpoint, 0, + USB_DMA_EP_TX | + USB_DMA_EP_DEVICE); + + // + // Set the DMA individual transfer size. + // + USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8INDMA, + 32); + + // + // Set the DMA arbitration size. + // + USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8INDMA, + 16); + } + else + { + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psInst->ui8OUTDMA != 0) + { + USBLibDMAChannelRelease(psInst->psDMAInstance, + psInst->ui8OUTDMA); + } + + // + // Allocate a DMA channel to the endpoint. + // + psInst->ui8OUTDMA = + USBLibDMAChannelAllocate(psInst->psDMAInstance, + psInst->ui8OUTEndpoint, 0, + USB_DMA_EP_RX | + USB_DMA_EP_DEVICE); + + // + // Set the DMA individual transfer size. + // + USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 32); + + // + // Set the DMA arbitration size. + // + USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 16); + } + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the LPM resume event to the client. + // + psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the LPM sleep event to the client. + // + psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the LPM error event to the client. + // + psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvMSCDevice) +{ + tUSBDMSCDevice *psMSCDevice; + + ASSERT(pvMSCDevice != 0); + + // + // Create the instance pointer. + // + psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice; + + // + // Close the drive requested. + // + if(psMSCDevice->sPrivateData.pvMedia != 0) + { + psMSCDevice->sPrivateData.pvMedia = 0; + psMSCDevice->sMediaFunctions.pfnClose(0); + } + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psMSCDevice->pfnEventCallback(pvMSCDevice, USB_EVENT_DISCONNECTED, 0, + 0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvMSCDevice, uint32_t ui32Value) +{ + tUSBDMSCDevice *psMSCDevice; + + ASSERT(pvMSCDevice != 0); + + // + // Create the instance pointer. + // + psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice; + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psMSCDevice->sPrivateData.ui8OUTDMA != 0) + { + USBLibDMAChannelRelease(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTDMA); + } + + // + // Configure the DMA for the OUT endpoint. + // + psMSCDevice->sPrivateData.ui8OUTDMA = + USBLibDMAChannelAllocate(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTEndpoint, 64, + USB_DMA_EP_RX | USB_DMA_EP_DEVICE); + + USBLibDMAUnitSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTDMA, 32); + + USBLibDMAArbSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTDMA, 16); + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psMSCDevice->sPrivateData.ui8INDMA != 0) + { + USBLibDMAChannelRelease(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INDMA); + } + + // + // Configure the DMA for the IN endpoint. + // + psMSCDevice->sPrivateData.ui8INDMA = + USBLibDMAChannelAllocate(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INEndpoint, 64, + USB_DMA_EP_TX | USB_DMA_EP_DEVICE); + + USBLibDMAUnitSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INDMA, 32); + + USBLibDMAArbSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INDMA, 16); + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psMSCDevice->pfnEventCallback(pvMSCDevice, USB_EVENT_CONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +//! This function should be called once for the mass storage class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psMSCDevice points to a structure containing parameters customizing +//! the operation of the mass storage device. +//! +//! In order for an application to initialize the USB device mass storage +//! class, it must first call this function with the a valid mass storage +//! device class structure in the \e psMSCDevice parameter. This allows this +//! function to initialize the USB controller and device code to be prepared to +//! enumerate and function as a USB mass storage device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the mass storage class. +//! +//! See the documentation on the tUSBDMSCDevice structure for more information +//! on how to properly fill the structure members. +//! +//! \return Returns 0 on failure or a non-zero void pointer on success. +// +//***************************************************************************** +void * +USBDMSCInit(uint32_t ui32Index, tUSBDMSCDevice *psMSCDevice) +{ + tDeviceDescriptor *psDevDesc; + tConfigDescriptor *pConfDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psMSCDevice); + ASSERT(psMSCDevice->ppui8StringDescriptors); + + USBDMSCCompositeInit(ui32Index, psMSCDevice, 0); + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8MSCDeviceDescriptor; + psDevDesc->idVendor = psMSCDevice->ui16VID; + psDevDesc->idProduct = psMSCDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + pConfDesc = (tConfigDescriptor *)g_pui8MSCDescriptor; + pConfDesc->bmAttributes = psMSCDevice->ui8PwrAttributes; + pConfDesc->bMaxPower = (uint8_t)(psMSCDevice->ui16MaxPowermA / 2); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psMSCDevice->sPrivateData.sDevInfo, + (void *)psMSCDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psMSCDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the mass storage class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psMSCDevice points to a structure containing parameters customizing +//! the operation of the mass storage device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! In order for an application to initialize the USB device mass storage +//! class, it must first call this function with the a valid mass storage +//! device class structure in the \e psMSCDevice parameter. This allows this +//! function to initialize the USB controller and device code to be prepared to +//! enumerate and function as a USB mass storage device. If this mass storage +//! device is part of a composite device, then the \e psCompEntry should +//! point to the composite device entry to initialize. This is part of the +//! array that is passed to the USBDCompositeInit() function. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the mass storage class. +//! +//! See the documentation on the tUSBDMSCDevice structure for more information +//! on how to properly fill the structure members. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB mass storage APIs. +// +//***************************************************************************** +void * +USBDMSCCompositeInit(uint32_t ui32Index, tUSBDMSCDevice *psMSCDevice, + tCompositeEntry *psCompEntry) +{ + tMSCInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psMSCDevice); + ASSERT(psMSCDevice->ppui8StringDescriptors); + ASSERT(psCompEntry != 0); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psMSCDevice->sPrivateData; + psInst->ui32USBBase = USB0_BASE; + psInst->bConnected = false; + psInst->iMediaStatus = eUSBDMSCMediaUnknown; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psMSCDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sMSCHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8MSCDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppsMSCConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Initialize the device info structure for the mass storage device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Set the initial interface and endpoints. + // + psInst->ui8Interface = 0; + psInst->ui8OUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ui8INEndpoint = DATA_IN_ENDPOINT; + + // + // Set the initial SCSI state to idle. + // + psInst->ui8SCSIState = STATE_SCSI_IDLE; + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psMSCDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psMSCDevice->ui32NumStringDescriptors; + + // + // Open the drive requested. + // + psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(0); + + if(psInst->pvMedia == 0) + { + // + // There is no media currently present. + // + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + else + { + // + // Media is now ready for use. + // + psInst->ui8SenseKey = SCSI_RS_KEY_UNIT_ATTN; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOTRDY2RDY; + } + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Get the DMA instance pointer. + // + psInst->psDMAInstance = USBLibDMAInit(0); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psMSCDevice); +} + +//***************************************************************************** +// +//! Shuts down the mass storage device. +//! +//! \param pvMSCDevice is the pointer to the device instance structure as +//! returned by USBDMSCInit() or USBDMSCCompositeInit(). +//! +//! This function terminates mass storage operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e pvMSCDevice instance may not me used in any other call to the mass +//! storage device other than USBDMSCInit() or USBDMSCCompositeInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDMSCTerm(void *pvMSCDevice) +{ + tUSBDMSCDevice *psMSCDevice; + + ASSERT(pvMSCDevice != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); + + // + // Create a device instance pointer. + // + psMSCDevice = pvMSCDevice; + + // + // If the media was opened the close it out. + // + if(psMSCDevice->sPrivateData.pvMedia != 0) + { + psMSCDevice->sPrivateData.pvMedia = 0; + psMSCDevice->sMediaFunctions.pfnClose(0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvMSCDevice is instance data for this request. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure to determine the command. +// The only mass storage command supported over endpoint 0 is the Get Max LUN +// command. +// +// \return None. +// +//***************************************************************************** +static void +HandleRequests(void *pvMSCDevice, tUSBRequest *pUSBRequest) +{ + // + // This class only support a single LUN. + // + static const uint8_t ui8MaxLun = 0; + + ASSERT(pvMSCDevice != 0); + + // + // Determine the type of request. + // + switch(pUSBRequest->bRequest) + { + // + // A Set Report request is received from the host when it sends an + // Output report via endpoint 0. + // + case USBREQ_GET_MAX_LUN: + { + // + // Need to ACK the data on end point 0 with last data since there + // is no more data expected. + // + USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui8MaxLun, 1); + + break; + } + case USBREQ_BULK_ONLY_RESET: + { + // + // Need to ACK the data on end point 0 with last data since there + // is no more data expected. + // + USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Send a null packet to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui8MaxLun, 0); + + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Inquiry command when it is received +// from the host. +// +//***************************************************************************** +static void +USBDSCSIInquiry(tUSBDMSCDevice *psMSCDevice) +{ + int32_t i32Idx; + tMSCInstance *psInst; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)g_pui8Command; + + // + // Create the serial instance data. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Direct Access device, Removable storage and SCSI 1 responses. + // + pui32Data[0] = SCSI_INQ_PDT_SBC | (SCSI_INQ_RMB << 8); + + // + // Additional Length is fixed at 31 bytes. + // + pui32Data[1] = 31; + + // + // Copy the Vendor string. + // + for(i32Idx = 0; i32Idx < 8; i32Idx++) + { + g_pui8Command[i32Idx + 8] = psMSCDevice->pui8Vendor[i32Idx]; + } + + // + // Copy the Product string. + // + for(i32Idx = 0; i32Idx < 16; i32Idx++) + { + g_pui8Command[i32Idx + 16] = psMSCDevice->pui8Product[i32Idx]; + } + + // + // Copy the Version string. + // + for(i32Idx = 0; i32Idx < 4; i32Idx++) + { + g_pui8Command[i32Idx + 32] = psMSCDevice->pui8Version[i32Idx]; + } + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 36); + + // + // Send the data to the host. + // + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read Capacities command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIReadCapacities(tUSBDMSCDevice *psMSCDevice) +{ + uint32_t ui32Blocks; + tMSCInstance *psInst; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)g_pui8Command; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + if(psInst->pvMedia != 0) + { + if(psMSCDevice->sMediaFunctions.pfnBlockSize) + { + // + // Query the block size for the device + // + g_pui32BlockSize = + psMSCDevice->sMediaFunctions.pfnBlockSize(psInst->pvMedia); + } + ui32Blocks = + psMSCDevice->sMediaFunctions.pfnNumBlocks(psInst->pvMedia); + + pui32Data[0] = 0x08000000; + + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pui8Command[4] = ui32Blocks >> 24; + g_pui8Command[5] = 0xff & (ui32Blocks >> 16); + g_pui8Command[6] = 0xff & (ui32Blocks >> 8); + g_pui8Command[7] = 0xff & (ui32Blocks); + + // + // Current media capacity + // + g_pui8Command[8] = 0x2; + + // + // Fill in the block size, which is g_pui32BlockSize. + // + g_pui8Command[9] = 0xff & (g_pui32BlockSize >> 16); + g_pui8Command[10] = 0xff & (g_pui32BlockSize >> 8); + g_pui8Command[11] = 0xff & g_pui32BlockSize; + + // + // Send out the 12 bytes that are in this response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 12); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, + USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read Capacity command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIReadCapacity(tUSBDMSCDevice *psMSCDevice) +{ + uint32_t ui32Blocks; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + if(psMSCDevice->sMediaFunctions.pfnBlockSize) + { + // + // Query the block size for the device + // + g_pui32BlockSize = + psMSCDevice->sMediaFunctions.pfnBlockSize(psInst->pvMedia); + } + + ui32Blocks = psMSCDevice->sMediaFunctions.pfnNumBlocks(psInst->pvMedia); + + // + // Only decrement if any blocks were found. + // + if(ui32Blocks != 0) + { + // + // One less than the maximum number is the last addressable + // block. + // + ui32Blocks--; + } + + if(psInst->pvMedia != 0) + { + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pui8Command[0] = 0xff & (ui32Blocks >> 24); + g_pui8Command[1] = 0xff & (ui32Blocks >> 16); + g_pui8Command[2] = 0xff & (ui32Blocks >> 8); + g_pui8Command[3] = 0xff & (ui32Blocks); + + g_pui8Command[4] = 0; + + // + // Fill in the block size, which is g_pui32BlockSize. + // + g_pui8Command[5] = 0xff & (g_pui32BlockSize >> 16); + g_pui8Command[6] = 0xff & (g_pui32BlockSize >> 8); + g_pui8Command[7] = 0xff & g_pui32BlockSize; + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 8); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, + USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Request Sense command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIRequestSense(tUSBDMSCDevice *psMSCDevice) +{ + tMSCInstance *psInst; + int32_t i32Idx; + + // + // Zero out the response data. + // + for(i32Idx = 0; i32Idx < 18; i32Idx++) + { + g_pui8Command[i32Idx] = 0; + } + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // The request sense response. + // + g_pui8Command[0] = psInst->ui8ErrorCode; + g_pui8Command[2] = psInst->ui8SenseKey; + + // + // There are 10 more bytes of data. + // + g_pui8Command[7] = 10; + + // + // Transition from not ready to ready. + // + g_pui8Command[12] = (uint8_t)psInst->ui16AddSenseCode; + g_pui8Command[13] = (uint8_t)(psInst->ui16AddSenseCode >> 8); + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 18); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN); + + // + // Reset the valid flag on errors. + // + psInst->ui8ErrorCode = SCSI_RS_CUR_ERRORS; + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Move on to the status phase. + // + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read 10 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIRead10(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + uint16_t ui16NumBlocks; + tMSCInstance *psInst; + + // + // Default the number of blocks. + // + ui16NumBlocks = 0; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + if(psInst->pvMedia != 0) + { + // + // Get the logical block from the CBW structure. This switching + // is required to convert from big to little endian. + // + psInst->ui32CurrentLBA = (psSCSICBW->CBWCB[2] << 24) | + (psSCSICBW->CBWCB[3] << 16) | + (psSCSICBW->CBWCB[4] << 8) | + (psSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + ui16NumBlocks = (psSCSICBW->CBWCB[7] << 8) | psSCSICBW->CBWCB[8]; + + // + // Read the next logical block from the storage device. + // + if(psMSCDevice->sMediaFunctions.pfnBlockRead(psInst->pvMedia, + (uint8_t *)psInst->pui32Buffer, psInst->ui32CurrentLBA, 1) == 0) + { + psInst->pvMedia = 0; + psMSCDevice->sMediaFunctions.pfnClose(0); + } + } + + // + // If there is media present then start transferring the data. + // + if(psInst->pvMedia != 0) + { + // + // Configure and DMA for the IN transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8INDMA, + psInst->pui32Buffer, g_pui32BlockSize); + + // + // Remember that a DMA is in progress. + // + psInst->ui32Flags |= USBD_FLAG_DMA_IN; + + // + // Schedule the remaining bytes to send. + // + psInst->ui32BytesToTransfer = (g_pui32BlockSize * ui16NumBlocks); + + // + // Move on and start sending blocks. + // + psInst->ui8SCSIState = STATE_SCSI_SEND_BLOCKS; + + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_READING, 0, 0); + } + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read 10 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIWrite10(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + uint16_t ui16NumBlocks; + tMSCInstance *psInst; + + // + // Get instance data pointers. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present then start transferring the data. + // + if(psInst->pvMedia != 0) + { + // + // Get the logical block from the CBW structure. This switching + // is required to convert from big to little endian. + // + psInst->ui32CurrentLBA = (psSCSICBW->CBWCB[2] << 24) | + (psSCSICBW->CBWCB[3] << 16) | + (psSCSICBW->CBWCB[4] << 8) | + (psSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + ui16NumBlocks = (psSCSICBW->CBWCB[7] << 8) | psSCSICBW->CBWCB[8]; + + psInst->ui32BytesToTransfer = g_pui32BlockSize * ui16NumBlocks; + + // + // Start sending logical blocks, these are always multiples of + // g_pui32BlockSize bytes. + // + psInst->ui8SCSIState = STATE_SCSI_RECEIVE_BLOCKS; + + // + // Configure and enable DMA for the OUT transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8OUTDMA, + psInst->pui32Buffer, g_pui32BlockSize); + + // + // Remember that a DMA is in progress. + // + psInst->ui32Flags |= USBD_FLAG_DMA_OUT; + + // + // Notify the application of the write event. + // + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_WRITING, 0, 0); + } + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8OUTEndpoint, + USB_EP_DEV_OUT); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Mode Sense 6 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIModeSense6(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + // + // Three extra bytes in this response. + // + g_pui8Command[0] = 3; + g_pui8Command[1] = 0; + g_pui8Command[2] = 0; + g_pui8Command[3] = 0; + + // + // Manually send the response back to the host. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 4); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, + USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = psSCSICBW->dCBWDataTransferLength - 4; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to send out the response data based on the current +// status of the mass storage class. +// +//***************************************************************************** +static void +USBDSCSISendStatus(tUSBDMSCDevice *psMSCDevice) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Respond with the requested status. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, + (uint8_t *)&g_sSCSICSW, 13); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN); + + // + // Move the state to status sent so that the next interrupt will move the + // statue to idle. + // + psInst->ui8SCSIState = STATE_SCSI_SENT_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the Prevent/Allow Medium Removal command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIPreventAllowMediumRemoval(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + // + // See if this was an allow or prevent removal request. + // + if((psSCSICBW->CBWCB[4] & SCSI_PE_MEDRMV_M) == SCSI_PE_MEDRMV_ALLOW) + { + psInst->ui32Flags |= USBD_FLAG_ALLOW_REMOVAL; + } + else + { + psInst->ui32Flags &= ~USBD_FLAG_ALLOW_REMOVAL; + } + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Start/Stop Unit command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIStartStopUnit(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + switch(psSCSICBW->CBWCB[4] & (SCSI_SS_UNIT_START | SCSI_SS_UNIT_LOEJ)) + { + case 0: + { + // + // Media state is now stopped but not ejected. + // + psInst->iMediaStatus = eUSBDMSCMediaStopped; + + g_sSCSICSW.bCSWStatus = 0; + + break; + } + case SCSI_SS_UNIT_START: + { + // + // Return to Media present. + // + psInst->iMediaStatus = eUSBDMSCMediaPresent; + + g_sSCSICSW.bCSWStatus = 0; + + break; + } + case SCSI_SS_UNIT_LOEJ: + { + // + // Only allow eject if the Prevent/Allow Medium Removal has + // been sent and enabled medium removal. + // + if(psInst->ui32Flags & USBD_FLAG_ALLOW_REMOVAL) + { + psInst->iMediaStatus = eUSBDMSCMediaNotPresent; + psMSCDevice->sMediaFunctions.pfnClose(0); + psMSCDevice->sPrivateData.pvMedia = 0; + g_sSCSICSW.bCSWStatus = 0; + } + else + { + g_sSCSICSW.bCSWStatus = 1; + } + + break; + } + case SCSI_SS_UNIT_START | SCSI_SS_UNIT_LOEJ: + { + // + // Since there was no media, check for media here. + // + psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(0); + + // + // If it is still not present then fail this command. + // + if(psInst->pvMedia != 0) + { + g_sSCSICSW.bCSWStatus = 0; + } + else + { + g_sSCSICSW.bCSWStatus = 1; + } + break; + } + default: + { + break; + } + } + + // + // There is no further data to send. + // + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle all SCSI commands. +// +//***************************************************************************** +uint32_t +USBDSCSICommand(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + uint32_t ui32RetCode, ui32TransferLength; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Initialize the return code. + // + ui32RetCode = 1; + + // + // Save the transfer length because it may be overwritten by some calls. + // + ui32TransferLength = psSCSICBW->dCBWDataTransferLength; + + switch(psSCSICBW->CBWCB[0]) + { + // + // Respond to the SCSI Inquiry command. + // + case SCSI_INQUIRY_CMD: + { + USBDSCSIInquiry(psMSCDevice); + + break; + } + + // + // Respond to the test unit ready command. + // + case SCSI_TEST_UNIT_READY: + { + g_sSCSICSW.dCSWDataResidue = 0; + + if(psInst->pvMedia != 0) + { + // + // Set the status to success for now, this could be different + // if there is no media present. + // + g_sSCSICSW.bCSWStatus = 0; + } + else if(psInst->iMediaStatus == eUSBDMSCMediaNotPresent) + { + // + // Set the status to success for now, this could be different + // if there is no media present. + // + g_sSCSICSW.bCSWStatus = 1; + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + else + { + // + // Since there was no media, check for media here. + // + psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(0); + + // + // If it is still not present then fail this command. + // + if(psInst->pvMedia != 0) + { + g_sSCSICSW.bCSWStatus = 0; + } + else + { + g_sSCSICSW.bCSWStatus = 1; + } + } + break; + } + + // + // Handle the Read Capacities command. + // + case SCSI_READ_CAPACITIES: + { + USBDSCSIReadCapacities(psMSCDevice); + + break; + } + + // + // Handle the Read Capacity command. + // + case SCSI_READ_CAPACITY: + { + USBDSCSIReadCapacity(psMSCDevice); + + break; + } + + // + // Handle the Request Sense command. + // + case SCSI_REQUEST_SENSE: + { + USBDSCSIRequestSense(psMSCDevice); + + break; + } + + // + // Handle the Read 10 command. + // + case SCSI_READ_10: + { + USBDSCSIRead10(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Write 10 command. + // + case SCSI_WRITE_10: + { + USBDSCSIWrite10(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Mode Sense 6 command. + // + case SCSI_MODE_SENSE_6: + { + USBDSCSIModeSense6(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Prevent/Allow Medium Removal command. + // + case SCSI_MEDIUM_REMOVAL: + { + USBDSCSIPreventAllowMediumRemoval(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Prevent/Allow Medium Removal command. + // + case SCSI_START_STOP_UNIT: + { + USBDSCSIStartStopUnit(psMSCDevice, psSCSICBW); + break; + } + + default: + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = psSCSICBW->dCBWDataTransferLength; + + // + // If there is data then there is more work to do. + // + if(psSCSICBW->dCBWDataTransferLength != 0) + { + if(psSCSICBW->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + } + else + { + // + // Stall the OUT endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8OUTEndpoint, + USB_EP_DEV_OUT); + + } + + // + // Go back to the idle state and wait for the host to clear + // the stall later. + // + psInst->ui8SCSIState = STATE_SCSI_IDLE; + } + + // + // Set the sense codes. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_ILGL_RQST; + psInst->ui16AddSenseCode = SCSI_RS_PV_INVALID; + + break; + } + } + + // + // If there is no data then send out the current status. + // + if(ui32TransferLength == 0) + { + USBDSCSISendStatus(psMSCDevice); + } + return(ui32RetCode); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdmsc.h b/usblib/device/usbdmsc.h new file mode 100644 index 0000000..9f33e4c --- /dev/null +++ b/usblib/device/usbdmsc.h @@ -0,0 +1,420 @@ +//***************************************************************************** +// +// usbdmsc.h - USB mass storage device class driver. +// +// Copyright (c) 2009-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. +// +//***************************************************************************** + +#ifndef __USBDMSC_H__ +#define __USBDMSC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup msc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Media Access functions. +// +//***************************************************************************** +typedef struct +{ + // + //! This function is used to initialize and open the physical drive number + //! associated with the parameter \e ui32Drive. The function returns + //! zero if the drive could not be opened for some reason. In the case of + //! removable device like an SD card this function must return zero if + //! the SD card is not present. + //! The function returns a pointer to data that should be passed to other + //! APIs or returns 0 if no drive was found. + // + void *(*pfnOpen)(uint32_t ui32Drive); + + //************************************************************************* + // + //! This function closes the drive number in use by the mass storage class + //! device. The \e pvDrive is the pointer that was returned from a call to + //! \e pfnOpen. This function is used to close the physical drive + //! number associated with the parameter \e pvDrive. This function + //! returns 0 if the drive was closed successfully and any other value + //! indicates a failure. + // + //************************************************************************* + void (*pfnClose)(void *pvDrive); + + //************************************************************************* + // + //! This function reads a block of data from a device opened by the + //! \e pfnOpen call. The \e pvDrive parameter is the pointer that was + //! returned from the original call to \e pfnOpen. The \e pui8Data + //! parameter is the buffer that data will be written into. The data area + //! pointed to by \e pui8Data must be at least \e ui32NumBlocks * Block + //! Size bytes to prevent overwriting data. The \e ui32Sector is the block + //! address to read and \e ui32NumBlocks is the number of blocks to read. + //! This function returns the number of bytes that were read from the + //! and placed into the \e pui8Data buffer.. + // + //************************************************************************* + uint32_t (*pfnBlockRead)(void *pvDrive, uint8_t *pui8Data, + uint32_t ui32Sector, uint32_t ui32NumBlocks); + + //************************************************************************* + // + //! This function is use to write blocks to a physical device from the + //! buffer pointed to by the \e pui8Data buffer. The \e pvDrive parameter + //! is the pointer that was returned from the original call to \e pfnOpen. + //! The \e pui8Data is the pointer to the data to write to the storage + //! device and \e ui32NumBlocks is the number of blocks to write. The + //! \e ui32Sector parameter is the sector number used to write the block. + //! If the number of blocks is greater than one then the block address + //! increments and writes to the next block until + //! \e ui32NumBlocks * Block Size bytes are written. This function returns + //! the number of bytes that were written to the device. + // + //************************************************************************* + uint32_t (*pfnBlockWrite)(void *pvDrive, uint8_t *pui8Data, + uint32_t ui32Sector, uint32_t ui32NumBlocks); + + //************************************************************************* + // + //! This function returns the total number of blocks on a physical device + //! based on the \e pvDrive parameter. The \e pvDrive parameter + //! is the pointer that was returned from the original call to \e pfnOpen. + // + //************************************************************************* + uint32_t (*pfnNumBlocks)(void *pvDrive); + + //************************************************************************* + // + //! This function returns the block size for a physical device based on the + //! \e pvDrive parameter. The \e pvDrive parameter is the pointer + //! that was returned from the original call to \e pfnOpen. + // + //************************************************************************* + uint32_t (*pfnBlockSize)(void *pvDrive); + +} +tMSCDMedia; + +//***************************************************************************** +// +// These defines control the default sizes of USB transfers for data and +// commands. +// +//***************************************************************************** +#define DEVICE_BLOCK_SIZE 512 + +//***************************************************************************** +// +// USBDMSCMediaChange() tUSBDMSCMediaStatus values. +// +//***************************************************************************** +typedef enum +{ + eUSBDMSCMediaPresent, + eUSBDMSCMediaNotPresent, + eUSBDMSCMediaStopped, + eUSBDMSCMediaUnknown +} +tUSBDMSCMediaStatus; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// mass storage class. The memory for this structure is in the the +// sPrivateData field in the tUSBDMSCDevice structure passed on +// USBDMSCInit() and should not be modified by any code outside of the mass +// storage device code. +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // These three values are used to return the current sense data for an + // instance of the mass storage class. + // + uint8_t ui8ErrorCode; + uint8_t ui8SenseKey; + uint16_t ui16AddSenseCode; + + // + // The pointer to the instance returned from the Open call to the media. + // + void *pvMedia; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // Holds the flag settings for this instance. + // + uint32_t ui32Flags; + + // + // Holds the current media status. + // + tUSBDMSCMediaStatus iMediaStatus; + + // + // MSC block buffer. + // + uint32_t pui32Buffer[0x1000>>2]; + + // + // Current number of bytes to transfer. + // + uint32_t ui32BytesToTransfer; + + // + // The LBA for the current transfer. + // + uint32_t ui32CurrentLBA; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8INEndpoint; + + // + // The IN DMA channel. + // + uint8_t ui8INDMA; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8OUTEndpoint; + + // + // The OUT DMA channel. + // + uint8_t ui8OUTDMA; + + // + // The bulk class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; + + // + // Active SCSI state. + // + uint8_t ui8SCSIState; + + // + // A copy of the DMA instance data used with calls to USBLibDMA functions. + // + tUSBDMAInstance *psDMAInstance; +} +tMSCInstance; + +//***************************************************************************** +// +// This is the size of the g_pui8MSCInterface array in bytes. +// +//***************************************************************************** +#define MSCINTERFACE_SIZE (23) + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Audio Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +// +//***************************************************************************** +#define COMPOSITE_DMSC_SIZE (MSCINTERFACE_SIZE) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the mass storage device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! 8 byte vendor string. + // + const uint8_t pui8Vendor[8]; + + // + //! 16 byte vendor string. + // + const uint8_t pui8Product[16]; + + // + //! 4 byte vendor string. + // + const uint8_t pui8Version[4]; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wakeup. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), MSC + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + // + const uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the \e ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + const uint32_t ui32NumStringDescriptors; + + // + //! This structure holds the access functions for the media used by this + //! instance of the mass storage class device. All of the functions in + //! this structure are required to be filled out with valid functions. + // + const tMSCDMedia sMediaFunctions; + + // + //! This is the callback function for various events that occur during + //! mass storage class operation. + // + const tUSBCallback pfnEventCallback; + + // + //! The private instance data for this device. This memory + //! must remain accessible for as long as the MSC device is in use and + //! must not be modified by any code outside the MSC class driver. + // + tMSCInstance sPrivateData; +} +tUSBDMSCDevice; + +//***************************************************************************** +// +// MSC-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the host has completed other operations and is +//! no longer accessing the device. +// +//***************************************************************************** +#define USBD_MSC_EVENT_IDLE (USBD_MSC_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This event indicates that the host is reading the storage media. +// +//***************************************************************************** +#define USBD_MSC_EVENT_READING (USBD_MSC_EVENT_BASE + 1) + +//***************************************************************************** +// +//! This event indicates that the host is writing to the storage media. +// +//***************************************************************************** +#define USBD_MSC_EVENT_WRITING (USBD_MSC_EVENT_BASE + 2) + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDMSCInit(uint32_t ui32Index, + tUSBDMSCDevice *psMSCDevice); +extern void *USBDMSCCompositeInit(uint32_t ui32Index, + tUSBDMSCDevice *psMSCDevice, + tCompositeEntry *psCompEntry); +extern void USBDMSCTerm(void *pvInstance); +extern void USBDMSCMediaChange(void *pvInstance, + tUSBDMSCMediaStatus eMediaStatus); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usblib/ewarm/Exe/usblib.a b/usblib/ewarm/Exe/usblib.a new file mode 100644 index 0000000..ac41998 Binary files /dev/null and b/usblib/ewarm/Exe/usblib.a differ diff --git a/usblib/gcc/libusb.a b/usblib/gcc/libusb.a new file mode 100644 index 0000000..8b25639 Binary files /dev/null and b/usblib/gcc/libusb.a differ diff --git a/usblib/host/usbhaudio.c b/usblib/host/usbhaudio.c new file mode 100644 index 0000000..129130c --- /dev/null +++ b/usblib/host/usbhaudio.c @@ -0,0 +1,1557 @@ +//***************************************************************************** +// +// usbhaudio.c - USB host audio driver. +// +// Copyright (c) 2010-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/usbaudio.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhaudio.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are used with the USBHostAudioFormatSet() +// USBHostAudioFormatGet() to parse out interface number and alternate +// setting number for an interface. +// +//***************************************************************************** +#define INTERFACE_NUM_M 0x000000FF +#define INTERFACE_ALTSETTING_M 0x0000FF00 +#define INTERFACE_ALTSETTING_S 8 + +//***************************************************************************** +// +// Used to indicate an invalid interface descriptor number. +// +//***************************************************************************** +#define INVALID_INTERFACE 0xffffffff + +//***************************************************************************** +// +// Forward declarations for the driver open and close calls. +// +//***************************************************************************** +static void *USBAudioOpen(tUSBHostDevice *psDevice); +static void USBAudioClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB host audio driver. +// +//***************************************************************************** +struct tUSBHostAudioInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the call back. + // + tUSBHostAudioCallback pfnCallback; + + // + // This is the control interface. + // + uint8_t ui8IControl; + + // + // This is the output streaming interface. + // + uint8_t ui8OutInterface; + + // + // This is the currently selected active output interface used with + // ui8OutInterface interface. + // + uint8_t ui8OutAltSetting; + + // + // This is the streaming interface. + // + uint8_t ui8InInterface; + + // + // This is the currently selected active input interface used with + // ui8InInterface interface. + // + uint8_t ui8InAltSetting; + + // + // The Isochronous endpoint addresses. + // + uint8_t ui8IsochInAddress; + uint8_t ui8IsochOutAddress; + + tACInputTerminal *psInTerminal; + tACOutputTerminal *psOutTerminal; + + // + // Holds the identifier for the Feature Unit for controlling volume. + // + uint8_t ui8VolumeID; + + tACFeatureUnit *psFeatureUnit; + + // + // Holds what types of controls are enabled on the device. + // + uint16_t pui16Controls[3]; + + // + // Isochronous IN pipe. + // + uint32_t ui32IsochInPipe; + uint16_t ui16PipeSizeIn; + tUSBHostAudioCallback pfnInCallback; + void *pvInBuffer; + + // + // Isochronous OUT pipe. + // + uint32_t ui32IsochOutPipe; + uint16_t ui16PipeSizeOut; + tUSBHostAudioCallback pfnOutCallback; + void *pvOutBuffer; + + // + // State flags for this audio instance. + // + uint32_t ui32Flags; +}; + +//***************************************************************************** +// +// The internal flags for an audio interface. +// +//***************************************************************************** +#define AUDIO_FLAG_OUT_ACTIVE 1 // Audio output is active. +#define AUDIO_FLAG_IN_ACTIVE 2 // Audio input is active. + +//***************************************************************************** +// +// The USB Host audio instance. +// +//***************************************************************************** +static tUSBHostAudioInstance g_sAudioDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Audio Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHostAudioClassDriver = +{ + USB_CLASS_AUDIO, + USBAudioOpen, + USBAudioClose, + 0 +}; + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB IN pipe. +// +//***************************************************************************** +static void +PipeCallbackIN(uint32_t ui32Pipe, uint32_t ui32Event) +{ + // + // Only handle the data available callback and pass it on to the + // application. + // + if(ui32Event == USB_EVENT_RX_AVAILABLE) + { + if(g_sAudioDevice.pfnInCallback) + { + g_sAudioDevice.pfnInCallback(&g_sAudioDevice, + USB_EVENT_RX_AVAILABLE, + USBHCDPipeTransferSizeGet(ui32Pipe), + g_sAudioDevice.pvInBuffer); + } + } +} + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB OUT pipe. +// +//***************************************************************************** +static void +PipeCallbackOUT(uint32_t ui32Pipe, uint32_t ui32Event) +{ + // + // Only handle the transmit complete callback and pass it on to the + // application. + // + if(ui32Event == USB_EVENT_TX_COMPLETE) + { + if(g_sAudioDevice.pfnOutCallback) + { + g_sAudioDevice.pfnOutCallback(&g_sAudioDevice, + USB_EVENT_TX_COMPLETE, 0, + g_sAudioDevice.pvOutBuffer); + } + } +} + +//***************************************************************************** +// +// Finds a given terminal and type in an audio configuration descriptor. +// +//***************************************************************************** +static tDescriptorHeader * +AudioTerminalGet(tConfigDescriptor *psConfigDesc, uint32_t ui32Terminal, + uint32_t ui32TerminalType) +{ + tACOutputTerminal *psOutput; + tDescriptorHeader *psHeader; + int32_t i32BytesRemaining; + + psHeader = (tDescriptorHeader *)psConfigDesc; + i32BytesRemaining = psConfigDesc->wTotalLength; + + while(i32BytesRemaining > 0) + { + // + // Output and input terminals are the same past the bDescriptorSubtype + // and wTerminalType that are being searched for. + // + psOutput = (tACOutputTerminal *)psHeader; + + // + // Only CS_INTERFACE descriptors can be a terminal. + // + if((psHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE) && + (ui32Terminal == psOutput->bDescriptorSubtype)) + { + if((psOutput->bDescriptorSubtype == USB_AI_OUTPUT_TERMINAL) || + (psOutput->bDescriptorSubtype == USB_AI_INPUT_TERMINAL)) + + { + // + // If this was the terminal type that was requested, the + // return it. + // + if(psOutput->wTerminalType == ui32TerminalType) + { + return(psHeader); + } + } + else if(psOutput->bDescriptorSubtype == USB_AI_FEATURE_UNIT) + { + return(psHeader); + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32BytesRemaining -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader *)((uint32_t)psHeader + + psHeader->bLength); + } + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +// This function returns the interface number for the control interface +// in the structure passed in the psConfigDesc. +// +// \param psConfigDescriptor is a pointer to the memory containing a valid +// configuration descriptor for a device. +// +// This function searches a configuration descriptor for a control interface +// descriptor. The function only search for the first descriptor and then +// returns when it finds one. +// +// \return The first control interface descriptor number for an audio device +// or INVALID_INTERFACE if no control interface descriptor was found. +// +//***************************************************************************** +static uint32_t +AudioControlGet(tConfigDescriptor *psConfigDesc) +{ + tDescriptorHeader *psHeader; + tInterfaceDescriptor *psInterface; + uint32_t ui32Interface; + int32_t i32Bytes; + + psHeader = (tDescriptorHeader *)psConfigDesc; + i32Bytes = psConfigDesc->wTotalLength; + + // + // Initialize the interface number to an invalid value. + // + ui32Interface = INVALID_INTERFACE; + + // + // Search the whole configuration descriptor. + // + while(i32Bytes > 0) + { + // + // Find an interface descriptor and see if it is a control interface. + // + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // If this is the control interface then return the value to the + // caller. + // + if(psInterface->bInterfaceSubClass == USB_ASC_AUDIO_CONTROL) + { + ui32Interface = psInterface->bInterfaceNumber; + + break; + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32Bytes -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader*)((uint32_t)psHeader + + psHeader->bLength); + } + return(ui32Interface); +} + +//***************************************************************************** +// +// If it exists, finds the correct audio interface for a given audio format. +// +//***************************************************************************** +static uint32_t +AudioGetInterface(tUSBHostAudioInstance *psAudioDevice, uint16_t ui16Format, + uint32_t ui32SampleRate, uint32_t ui32Bytes, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + tDescriptorHeader *psHeader; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *pINEndpoint, *pOUTEndpoint; + tACHeader *pACHeader; + tACGeneral *pGeneral; + tASFormat *pFormat; + tEndpointDescriptor *pEndpoint; + uint8_t *pui8Value; + uint32_t ui32Value; + int32_t i32Bytes, i32Idx; + + // + // Initialize the Interface pointer to null. + // + psInterface = 0; + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // Start at the top of the configuration descriptor. + // + psHeader = (tDescriptorHeader *)psAudioDevice->psDevice->psConfigDescriptor; + + i32Bytes = psAudioDevice->psDevice->psConfigDescriptor->wTotalLength; + + while(i32Bytes > 0) + { + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + // + // If a new interface was found and the last one satisfied all + // requirements then a valid interface was found so break out. + // + if(psInterface) + { + break; + } + + // + // Get the new interface pointer. + // + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // Reset the endpoints on finding a new interface descriptor. + // + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // If this is not a valid audio streaming interface then reset + // the interface pointer to null. + // + if((psInterface->bNumEndpoints == 0) || + (psInterface->bInterfaceClass != USB_CLASS_AUDIO) || + (psInterface->bInterfaceSubClass != USB_ASC_AUDIO_STREAMING)) + { + psInterface = 0; + } + } + if((psInterface) && + (psHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE)) + { + pACHeader = (tACHeader *)psHeader; + + // + // If this is a General descriptor the check if the format matches. + // + if(pACHeader->bDescriptorSubtype == USB_AS_GENERAL) + { + // + // Just save the pointer to the format descriptor. + // + pGeneral = (tACGeneral *)psHeader; + + // + // If this interface has the wrong format then set it to null + // so that the rest of this interface is ignored. + // + if(pGeneral->wFormatTag != ui16Format) + { + psInterface = 0; + } + } + else if(pACHeader->bDescriptorSubtype == USB_AS_FORMAT_TYPE) + { + pFormat = (tASFormat *)psHeader; + + // + // If the number of bytes per sample and number of channels do + // not match then reset the interface pointer so that the rest + // of this interface is ignored. + // + if((pFormat->bNrChannels != ui32Channels) || + (pFormat->bSubFrameSize != ui32Bytes)) + { + psInterface = 0; + } + else + { + pui8Value = &pFormat->tSamFreq; + + // + // Attempt to find the sample rate in the sample rate + // table for this interface. + // + for(i32Idx = 0; i32Idx < pFormat->bSamFreqType; i32Idx++) + { + ui32Value = (*((uint32_t *)&pui8Value[i32Idx * 3]) & + 0xffffff); + + if(ui32Value == ui32SampleRate) + { + break; + } + } + + // + // If the sample rate was not found then set the interface + // pointer to null so that the rest of this interface is + // ignored. + // + if(i32Idx == pFormat->bSamFreqType) + { + psInterface = 0; + } + } + } + } + else if((psInterface) && + (psHeader->bDescriptorType == USB_DTYPE_ENDPOINT)) + { + pEndpoint = (tEndpointDescriptor *)psHeader; + + // + // See what direction is being requested. + // + if(ui32Flags & USBH_AUDIO_FORMAT_IN) + { + // + // If this is an input endpoint and is just a feed back input + // then ignore it. + // + if(pEndpoint->bEndpointAddress & USB_EP_DESC_IN) + { + if((pEndpoint->bmAttributes & USB_EP_ATTR_USAGE_M) + == USB_EP_ATTR_USAGE_FEEDBACK) + { + psInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint + // + pINEndpoint = pEndpoint; + } + } + } + else + { + // + // If this is an output endpoint and is just a feed back input + // then ignore it. + // + if((pEndpoint->bEndpointAddress & USB_EP_DESC_IN) == 0) + { + if((pEndpoint->bmAttributes & USB_EP_ATTR_USAGE_M) + == USB_EP_ATTR_USAGE_FEEDBACK) + { + psInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint; + // + pOUTEndpoint = pEndpoint; + } + } + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32Bytes -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader*)((uint32_t)psHeader + + psHeader->bLength); + } + + // + // If there is still a valid interface then return the values. + // + if(psInterface) + { + // + // Check a valid IN endpoint descriptor. + // + if(pINEndpoint) + { + // + // Save the endpoint address. + // + g_sAudioDevice.ui8IsochInAddress = pINEndpoint->bEndpointAddress & + USB_EP_DESC_NUM_M; + + // + // If there is no current pipe then just allocate a new one with + // the settings for this interface. + // + if(g_sAudioDevice.ui32IsochInPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + } + else if(g_sAudioDevice.ui16PipeSizeIn < pINEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_sAudioDevice.ui32IsochInPipe); + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_sAudioDevice.ui16PipeSizeIn = pINEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous IN end point. + // + USBHCDPipeConfig(g_sAudioDevice.ui32IsochInPipe, + pINEndpoint->wMaxPacketSize, + 0, + g_sAudioDevice.ui8IsochInAddress); + } + + // + // Check a valid OUT endpoint descriptor. + // + if(pOUTEndpoint) + { + // + // Save the endpoint address. + // + g_sAudioDevice.ui8IsochOutAddress = + pOUTEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + + // + // If there is no current pipe then just allocate a new one with + // the settings for this interface. + // + if(g_sAudioDevice.ui32IsochOutPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + } + else if(g_sAudioDevice.ui16PipeSizeOut < + pOUTEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_sAudioDevice.ui32IsochOutPipe); + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_sAudioDevice.ui16PipeSizeOut = pOUTEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous OUT end point. + // + USBHCDPipeConfig(g_sAudioDevice.ui32IsochOutPipe, + pOUTEndpoint->wMaxPacketSize, 0, + g_sAudioDevice.ui8IsochOutAddress); + } + + return(psInterface->bInterfaceNumber | + (psInterface->bAlternateSetting << INTERFACE_ALTSETTING_S)); + } + return(INVALID_INTERFACE); +} + +//***************************************************************************** +// +// This function is used to open an instance of the USB host audio driver. +// +// \param psDevice is a pointer to the device information structure. +// +// This function attempts to open an instance of the USB host audio driver +// based on the information contained in the psDevice structure. This call +// fails if there are not sufficient resources to open the device. The +// function returns a value that should be passed back into USBHostAudioClose() +// when the driver is no longer needed. +// +// \return The function returns a pointer to a USB host audio driver +// instance. +// +//***************************************************************************** +static void * +USBAudioOpen(tUSBHostDevice *psDevice) +{ + uint32_t ui32Temp; + tConfigDescriptor *psConfigDesc; + + // + // Don't allow the device to be opened without closing first. + // + if(g_sAudioDevice.psDevice) + { + return(0); + } + + // + // Save the Host device pointer. + // + g_sAudioDevice.psDevice = psDevice; + + // + // Save a shorter name for the configuration descriptor. + // + psConfigDesc = psDevice->psConfigDescriptor; + + // + // Find the input terminal. + // + g_sAudioDevice.psInTerminal = + (tACInputTerminal *)AudioTerminalGet(psConfigDesc, + USB_AI_INPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the output terminal. + // + g_sAudioDevice.psOutTerminal = + (tACOutputTerminal *)AudioTerminalGet(psConfigDesc, + USB_AI_OUTPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the feature unit. + g_sAudioDevice.psFeatureUnit = + (tACFeatureUnit *)AudioTerminalGet(psConfigDesc, + USB_AI_FEATURE_UNIT, 0); + + // + // Need some kind of terminal to send or receive audio from. + // + if((g_sAudioDevice.psOutTerminal == 0) && + (g_sAudioDevice.psInTerminal == 0)) + { + return(0); + } + + // + // Find the Audio control interface. + // + ui32Temp = AudioControlGet(psConfigDesc); + + if(ui32Temp == INVALID_INTERFACE) + { + return(0); + } + + // + // Save the control interface index and increment the number + // of interfaces that have been found. + // + g_sAudioDevice.ui8IControl = (uint8_t)ui32Temp; + + // + // If the call back exists, call it with an Open event. + // + if(g_sAudioDevice.pfnCallback != 0) + { + g_sAudioDevice.pfnCallback(&g_sAudioDevice, + USBH_AUDIO_EVENT_OPEN, 0, 0); + } + + // + // If a feature unit was found, save the ID + // + if(g_sAudioDevice.psFeatureUnit != 0) + { + g_sAudioDevice.ui8VolumeID = g_sAudioDevice.psFeatureUnit->bUnitID; + } + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, 256, PipeCallbackIN); + g_sAudioDevice.ui16PipeSizeIn = 256; + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, 256, PipeCallbackOUT); + g_sAudioDevice.ui16PipeSizeOut = 256; + + // + // Clear the flags. + // + g_sAudioDevice.ui32Flags = 0; + + // + // Return the only instance of this device. + // + return(&g_sAudioDevice); +} + +//***************************************************************************** +// +// This function is used to release an instance of the USB host audio driver. +// +// \param pvAudioDevice is an instance pointer that needs to be released. +// +// This function frees up any resources in use by the USB host audio +// driver instance that is passed in. The \e pvAudioDevice pointer should be a +// valid value that was returned from a call to USBHostAudioOpen(). +// +// \return None. +// +//***************************************************************************** +static void +USBAudioClose(void *pvAudioDevice) +{ + tUSBHostAudioInstance *psAudioDevice; + + psAudioDevice = (tUSBHostAudioInstance *)pvAudioDevice; + + // + // Do nothing if there is not a driver open. + // + if(psAudioDevice->psDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + psAudioDevice->psDevice = 0; + + // + // Free the Isochronous IN pipe. + // + if(psAudioDevice->ui32IsochInPipe != 0) + { + USBHCDPipeFree(psAudioDevice->ui32IsochInPipe); + } + + // + // Free the Isochronous OUT pipe. + // + if(psAudioDevice->ui32IsochOutPipe != 0) + { + USBHCDPipeFree(psAudioDevice->ui32IsochOutPipe); + } + + // + // If the call back exists then call it. + // + if(psAudioDevice->pfnCallback != 0) + { + psAudioDevice->pfnCallback(psAudioDevice, USBH_AUDIO_EVENT_CLOSE, 0, + 0); + } +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the host audio class driver. +//! +//! \param ui32Index is the audio device to open (currently only 0 is +//! supported). +//! \param pfnCallback is the driver call back for host audio events. +//! +//! This function is called to open an instance of a host audio device and +//! should provide a valid callback function for host audio events in the +//! \e pfnCallback parameter. This function must be called before the USB +//! host code can successfully enumerate an audio device. +//! +//! \return This function returns the driver instance to use for the other +//! host audio functions. If there is no instance available at the time of +//! this call, this function returns zero. +// +//***************************************************************************** +tUSBHostAudioInstance * +USBHostAudioOpen(uint32_t ui32Index, tUSBHostAudioCallback pfnCallback) +{ + // + // Only one audio device is supported at this time and on one instance + // is supported so if there is already a call back then fail. + // + if((ui32Index != 0) || (g_sAudioDevice.pfnCallback)) + { + return(0); + } + + // + // Save the call back. + // + g_sAudioDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return(&g_sAudioDevice); +} + +//***************************************************************************** +// +//! This function should be called to release an audio device instance. +//! +//! \param psAudioInstance is the device instance that is to be released. +//! +//! This function is called when a host audio device needs to be released. +//! This could be in preparation for shutdown or a switch to USB device mode, +//! for example. Following this call, the audio device is available and can +//! be opened again using a call to USBHostAudioOpen(). After calling this +//! function, the host audio driver will no longer provide any callbacks or +//! accept calls to other audio driver APIs. +//! +//! \return None. +// +//***************************************************************************** +void +USBHostAudioClose(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Close the audio device. + // + USBAudioClose(psAudioInstance); + + // + // Clear the call back indicating that the device is now closed. + // + psAudioInstance->pfnCallback = 0; +} + +//***************************************************************************** +// +// This function is used to request settings from a given audio interface. +// +// \param psAudioDevice is the audio device instance to access. +// \param ui32Interface is the interface to access. +// \param ui32Channel is the channel number to access. +// \param ui32Request is the audio device request. +// +// This function is used to get volume control parameters from a given +// interface and on a given channel. The \e ui32Interface is the interface to +// make the request specified by \e ui32Channel and \e ui32Request. The +// \e ui32Request parameter must be one of the USB_AC_GET_* values. +// +// \return This function returns the requested value. +// +//***************************************************************************** +static uint32_t +VolumeSettingGet(tUSBHostAudioInstance *psAudioDevice, uint32_t ui32Interface, + uint32_t ui32Channel, uint32_t ui32Request) +{ + uint32_t ui32Value; + tUSBRequest sSetupPacket; + + ui32Value = 0; + + // + // This is a Class specific Interface IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = (ui32Request & 0xff); + + // + // Request for a string descriptor. + // + sSetupPacket.wValue = VOLUME_CONTROL | (ui32Channel & 0xff); + + // + // Set the language ID. + // + sSetupPacket.wIndex = (psAudioDevice->ui8VolumeID << 8) | + (ui32Interface & 0xff); + + // + // Only request the space available. + // + sSetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psAudioDevice->psDevice, + (uint8_t *)&ui32Value, 4, + psAudioDevice->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Value); +} + +//***************************************************************************** +// +//! This function is used to get the current volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the current +//! volume setting. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the current volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the current volume setting for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_CUR)); +} + +//***************************************************************************** +// +//! This function is used to get the maximum volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the maximum +//! volume control value. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the maximum volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the maximum volume setting for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeMaxGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_MAX)); +} + +//***************************************************************************** +// +//! This function is used to get the minimum volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the minimum +//! volume control value. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the minimum volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the minimum volume setting for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeMinGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_MIN)); +} + +//***************************************************************************** +// +//! This function is used to get the volume control resolution for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the resolution +//! for the volume control. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the volume control resolution for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the volume control resolution for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeResGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_RES)); +} + +//***************************************************************************** +// +//! This function is used to set the current volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to set the current +//! volume setting. +//! \param ui32Channel is the 0 based channel number to query. +//! \param ui32Value is the value to write to the USB audio device. +//! +//! The function is used to set the current volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return None. +// +//***************************************************************************** +void +USBHostAudioVolumeSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel, + uint32_t ui32Value) +{ + tUSBRequest sSetupPacket; + + // + // This is a Class specific Interface OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request is to set the current value. + // + sSetupPacket.bRequest = USB_AC_SET_CUR; + + // + // Request the volume control. + // + sSetupPacket.wValue = VOLUME_CONTROL | (ui32Channel & 0xff); + + // + // Set Volume control ID and interface to 0. + // + sSetupPacket.wIndex = psAudioInstance->ui8VolumeID << 8; + + // + // Only request the space available. + // + sSetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psAudioInstance->psDevice, + (uint8_t *)&ui32Value, 2, + psAudioInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +//! This function is called to determine if an audio format is supported by the +//! connected USB Audio device. +//! +//! \param psAudioInstance is the device instance for this call. +//! \param ui32SampleRate is the sample rate of the audio stream. +//! \param ui32Bits is the number of bits per sample in the audio stream. +//! \param ui32Channels is the number of channels in the audio stream. +//! \param ui32Flags is a set of flags to determine what type of interface to +//! retrieve. +//! +//! This function is called when an application needs to determine which audio +//! formats are supported by a USB audio device that has been connected. The +//! \e psAudioInstance value that is used with this call is the value that was +//! returned from the USBHostAudioOpen() function. This call checks the +//! USB audio device to determine if it can support the values provided in the +//! \e ui32SampleRate, \e ui32Bits, and \e ui32Channels values. The +//! \e ui32Flags currently only supports either the \b USBH_AUDIO_FORMAT_IN or +//! \b USBH_AUDIO_FORMAT_OUT values that indicates if a request is for an +//! audio input and an audio output. If the format is supported this +//! function returns zero, and this function returns a non-zero value if the +//! format is not supported. This function does not set the current output or +//! input format. +//! +//! \return A value of zero indicates the supplied format is supported and +//! a non-zero value indicates that the format is not supported. +// +//***************************************************************************** +uint32_t +USBHostAudioFormatGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, uint32_t ui32Bits, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + // + // Look for the requested format. + // + if(AudioGetInterface(psAudioInstance, USB_ADF_PCM, ui32SampleRate, + ui32Bits >> 3, ui32Channels, ui32Flags) != + INVALID_INTERFACE) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is called to set the current sample rate on an audio +//! interface. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param ui32SampleRate is the sample rate in Hz. +//! \param ui32Bits is the number of bits per sample. +//! \param ui32Channels is then number of audio channels. +//! \param ui32Flags is a set of flags that determine the access type. +//! +//! This function is called when to set the current audio output or input +//! format for a USB audio device. The \e psAudioInstance value that is used +//! with this call is the value that was returned from the USBHostAudioOpen() +//! function. The application can use this call to insure that the audio +//! format is supported and set the format at the same time. If the +//! application is just checking for supported rates, then it should call the +//! USBHostAudioFormatGet(). +//! +//! \note This function must be called before attempting to send or receive +//! audio with the USBHostAudioPlay() or USBHostAudioRecord() functions. +//! +//! \return A non-zero value indicates the supplied format is not supported and +//! a zero value indicates that the format was supported and has been +//! configured. +// +//***************************************************************************** +uint32_t +USBHostAudioFormatSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, uint32_t ui32Bits, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + uint32_t ui32Interface; + + // + // Look for the requested format. + // + ui32Interface = AudioGetInterface(psAudioInstance, USB_ADF_PCM, + ui32SampleRate, ui32Bits >> 3, + ui32Channels, ui32Flags); + + if(ui32Interface == INVALID_INTERFACE) + { + return(1); + } + + // + // Determine if this is an input or output request. + // + if(ui32Flags & USBH_AUDIO_FORMAT_IN) + { + // + // Get the active interface number and alternate setting for this + // format. + // + psAudioInstance->ui8InInterface = + (uint8_t)(ui32Interface & INTERFACE_NUM_M); + psAudioInstance->ui8InAltSetting = + (uint8_t)((ui32Interface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + else + { + // + // Get the active interface number and alternate setting for this + // format. + // + psAudioInstance->ui8OutInterface = + (uint8_t)(ui32Interface & INTERFACE_NUM_M); + psAudioInstance->ui8OutAltSetting = + (uint8_t)((ui32Interface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + return(0); +} + +//***************************************************************************** +// +//! This function is called to send an audio buffer to the USB audio device. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ui32Size is the size of the buffer in bytes. +//! \param pfnCallback is a pointer to a callback function that is called +//! when the buffer can be used again. +//! +//! This function is called when an application needs to schedule a new buffer +//! for output to the USB audio device. Since this call schedules the transfer +//! and returns immediately, the application should provide a \e pfnCallback +//! function to be notified when the buffer can be used again by the +//! application. The \e pfnCallback function provided is called with the +//! \e pvBuffer parameter set to the \e pvBuffer provided by this call, the +//! \e ui32Param can be ignored and the \e ui32Event parameter is +//! \b USB_EVENT_TX_COMPLETE. +//! +//! \return This function returns the number of bytes that were scheduled +//! to be sent. If this function returns zero then there was no USB audio +//! device present or the request could not be satisfied at this time. +// +//***************************************************************************** +int32_t +USBHostAudioPlay(tUSBHostAudioInstance *psAudioInstance, void *pvBuffer, + uint32_t ui32Size, tUSBHostAudioCallback pfnCallback) +{ + uint32_t ui32Bytes; + + // + // Make sure that there is a device present. + // + if(psAudioInstance->psDevice == 0) + { + return(0); + } + + // + // If the audio output interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_OUT_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_OUT_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (uint32_t)psAudioInstance->psDevice, + psAudioInstance->ui8OutInterface, + psAudioInstance->ui8OutAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + psAudioInstance->pfnOutCallback = pfnCallback; + psAudioInstance->pvOutBuffer = (void *)pvBuffer; + + // + // Schedule the data to be written out to the FIFO. + // + ui32Bytes = USBHCDPipeSchedule(psAudioInstance->ui32IsochOutPipe, pvBuffer, + ui32Size); + + // + // Return the number of bytes scheduled to be sent. + // + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function is called to provide an audio buffer to the USB audio device +//! for audio input. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ui32Size is the size of the buffer in bytes. +//! \param pfnCallback is a pointer to a callback function that is called +//! when the buffer has been filled. +//! +//! This function is called when an application needs to schedule a new buffer +//! for input from the USB audio device. Since this call schedules the +//! transfer and returns immediately, the application should provide a +//! \e pfnCallback function to be notified when the buffer has been filled with +//! audio data. When the \e pfnCallback function is called, the \e pvBuffer +//! parameter is set to \e pvBuffer provided in this call, the \e ui32Param is +//! the number of valid bytes in the pvBuffer and the \e ui32Event is set to +//! \b USB_EVENT_RX_AVAILABLE. +//! +//! \return This function returns the number of bytes that were scheduled +//! to be sent. If this function returns zero then there was no USB audio +//! device present or the device does not support audio input. +// +//***************************************************************************** +int32_t +USBHostAudioRecord(tUSBHostAudioInstance *psAudioInstance, void *pvBuffer, + uint32_t ui32Size, tUSBHostAudioCallback pfnCallback) +{ + uint32_t ui32Bytes; + + // + // Make sure that there is a device present. + // + if(psAudioInstance->psDevice == 0) + { + return(0); + } + + // + // If the audio input interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_IN_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_IN_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (uint32_t)psAudioInstance->psDevice, + psAudioInstance->ui8InInterface, + psAudioInstance->ui8InAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + psAudioInstance->pfnInCallback = pfnCallback; + psAudioInstance->pvInBuffer = (void *)pvBuffer; + + // + // Schedule the data to be read from the FIFO. + // + ui32Bytes = USBHCDPipeSchedule(psAudioInstance->ui32IsochInPipe, pvBuffer, + ui32Size); + + // + // Return the number of bytes scheduled to be sent. + // + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psAudioInstance is the audio device instance that was returned +//! from the call to USBHostAudioOpen(). +//! +//! This function forwards a request from an application to the audio 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: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHostAudioLPMSleep(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Call the host controller function to send the sleep command. + // + return(USBHCDLPMSleep(psAudioInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psAudioInstance is the audio device instance that was returned +//! from the call to USBHostAudioOpen(). +//! +//! 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 +USBHostAudioLPMStatus(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psAudioInstance->psDevice)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/host/usbhaudio.h b/usblib/host/usbhaudio.h new file mode 100644 index 0000000..fc768ee --- /dev/null +++ b/usblib/host/usbhaudio.h @@ -0,0 +1,163 @@ +//***************************************************************************** +// +// usbhaudio.h - USB host audio class driver. +// +// Copyright (c) 2010-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. +// +//***************************************************************************** + +#ifndef __USBHAUDIO_H__ +#define __USBHAUDIO_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// USB host audio specific events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This USB host audio event indicates that the device is connected and +//! ready to send or receive buffers. The \e pvBuffer and \e ui32Param +//! values are not used in this event. +// +//***************************************************************************** +#define USBH_AUDIO_EVENT_OPEN (USBH_AUDIO_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This USB host audio event indicates that the previously connected device +//! has been disconnected. The \e pvBuffer and \e ui32Param values are not used +//! in this event. +// +//***************************************************************************** +#define USBH_AUDIO_EVENT_CLOSE (USBH_AUDIO_EVENT_BASE + 1) + +//***************************************************************************** +// +// This definition is used with the USBHostAudioFormatGet() and +// USBHostAudioFormatSet() API's to determine if the audio input is being +// accesses(USBH_AUDIO_FORMAT_IN set) or audio output(USBH_AUDIO_FORMAT clear). +// +//***************************************************************************** +#define USBH_AUDIO_FORMAT_IN 0x00000001 +#define USBH_AUDIO_FORMAT_OUT 0x00000000 + +typedef struct +{ + uint8_t ui8Channels; + uint8_t ui8Bits; + uint32_t ui32SampleRate; +} +tUSBAudioFormat; + +typedef struct tUSBHostAudioInstance tUSBHostAudioInstance; + +//***************************************************************************** +// +// The prototype for the host USB Audio driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHostAudioCallback)(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Event, + uint32_t ui32MsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern tUSBHostAudioInstance * USBHostAudioOpen(uint32_t ui32Index, + tUSBHostAudioCallback pfnCallback); +extern void USBHostAudioClose(tUSBHostAudioInstance *psAudioInstance); +extern int32_t USBHostAudioPlay(tUSBHostAudioInstance *psAudioInstance, + void *pvBuffer, uint32_t ui32Size, + tUSBHostAudioCallback pfnCallback); + +extern uint32_t USBHostAudioFormatGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, + uint32_t ui32Bits, uint32_t ui32Channels, + uint32_t ui32Flags); +extern uint32_t USBHostAudioFormatSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, + uint32_t ui32Bits, uint32_t ui32Channels, + uint32_t ui32Flags); + +extern int32_t USBHostAudioRecord(tUSBHostAudioInstance *psAudioInstance, + void *pvBuffer, uint32_t ui32Size, + tUSBHostAudioCallback pfnAudioCallback); + +extern uint32_t USBHostAudioVolumeGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); + +extern void USBHostAudioVolumeSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel, + uint32_t ui32Value); + +extern uint32_t USBHostAudioVolumeMaxGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); + +extern uint32_t USBHostAudioVolumeMinGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); + +extern uint32_t USBHostAudioVolumeResGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); +extern uint32_t USBHostAudioLPMSleep(tUSBHostAudioInstance *psAudioInstance); +extern uint32_t USBHostAudioLPMStatus(tUSBHostAudioInstance *psAudioInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/usblib/host/usbhhid.c b/usblib/host/usbhhid.c new file mode 100644 index 0000000..bfbea85 --- /dev/null +++ b/usblib/host/usbhhid.c @@ -0,0 +1,746 @@ +//***************************************************************************** +// +// usbhhid.c - This file contains the host HID 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/usbhid.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhhid.h" + +static void * HIDDriverOpen(tUSBHostDevice *psDevice); +static void HIDDriverClose(void *pvInstance); + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If the user has not explicitly stated the maximum number of HID devices to +// support, we assume that we need to support up to the maximum number of USB +// devices that the build is configured for. +// +//***************************************************************************** +#ifndef MAX_HID_DEVICES +#define MAX_HID_DEVICES MAX_USB_DEVICES +#endif + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a HID device. +// +//***************************************************************************** +struct tHIDInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the callback. + // + tUSBCallback pfnCallback; + + // + // Callback data provided by caller. + // + void *pvCBData; + + // + // Used to remember what type of device was registered. + // + tHIDSubClassProtocol iDeviceType; + + // + // Interrupt IN pipe. + // + uint32_t ui32IntInPipe; +}; + +//***************************************************************************** +// +// The instance data storage for attached hid devices. +// +//***************************************************************************** +static tHIDInstance g_psHIDDevice[MAX_HID_DEVICES]; + +//***************************************************************************** +// +//! This constant global structure defines the HID Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHIDClassDriver = +{ + USB_CLASS_HID, + HIDDriverOpen, + HIDDriverClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of a HID device. +//! +//! \param iDeviceType is the type of device that should be loaded for this +//! instance of the HID device. +//! \param pfnCallback is the function that will be called whenever changes +//! are detected for this device. +//! \param pvCBData is the data that will be returned in when the +//! \e pfnCallback function is called. +//! +//! This function creates an instance of an specific type of HID device. The +//! \e iDeviceType parameter is one subclass/protocol values of the types +//! specified in enumerated types tHIDSubClassProtocol. Only devices that +//! enumerate with this type will be called back via the \e pfnCallback +//! function. The \e pfnCallback parameter is the callback function for any +//! events that occur for this device type. The \e pfnCallback function must +//! point to a valid function of type \e tUSBCallback for this call to complete +//! successfully. To release this device instance the caller of USBHHIDOpen() +//! should call USBHHIDClose() and pass in the value returned from the +//! USBHHIDOpen() call. +//! +//! \return This function returns and instance value that should be used with +//! any other APIs that require an instance value. If a value of 0 is returned +//! then the device instance could not be created. +// +//***************************************************************************** +tHIDInstance * +USBHHIDOpen(tHIDSubClassProtocol iDeviceType, tUSBCallback pfnCallback, + void *pvCBData) +{ + uint32_t ui32Loop; + + // + // Find a free device instance structure. + // + for(ui32Loop = 0; ui32Loop < MAX_HID_DEVICES; ui32Loop++) + { + if(g_psHIDDevice[ui32Loop].iDeviceType == eUSBHHIDClassNone) + { + // + // Save the instance data for this device. + // + g_psHIDDevice[ui32Loop].pfnCallback = pfnCallback; + g_psHIDDevice[ui32Loop].iDeviceType = iDeviceType; + g_psHIDDevice[ui32Loop].pvCBData = pvCBData; + + // + // Return the device instance pointer. + // + return(&g_psHIDDevice[ui32Loop]); + } + } + + // + // If we get here, there are no space device slots so return NULL to + // indicate a problem. + // + return(0); +} + +//***************************************************************************** +// +//! This function is used to release an instance of a HID device. +//! +//! \param psHIDInstance is the instance value for a HID device to release. +//! +//! This function releases an instance of a HID device that was created by a +//! call to USBHHIDOpen(). This call is required to allow other HID devices +//! to be enumerated after another HID device has been disconnected. The +//! \e psHIDInstance parameter should hold the value that was returned from +//! the previous call to USBHHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +void +USBHHIDClose(tHIDInstance *psHIDInstance) +{ + // + // Disable any more notifications from the HID layer. + // + psHIDInstance->pfnCallback = 0; + + // + // Mark this device slot as free. + // + psHIDInstance->iDeviceType = eUSBHHIDClassNone; +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint. +// +//***************************************************************************** +static void +HIDIntINCallback(uint32_t ui32Pipe, uint32_t ui32Event) +{ + int32_t i32Dev; + + switch (ui32Event) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + USBHCDPipeSchedule(ui32Pipe, 0, 1); + break; + } + // + // Called when new data is available on the interrupt IN pipe. + // + case USB_EVENT_RX_AVAILABLE: + { + // + // Determine which device this notification is intended for. + // + for(i32Dev = 0; i32Dev < MAX_HID_DEVICES; i32Dev++) + { + // + // Does this device own the pipe we have been passed? + // + if(g_psHIDDevice[i32Dev].ui32IntInPipe == ui32Pipe) + { + // + // Yes - send the report data to the USB host HID device + // class driver. + // + g_psHIDDevice[i32Dev].pfnCallback( + g_psHIDDevice[i32Dev].pvCBData, + USB_EVENT_RX_AVAILABLE, ui32Pipe, 0); + } + } + + break; + } + } +} + +//***************************************************************************** +// +//! This function is used to open an instance of the HID driver. +//! +//! \param psDevice is a pointer to the device information structure. +//! +//! This function will attempt to open an instance of the HID driver based on +//! the information contained in the psDevice structure. This call can fail if +//! there are not sufficient resources to open the device. The function will +//! return a value that should be passed back into USBHIDClose() when the +//! driver is no longer needed. +//! +//! \return The function will return a pointer to a HID driver instance. +// +//***************************************************************************** +static void * +HIDDriverOpen(tUSBHostDevice *psDevice) +{ + int32_t i32Idx, i32Dev; + tEndpointDescriptor *psEndpointDescriptor; + tInterfaceDescriptor *psInterface; + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); + + // + // Search the currently open instances for one that supports the protocol + // of this device. + // + for(i32Dev = 0; i32Dev < MAX_HID_DEVICES; i32Dev++) + { + if(g_psHIDDevice[i32Dev].iDeviceType == + psInterface->bInterfaceProtocol) + { + // + // Save the device pointer. + // + g_psHIDDevice[i32Dev].psDevice = psDevice; + + for(i32Idx = 0; i32Idx < 3; i32Idx++) + { + // + // Get the first endpoint descriptor. + // + psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, + i32Idx, + 256); + + // + // If no more endpoints then break out. + // + if(psEndpointDescriptor == 0) + { + break; + } + + // + // Interrupt + // + if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // Interrupt IN. + // + if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + g_psHIDDevice[i32Dev].ui32IntInPipe = + USBHCDPipeAlloc(0, USBHCD_PIPE_INTR_IN, + psDevice, HIDIntINCallback); + USBHCDPipeConfig(g_psHIDDevice[i32Dev].ui32IntInPipe, + psEndpointDescriptor->wMaxPacketSize, + psEndpointDescriptor->bInterval, + (psEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + } + } + + // + // If there is a callback function call it to inform the application that + // the device has been enumerated. + // + if(g_psHIDDevice[i32Dev].pfnCallback != 0) + { + g_psHIDDevice[i32Dev].pfnCallback( + g_psHIDDevice[i32Dev].pvCBData, + USB_EVENT_CONNECTED, + (uint32_t)&g_psHIDDevice[i32Dev], 0); + } + + // + // Save the device pointer. + // + g_psHIDDevice[i32Dev].psDevice = psDevice; + + return (&g_psHIDDevice[i32Dev]); + } + } + + // + // If we get here, no user has registered an interest in this particular + // HID device so we return an error. + // + return(0); +} + +//***************************************************************************** +// +//! This function is used to release an instance of the HID driver. +//! +//! \param pvInstance is an instance pointer that needs to be released. +//! +//! This function will free up any resources in use by the HID driver instance +//! that is passed in. The \e pvInstance pointer should be a valid value that +//! was returned from a call to USBHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +static void +HIDDriverClose(void *pvInstance) +{ + tHIDInstance *psInst; + + // + // Get our instance pointer. + // + psInst = (tHIDInstance *)pvInstance; + + // + // Reset the device pointer. + // + psInst->psDevice = 0; + + // + // Free the Interrupt IN pipe. + // + if(psInst->ui32IntInPipe != 0) + { + USBHCDPipeFree(psInst->ui32IntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(psInst->pfnCallback != 0) + { + psInst->pfnCallback(psInst->pvCBData, USB_EVENT_DISCONNECTED, + (uint32_t)pvInstance, 0); + } +} + +//***************************************************************************** +// +//! This function is used to set the idle timeout for a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui8Duration is the duration of the timeout in milliseconds. +//! \param ui8ReportID is the report identifier to set the timeout on. +//! +//! This function will send the Set Idle command to a HID device to set the +//! idle timeout for a given report. The length of the timeout is specified +//! by the \e ui8Duration parameter and the report the timeout for is in the +//! \e ui8ReportID value. +//! +//! \return Always returns 0. +// +//***************************************************************************** +uint32_t +USBHHIDSetIdle(tHIDInstance *psHIDInstance, uint8_t ui8Duration, + uint8_t ui8ReportID) +{ + tUSBRequest sSetupPacket; + + // + // This is a Class specific interface OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_IDLE; + sSetupPacket.wValue = (ui8Duration << 8) | ui8ReportID; + + // + // Set this on interface 1. + // + sSetupPacket.wIndex = 0; + + // + // This is always 0 for this request. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + return(USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, + 0, 0, MAX_PACKET_SIZE_EP0)); +} + +//***************************************************************************** +// +//! This function can be used to retrieve the report descriptor for a given +//! device instance. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param pui8Buffer is the memory buffer to use to store the report +//! descriptor. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! This function is used to return a report descriptor from a HID device +//! instance so that it can determine how to interpret reports that are +//! returned from the device indicated by the \e psHIDInstance parameter. +//! This call is blocking and will return the number of bytes read into the +//! \e pui8Buffer. +//! +//! \return Returns the number of bytes read into the \e pui8Buffer. +// +//***************************************************************************** +uint32_t +USBHHIDGetReportDescriptor(tHIDInstance *psHIDInstance, uint8_t *pui8Buffer, + uint32_t ui32Size) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Bytes; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = USB_HID_DTYPE_REPORT << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // All devices must have at least an 8 byte max packet size so just ask + // for 8 bytes to start with. + // + sSetupPacket.wLength = ui32Size; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + ui32Bytes = USBHCDControlTransfer(0, &sSetupPacket, + psHIDInstance->psDevice, pui8Buffer, ui32Size, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function is used to set or clear the boot protocol state of a device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32BootProtocol is either zero or non-zero to indicate which +//! protocol to use for the device. +//! +//! A USB host device can use this function to set the protocol for a connected +//! HID device. This is commonly used to set keyboards and mice into their +//! simplified boot protocol modes to fix the report structure to a know +//! state. +//! +//! \return This function returns 0. +// +//***************************************************************************** +uint32_t +USBHHIDSetProtocol(tHIDInstance *psHIDInstance, uint32_t ui32BootProtocol) +{ + tUSBRequest sSetupPacket; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_PROTOCOL; + + if(ui32BootProtocol) + { + // + // Boot Protocol. + // + sSetupPacket.wValue = 0; + } + else + { + // + // Report Protocol. + // + sSetupPacket.wValue = 1; + } + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // Always 0. + // + sSetupPacket.wLength = 0; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, 0, 0, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to retrieve a report from a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32Interface is the interface to retrieve the report from. +//! \param pui8Data is the memory buffer to use to store the report. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! This function is used to retrieve a report from a USB pipe. It is usually +//! called when the USB HID layer has detected a new data available in a USB +//! pipe. The USB HID host device code will receive a +//! \b USB_EVENT_RX_AVAILABLE event when data is available, allowing the +//! callback function to retrieve the data. +//! +//! \return Returns the number of bytes read from report. +// +//***************************************************************************** +uint32_t +USBHHIDGetReport(tHIDInstance *psHIDInstance, uint32_t ui32Interface, + uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Read the Data out. + // + ui32Size = USBHCDPipeReadNonBlocking(psHIDInstance->ui32IntInPipe, + pui8Data, ui32Size); + + // + // Return the number of bytes read from the interrupt in pipe. + // + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to send a report to a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32Interface is the interface to send the report to. +//! \param pui8Data is the memory buffer to use to store the report. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! This function is used to send a report to a USB HID device. It can be +//! only be called from outside the callback context as this function will not +//! return from the call until the data has been sent successfully. +//! +//! \return Returns the number of bytes sent to the device. +// +//***************************************************************************** +uint32_t +USBHHIDSetReport(tHIDInstance *psHIDInstance, uint32_t ui32Interface, + uint8_t *pui8Data, uint32_t ui32Size) +{ + tUSBRequest sSetupPacket; + + // + // This is a class specific OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_REPORT; + sSetupPacket.wValue = USB_HID_REPORT_OUTPUT << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = (uint16_t)ui32Interface; + + // + // Always 0. + // + sSetupPacket.wLength = ui32Size; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, + pui8Data, ui32Size, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psHIDInstance is the HID instance that was returned from the call +//! to USBHHIDOpen(). +//! +//! This function forwards a request from a HID device class to the host +//! controller 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 +USBHHIDLPMSleep(tHIDInstance *psHIDInstance) +{ + // + // Forward the request to the control endpoint of the device. + // + return(USBHCDLPMSleep(psHIDInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psHIDInstance is the HID instance that was returned from the call +//! to USBHHIDOpen(). +//! +//! 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 +USBHHIDLPMStatus(tHIDInstance *psHIDInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psHIDInstance->psDevice)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhid.h b/usblib/host/usbhhid.h new file mode 100644 index 0000000..f2df536 --- /dev/null +++ b/usblib/host/usbhhid.h @@ -0,0 +1,166 @@ +//***************************************************************************** +// +// usbhhid.h - This hold the host driver for hid class. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHHID_H__ +#define __USBHHID_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +typedef struct tHIDInstance tHIDInstance; + +//***************************************************************************** +// +// These defines are the the events that will be passed in the ui32Event +// parameter of the callback from the driver. +// +//***************************************************************************** +#define USBH_EVENT_HID_SETRPT USBH_HID_EVENT_BASE + 0 +#define USBH_EVENT_HID_REPORT USBH_HID_EVENT_BASE + 1 + +// +//! The HID keyboard detected a key being pressed. +// +#define USBH_EVENT_HID_KB_PRESS USBH_HID_EVENT_BASE + 16 + +// +//! The HID keyboard detected a key being released. +// +#define USBH_EVENT_HID_KB_REL USBH_HID_EVENT_BASE + 17 + +// +//! The HID keyboard detected one of the keyboard modifiers being pressed. +// +#define USBH_EVENT_HID_KB_MOD USBH_HID_EVENT_BASE + 18 + +// +//! A button was pressed on a HID mouse. +// +#define USBH_EVENT_HID_MS_PRESS USBH_HID_EVENT_BASE + 32 + +// +//! A button was released on a HID mouse. +// +#define USBH_EVENT_HID_MS_REL USBH_HID_EVENT_BASE + 33 + +// +//! The HID mouse detected movement in the X direction. +// +#define USBH_EVENT_HID_MS_X USBH_HID_EVENT_BASE + 34 + +// +//! The HID mouse detected movement in the Y direction. +// +#define USBH_EVENT_HID_MS_Y USBH_HID_EVENT_BASE + 35 + +//***************************************************************************** +// +//! The following values are used to register callbacks to the USB HOST HID +//! device class layer. +// +//***************************************************************************** +typedef enum +{ + // + //! No device should be used. This value should not be used by + //! applications. + // + eUSBHHIDClassNone = 0, + + // + //! This is a keyboard device. + // + eUSBHHIDClassKeyboard, + + // + //! This is a mouse device. + // + eUSBHHIDClassMouse, + + // + //! This is a vendor specific device. + // + eUSBHHIDClassVendor +} +tHIDSubClassProtocol; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern tHIDInstance * USBHHIDOpen(tHIDSubClassProtocol iDeviceType, + tUSBCallback pfnCallback, + void *pvCBData); +extern void USBHHIDClose(tHIDInstance *psHIDInstance); +extern uint32_t USBHHIDGetReportDescriptor(tHIDInstance *psHIDInstance, + uint8_t *pui8Buffer, + uint32_t ui32Size); +extern uint32_t USBHHIDSetIdle(tHIDInstance *psHIDInstance, uint8_t ui8Duration, + uint8_t ui8ReportID); +extern uint32_t USBHHIDSetProtocol(tHIDInstance *psHIDInstance, + uint32_t ui32BootProtocol); +extern uint32_t USBHHIDSetReport(tHIDInstance *psHIDInstance, + uint32_t ui32Interface, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHHIDGetReport(tHIDInstance *psHIDInstance, + uint32_t ui32Interface, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHHIDLPMSleep(tHIDInstance *psHIDInstance); +extern uint32_t USBHHIDLPMStatus(tHIDInstance *psHIDInstance); + +extern const tUSBHostClassDriver g_sUSBHIDClassDriver; + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHHID_H__ diff --git a/usblib/host/usbhhidkeyboard.c b/usblib/host/usbhhidkeyboard.c new file mode 100644 index 0000000..08f2b6a --- /dev/null +++ b/usblib/host/usbhhidkeyboard.c @@ -0,0 +1,752 @@ +//***************************************************************************** +// +// usbhhidkeyboard.c - This file holds the application interfaces for USB +// keyboard devices. +// +// 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 "usblib/usblib.h" +#include "usblib/host/usbhost.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhhid.h" +#include "usblib/host/usbhhidkeyboard.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for local functions. +// +//***************************************************************************** +static uint32_t USBHKeyboardCallback(void *pvKeyboard, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB keyboard report. +// +//***************************************************************************** +#define USBHKEYB_REPORT_SIZE 8 + +//***************************************************************************** +// +// These are the flags for the tUSBHKeyboard.ui32HIDFlags member variable. +// +//***************************************************************************** +#define USBHKEYB_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a keyboard device instance. +// +//***************************************************************************** +struct tUSBHKeyboard +{ + // + // Global flags for an instance of a keyboard. + // + uint32_t ui32HIDFlags; + + // + // The applications registered callback. + // + tUSBHIDKeyboardCallback pfnCallback; + + // + // The HID instance pointer for this keyboard instance. + // + tHIDInstance *psHIDInstance; + + // + // NUM_LOCK, CAPS_LOCK, SCROLL_LOCK, COMPOSE or KANA keys. + // + uint8_t ui8KeyModSticky; + + // + // This is the current state of the keyboard modifier keys. + // + uint8_t ui8KeyModState; + + // + // This holds the keyboard usage codes for keys that are being held down. + // + uint8_t pui8KeyState[6]; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + uint8_t pui8Buffer[USBHKEYB_REPORT_SIZE]; +}; + +//***************************************************************************** +// +// This is the per instance information for a keyboard device. +// +//***************************************************************************** +static tUSBHKeyboard g_sUSBHKeyboard = +{ + 0 +}; + +//***************************************************************************** +// +//! This function is used open an instance of a keyboard. +//! +//! \param pfnCallback is the callback function to call when new events occur +//! with the keyboard returned. +//! \param pui8Buffer is the memory used by the keyboard to interact with the +//! USB keyboard. +//! \param ui32Size is the size of the buffer provided by \e pui8Buffer. +//! +//! This function is used to open an instance of the keyboard. The value +//! returned from this function should be used as the instance identifier for +//! all other USBHKeyboard calls. The \e pui8Buffer memory buffer is used to +//! access the keyboard. The buffer size required is at least enough to hold +//! a normal report descriptor for the device. If there is not enough space +//! only a partial report descriptor will be read out. +//! +//! \return Returns the instance identifier for the keyboard that is attached. +//! If there is no keyboard present this will return 0. +// +//***************************************************************************** +tUSBHKeyboard * +USBHKeyboardOpen(tUSBHIDKeyboardCallback pfnCallback, uint8_t *pui8Buffer, + uint32_t ui32Size) +{ + // + // Save the callback and data pointers. + // + g_sUSBHKeyboard.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHKeyboard.psHIDInstance = + USBHHIDOpen(eUSBHHIDClassKeyboard, USBHKeyboardCallback, + (void *)&g_sUSBHKeyboard); + + return(&g_sUSBHKeyboard); +} + +//***************************************************************************** +// +//! This function is used close an instance of a keyboard. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! +//! This function is used to close an instance of the keyboard that was opened +//! with a call to USBHKeyboardOpen(). The \e psKbInstance value is the +//! value that was returned when the application called USBHKeyboardOpen(). +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +uint32_t +USBHKeyboardClose(tUSBHKeyboard *psKbInstance) +{ + // + // Reset the callback to null. + // + psKbInstance->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(psKbInstance->psHIDInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to map a USB usage ID to a printable character. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! \param psTable is the table to use to map the usage ID to characters. +//! \param ui8UsageID is the USB usage ID to map to a character. +//! +//! This function is used to map a USB usage ID to a character. The provided +//! \e psTable is used to perform the mapping and is described by the +//! tHIDKeyboardUsageTable type defined structure. See the documentation on +//! the tHIDKeyboardUsageTable structure for more details on the internals of +//! this structure. This function uses the current state of the shift keys +//! and the Caps Lock key to modify the data returned by this function. The +//! psTable structure has values indicating which keys are modified by Caps +//! and alternate values for shifted cases. The number of bytes returned from +//! Lock this function depends on the \e psTable structure passed in as it +//! holds the number of bytes per character in the table. +//! +//! \return Returns the character value for the given usage id. +// +//***************************************************************************** +uint32_t +USBHKeyboardUsageToChar(tUSBHKeyboard *psKbInstance, + const tHIDKeyboardUsageTable *psTable, + uint8_t ui8UsageID) +{ + uint32_t ui32Value, ui32Offset, ui32Shift; + const uint8_t *pui8KeyBoardMap; + const uint16_t *pui16KeyBoardMap; + + // + // The added offset for the shifted character value. + // + ui32Shift = 0; + + // + // Offset in the table for the character. + // + ui32Offset = (ui8UsageID * psTable->ui8BytesPerChar * 2); + + // + // Handle the case where CAPS lock has been set. + // + if(psKbInstance->ui8KeyModSticky &= HID_KEYB_CAPS_LOCK) + { + // + // See if this usage ID is modified by Caps Lock by checking the packed + // bit array in the pui32ShiftState member of the psTable array. + // + if((psTable->pui32CapsLock[ui8UsageID >> 5]) >> + (ui8UsageID & 0x1f) & 1) + { + ui32Shift = psTable->ui8BytesPerChar; + } + } + + // + // Now handle if a shift key is being held. + // + if((psKbInstance->ui8KeyModState & 0x22) != 0) + { + // + // Not shifted yet so we need to shift. + // + if(ui32Shift == 0) + { + ui32Shift = psTable->ui8BytesPerChar; + } + else + { + // + // Unshift because CAPS LOCK and shift were pressed. + // + ui32Shift = 0; + } + } + + // + // One byte per character. + // + if(psTable->ui8BytesPerChar == 1) + { + // + // Get the base address of the table. + // + pui8KeyBoardMap = psTable->pvCharMapping; + + ui32Value = pui8KeyBoardMap[ui32Offset + ui32Shift]; + } + // + // Two bytes per character. + // + else if(psTable->ui8BytesPerChar == 2) + { + // + // Get the base address of the table. + // + pui16KeyBoardMap = (uint16_t *)psTable->pvCharMapping; + + ui32Value = pui16KeyBoardMap[ui32Offset + ui32Shift]; + } + // + // All other sizes are unsupported for now. + // + else + { + ui32Value = 0; + } + + return(ui32Value); +} + +//***************************************************************************** +// +//! This function is used to set one of the fixed modifier keys on a keyboard. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! \param ui32Modifiers is a bit mask of the modifiers to set on the keyboard. +//! +//! This function is used to set the modifier key states on a keyboard. The +//! \e ui32Modifiers value is a bitmask of the following set of values: +//! - \b HID_KEYB_NUM_LOCK +//! - \b HID_KEYB_CAPS_LOCK +//! - \b HID_KEYB_SCROLL_LOCK +//! - \b HID_KEYB_COMPOSE +//! - \b HID_KEYB_KANA +//! +//! Not all of these will be supported on all keyboards however setting values +//! on a keyboard that does not have them should have no effect. The +//! \e psKbInstance value is the value that was returned when the application +//! called USBHKeyboardOpen(). If the value \b HID_KEYB_CAPS_LOCK is used it +//! will modify the values returned from the USBHKeyboardUsageToChar() +//! function. +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +uint32_t +USBHKeyboardModifierSet(tUSBHKeyboard *psKbInstance, uint32_t ui32Modifiers) +{ + // + // Remember the fact that this is set. + // + psKbInstance->ui8KeyModSticky = (uint8_t)ui32Modifiers; + + // + // Set the LEDs on the keyboard. + // + USBHHIDSetReport(psKbInstance->psHIDInstance, 0, + (uint8_t *)&ui32Modifiers, 1); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a keyboard interface after a keyboard +//! has been detected. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! +//! This function should be called after receiving a \b USB_EVENT_CONNECTED +//! event in the callback function provided by USBHKeyboardOpen(), however this +//! function should only be called outside the callback function. This will +//! initialize the keyboard interface and determine the keyboard's +//! layout and how it reports keys to the USB host controller. The +//! \e psKbInstance value is the value that was returned when the application +//! called USBHKeyboardOpen(). This function only needs to be called once +//! per connection event but it should be called every time a +//! \b USB_EVENT_CONNECTED event occurs. +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +uint32_t +USBHKeyboardInit(tUSBHKeyboard *psKbInstance) +{ + uint8_t ui8ModData; + int32_t i32Idx; + + // + // Set the initial rate to only update on keyboard state changes. + // + USBHHIDSetIdle(psKbInstance->psHIDInstance, 0, 0); + + // + // Read out the Report Descriptor from the keyboard and parse it for + // the format of the reports coming back from the keyboard. + // + USBHHIDGetReportDescriptor(psKbInstance->psHIDInstance, + psKbInstance->pui8Buffer, + USBHKEYB_REPORT_SIZE); + + // + // Set the keyboard to boot protocol. + // + USBHHIDSetProtocol(psKbInstance->psHIDInstance, 1); + + // + // Used to clear the initial state of all on keyboard modifiers. + // + ui8ModData = 0; + + // + // Update the keyboard LED state. + // + USBHHIDSetReport(psKbInstance->psHIDInstance, 0, &ui8ModData, 1); + + // + // Reset the key state. + // + for(i32Idx = 0; + i32Idx < sizeof(psKbInstance->pui8KeyState) / sizeof(uint8_t); + i32Idx++) + { + psKbInstance->pui8KeyState[i32Idx] =0; + } + + return(0); +} + +//***************************************************************************** +// +//! This function is used to set the automatic poll rate of the keyboard. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! \param ui32PollRate is the rate in ms to cause the keyboard to update the +//! host regardless of no change in key state. +//! +//! This function will allow an application to tell the keyboard how often it +//! should send updates to the USB host controller regardless of any changes +//! in keyboard state. The \e psKbInstance value is the value that was +//! returned when the application called USBHKeyboardOpen(). The +//! \e ui32PollRate is the new value in ms for the update rate on the keyboard. +//! This value is initially set to 0 which indicates that the keyboard should +//! only to update when the keyboard state changes. Any value other than 0 can +//! be used to force the keyboard to generate auto-repeat sequences for the +//! application. +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +uint32_t +USBHKeyboardPollRateSet(tUSBHKeyboard *psKbInstance, uint32_t ui32PollRate) +{ + // + // Send the Set Idle command to the USB keyboard. + // + USBHHIDSetIdle(psKbInstance->psHIDInstance, ui32PollRate, 0); + + return(0); +} + +//***************************************************************************** +// +// This is an internal function used to modify the current keyboard state. +// +// This function checks for changes in the keyboard state due to a new report +// being received from the device. It first checks if this is a "roll-over" +// case by seeing if 0x01 is in the first position of the new keyboard report. +// This indicates that too many keys were pressed to handle and to ignore this +// report. Next the keyboard modifier state is stored and if any changes are +// detected a \b USBH_EVENT_HID_KB_MOD event is sent back to the application. +// Then this function will check for any keys that have been released and send +// a \b USBH_EVENT_HID_KB_REL even for each of these keys. The last check is +// for any new keys that are pressed and a \b USBH_EVENT_HID_KB_PRESS event +// will be sent for each new key pressed. +// +// \return None. +// +//***************************************************************************** +static void +UpdateKeyboardState(tUSBHKeyboard *psKbInstance) +{ + int32_t i32NewKey, i32OldKey; + + // + // rollover code so ignore this buffer. + // + if(psKbInstance->pui8Buffer[2] == 0x01) + { + return; + } + + // + // Handle the keyboard modifier states. + // + if(psKbInstance->ui8KeyModState != psKbInstance->pui8Buffer[0]) + { + // + // Notify the application of the event. + // + psKbInstance->pfnCallback(0, USBH_EVENT_HID_KB_MOD, + psKbInstance->pui8Buffer[0], 0); + + // + // Save the new state of the modifier keys. + // + psKbInstance->ui8KeyModState = psKbInstance->pui8Buffer[0]; + } + + // + // This loop checks for keys that have been released to make room for new + // ones that may have been pressed. + // + for(i32OldKey = 0; i32OldKey < 6; i32OldKey++) + { + // + // If there is no old key pressed in this entry go to the next one. + // + if(psKbInstance->pui8KeyState[i32OldKey] == 0) + { + continue; + } + + // + // Check if this old key is still in the list of currently pressed + // keys. + // + for(i32NewKey = 2; i32NewKey < 8; i32NewKey++) + { + // + // Break out if the key is still present. + // + if(psKbInstance->pui8Buffer[i32NewKey] == + psKbInstance->pui8KeyState[i32OldKey]) + { + break; + } + } + // + // If the old key was no longer in the list of pressed keys then + // notify the application of the key release. + // + if(i32NewKey == 8) + { + // + // Send the key release notification to the application. + // + psKbInstance->pfnCallback(0, USBH_EVENT_HID_KB_REL, + psKbInstance->pui8KeyState[i32OldKey], + 0); + // + // Remove the old key from the currently held key list. + // + psKbInstance->pui8KeyState[i32OldKey] = 0; + + } + } + + // + // This loop checks for new keys that have been pressed. + // + for(i32NewKey = 2; i32NewKey < 8; i32NewKey++) + { + // + // The new list is empty so no new keys are pressed. + // + if(psKbInstance->pui8Buffer[i32NewKey] == 0) + { + break; + } + + // + // This loop checks if the current key was already pressed. + // + for(i32OldKey = 0; i32OldKey < 6; i32OldKey++) + { + // + // If it is in both lists then it was already pressed so ignore it. + // + if(psKbInstance->pui8Buffer[i32NewKey] == + psKbInstance->pui8KeyState[i32OldKey]) + { + break; + } + } + // + // The key in the new list was not found so it is new. + // + if(i32OldKey == 6) + { + // + // Look for a free location to store this key usage code. + // + for(i32OldKey = 0; i32OldKey < 6; i32OldKey++) + { + // + // If an empty location is found, store it and notify the + // application. + // + if(psKbInstance->pui8KeyState[i32OldKey] == 0) + { + // + // Save the newly pressed key. + // + psKbInstance->pui8KeyState[i32OldKey] = + psKbInstance->pui8Buffer[i32NewKey]; + + // + // Notify the application of the new key that has been + // pressed. + // + psKbInstance->pfnCallback( 0, USBH_EVENT_HID_KB_PRESS, + psKbInstance->pui8Buffer[i32NewKey], + 0); + + break; + } + } + } + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvKeyboard is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ui32Event is the event that has been passed up from the HID driver. +//! \param ui32MsgParam has meaning related to the \e ui32Event that occurred. +//! \param pvMsgData has meaning related to the \e ui32Event that occurred. +//! +//! This function will receive all event updates from the HID driver layer. +//! The keyboard driver itself will mostly be concerned with report callbacks +//! from the HID driver layer and parsing them into keystrokes for the +//! application that has registered for callbacks with the USBHKeyboardOpen() +//! call. +//! +//! \return Non-zero values should be assumed to indicate an error condition. +// +//***************************************************************************** +static uint32_t +USBHKeyboardCallback(void *pvKeyboard, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData) +{ + tUSBHKeyboard *psKbInstance; + + // + // Recover the pointer to the instance data. + // + psKbInstance = (tUSBHKeyboard *)pvKeyboard; + + switch (ui32Event) + { + // + // New keyboard has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a keyboard is present. + // + psKbInstance->ui32HIDFlags |= USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that a new keyboard was connected. + // + psKbInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No keyboard is present. + // + psKbInstance->ui32HIDFlags &= ~USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that the keyboard was disconnected. + // + psKbInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New keyboard report structure was received. + // + USBHHIDGetReport(psKbInstance->psHIDInstance, 0, + psKbInstance->pui8Buffer, + USBHKEYB_REPORT_SIZE); + + // + // Update the application on the changes in the keyboard state. + // + UpdateKeyboardState(psKbInstance); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psKbInstance is the HID keyboard instance that was +//! returned from the call to USBHKeyboardOpen(). +//! +//! This function forwards a request from an application to the HID 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: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHKeyboardLPMSleep(tUSBHKeyboard *psKbInstance) +{ + // + // Call the HID function to send the sleep command. + // + return(USBHHIDLPMSleep(psKbInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psKbInstance is the HID keyboard instance that was +//! returned from the call to USBHKeyboardOpen(). +//! +//! 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 +USBHKeyboardLPMStatus(tUSBHKeyboard *psKbInstance) +{ + // + // Call the HID function to get the current LPM status. + // + return(USBHHIDLPMStatus(psKbInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidkeyboard.h b/usblib/host/usbhhidkeyboard.h new file mode 100644 index 0000000..09852f1 --- /dev/null +++ b/usblib/host/usbhhidkeyboard.h @@ -0,0 +1,89 @@ +//***************************************************************************** +// +// usbhhidkeyboard.h - This file holds the application interfaces for USB +// keyboard devices. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHHIDKEYBOARD_H__ +#define __USBHHIDKEYBOARD_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +typedef struct tUSBHKeyboard tUSBHKeyboard; + +//***************************************************************************** +// +// The prototype for the host USB Keyboard driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHIDKeyboardCallback)(tUSBHKeyboard *psKbInstance, + uint32_t ui32Event, + uint32_t ui32MsgParam, + void *pvMsgData); + +extern tUSBHKeyboard * USBHKeyboardOpen(tUSBHIDKeyboardCallback pfnCallback, + uint8_t *pui8Buffer, + uint32_t ui32BufferSize); +extern uint32_t USBHKeyboardClose(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardInit(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardModifierSet(tUSBHKeyboard *psKbInstance, + uint32_t ui32Modifiers); +extern uint32_t USBHKeyboardPollRateSet(tUSBHKeyboard *psKbInstance, + uint32_t ui32PollRate); +extern uint32_t USBHKeyboardLPMSleep(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardLPMStatus(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardUsageToChar(tUSBHKeyboard *psKbInstance, + const tHIDKeyboardUsageTable *psTable, + uint8_t ui8UsageID); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usblib/host/usbhhidmouse.c b/usblib/host/usbhhidmouse.c new file mode 100644 index 0000000..f1eb8d5 --- /dev/null +++ b/usblib/host/usbhhidmouse.c @@ -0,0 +1,452 @@ +//***************************************************************************** +// +// usbhhidmouse.c - This file holds the application interfaces for USB +// mouse devices. +// +// 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 "usblib/usblib.h" +#include "usblib/host/usbhost.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhhid.h" +#include "usblib/host/usbhhidmouse.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for local functions. +// +//***************************************************************************** +static uint32_t USBHMouseCallback(void *pvMouse, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB mouse report. +// +//***************************************************************************** +#define USBHMS_REPORT_SIZE 4 + +//***************************************************************************** +// +// These are the flags for the tUSBHMouse.ui32HIDFlags member variable. +// +//***************************************************************************** +#define USBHMS_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a mouse device instance. +// +//***************************************************************************** +struct tUSBHMouse +{ + // + // Global flags for an instance of a mouse. + // + uint32_t ui32HIDFlags; + + // + // The applications registered callback. + // + tUSBHIDMouseCallback pfnCallback; + + // + // The current state of the buttons. + // + uint8_t ui8Buttons; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + uint8_t pui8Buffer[USBHMS_REPORT_SIZE]; + + // + // Heap data for the mouse currently used to read the HID Report + // Descriptor. + // + uint8_t *pui8Heap; + + // + // Size of the heap in bytes. + // + uint32_t ui32HeapSize; + + // + // This is the instance value for the HID device that will be used for the + // mouse. + // + tHIDInstance *psHIDInstance; +}; + +//***************************************************************************** +// +// This is the per instance information for a mouse device. +// +//***************************************************************************** +static tUSBHMouse g_sUSBHMouse = +{ + 0 +}; + +//***************************************************************************** +// +//! This function is used open an instance of a mouse. +//! +//! \param pfnCallback is the callback function to call when new events occur +//! with the mouse returned. +//! \param pui8Buffer is the memory used by the driver to interact with the +//! USB mouse. +//! \param ui32Size is the size of the buffer provided by \e pui8Buffer. +//! +//! This function is used to open an instance of the mouse. The value +//! returned from this function should be used as the instance identifier for +//! all other USBHMouse calls. The \e pui8Buffer memory buffer is used to +//! access the mouse. The buffer size required is at least enough to hold +//! a normal report descriptor for the device. +//! +//! \return Returns the instance identifier for the mouse that is attached. +//! If there is no mouse present this will return 0. +// +//***************************************************************************** +tUSBHMouse * +USBHMouseOpen(tUSBHIDMouseCallback pfnCallback, uint8_t *pui8Buffer, + uint32_t ui32Size) +{ + // + // Save the callback and data pointers. + // + g_sUSBHMouse.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHMouse.psHIDInstance = USBHHIDOpen(eUSBHHIDClassMouse, + USBHMouseCallback, + (void *)&g_sUSBHMouse); + + // + // Save the heap buffer and size. + // + g_sUSBHMouse.pui8Heap = pui8Buffer; + g_sUSBHMouse.ui32HeapSize = ui32Size; + + return(&g_sUSBHMouse); +} + +//***************************************************************************** +// +//! This function is used close an instance of a mouse. +//! +//! \param psMsInstance is the instance value for this mouse. +//! +//! This function is used to close an instance of the mouse that was opened +//! with a call to USBHMouseOpen(). The \e psMsInstance value is the value +//! that was returned when the application called USBHMouseOpen(). +//! +//! \return Returns 0. +// +//***************************************************************************** +uint32_t +USBHMouseClose(tUSBHMouse *psMsInstance) +{ + // + // Reset the callback to null. + // + psMsInstance->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(psMsInstance->psHIDInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a mouse interface after a mouse has +//! been detected. +//! +//! \param psMsInstance is the instance value for this mouse. +//! +//! This function should be called after receiving a \b USB_EVENT_CONNECTED +//! event in the callback function provided by USBHMouseOpen(), however it +//! should only be called outside of the callback function. This will +//! initialize the mouse interface and determine how it reports events to the +//! USB host controller. The \e psMsInstance value is the value that was +//! returned when the application called USBHMouseOpen(). This function only +//! needs to be called once per connection event but it should be called every +//! time a \b USB_EVENT_CONNECTED event occurs. +//! +//! \return Non-zero values should be assumed to indicate an error condition. +// +//***************************************************************************** +uint32_t +USBHMouseInit(tUSBHMouse *psMsInstance) +{ + // + // Set the initial rate to only update on mouse state changes. + // + USBHHIDSetIdle(psMsInstance->psHIDInstance, 0, 0); + + // + // Read out the Report Descriptor from the mouse and parse it for + // the format of the reports coming back from the mouse. + // + USBHHIDGetReportDescriptor(psMsInstance->psHIDInstance, + psMsInstance->pui8Heap, + psMsInstance->ui32HeapSize); + + // + // Set the mouse to boot protocol. + // + USBHHIDSetProtocol(psMsInstance->psHIDInstance, 1); + + return(0); +} + +//***************************************************************************** +// +// This function handles updating the state of the mouse buttons and axis. +// +// \param psMsInstance is the pointer to an instance of the mouse data. +// +// This function will check for updates to buttons or X/Y movements and send +// callbacks to the mouse callback function. +// +// \return None. +// +//***************************************************************************** +static void +UpdateMouseState(tUSBHMouse *psMsInstance) +{ + uint32_t ui32Button; + + if(psMsInstance->pui8Buffer[0] != psMsInstance->ui8Buttons) + { + for(ui32Button = 1; ui32Button <= 0x4; ui32Button <<= 1) + { + if(((psMsInstance->pui8Buffer[0] & ui32Button) != 0) && + ((psMsInstance->ui8Buttons & ui32Button) == 0)) + { + // + // Send the mouse button press notification to the application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_PRESS, + ui32Button, 0); + } + if(((psMsInstance->pui8Buffer[0] & ui32Button) == 0) && + ((psMsInstance->ui8Buttons & ui32Button) != 0)) + { + // + // Send the mouse button release notification to the + // application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_REL, + ui32Button, 0); + } + } + + // + // Save the new state. + // + psMsInstance->ui8Buttons = psMsInstance->pui8Buffer[0]; + } + + if(psMsInstance->pui8Buffer[1] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_X, + (uint32_t)psMsInstance->pui8Buffer[1], 0); + } + + if(psMsInstance->pui8Buffer[2] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_Y, + (uint32_t)psMsInstance->pui8Buffer[2], 0); + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvMouse is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ui32Event is the event that has been passed up from the HID driver. +//! \param ui32MsgParam has meaning related to the \e ui32Event that occurred. +//! \param pvMsgData has meaning related to the \e ui32Event that occurred. +//! +//! This function will receive all event updates from the HID driver layer. +//! The mouse driver itself will mostly be concerned with report callbacks +//! from the HID driver layer and parsing them into keystrokes for the +//! application that has registered for callbacks with the USBHMouseOpen() +//! call. +//! +//! \return Non-zero values should be assumed to indicate an error condition. +// +//***************************************************************************** +uint32_t +USBHMouseCallback(void *pvMouse, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData) +{ + tUSBHMouse *psMsInstance; + + // + // Recover the pointer to the instance data. + // + psMsInstance = (tUSBHMouse *)pvMouse; + + switch(ui32Event) + { + // + // New mouse has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a mouse is present. + // + psMsInstance->ui32HIDFlags |= USBHMS_DEVICE_PRESENT; + + // + // Notify the application that a new mouse was connected. + // + psMsInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No mouse is present. + // + psMsInstance->ui32HIDFlags &= ~USBHMS_DEVICE_PRESENT; + + // + // Notify the application that the mouse was disconnected. + // + psMsInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New mouse report structure was received. + // + USBHHIDGetReport(psMsInstance->psHIDInstance, 0, + psMsInstance->pui8Buffer, USBHMS_REPORT_SIZE); + + // + // Update the current state of the mouse and notify the application + // of any changes. + // + UpdateMouseState(psMsInstance); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psMsInstance is the HID keyboard instance that was returned +//! from the call to USBHMouseOpen(). +//! +//! This function forwards a request from an application to the HID 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: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHMouseLPMSleep(tUSBHMouse *psMsInstance) +{ + // + // Call the HID function to send the sleep command. + // + return(USBHHIDLPMSleep(psMsInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psMsInstance is the HID keyboard instance that was returned +//! from the call to USBHMouseOpen(). +//! +//! 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 +USBHMouseLPMStatus(tUSBHMouse *psMsInstance) +{ + // + // Call the HID function to get the current LPM status. + // + return(USBHHIDLPMStatus(psMsInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidmouse.h b/usblib/host/usbhhidmouse.h new file mode 100644 index 0000000..295f6c1 --- /dev/null +++ b/usblib/host/usbhhidmouse.h @@ -0,0 +1,81 @@ +//***************************************************************************** +// +// usbhhidmouse.h - This file holds the application interfaces for USB +// mouse devices. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHHIDMOUSE_H__ +#define __USBHHIDMOUSE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +typedef struct tUSBHMouse tUSBHMouse; + +//***************************************************************************** +// +// The prototype for the host USB mouse driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHIDMouseCallback)(tUSBHMouse *psMsInstance, + uint32_t ui32Event, + uint32_t ui32MsgParam, + void *pvMsgData); + +extern tUSBHMouse * USBHMouseOpen(tUSBHIDMouseCallback pfnCallback, + uint8_t *pui8Buffer, uint32_t ui32Size); +extern uint32_t USBHMouseClose(tUSBHMouse *psMsInstance); +extern uint32_t USBHMouseInit(tUSBHMouse *psMsInstance); +extern uint32_t USBHMouseLPMSleep(tUSBHMouse *psMsInstance); +extern uint32_t USBHMouseLPMStatus(tUSBHMouse *psMsInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usblib/host/usbhhub.c b/usblib/host/usbhhub.c new file mode 100644 index 0000000..8042dfc --- /dev/null +++ b/usblib/host/usbhhub.c @@ -0,0 +1,1522 @@ +//***************************************************************************** +// +// usbhhub.c - This file contains the host HID driver. +// +// Copyright (c) 2011-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 "inc/hw_ints.h" +#include "inc/hw_sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom_map.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhhub.h" +#ifdef INCLUDE_DEBUG_OUTPUT +#include "utils/uartstdio.h" +#define DEBUG_OUTPUT UARTprintf +#else +#define DEBUG_OUTPUT while(0)((int (*)(char *, ...))0) +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +//! The USB standard hub descriptor structure. Full documentation for the +//! contents of this structure can be found in chapter 11.23.2.1 of the USB +//! 2.0 specification. +// +//***************************************************************************** +typedef struct +{ + // + //! The total number of bytes in the descriptor (including this field). + // + uint8_t bLength; + + // + //! The descriptor type. For a hub descriptor, this will be USB_DTYPE_HUB + //! (0x29 or 41 decimal). + // + uint8_t bDescType; + + // + //! The number of downstream-facing ports that the hub supports. + // + uint8_t bNbrPorts; + + // + //! Characteristics of the hub device including its power switching + //! capabilities and over-current protection mode. + // + uint16_t wHubCharacteristics; + + // + //! The time between the start of the power-on sequence for a port and + //! the power to the port becoming stable. This is expressed in 2mS units. + // + uint8_t bPwrOn2PwrGood; + + // + //! The maximum current requirement for the hub circuitry in mA. + // + uint8_t bHubContrCurrent; + + // + //! The last two fields in the structure are bit masks indicating which + //! downstream ports support removable devices and, following this, another + //! obsolete field from USB1.0 related to port power control. Each field + //! is byte aligned and contains a bit for each hub port. This structure + //! definition is set up with enough storage to handle ROOT_HUB_MAX_PORTS + //! ports but beware that the actual size of each field is dependent upon + //! the bNbrPorts field above. + // + uint8_t PortInfo[((ROOT_HUB_MAX_PORTS + 7) / 8) * 2]; +} +PACKED tUsbHubDescriptor; + +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// This structure holds all data specific to a single hub port. +// +//***************************************************************************** +typedef struct +{ + // + // The handle used by the HCD layer to identify this device. + // + uint32_t ui32DevHandle; + + // + // The current state of the port. + // + volatile tHubPortState iState; + + // + // General counter used in various states. + // + volatile uint32_t ui32Count; + + // + // A flag used to indicate that the downstream device is a low speed + // device. + // + bool bLowSpeed; + + // + // The speed of the device on this port. + // + uint32_t ui32Speed; + + // + // This flag is set if the hub reports that a change is pending on this + // port. + // + volatile bool bChanged; +} +tHubPort; + +//***************************************************************************** +// +// USB hub flags values for tHubInstance.ui32Flags. +// +//***************************************************************************** +#define USBLIB_HUB_ACTIVE 0x00000001 +#define USBLIB_HUB_HS 0x00000002 +#define USBLIB_HUB_MULTI_TT 0x00000004 + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a Hub device. +// +//***************************************************************************** +struct tHubInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the callback function pointer. + // + tUSBHHubCallback pfnCallback; + + // + // Callback data provided by caller. + // + uint32_t ui32CBData; + + // + // Interrupt IN pipe. + // + uint32_t ui32IntInPipe; + + // + // Hub characteristics as reported in the class-specific hub descriptor. + // + uint16_t ui16HubCharacteristics; + + // + // The number of downstream-facing ports the hub supports. + // + uint8_t ui8NumPorts; + + // + // The number of ports on the hub that we can actually talk to. This will + // be the smaller of the number of ports on the hub and MAX_USB_DEVICES. + // + uint8_t ui8NumPortsInUse; + + // + // The size of a status change packet sent by the hub. This is determined + // from the number of ports supported by the hub. + // + uint8_t ui8ReportSize; + + // + // Flags indicating whether the hub is connected. + // + uint32_t ui32Flags; + + // + // Flag indicating that a device is currently in process of being + // enumerated. + // + volatile bool bEnumerationBusy; + + // + // This is valid if bEnumerationBusy is set and indicates the port + // that is in the process of enumeration. + // + uint8_t ui8EnumIdx; + + // + // The state of each of the ports we support on the hub. + // + tHubPort psPorts[MAX_USB_DEVICES]; + + // + // The interrupt number for this instance. + // + uint32_t ui32IntNum; +}; + +//***************************************************************************** +// +//! Forward references to the hub class driver functions. +// +//***************************************************************************** +static void *HubDriverOpen(tUSBHostDevice *psDevice); +static void HubDriverClose(void *pvHubDevice); + +//***************************************************************************** +// +//! This constant global structure defines the Hub Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHubClassDriver = +{ + USB_CLASS_HUB, + HubDriverOpen, + HubDriverClose, + 0 +}; + +//***************************************************************************** +// +// The instance data storage for attached hub. +// +//***************************************************************************** +static tHubInstance g_sRootHub; + +//***************************************************************************** +// +// Hub and port state change flags as reported via the hub's IN endpoint. +// +//***************************************************************************** +static volatile uint32_t g_ui32ChangeFlags; + +// +// Note: The following assumes ROOT_HUB_MAX_PORTS is less than 32! +// +static uint32_t g_ui32HubChanges; + +//***************************************************************************** +// +// This function is called to set the operating speed of a given port. +// +// \param ui8Port is the port number for this request. +// \param ui32Speed is one of the HUB_FEATURE_PORT_* values. +// +// This function sets the operating speed of the hub port specified in the +// \e ui8Port parameter. A \e ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui32Speed value is one of the +// \b USB_EP_SPEED_ values. +// +// \return None. +// +//***************************************************************************** +static void +USBHubPortSpeedSet(uint8_t ui8Port, uint32_t ui32Speed) +{ + g_sRootHub.psPorts[ui8Port].ui32Speed = ui32Speed; +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to set a feature on +// a given port. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param ui16Feature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the set feature request to the hub indicated by the +// \e psHubInstance parameter. The \e ui8Port value indicates which port +// number to send this request to and can range from 0 to the number of valid +// ports on the given hub. A \e ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui16Feature is the feature +// request toset on the given port. For example, a \e ui16Feature value of +// \e HUB_FEATURE_PORT_RESET and \e ui8Port value of 1 will cause reset +// signaling to hub port 1. +// +// \return None. +// +//***************************************************************************** +static void +HubSetPortFeature(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t ui16Feature) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the hub instance and device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + sSetupPacket.bRequest = USBREQ_SET_FEATURE; + sSetupPacket.wValue = ui16Feature; + sSetupPacket.wIndex = ui8Port; + sSetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to clear a feature on +// a given port. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param ui16Feature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the clear feature request to the hub indicated by +// the \e psHubInstance parameter. The \e ui8Port value indicates which port +// number to send this request to and can range from 0 to the number of valid +// ports on the given hub. A \e ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui16Feature is the feature +// request to clear on the given port. For example, a \e ui16Feature value of +// \e HUB_FEATURE_C_PORT_RESET and \e ui8Port value of 1 will clear the reset +// complete signaling on hub port 1. Values like the reset feature will +// remain set until actively cleared by this function. +// +// \return None. +// +//***************************************************************************** +static void +HubClearPortFeature(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t ui16Feature) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the hub instance and device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + sSetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + sSetupPacket.wValue = ui16Feature; + sSetupPacket.wIndex = ui8Port; + sSetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is used to retrieve the current status of a port on the +// hub. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param pui16PortStatus is a pointer to the memory to store the current +// status of the port. +// \param pui16PortChange is a pointer to the memory to store the current +// change status of the ports. +// +// This function is used to retrieve the current overall status and change +// status for the port given in the \e ui8Port parameter. The \e ui8Port value +// indicates which port number to send this request to and can range from 0 to +// the number of valid ports on the given hub. A \e ui8Port value of 0 is an +// access to the hub itself and not one of the hub ports. +// +// \return None. +// +//***************************************************************************** +static bool +HubGetPortStatus(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t *pui16PortStatus, uint16_t *pui16PortChange) +{ + uint32_t ui32Data, ui32Read; + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the fields to get the hub status. + // + sSetupPacket.bRequest = USBREQ_GET_STATUS; + sSetupPacket.wValue = 0; + sSetupPacket.wIndex = (uint16_t)ui8Port; + sSetupPacket.wLength = 4; + + // + // Send the request. + // + ui32Read = USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (uint8_t *)&ui32Data, 4, + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Check that we received the correct number of bytes. + // + if(ui32Read != 4) + { + return(false); + } + else + { + // + // We got 4 bytes from the device. Now translate these into the 2 + // 16-bit values we pass back to the caller. + // + *pui16PortStatus = (uint16_t)(ui32Data & 0xFFFF); + *pui16PortChange = (uint16_t)(ui32Data >> 16); + + DEBUG_OUTPUT("Port %d, status 0x%04x, change 0x%04x\n", ui8Port, + *pui16PortStatus, *pui16PortChange); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint for the hub +// device. +// +//***************************************************************************** +static void +HubIntINCallback(uint32_t ui32Pipe, uint32_t ui32Event) +{ + switch (ui32Event) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + // + // Set things up to read the next change indication from the hub. + // + USBHCDPipeSchedule(ui32Pipe, (uint8_t *)&g_ui32HubChanges, + (uint32_t)g_sRootHub.ui8ReportSize); + break; + } + + // + // Called when new data is available on the interrupt IN pipe. + // + case USB_EVENT_RX_AVAILABLE: + { + // + // For data transfers on INT IN endpoints, we need to acknowledge + // the data from this callback. + // + USBHCDPipeDataAck(ui32Pipe); + + // + // Update our global "ports needing service" flags with the latest + // information we have just received. + // + g_ui32ChangeFlags |= g_ui32HubChanges; + + // + // Send the report data to the USB host hub device class driver if + // we have been given a callback function. + // + if(g_sRootHub.pfnCallback) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_RX_AVAILABLE, + ui32Pipe, &g_ui32HubChanges); + } + + break; + } + case USB_EVENT_ERROR: + { + break; + } + } +} + +//***************************************************************************** +// +// Query the class-specific hub descriptor. +// +//***************************************************************************** +static bool +GetHubDescriptor(tUsbHubDescriptor *psDesc) +{ + uint32_t ui32Read; + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the device pointer. + // + psDevice = g_sRootHub.psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_DEVICE; + + // + // Set the fields to get the hub descriptor. Initially, we request only + // the first 4 bytes of the descriptor. This will give us the size which + // we use to determine how many bytes to read to get the full descriptor. + // This is necessary since we don't know how many ports the hub can support + // and we only support up to MAX_USB_DEVICES. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = (USB_DTYPE_HUB << 8); + sSetupPacket.wIndex = 0; + sSetupPacket.wLength = sizeof(tUsbHubDescriptor); + + // + // Send the request. + // + ui32Read = USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (void *)psDesc, sizeof(tUsbHubDescriptor), + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Make sure we got at least some data. + // + if(ui32Read == 0) + { + return(false); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// Open an instance of the hub driver. This is called when the USB host +// has enumerated a new hub device. +// +//***************************************************************************** +static void * +HubDriverOpen(tUSBHostDevice *psDevice) +{ + tEndpointDescriptor *psEndpointDescriptor; + tInterfaceDescriptor *psInterface; + tUsbHubDescriptor sHubDesc; + bool bRetcode; + uint32_t ui32Loop; + + // + // If we are already talking to a hub, fail the call. We only support + // a single hub. + // + if(g_sRootHub.ui32Flags & USBLIB_HUB_ACTIVE) + { + return(0); + } + + // + // Get pointers to the device descriptors we need to look at. + // + psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); + psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, 0, + psDevice->ui32ConfigDescriptorSize); + + // + // If there are no endpoints, something is wrong since a hub must have + // a single INT endpoint for signaling. + // + if(psEndpointDescriptor == 0) + { + return 0; + } + + // + // Make sure we really are talking to a hub. + // + if((psInterface->bInterfaceClass != USB_CLASS_HUB) || + (psInterface->bInterfaceSubClass != 0)) + { + // + // Something is wrong - this isn't a hub or, if it is, we don't + // understand the protocol it is using. + // + return(0); + } + + // + // Remember that this is a high speed hub with either single or multiple + // transaction translators. + // + if(psInterface->bInterfaceProtocol == USB_HUB_PROTOCOL_SINGLE) + { + g_sRootHub.ui32Flags |= USBLIB_HUB_HS; + } + else if(psInterface->bInterfaceProtocol == USB_HUB_PROTOCOL_MULTI) + { + g_sRootHub.ui32Flags |= USBLIB_HUB_HS | USBLIB_HUB_MULTI_TT; + } + + // + // Remember the device information for later. + // + g_sRootHub.psDevice = psDevice; + + // + // A hub must support an interrupt endpoint so check this. + // + if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // The endpoint is the correct type. Is it an IN endpoint? + // + if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Yes - all is well with the hub endpoint so allocate a pipe to + // handle traffic from the hub. + // + g_sRootHub.ui32IntInPipe = USBHCDPipeAlloc(0, USBHCD_PIPE_INTR_IN, + psDevice, + HubIntINCallback); + USBHCDPipeConfig(g_sRootHub.ui32IntInPipe, + psEndpointDescriptor->wMaxPacketSize, + psEndpointDescriptor->bInterval, + psEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M); + } + } + + // + // Did we allocate the endpoint successfully? + // + if(!g_sRootHub.ui32IntInPipe) + { + // + // No - return an error. + // + return 0; + } + + // + // Assuming we have a callback, call it to tell the owner that a hub is + // now connected. + // + if(g_sRootHub.pfnCallback != 0) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_CONNECTED, (uint32_t)&g_sRootHub, 0); + } + + // + // Get the hub descriptor and store information we'll need for later. + // + bRetcode = GetHubDescriptor(&sHubDesc); + if(bRetcode) + { + + // + // We read the descriptor successfully so extract the parts we need. + // + g_sRootHub.ui8NumPorts = sHubDesc.bNbrPorts; + g_sRootHub.ui16HubCharacteristics = sHubDesc.wHubCharacteristics; + g_sRootHub.ui8NumPortsInUse = + (sHubDesc.bNbrPorts > MAX_USB_DEVICES) ? MAX_USB_DEVICES : + sHubDesc.bNbrPorts; + + // + // The size of the status change report that the hub sends is dependent + // upon the number of ports that the hub supports. Calculate this by + // adding 1 to the number of ports (bit 0 of the report is the hub + // status, higher bits are one per port) then dividing by 8 (bits per + // byte) and rounding up. + // + g_sRootHub.ui8ReportSize = ((sHubDesc.bNbrPorts + 1) + 7) / 8; + + // + // Enable power to all ports on the hub. + // + for(ui32Loop = 1; ui32Loop <= sHubDesc.bNbrPorts; ui32Loop++) + { + // + // Turn on power to this port. + // + HubSetPortFeature(&g_sRootHub, ui32Loop, + HUB_FEATURE_PORT_POWER); + } + + // + // Clear out our port state structures. + // + for(ui32Loop = 0; ui32Loop < MAX_USB_DEVICES; ui32Loop++) + { + g_sRootHub.psPorts[ui32Loop].bChanged = false; + g_sRootHub.psPorts[ui32Loop].iState = ePortIdle; + } + } + else + { + // + // Oops - we can't read the hub descriptor! Tidy up and return + // an error. + // + USBHCDPipeFree(g_sRootHub.ui32IntInPipe); + g_sRootHub.pfnCallback = 0; + g_sRootHub.ui32Flags &= ~USBLIB_HUB_ACTIVE; + return(0); + } + + // + // If we get here, all is well so remember that the hub is connected and + // active. + // + g_sRootHub.ui32Flags |= USBLIB_HUB_ACTIVE; + + // + // Return our instance data pointer to the caller to use as a handle. + // + return((void *)&g_sRootHub); +} + +//***************************************************************************** +// +// Close an instance of the hub driver. +// +//***************************************************************************** +static void +HubDriverClose(void *pvHubDevice) +{ + uint32_t ui32Loop; + + // + // No device so just exit. + // + if(g_sRootHub.psDevice == 0) + { + return; + } + + // + // Disconnect any devices that are currently connected to the hub. + // + for(ui32Loop = 0; ui32Loop < MAX_USB_DEVICES; ui32Loop++) + { + // + // Does this port have a device connected to it that we have previously + // reported to the host control layer?h + // + if((g_sRootHub.psPorts[ui32Loop].iState == ePortActive) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortEnumerated) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortError)) + { + // + // Yes - tell the host controller to disconnect the device. + // + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui32Loop].ui32DevHandle); + + } + + // + // Make sure that the state returns to idle. + // + g_sRootHub.psPorts[ui32Loop].iState = ePortIdle; + + } + + // + // Reset the device pointer. + // + g_sRootHub.psDevice = 0; + + // + // Mark the hub as absent. + // + g_sRootHub.ui32Flags &= ~USBLIB_HUB_ACTIVE; + + // + // Note that we are not in the middle of enumerating anything. + // + g_sRootHub.bEnumerationBusy = false; + + // + // Free the Interrupt IN pipe. + // + if(g_sRootHub.ui32IntInPipe != 0) + { + USBHCDPipeFree(g_sRootHub.ui32IntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(g_sRootHub.pfnCallback != 0) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_DISCONNECTED, (uint32_t)&g_sRootHub, + 0); + } +} + +//***************************************************************************** +// +// Perform any processing required as a result of a change in the reset +// signaling for a given port. +// +//***************************************************************************** +static void +HubDriverReset(uint8_t ui8Port, bool bResetActive) +{ + // + // Did the reset sequence end or begin? + // + if(!bResetActive) + { + // + // The reset ended. Now wait for at least 10ms before signaling + // USB enumeration code that a new device is waiting to be enumerated. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetWait; + + // + // Set the wait to 10ms (10 frames) from now. + // + g_sRootHub.psPorts[ui8Port].ui32Count = 10; + } + else + { + // + // Was this device previously active? + // + if(g_sRootHub.psPorts[ui8Port].iState == ePortActive) + { + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui8Port].ui32DevHandle); + } + + // + // The reset is active so mark our port as in reset. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetActive; + } +} + +//***************************************************************************** +// +// Start the process of enumerating a new device by issuing a reset to the +// appropriate downstream port. +// +//***************************************************************************** +static void +HubDriverDeviceReset(uint8_t ui8Port) +{ + DEBUG_OUTPUT("Starting enumeration for port %d\n", ui8Port); + + // + // Record the fact that we are in the process of enumerating a device. + // + g_sRootHub.bEnumerationBusy = true; + + // + // Save the port that is being enumerated. + // + g_sRootHub.ui8EnumIdx = ui8Port; + + // + // Mark the port as being reset. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetActive; + + // + // Initiate a reset on the relevant port to start the enumeration process. + // + HubSetPortFeature(&g_sRootHub, ui8Port, HUB_FEATURE_PORT_RESET); +} + +//***************************************************************************** +// +// A new device has been connected to the hub. Allocate resources to manage +// it and pass details back to the main USB host enumeration code to have the +// device enumerated. +// +//***************************************************************************** +static void +HubDriverDeviceConnect(uint8_t ui8Port) +{ + DEBUG_OUTPUT("HubDriverDeviceConnect\n"); + + // + // We've allocated a port table entry so fill it in then initiate a reset + // on the device. + // + g_sRootHub.psPorts[ui8Port].bChanged = false; + + // + // Mark the port as having a device present but not enumerated. + // + DEBUG_OUTPUT("Deferring enumeration for port %d\n", ui8Port); + g_sRootHub.psPorts[ui8Port].iState = ePortConnected; + + // + // Wait 100ms to reset the device. + // + g_sRootHub.psPorts[ui8Port].ui32Count = 100; +} + +//***************************************************************************** +// +// An existing device has been removed from the hub. Tidy up and let the main +// USB host code know so that it can free device resources. +// +//***************************************************************************** +static void +HubDriverDeviceDisconnect(uint8_t ui8Port) +{ + // + // This is a device we are currently managing. Have we already informed + // the host controller that it is present? + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortActive) || + (g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui8Port].iState == ePortEnumerated) || + (g_sRootHub.psPorts[ui8Port].iState == ePortError)) + { + // + // Yes - tell the host controller that the device is not longer + // connected. + // + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui8Port].ui32DevHandle); + } + + // + // If the device was being enumerated, make sure we clear the flag + // indicating that an enumeration is still ongoing. + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortResetActive) || + (g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui8Port].iState == ePortActive)) + { + g_sRootHub.bEnumerationBusy = false; + } + + // + // Free up the port state structure. + // + g_sRootHub.psPorts[ui8Port].iState = ePortIdle; +} + +//***************************************************************************** +// +// This function is called periodically by USBHCDMain(). We use it to handle +// the hub port state machine. +// +//***************************************************************************** +void +USBHHubMain(void) +{ + uint16_t ui16Status, ui16Changed; + uint_fast8_t ui8Port; + bool bRetcode; + + // + // If the hub is not present, just return. + // + if((g_sRootHub.ui32Flags & USBLIB_HUB_ACTIVE) == 0) + { + return; + } + + // + // Initialize the status variables. + // + ui16Status = 0; + ui16Changed = 0; + + // + // The hub is active and something changed. Check to see which port changed + // state and handle as necessary. + // + for(ui8Port = 0; ui8Port <= g_sRootHub.ui8NumPortsInUse; ui8Port++) + { + // + // Decrement any wait counter if there is one present. + // + if(g_sRootHub.psPorts[ui8Port].ui32Count != 0) + { + g_sRootHub.psPorts[ui8Port].ui32Count--; + } + + // + // Is this port waiting to be enumerated and is the last device + // enumeration finished? + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortConnected) && + (!g_sRootHub.bEnumerationBusy) && + (g_sRootHub.psPorts[ui8Port].ui32Count == 0)) + { + // + // Yes - start the enumeration processing for this device. + // + HubDriverDeviceReset(ui8Port); + } + + // + // If the state is ePortResetWait then the hub is waiting before + // accessing device as the USB 2.0 specification requires. + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) && + (g_sRootHub.psPorts[ui8Port].ui32Count == 0)) + { + // + // Start the enumeration process if the timeout has passed and + // the hub is waiting to start enumerating the device. + // + g_sRootHub.psPorts[ui8Port].iState = ePortActive; + + // + // Call the main host controller layer to have it enumerate the + // newly connected device. + // + g_sRootHub.psPorts[ui8Port].ui32DevHandle = + USBHCDHubDeviceConnected(0, 1, ui8Port, + g_sRootHub.psPorts[ui8Port].ui32Speed); + } + + // + // If an enumeration is in progress and the loop is not on the port + // being enumerated then skip the port. + // + if(g_sRootHub.bEnumerationBusy && + (g_sRootHub.ui8EnumIdx != ui8Port)) + { + continue; + } + + // + // Did something change for this particular port? + // + if(g_ui32ChangeFlags & (1 << ui8Port)) + { + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus(&g_sRootHub, ui8Port, &ui16Status, + &ui16Changed); + + // + // Clear this change with the USB interrupt temporarily disabled to + // ensure that we do not clear a flag that the interrupt routine + // has just set. + // + OS_INT_DISABLE(g_sRootHub.ui32IntNum); + g_ui32ChangeFlags &= ~(1 << ui8Port); + OS_INT_ENABLE(g_sRootHub.ui32IntNum); + + // + // If there was an error, go on and look at the next bit. + // + if(!bRetcode) + { + continue; + } + + // + // Now consider what changed and handle it as necessary. + // + + // + // Was a device connected to or disconnected from the port? + // + if(ui16Changed & HUB_PORT_CHANGE_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connection change on port %d\n", ui8Port); + + // + // Clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_CONNECTION); + + // + // Was a device connected or disconnected? + // + if(ui16Status & HUB_PORT_STATUS_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connected\n"); + + // + // A device was connected. + // + HubDriverDeviceConnect(ui8Port); + } + else + { + DEBUG_OUTPUT("Disconnected\n"); + + // + // A device was disconnected. + // + HubDriverDeviceDisconnect(ui8Port); + } + } + + // + // Did a reset on the port complete? + // + if(ui16Changed & HUB_PORT_CHANGE_RESET) + { + // + // Clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_RESET); + + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus(&g_sRootHub, ui8Port, + &ui16Status, &ui16Changed); + + DEBUG_OUTPUT("Reset %s for port %d\n", + ((ui16Status & HUB_PORT_STATUS_RESET) ? "asserted" : + "deasserted"), ui8Port); + + // + // Handle the reset case. + // + HubDriverReset(ui8Port, (ui16Status & HUB_PORT_STATUS_RESET) ? + true : false); + + // + // A device was connected. + // + if(ui16Status & HUB_PORT_STATUS_LOW_SPEED) + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_LOW); + } + else if(ui16Status & HUB_PORT_STATUS_HIGH_SPEED) + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_HIGH); + } + else + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_FULL); + } + } + + // + // Did an over-current reset on the port complete? + // + if(ui16Changed & HUB_PORT_CHANGE_OVER_CURRENT) + { + DEBUG_OUTPUT("Port %d over current.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_OVER_CURRENT); + } + + // + // Has the port been enabled or disabled? + // + if(ui16Changed & HUB_PORT_CHANGE_ENABLED) + { + DEBUG_OUTPUT("Enable change for port %d.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_ENABLE); + } + + // + // Has the port been suspended or resumed? + // + if(ui16Changed & HUB_PORT_CHANGE_SUSPENDED) + { + DEBUG_OUTPUT("Suspend change for port %d.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_SUSPEND); + } + } + } +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device has been enumerated. +//! +//! \param ui8Hub is the address of the hub to which the downstream device +//! is attached. +//! \param ui8Port is the port on the hub to which the downstream device is +//! attached. +//! +//! This function is called by the host controller driver to inform the hub +//! class driver that a downstream device has been enumerated successfully. +//! The hub driver then moves on and continues enumeration of any other newly +//! connected devices. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubEnumerationComplete(uint8_t ui8Hub, uint8_t ui8Port) +{ + DEBUG_OUTPUT("Enumeration complete for hub %d, port %d\n", ui8Hub, ui8Port); + + // + // Record the fact that the device is up and running. + // + g_sRootHub.psPorts[ui8Port].iState = ePortEnumerated; + + // + // Clear the flag we use to defer further enumerations. This will cause + // the next connected device (if any) to start enumeration on the next + // call to USBHHubMain(). + // + g_sRootHub.bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device failed to enumerate. +//! +//! \param ui8Hub is the address of the hub to which the downstream device +//! is attached. +//! \param ui8Port is the port on the hub to which the downstream device is +//! attached. +//! +//! This function is called by the host controller driver to inform the hub +//! class driver that an attempt to enumerate a downstream device has failed. +//! The hub driver then cleans up and continues enumeration of any other newly +//! connected devices. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubEnumerationError(uint8_t ui8Hub, uint8_t ui8Port) +{ + DEBUG_OUTPUT("Enumeration error for hub %d, port %d\n", ui8Hub, ui8Port); + + // + // Record the fact that the device is not working correctly. + // + g_sRootHub.psPorts[ui8Port].iState = ePortError; + + // + // Clear the flag we use to defer further enumerations. This will cause + // the next connected device (if any) to start enumeration on the next + // call to USBHHubMain(). + // + g_sRootHub.bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! This function is used to enable the host hub class driver before any +//! devices are present. +//! +//! \param pfnCallback is the driver call back for host hub events. +//! +//! This function is called to open an instance of a host hub device and +//! provides a valid callback function for host hub events in the +//! \e pfnCallback parameter. This function must be called before the USB +//! host code can successfully enumerate a hub device or any devices attached +//! to the hub. The \e pui8HubPool is memory provided to the hub class to +//! manage the devices that are connected to the hub. The \e ui32PoolSize is +//! the number of bytes and should be at least 32 bytes per device including +//! the hub device itself. A simple formula for providing memory to the hub +//! class is \b MAX_USB_DEVICES * 32 bytes of data to allow for proper +//! enumeration of connected devices. The value for \b MAX_USB_DEVICES is +//! defined in the usblib.h file and controls the number of devices +//! supported by the USB library. The \e ui32NumHubs parameter +//! defaults to one and only one buffer of size tHubInstance is required to +//! be passed in the \e psHubInstance parameter. +//! +//! \note Changing the value of \b MAX_USB_DEVICES requires a rebuild of the +//! USB library to have an effect on the library. +//! +//! \return This function returns the driver instance to use for the other +//! host hub functions. If there is no instance available at the time of +//! this call, this function returns zero. +// +//***************************************************************************** +tHubInstance * +USBHHubOpen(tUSBHHubCallback pfnCallback) +{ + // + // Only one hub is supported. + // + if(g_sRootHub.pfnCallback) + { + DEBUG_OUTPUT("USBHHubOpen failed - already connected.\n"); + return(0); + } + + // + // Save the instance data for this device. + // + g_sRootHub.pfnCallback = pfnCallback; + + DEBUG_OUTPUT("USBHHubOpen completed.\n"); + + // + // Return the device instance pointer. + // + return(&g_sRootHub); +} + +//***************************************************************************** +// +//! This function is used to release a hub device instance. +//! +//! \param psHubInstance is the hub device instance that is to be released. +//! +//! This function is called when an instance of the hub device must be +//! released. This function is typically made in preparation for shutdown or a +//! switch to function as a USB device when in OTG mode. Following this call, +//! the hub device is no longer available, but it can be opened again using a +//! call to USBHHubOpen(). After calling USBHHubClose(), the host hub driver +//! no longer provides any callbacks or accepts calls to other hub driver APIs. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubClose(tHubInstance *psHubInstance) +{ + // + // Forget the instance pointer and callback. + // + psHubInstance->psDevice = 0; + psHubInstance->pfnCallback = 0; + + DEBUG_OUTPUT("USBHHubClose completed.\n"); +} + +//***************************************************************************** +// +// This function is used to initialize the Hub driver. This is an internal +// function that should not be called by the application. +// +//***************************************************************************** +void +USBHHubInit(void) +{ + // + // Initialize Hub state. + // + g_ui32ChangeFlags = 0; + g_ui32HubChanges = 0; + + if(g_sRootHub.psDevice != 0) + { + // + // Save the USB interrupt number. + // + g_sRootHub.ui32IntNum = INT_USB0_TM4C123; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_sRootHub.ui32IntNum = INT_USB0_TM4C129; + } + } +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psHubInstance is the hub device instance that was returned +//! from the call to USBHHubOpen(). +//! +//! This function forwards a request from an application to the hub 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: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHHubLPMSleep(tHubInstance *psHubInstance) +{ + // + // Call the host controller function to send the sleep command. + // + return(USBHCDLPMSleep(psHubInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psHubInstance is the hub device instance that was returned +//! from the call to USBHHubOpen(). +//! +//! 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 +USBHHubLPMStatus(tHubInstance *psHubInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psHubInstance->psDevice)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhub.h b/usblib/host/usbhhub.h new file mode 100644 index 0000000..17a3c08 --- /dev/null +++ b/usblib/host/usbhhub.h @@ -0,0 +1,174 @@ +//***************************************************************************** +// +// usbhhub.h - This hold the host driver for hid class. +// +// Copyright (c) 2011-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. +// +//***************************************************************************** + +#ifndef __USBHHUB_H__ +#define __USBHHUB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct tHubInstance tHubInstance; + +extern const tUSBHostClassDriver g_sUSBHubClassDriver; + +//***************************************************************************** +// +// The USB standard allows for up to 127 downstream ports on a single hub. +// This would require rather more memory than we would like to set aside so the +// default configuration of the hub driver supports hubs with up to 7 +// downstream-facing ports. In practice, this should be more than enough +// since this covers the vast majority of consumer hubs. Note that, by +// default, we will only support 4 devices so you can't fully populate a 7 port +// hub and have everything work. +// +// Feel free to change this but bad things will happen if you increase it above +// 31 since we assume the reports will always fit inside a 4 byte buffer. +// +//***************************************************************************** +#define ROOT_HUB_MAX_PORTS 7 + +//***************************************************************************** +// +// Values used as the ui16Feature parameter to USBHHubClearHubFeature(). +// +//***************************************************************************** +#define HUB_FEATURE_C_HUB_LOCAL_POWER \ + 0 +#define HUB_FEATURE_C_HUB_OVER_CURRENT \ + 1 + +//***************************************************************************** +// +// Values used as the ui16Feature parameter to USBHHubSetPortFeature() and +// USBHHubClearPortFeature(). +// +//***************************************************************************** +#define HUB_FEATURE_PORT_CONNECTION \ + 0 +#define HUB_FEATURE_PORT_ENABLE 1 +#define HUB_FEATURE_PORT_SUSPEND \ + 2 +#define HUB_FEATURE_PORT_OVER_CURRENT \ + 3 +#define HUB_FEATURE_PORT_RESET 4 +#define HUB_FEATURE_PORT_POWER 8 +#define HUB_FEATURE_PORT_LOW_SPEED \ + 9 +#define HUB_FEATURE_C_PORT_CONNECTION \ + 16 +#define HUB_FEATURE_C_PORT_ENABLE \ + 17 +#define HUB_FEATURE_C_PORT_SUSPEND \ + 18 +#define HUB_FEATURE_C_PORT_OVER_CURRENT \ + 19 +#define HUB_FEATURE_C_PORT_RESET \ + 20 +#define HUB_FEATURE_PORT_TEST 21 +#define HUB_FEATURE_PORT_INDICATOR \ + 22 + +//***************************************************************************** +// +// Values returned via the *pui16HubStatus and *pui16HubChange parameters +// passed to USBHHubGetHubStatus(). These may be ORed together into the +// returned status value. +// +//***************************************************************************** +#define HUB_STATUS_PWR_LOST 1 +#define HUB_STATUS_OVER_CURRENT 2 + +//***************************************************************************** +// +// Values returned via the *pui16PortStatus parameter passed to +// USBHHubGetPortStatus(). These may be ORed together into the returned status +// value. +// +//***************************************************************************** +#define HUB_PORT_STATUS_DEVICE_PRESENT \ + 0x0001 +#define HUB_PORT_STATUS_ENABLED 0x0002 +#define HUB_PORT_STATUS_SUSPENDED \ + 0x0004 +#define HUB_PORT_STATUS_OVER_CURRENT \ + 0x0008 +#define HUB_PORT_STATUS_RESET 0x0010 +#define HUB_PORT_STATUS_POWERED 0x0100 +#define HUB_PORT_STATUS_LOW_SPEED \ + 0x0200 +#define HUB_PORT_STATUS_HIGH_SPEED \ + 0x0400 +#define HUB_PORT_STATUS_TEST_MODE \ + 0x0800 +#define HUB_PORT_STATUS_INDICATOR_CONTROL \ + 0x1000 + +//***************************************************************************** +// +// Values returned via the *pui16PortChange parameter passed to +// USBHHubGetPortStatus(). These may be ORed together into the returned status +// value. +// +//***************************************************************************** +#define HUB_PORT_CHANGE_DEVICE_PRESENT \ + 0x0001 +#define HUB_PORT_CHANGE_ENABLED 0x0002 +#define HUB_PORT_CHANGE_SUSPENDED \ + 0x0004 +#define HUB_PORT_CHANGE_OVER_CURRENT \ + 0x0008 +#define HUB_PORT_CHANGE_RESET 0x0010 + +//***************************************************************************** +// +// The prototype for the USB Hub host driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHHubCallback)(tHubInstance *psHubInstance, + uint32_t ui32Event, uint32_t ui32MsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// Public function prototypes for the HUB class driver. +// +//***************************************************************************** +extern tHubInstance * USBHHubOpen(tUSBHHubCallback pfnCallback); +extern void USBHHubClose(tHubInstance *psHubInstance); +extern uint32_t USBHHubLPMSleep(tHubInstance *psHubInstance); +extern uint32_t USBHHubLPMStatus(tHubInstance *psHubInstance); + +#ifdef __cplusplus +} +#endif + +#endif // __USBHHUB_H__ diff --git a/usblib/host/usbhmsc.c b/usblib/host/usbhmsc.c new file mode 100644 index 0000000..9ef72fa --- /dev/null +++ b/usblib/host/usbhmsc.c @@ -0,0 +1,756 @@ +//***************************************************************************** +// +// 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. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhmsc.h b/usblib/host/usbhmsc.h new file mode 100644 index 0000000..f163b71 --- /dev/null +++ b/usblib/host/usbhmsc.h @@ -0,0 +1,99 @@ +//***************************************************************************** +// +// usbhmsc.h - Definitions for the 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. +// +//***************************************************************************** + +#ifndef __USBHMSC_H__ +#define __USBHMSC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +typedef struct tUSBHMSCInstance tUSBHMSCInstance; + +//***************************************************************************** +// +// These defines are the the events that will be passed in the \e ui32Event +// parameter of the callback from the driver. +// +//***************************************************************************** +#define MSC_EVENT_OPEN 1 +#define MSC_EVENT_CLOSE 2 + +//***************************************************************************** +// +// The prototype for the USB MSC host driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHMSCCallback)(tUSBHMSCInstance *psMSCInstance, + uint32_t ui32Event, + void *pvEventData); + +//***************************************************************************** +// +// Prototypes for the USB MSC host driver APIs. +// +//***************************************************************************** +extern tUSBHMSCInstance * USBHMSCDriveOpen(uint32_t ui32Drive, + tUSBHMSCCallback pfnCallback); +extern void USBHMSCDriveClose(tUSBHMSCInstance *psMSCInstance); +extern int32_t USBHMSCDriveReady(tUSBHMSCInstance *psMSCInstance); +extern int32_t USBHMSCBlockRead(tUSBHMSCInstance *psMSCInstance, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t ui32NumBlocks); +extern int32_t USBHMSCBlockWrite(tUSBHMSCInstance *psMSCInstance, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t ui32NumBlocks); +extern uint32_t USBHMSCLPMSleep(tUSBHMSCInstance *psMSCInstance); +extern uint32_t USBHMSCLPMStatus(tUSBHMSCInstance *psMSCInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHMSC_H__ diff --git a/usblib/host/usbhost.h b/usblib/host/usbhost.h new file mode 100644 index 0000000..1d4c455 --- /dev/null +++ b/usblib/host/usbhost.h @@ -0,0 +1,295 @@ +//***************************************************************************** +// +// usbhost.h - Host specific definitions for the USB host library. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHOST_H__ +#define __USBHOST_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_hcd +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the type used to identify what the pipe is currently in use for. +// +//***************************************************************************** +#define USBHCD_PIPE_UNUSED 0x00100000 +#define USBHCD_PIPE_CONTROL 0x00130000 +#define USBHCD_PIPE_BULK_OUT 0x00210000 +#define USBHCD_PIPE_BULK_IN 0x00220000 +#define USBHCD_PIPE_INTR_OUT 0x00410000 +#define USBHCD_PIPE_INTR_IN 0x00420000 +#define USBHCD_PIPE_ISOC_OUT 0x00810000 +#define USBHCD_PIPE_ISOC_IN 0x00820000 +#define USBHCD_PIPE_ISOC_OUT_DMA 0x01810000 +#define USBHCD_PIPE_ISOC_IN_DMA 0x01820000 +#define USBHCD_PIPE_BULK_OUT_DMA 0x01210000 +#define USBHCD_PIPE_BULK_IN_DMA 0x01220000 + +//***************************************************************************** +// +// These are the defines that are used with USBHCDPowerConfigInit(). +// +//***************************************************************************** +#define USBHCD_FAULT_LOW 0x00000010 +#define USBHCD_FAULT_HIGH 0x00000030 +#define USBHCD_FAULT_VBUS_NONE 0x00000000 +#define USBHCD_FAULT_VBUS_TRI 0x00000140 +#define USBHCD_FAULT_VBUS_DIS 0x00000400 +#define USBHCD_VBUS_MANUAL 0x00000004 +#define USBHCD_VBUS_AUTO_LOW 0x00000002 +#define USBHCD_VBUS_AUTO_HIGH 0x00000003 +#define USBHCD_VBUS_FILTER 0x00010000 + +//***************************************************************************** +// +// These are the defines that are used with USBHCDLPMStatus(). +// +//***************************************************************************** +#define USBHCD_LPM_AVAIL 0x00000000 +#define USBHCD_LPM_ERROR 0x00000001 +#define USBHCD_LPM_PENDING 0x00000002 + +//***************************************************************************** +// +//! This macro is used to declare an instance of an Event driver for the USB +//! library. +//! +//! \param VarName is the name of the variable. +//! \param pfnOpen is the callback for the Open call to this driver. This +//! value is currently reserved and should be set to 0. +//! \param pfnClose is the callback for the Close call to this driver. This +//! value is currently reserved and should be set to 0. +//! \param pfnEvent is the callback that will be called for various USB events. +//! +//! The first parameter is the actual name of the variable that will +//! be declared by this macro. The second and third parameter are reserved +//! for future functionality and are unused and should be set to zero. The +//! last parameter is the actual callback function and is specified as +//! a function pointer of the type: +//! +//! \verbatim +//! void (*pfnEvent)(void *pvData); +//! \endverbatim +//! +//! When the \e pfnEvent function is called the void pointer that is passed in +//! as a parameter should be cast to a pointer to a structure of type +//! tEventInfo. This will contain the event that caused the pfnEvent function +//! to be called. +// +//***************************************************************************** +#define DECLARE_EVENT_DRIVER(VarName, pfnOpen, pfnClose, pfnEvent) \ +void IntFn(void *pvData); \ +const tUSBHostClassDriver VarName = \ +{ \ + USB_CLASS_EVENTS, \ + 0, \ + 0, \ + pfnEvent \ +} + +//***************************************************************************** +// +// This is the type definition a callback for events on USB Pipes allocated +// by USBHCDPipeAlloc(). +// +// \param ui32Pipe is well the pipe +// \param ui32Event is well the event +// +// This prototype is used by any Pipe callbacks that are used in the host +// class drivers. These functions typically handle data events like +// USB_EVENT_RX_AVAILABLE or USB_EVENT_TX_COMPLETE but can be sent other events +// depending on the USB host class in use. See the documentation for the +// individual classes for the valid events for that class. +// +// \return None. +// +//***************************************************************************** +typedef void (* tHCDPipeCallback)(uint32_t ui32Pipe, uint32_t ui32Event); + +//***************************************************************************** +// +// Predeclare the private tUSBHostDevice structure. +// +//***************************************************************************** +typedef struct tUSBHostDevice tUSBHostDevice; + +//***************************************************************************** +// +//! This structure defines a USB host class driver interface, it is parsed to +//! find a USB class driver once a USB device is enumerated. +// +//***************************************************************************** +typedef struct +{ + // + //! The interface class that this device class driver supports. + // + uint32_t ui32InterfaceClass; + + // + //! The function is called when this class of device has been detected. + // + void *(*pfnOpen)(tUSBHostDevice *psDevice); + + // + //! The function is called when the device, originally opened with a call + //! to the pfnOpen function, is disconnected. + // + void (*pfnClose)(void *pvInstance); + + // + //! This is the optional interrupt handler that will be called when an + //! endpoint associated with this device instance generates an interrupt. + // + void (*pfnIntHandler)(void *pvInstance); +} +tUSBHostClassDriver; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// If the g_USBEventDriver is included in the host controller driver list then +// this function must be provided by the application. +// +//***************************************************************************** +extern void USBHCDEvents(void *pvData); + +//***************************************************************************** +// +// Prototypes for the USB Host controller APIs. +// +//***************************************************************************** +extern void USBHCDMain(void); +extern int32_t USBHCDEventEnable(uint32_t ui32Index, void *pvEventDriver, + uint32_t ui32Event); +extern int32_t USBHCDEventDisable(uint32_t ui32Index, void *pvEventDriver, + uint32_t ui32Event); +extern void USBHCDInit(uint32_t ui32Index, void *pvData, + uint32_t ui32Size); +extern void USBHCDPowerConfigInit(uint32_t ui32Index, + uint32_t ui32Flags); +extern uint32_t USBHCDPowerConfigGet(uint32_t ui32Index); +extern uint32_t USBHCDPowerConfigSet(uint32_t ui32Index, + uint32_t ui32Config); +extern uint32_t USBHCDPowerAutomatic(uint32_t ui32Index); +extern void USBHCDRegisterDrivers(uint32_t ui32Index, + const tUSBHostClassDriver * const *ppsHClassDrvrs, + uint32_t ui32NumDrivers); +extern void USBHCDTerm(uint32_t ui32Index); +extern void USBHCDSetConfig(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Configuration); +extern void USBHCDSetInterface(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Interface, + uint32_t ui32AltSetting); +extern void USBHCDSuspend(uint32_t ui32Index); +extern void USBHCDResume(uint32_t ui32Index); +extern void USBHCDReset(uint32_t ui32Index); +extern void USBHCDPipeFree(uint32_t ui32Pipe); +extern uint32_t USBHCDPipeAlloc(uint32_t ui32Index, + uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, + tHCDPipeCallback pfnCallback); +extern uint32_t USBHCDPipeAllocSize(uint32_t ui32Index, + uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, + uint32_t ui32FIFOSize, + tHCDPipeCallback pfnCallback); +extern uint32_t USBHCDPipeConfig(uint32_t ui32Pipe, uint32_t ui32MaxPayload, + uint32_t ui32Interval, + uint32_t ui32TargetEndpoint); +extern uint32_t USBHCDPipeStatus(uint32_t ui32Pipe); +extern uint32_t USBHCDPipeWrite(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDPipeRead(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDPipeSchedule(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDPipeTransferSizeGet(uint32_t ui32Pipe); +extern void USBHCDPipeDataAck(uint32_t ui32Pipe); +extern uint32_t USBHCDPipeReadNonBlocking(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDStringDescriptorGet(tUSBHostDevice *psDevice, + uint8_t *pui8Buffer, + uint32_t ui32Size, + uint32_t ui32LangID, + uint32_t ui32StringIndex); +extern uint32_t USBHCDControlTransfer(uint32_t ui32Index, + tUSBRequest *psSetupPacket, + tUSBHostDevice *psDevice, + uint8_t *pui8Data, uint32_t ui32Size, + uint32_t ui32MaxPacketSize); +extern void USB0HostIntHandler(void); + +extern uint8_t USBHCDDevHubPort(uint32_t ui32Instance); +extern uint8_t USBHCDDevAddress(uint32_t ui32Instance); +extern uint8_t USBHCDDevClass(uint32_t ui32Instance, uint32_t ui32Interface); +extern uint8_t USBHCDDevSubClass(uint32_t ui32Instance, + uint32_t ui32Interface); +extern uint8_t USBHCDDevProtocol(uint32_t ui32Instance, + uint32_t ui32Interface); +extern bool USBHCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, + void *pvFeature); +extern uint32_t USBHCDLPMSleep(tUSBHostDevice *psDevice); +extern void USBHCDLPMResume(uint32_t ui32Index); + +//***************************************************************************** +// +// The host class drivers supported by the USB library. +// +//***************************************************************************** +extern const tUSBHostClassDriver g_sUSBHostMSCClassDriver; +extern const tUSBHostClassDriver g_sUSBHIDClassDriver; +extern const tUSBHostClassDriver g_sUSBHostAudioClassDriver; + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHOST_H__ diff --git a/usblib/host/usbhostenum.c b/usblib/host/usbhostenum.c new file mode 100644 index 0000000..69b8c39 --- /dev/null +++ b/usblib/host/usbhostenum.c @@ -0,0 +1,6284 @@ +//***************************************************************************** +// +// usbhostenum.c - Device enumeration code for the USB host library. +// +// 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_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbulpi.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhhub.h" + +#ifdef INCLUDE_DEBUG_OUTPUT +#include "utils/uartstdio.h" +#define DEBUG_OUTPUT UARTprintf +#else +#define DEBUG_OUTPUT while(0)((int32_t (*)(char *, ...))0) +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_hcd +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_iUSBMode; + +extern void OTGDeviceDisconnect(uint32_t ui32Index); + +//***************************************************************************** +// +// Internal function prototypes. +// +//***************************************************************************** +static void USBHCDEP0StateTx(void); +static void USBHCDEnumHandler(void); +static void USBHCDClearFeature(uint32_t ui32DevAddress, uint32_t ui32Endpoint, + uint32_t ui32Feature); + +//***************************************************************************** +// +// Automatic power enable. +// +//***************************************************************************** +#define USB_HOST_PWREN_AUTO 0x00000002 + +//***************************************************************************** +// +// Flags used to signal between the interrupt handler and USBHCDMain(). +// +//***************************************************************************** +#define INT_EVENT_VBUS_ERR 0x01 +#define INT_EVENT_CONNECT 0x02 +#define INT_EVENT_DISCONNECT 0x04 +#define INT_EVENT_POWER_FAULT 0x08 +#define INT_EVENT_SOF 0x10 +#define INT_EVENT_ENUM 0x20 +#define INT_EVENT_LPM_PEND 0x40 +#define INT_EVENT_LPM 0x80 + +//***************************************************************************** +// +// Flags used with the tUSBHostDevice.ui32Flags structure member. +// +//***************************************************************************** +#define USBHDEV_FLAG_NOTIFYINT 0x00000001 +#define USBHDEV_FLAG_LPMPEND 0x00000002 +#define USBHDEV_FLAG_LPMERROR 0x00000004 +#define USBHDEV_FLAG_ALLOCATED 0x80000000 + +//***************************************************************************** +// +// This holds the current power configuration that is used when USBHCDInit() +// is called. +// +//***************************************************************************** +static uint32_t g_ui32PowerConfig = USBHCD_VBUS_AUTO_HIGH; + +//***************************************************************************** +// +// The states for endpoint 0 during enumeration. +// +//***************************************************************************** +typedef enum +{ + // + // The USB device is waiting on a request from the host controller on + // endpoint 0. + // + eEP0StateIdle, + + // + // Setup packet is expecting data IN. + // + eEP0StateSetupIN, + + // + // Setup packet is sending data OUT. + // + eEP0StateSetupOUT, + + // + // The USB device is receiving data from the device due to an SETUP IN + // request. + // + eEP0StateRx, + + // + // The USB device has completed the IN or OUT request and is now waiting + // for the host to acknowledge the end of the IN/OUT transaction. This + // is the status phase for a USB control transaction. + // + eEP0StateStatus, + + // + // This state is for when a response only has a status phase and no + // data phase. + // + eEP0StateStatusIN, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + eEP0StateStall, + + // + // An error has occurred on endpoint 0. + // + eEP0StateError +} +tEP0State; + +//***************************************************************************** +// +// This structure holds the full state for the device enumeration. +// +//***************************************************************************** +typedef struct +{ + // + // This is the pointer to the current data being sent out or received + // on endpoint 0. + // + uint8_t *pui8Data; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_DeviceState.pEP0Data data buffer. + // + volatile uint32_t ui32BytesRemaining; + + // + // The amount of data being sent/received due to a request. + // + uint32_t ui32DataSize; + + // + // This is the current device address in use by endpoint 0. + // + uint32_t ui32DevAddress; + + // + // The maximum packet size for the device responding to the setup packet. + // + uint32_t ui32MaxPacketSize; + + // + // The host controller's state. + // + tEP0State iState; +} +tHostState; + +//***************************************************************************** +// +// This variable holds the current state of endpoint 0. +// +//***************************************************************************** +static volatile tHostState g_sUSBHEP0State = +{ + 0, // pui8Data + 0, // ui32BytesRemaining + 0, // ui32DataSize + 0, // ui32DevAddress + 0, // ui32MaxPacketSize + eEP0StateIdle // iState +}; + +//***************************************************************************** +// +// The global delay time for use by SysCtlDelay() function. This is +// initialized to an appropriate value for a 50MHz clock. The correct value +// will be set in USBHCDInit(). +// +//***************************************************************************** +static uint32_t g_ui32Tickms = 0; +static volatile uint32_t g_ui32CurrentTick = 0; + +//***************************************************************************** +// +// This is 480000000/60000000 or a PLL Divide of 8. +// +//***************************************************************************** +static uint32_t g_ui32PLLDiv = 8; + +//***************************************************************************** +// +// Holds the ULPI configuration. +// +//***************************************************************************** +static uint32_t g_ui32ULPISupport; + +//***************************************************************************** +// +// The current active drivers. +// +//***************************************************************************** +static int32_t g_pi32USBHActiveDriver[MAX_USB_DEVICES + 1]; +static void *g_ppvDriverInstance[MAX_USB_DEVICES + 1]; + +//***************************************************************************** +// +// This is the structure used to hold the information for a given USB pipe +// that is attached to a device. +// +//***************************************************************************** +typedef struct +{ + // + // The current address for this pipe. + // + tUSBHostDevice *psDevice; + + // + // The current address for this pipe. + // + uint8_t ui8EPNumber; + + // + // The DMA channel assigned to this endpoint. + // + uint8_t ui8DMAChannel; + + // + // The current type for this pipe. + // + uint32_t ui32Type; + + // + // The millisecond interval for this pipe. + // + uint32_t ui32Interval; + + // + // The next tick value to trigger and event on this pipe. + // + uint32_t ui32NextEventTick; + + // + // The current call back for this pipe. + // + tHCDPipeCallback pfnCallback; + + // + // The pointer to which IN data must be copied. + // + uint8_t *pui8ReadPtr; + + // + // The size of the buffer pointed to by pui8ReadPtr. + // + uint32_t ui32ReadSize; + + // + // The number of bytes read, which can be less than ui32ReadSize. + // + uint32_t ui32DataRead; + + // + // The state of a given USB pipe. + // + volatile enum + { + ePipeReading, + ePipeReadDMA, + ePipeReadDMAWait, + ePipeReadSingle, + ePipeDataReady, + ePipeDataSent, + ePipeWriting, + ePipeWriteDMA, + ePipeWriteDMASend, + ePipeWriteDMAWait, + ePipeWriteSingle, + ePipeStalled, + ePipeError, + ePipeIdle, + ePipeDisabled + } + iState; + + // + // The actual FIFO offset allocated to this endpoint. + // + uint16_t ui16FIFOAddr; + + // + // The size of the FIFO entry based on the size parameter. These are + // equivalent to the USB_FIFO_SZ_* values in usb.h. + // + uint8_t ui8FIFOSize; + + // + // The bit offset in the allocation structure. + // + uint8_t ui8FIFOBitOffset; +} +tUSBHCDPipe; + +//***************************************************************************** +// +// The internal state of the device. +// +//***************************************************************************** +typedef enum +{ + eHCDDevDisconnected, + eHCDDevConnected, + eHCDDevConnectedHub, + eHCDDevRequest, + eHCDDevReset, + eHCDDevAddressed, + eHCDDevConfigured, + eHCDDevGetStrings, + eHCDDevError, + eHCDVBUSError, + eHCDPowerFault, + eHCDIdle +} +tUSBHDeviceState; + +static void ProcessUSBDeviceStateMachine(tUSBHDeviceState iOldState, + uint32_t ui32DevIndex); + +//***************************************************************************** +// +// This is a fixed number as it relates to the maximum number of USB pipes +// available on any USB controller. The actual number on a given device may +// be less than this number. +// +//***************************************************************************** +#define MAX_NUM_PIPES 15 + +//***************************************************************************** +// +// This is a fixed number as it relates to the number of USB pipes available +// in the USB controller. +// +//***************************************************************************** +#define MAX_NUM_DMA_CHANNELS 6 + +//***************************************************************************** +// +// Marker for an unused DMA channel slot. +// +//***************************************************************************** +#define USBHCD_DMA_UNUSED 0xff + +//***************************************************************************** +// +// These definitions are used to manipulate the values returned as allocated +// USB pipes. +// +//***************************************************************************** +#define EP_PIPE_TYPE_LOW_SPEED 0x02000000 +#define EP_PIPE_USE_UDMA 0x01000000 +#define EP_PIPE_TYPE_ISOC 0x00800000 +#define EP_PIPE_TYPE_INTR 0x00400000 +#define EP_PIPE_TYPE_BULK 0x00200000 +#define EP_PIPE_TYPE_CONTROL 0x00100000 +#define EP_PIPE_TYPE_IN 0x00020000 +#define EP_PIPE_TYPE_OUT 0x00010000 +#define EP_PIPE_IDX_M 0x0000ffff + +//***************************************************************************** +// +// This creates a USB pipe handle from an index. +// +//***************************************************************************** +#define OUT_PIPE_HANDLE(ui32Idx) \ + (g_sUSBHCD.psUSBOUTPipes[ui32Idx].ui32Type | \ + ui32Idx) +#define IN_PIPE_HANDLE(ui32Idx) (g_sUSBHCD.psUSBINPipes[ui32Idx].ui32Type | \ + ui32Idx) + +//***************************************************************************** +// +// Converts from an endpoint specifier to the offset of the endpoint's +// control/status registers. +// +//***************************************************************************** +#define EP_OFFSET(Endpoint) (Endpoint - 0x10) + +//***************************************************************************** +// +// This structure holds the state information for a given host controller. +// +//***************************************************************************** +typedef struct +{ + uint32_t ui32USBBase; + + tUSBHCDPipe sUSBControlPipe; + tUSBHCDPipe psUSBOUTPipes[MAX_NUM_PIPES]; + tUSBHCDPipe psUSBINPipes[MAX_NUM_PIPES]; + + // + // Each devices state. We support a total of (MAX_USB_DEVICES + 1) devices + // to allow for the use if MAX_USB_DEVICES through a single hub (which is + // itself a device). + // + tUSBHostDevice psUSBDevice[MAX_USB_DEVICES + 1]; + + // + // Holds the current state of the device. + // + volatile tUSBHDeviceState piDeviceState[MAX_USB_DEVICES + 1]; + + // + // Pointer to the memory pool for this controller. + // + void *pvPool; + + // + // The pool size for this controller. + // + uint32_t ui32PoolSize; + + // + // The number of endpoint pairs supported by the controller. + // + uint32_t ui32NumEndpoints; + + // + // The class drivers for this controller. + // + const tUSBHostClassDriver * const *ppsClassDrivers; + + // + // The number of class drivers. + // + uint32_t ui32NumClassDrivers; + + // + // This is the index in the driver list of the event driver. + // + int32_t i32EventDriver; + + // + // These are the generic event information used by the event driver. + // + uint32_t ui32EventEnables; + + uint32_t ui32Class; + + // + // The DMA instance information for this class. + // + tUSBDMAInstance *psDMAInstance; + + // + // The interrupt number for this instance. + // + uint32_t ui32IntNum; + + // + // The pending interrupt events that are processed in USBHCDMain(). + // + uint32_t ui32IntEvents; + + // + // Features. + // + uint32_t ui32Features; + + // + // The host initiated resume duration in us. + // + uint32_t ui32LPMHIRD; +} +tUSBHCD; + +//***************************************************************************** +// +// The global to hold all of the state information for a given host controller. +// +//***************************************************************************** +static tUSBHCD g_sUSBHCD; + +//***************************************************************************** +// +// Allocates the memory needed to support configuration descriptors for +// devices. +// +//***************************************************************************** +uint32_t +ConfigDescAlloc(tUSBHostDevice *psDevice, uint32_t ui32Size) +{ + uint32_t ui32Idx, ui32BlockSize, ui32PoolSize; + uint8_t *pui8Pool; + + if(g_sUSBHCD.psUSBDevice[0].psConfigDescriptor == 0) + { + // + // 32 bit align the allocation. + // + ui32Size = (ui32Size + 3) & ~3; + + if(g_sUSBHCD.ui32PoolSize < ui32Size) + { + return(0); + } + + // + // Allocate the root connection to the device. + // + g_sUSBHCD.psUSBDevice[0].psConfigDescriptor = g_sUSBHCD.pvPool; + g_sUSBHCD.psUSBDevice[0].ui32ConfigDescriptorSize = ui32Size; + + // + // Allocate the hub memory pools (these can end up null). + // + pui8Pool = (uint8_t *)g_sUSBHCD.pvPool + ui32Size; + ui32PoolSize = g_sUSBHCD.ui32PoolSize - ui32Size; + + // + // Divide the pool up into blocks, one for each supported port. We make + // sure that each block is a multiple of 4 bytes. + // + ui32BlockSize = (ui32PoolSize / MAX_USB_DEVICES) & ~3; + for(ui32Idx = 1; ui32Idx < MAX_USB_DEVICES; ui32Idx++) + { + g_sUSBHCD.psUSBDevice[ui32Idx].psConfigDescriptor = + (tConfigDescriptor *)(pui8Pool + + (ui32Idx * ui32BlockSize)); + g_sUSBHCD.psUSBDevice[ui32Idx].ui32ConfigDescriptorSize = + ui32BlockSize; + } + } + return(ui32Size); +} + +//***************************************************************************** +// +// Frees the memory needed to support configuration descriptors for +// devices. +// +//***************************************************************************** +void +ConfigDescFree(tUSBHostDevice *psDevice) +{ + uint32_t ui32Idx; + + // + // If this is the root device then deallocate. + // + if(&g_sUSBHCD.psUSBDevice[0] == psDevice) + { + for(ui32Idx = 0; ui32Idx < MAX_USB_DEVICES; ui32Idx++) + { + g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags &= + ~USBHDEV_FLAG_ALLOCATED; + g_sUSBHCD.psUSBDevice[ui32Idx].psConfigDescriptor = 0; + g_sUSBHCD.psUSBDevice[ui32Idx].ui32ConfigDescriptorSize = 0; + } + } +} + +//***************************************************************************** +// +// Return the device index from a ui32Instance value passed from an external +// source. +// +//***************************************************************************** +static uint8_t +HCDInstanceToDevIndex(uint32_t ui32Instance) +{ + uint32_t ui32DevIndex; + + // + // Get the device instance from the instance value. + // + ui32DevIndex = (ui32Instance & 0xff); + + // + // If the above math went negative or is too large just return 0xff. + // + if(ui32DevIndex > MAX_USB_DEVICES) + { + ui32DevIndex = 0xff; + } + + return(ui32DevIndex); +} + +//============================================================================= +// +// This is the internal function that will map an event to a valid event flag. +// +// \param ui32Event specifies which event flag to retrieve. +// +// \return The event flag or 0 if there is no support event flag for the +// event specified by the \e ui32Event parameter. +// +//============================================================================= +static uint32_t +GetEventFlag(uint32_t ui32Event) +{ + uint32_t ui32EventFlag; + + ui32EventFlag = 0; + + // + // Search for a valid event flag for the requested event. + // + switch(ui32Event) + { + case USB_EVENT_SOF: + { + ui32EventFlag |= USBHCD_EVFLAG_SOF; + break; + } + case USB_EVENT_CONNECTED: + { + ui32EventFlag |= USBHCD_EVFLAG_CONNECT; + break; + } + case USB_EVENT_DISCONNECTED: + { + ui32EventFlag |= USBHCD_EVFLAG_DISCNCT; + break; + } + case USB_EVENT_UNKNOWN_CONNECTED: + { + ui32EventFlag |= USBHCD_EVFLAG_UNKCNCT; + break; + } + case USB_EVENT_POWER_FAULT: + { + ui32EventFlag |= USBHCD_EVFLAG_PWRFAULT; + break; + } + case USB_EVENT_POWER_DISABLE: + { + ui32EventFlag |= USBHCD_EVFLAG_PWRDIS; + break; + } + case USB_EVENT_POWER_ENABLE: + { + ui32EventFlag |= USBHCD_EVFLAG_PWREN; + break; + } + default: + { + break; + } + } + return(ui32EventFlag); +} + +//============================================================================= +// +//! This function is called to enable a specific USB HCD event notification. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param pvEventDriver is the event driver structure that was passed into +//! the USBHCDRegisterDrivers() function as part of the array of +//! tUSBHostClassDriver structures. +//! \param ui32Event is the event to enable. +//! +//! This function is called to enable event callbacks for a specific USB HCD +//! event. The requested event is passed in the \e ui32Event parameter. Not +//! all events can be enables so the function will return zero if the event +//! provided cannot be enabled. The \e pvEventDriver is a pointer to the +//! event driver structure that the caller passed into the +//! USBHCDRegisterDrivers() function. This structure is typically declared +//! with the DECLARE_EVENT_DRIVER() macro and included as part of the array +//! of pointers to tUSBHostClassDriver structures that is passed to the +//! USBHCDRegisterDrivers() function. +//! +//! \return This function returns a non-zero number if the event was +//! successfully enabled and returns zero if the event cannot be enabled. +// +//============================================================================= +int32_t +USBHCDEventEnable(uint32_t ui32Index, void *pvEventDriver, uint32_t ui32Event) +{ + int32_t i32Ret; + uint32_t ui32EventFlag; + + ASSERT(ui32Index == 0); + + // + // Default the return to fail the call unless a valid event is found. + // + i32Ret = 0; + + // + // Get the event flag for this event. + // + ui32EventFlag = GetEventFlag(ui32Event); + + // + // Check if there was an event flag for the corresponding event. + // + if(ui32EventFlag) + { + // + // Set the enable for this event. + // + g_sUSBHCD.ui32EventEnables |= ui32EventFlag; + + // + // Indicate that the event was valid and is now enabled. + // + i32Ret = 1; + } + + return(i32Ret); +} + +//============================================================================= +// +//! This function is called to disable a specific USB HCD event notification. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param pvEventDriver is the event driver structure that was passed into +//! the USBHCDRegisterDrivers() function as part of the array of +//! tUSBHostClassDriver structures. +//! \param ui32Event is the event to disable. +//! +//! This function is called to disable event callbacks for a specific USB HCD +//! event. The requested event is passed in the \e ui32Event parameter. Not +//! all events can be enables so the function will return zero if the event +//! provided cannot be enabled. The \e pvEventDriver is a pointer to the +//! event driver structure that the caller passed into the +//! USBHCDRegisterDrivers() function. This structure is typically declared +//! with the DECLARE_EVENT_DRIVER() macro and included as part of the array +//! of pointers to tUSBHostClassDriver structures that is passed to the +//! USBHCDRegisterDrivers() function. +//! +//! \return This function returns a non-zero number if the event was +//! successfully disabled and returns zero if the event cannot be disabled. +// +//============================================================================= +int32_t +USBHCDEventDisable(uint32_t ui32Index, void *pvEventDriver, uint32_t ui32Event) +{ + int32_t i32Ret; + uint32_t ui32EventFlag; + + ASSERT(ui32Index == 0); + + // + // Default the return to fail the call unless a valid event is found. + // + i32Ret = 0; + + // + // Get the event flag for this event. + // + ui32EventFlag = GetEventFlag(ui32Event); + + // + // Check if there was an event flag for the corresponding event. + // + if(ui32EventFlag) + { + // + // Clear the enable for this event. + // + g_sUSBHCD.ui32EventEnables &= ~ui32EventFlag; + + // + // Indicate that the event was valid and is now disabled. + // + i32Ret = 1; + } + + return(i32Ret); +} + +//***************************************************************************** +// +// If there is an event driver this function will send out a generic connection +// event USB_EVENT_UNKNOWN_CONNECTED indicating that an unknown connection +// event has occurred. +// +//***************************************************************************** +static void +SendUnknownConnect(uint32_t ui32Index, uint32_t ui32Class) +{ + tEventInfo sEvent; + + // + // If there is an event driver registered and it has a event handler and + // the USBHCD_EVFLAG_UNKCNCT is enabled then call the function. + // + sEvent.ui32Event = USB_EVENT_UNKNOWN_CONNECTED; + sEvent.ui32Instance = ui32Class; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_UNKCNCT); +} + +//***************************************************************************** +// +// Internal memory allocation space is two 32-bit values where each +// bit represents a 64 byte block in the FIFO. This requires 64 bits for +// the 4096 bytes of FIFO available. +// +//***************************************************************************** +static uint32_t g_pui32Alloc[2]; + +//***************************************************************************** +// +// This function handles freeing FIFO memory that has been allocated using the +// FIFOAlloc() function. +// +//***************************************************************************** +static void +FIFOFree(tUSBHCDPipe *psUSBPipe) +{ + uint32_t ui32Mask; + + // + // Calculate the mask value to use to clear off the allocated blocks used + // by the USB pipe specified by psUSBPipe. + // + ui32Mask = (1 << (psUSBPipe->ui8FIFOSize - 2)) - 1; + ui32Mask = ui32Mask << psUSBPipe->ui8FIFOBitOffset; + + // + // Determine which 32 bit word to access based on the size. + // + if(psUSBPipe->ui8FIFOSize > USB_FIFO_SZ_64) + { + // + // If the FIFO size is greater than 64 then use the upper 32 bits. + // + g_pui32Alloc[1] &= ~ui32Mask; + } + else + { + // + // If the FIFO size is less than or equal to 64 then use the lower + // 32 bits. + // + g_pui32Alloc[0] &= ~ui32Mask; + } +} + +//***************************************************************************** +// +// This function is used to allocate FIFO memory to a given USB pipe. +// +// \param psUSBPipe is the USB pipe that needs FIFO memory allocated. +// \param ui32Size is the minimum size in bytes of the FIFO to allocate. +// +// This function will allocate \e ui32Size bytes to the USB pipe in the +// \e psUSBPipe parameter. The function will fill the psUSBPipe structure +// members ui8FIFOSize and ui8FIFOAddr with values that can be used with the +// USBFIFOConfigSet() API. This allocation uses a first fit algorithm. +// +// \return This function returns the size of the block allocated. +// +//***************************************************************************** +static uint32_t +FIFOAlloc(tUSBHCDPipe *psUSBPipe, uint32_t ui32Size) +{ + uint32_t ui32Blocks, ui32Start, ui32BlockSize, ui32Temp, ui32Index; + uint16_t ui16FIFOAddr; + + // + // Save which 32 bit value to access, the upper is for blocks greater + // than 64 and the lower is for block 64 or less. + // + if(ui32Size > 64) + { + ui32Index = 1; + } + else + { + ui32Index = 0; + } + + // + // Initial FIFO address is 0. + // + ui16FIFOAddr = 0; + + // + // Initialize the bit pattern and bit location. + // + ui32Blocks = 1; + ui32Start = 0; + + // + // The initial block size is always the minimum size of 64 bytes. + // + ui32BlockSize = 64; + + // + // The initial size and offset are 64 and 0. + // + psUSBPipe->ui8FIFOBitOffset = 0; + psUSBPipe->ui8FIFOSize = 3; + + // + // Scan through 32 bits looking for a memory block large enough to fill + // the request. + // + while(ui16FIFOAddr <= 32) + { + // + // If the pattern is zero then it is a possible match. + // + if((g_pui32Alloc[ui32Index] & ui32Blocks) == 0) + { + // + // If the size is large enough then save it and break out of the + // loop. + // + if(ui32BlockSize >= ui32Size) + { + // + // Mark the memory as allocated. + // + g_pui32Alloc[ui32Index] |= ui32Blocks; + + break; + } + + // + // Increment the size of the FIFO block. + // + psUSBPipe->ui8FIFOSize++; + + // + // Add in a new bit to the size of the allocation. + // + ui32Blocks = ui32Blocks | (ui32Blocks << 1) ; + + // + // Double the current size. + // + ui32BlockSize <<= 1; + + } + else + { + // + // Need to start over looking because the last allocation match + // failed, so reset the bit offset to the current location and the + // size to 64 bytes. + // + psUSBPipe->ui8FIFOBitOffset = ui16FIFOAddr; + psUSBPipe->ui8FIFOSize = 3; + + // + // Reset the block size to the minimum (64 bytes). + // + ui32BlockSize = 64; + + // + // Store the current starting bit location and set the block mask + // to this value. + // + ui32Start = 1 << ui16FIFOAddr; + ui32Blocks = ui32Start; + } + + // + // Increase the address of the FIFO offset. + // + ui16FIFOAddr++; + } + + // + // If there was no block large enough then fail this call. + // + if(ui16FIFOAddr > 32) + { + ui32BlockSize = 0; + psUSBPipe->ui16FIFOAddr = 0; + psUSBPipe->ui8FIFOBitOffset = 0; + psUSBPipe->ui8FIFOSize = 0; + } + else + { + // + // Calculate the offset in the FIFO. + // + ui32Temp = psUSBPipe->ui8FIFOBitOffset * 64; + + // + // Sizes greater than 64 are allocated in the second half of the FIFO + // memory space. + // + if(ui32Size > 64) + { + ui32Temp += 2048; + } + + // + // Convert this to the value that can be set in the USB controller. + // + psUSBPipe->ui16FIFOAddr = (uint16_t)ui32Temp; + } + return(ui32BlockSize); +} + +//***************************************************************************** +// +//! This function returns the current byte count of a USB HCD pipe. +//! +//! \param ui32Pipe is the allocated endpoint to modify. +//! +//! This call returns the current or last byte count for a transfer using the +//! pipe specified by the \e ui32Pipe parameter. This is typically used to +//! determine the actual byte count received when a \b USB_EVENT_RX_AVAILABLE +//! occurs. +//! +//! \return If the call was successful, this function returns the number of +//! bytes transfered by the USB pipe. +// +//***************************************************************************** +uint32_t +USBHCDPipeTransferSizeGet(uint32_t ui32Pipe) +{ + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + return(g_sUSBHCD.psUSBINPipes[ui32Index].ui32DataRead); +} + +//***************************************************************************** +// +//! This function is used to allocate a USB HCD pipe. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32EndpointType is the type of endpoint that this pipe will be +//! communicating with. +//! \param psDevice is the device instance associated with this endpoint. +//! \param ui32Size is the size of the FIFO in bytes. +//! \param pfnCallback is the function that will be called when events occur on +//! this USB Pipe. +//! +//! Since there are a limited number of USB HCD pipes that can be used in the +//! host controller, this function is used to temporarily or permanently +//! acquire one of the endpoints. Unlike the USBHCDPipeAlloc() function this +//! function allows the caller to specify the size of the FIFO allocated to +//! this endpoint in the \e ui32Size parameter. This function also provides a +//! method to register a callback for status changes on this endpoint. If no +//! callbacks are desired then the \e pfnCallback function should be set to 0. +//! The callback should be used when using the USBHCDPipeSchedule() function +//! so that the caller is notified when the action is complete. +//! +//! \return This function returns a value indicating which pipe was reserved. +//! If the value is 0 then there were no pipes currently available. This value +//! should be passed to any USBHCDPipe APIs to indicate which pipe is being +//! accessed. +// +//***************************************************************************** +uint32_t +USBHCDPipeAllocSize(uint32_t ui32Index, uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, uint32_t ui32Size, + tHCDPipeCallback pfnCallback) +{ + int32_t i32Idx; + + uint32_t ui32HubAddr; + + ASSERT(ui32Index == 0); + + // + // Find a USB pipe that is free. + // + for(i32Idx = 0; i32Idx < MAX_NUM_PIPES; i32Idx++) + { + // + // Handle OUT Pipes. + // + if(ui32EndpointType & EP_PIPE_TYPE_OUT) + { + // + // A zero address indicates free. + // + if(g_sUSBHCD.psUSBOUTPipes[i32Idx].psDevice == 0) + { + // + // Set up uDMA for the pipe. + // + if(ui32EndpointType & EP_PIPE_USE_UDMA) + { + // + // Allocate a DMA channel to the endpoint. + // + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8DMAChannel = + USBLibDMAChannelAllocate(g_sUSBHCD.psDMAInstance, + IndexToUSBEP(i32Idx + 1), + ui32Size, + USB_DMA_EP_TX | + USB_DMA_EP_HOST); + + // + // If no DMA channel was available then just disable DMA + // on this pipe. + // + if(g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8DMAChannel == 0) + { + ui32EndpointType &= ~EP_PIPE_USE_UDMA; + } + } + + // + // Save the endpoint type and device address and callback + // function. + // + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui32Type = ui32EndpointType; + g_sUSBHCD.psUSBOUTPipes[i32Idx].psDevice = psDevice; + g_sUSBHCD.psUSBOUTPipes[i32Idx].pfnCallback = pfnCallback; + + // + // Clear out any pending status on this endpoint in case it + // was in use before a allowing a new device class to use it. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(i32Idx + 1), + USB_HOST_OUT_STATUS); + + // + // Make sure to reset the data toggle. + // + USBEndpointDataToggleClear(USB0_BASE, IndexToUSBEP(i32Idx + 1), + USB_EP_HOST_OUT); + + // + // Initialize the endpoint as idle. + // + g_sUSBHCD.psUSBOUTPipes[i32Idx].iState = ePipeIdle; + + // + // Allocate space in the FIFO for this endpoint. + // + if(FIFOAlloc(&g_sUSBHCD.psUSBOUTPipes[i32Idx], ui32Size) != 0) + { + // + // Configure the FIFO. + // + MAP_USBFIFOConfigSet(USB0_BASE, + IndexToUSBEP(i32Idx + 1), + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui16FIFOAddr, + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8FIFOSize, + USB_EP_HOST_OUT); + } + + // + // Set the function address for this endpoint. + // + MAP_USBHostAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + psDevice->ui32Address, USB_EP_HOST_OUT); + + // + // Set the hub and port address for the endpoint. + // + ui32HubAddr = psDevice->ui8Hub | (psDevice->ui8HubPort << 8); + USBHostHubAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + ui32HubAddr, (USB_EP_HOST_OUT | + psDevice->ui32Speed)); + break; + } + } + // + // Handle IN Pipes. + // + else if(ui32EndpointType & EP_PIPE_TYPE_IN) + { + // + // A zero address indicates free. + // + if(g_sUSBHCD.psUSBINPipes[i32Idx].psDevice == 0) + { + // + // Set up uDMA for the pipe. + // + if(ui32EndpointType & EP_PIPE_USE_UDMA) + { + // + // Allocate a DMA channel to the endpoint. + // + g_sUSBHCD.psUSBINPipes[i32Idx].ui8DMAChannel = + USBLibDMAChannelAllocate(g_sUSBHCD.psDMAInstance, + IndexToUSBEP(i32Idx + 1), + ui32Size, + USB_DMA_EP_RX | + USB_DMA_EP_HOST); + + // + // If no DMA channel was available then just disable DMA + // on this pipe. + // + if(g_sUSBHCD.psUSBINPipes[i32Idx].ui8DMAChannel == 0) + { + ui32EndpointType &= ~EP_PIPE_USE_UDMA; + } + } + + // + // Save the endpoint type and device address and callback + // function. + // + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type = ui32EndpointType; + g_sUSBHCD.psUSBINPipes[i32Idx].psDevice = psDevice; + g_sUSBHCD.psUSBINPipes[i32Idx].pfnCallback = pfnCallback; + + // + // Clear out any pending status on this endpoint in case it + // was in use before a allowing a new device class to use it. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(i32Idx + 1), + USB_HOST_IN_STATUS); + + // + // Make sure to reset the data toggle. + // + USBEndpointDataToggleClear(USB0_BASE, IndexToUSBEP(i32Idx + 1), + USB_EP_HOST_IN); + + // + // Allocate space in the FIFO for this endpoint. + // + if(FIFOAlloc(&g_sUSBHCD.psUSBINPipes[i32Idx], ui32Size) != 0) + { + // + // Configure the FIFO. + // + MAP_USBFIFOConfigSet(USB0_BASE, + IndexToUSBEP(i32Idx + 1), + g_sUSBHCD.psUSBINPipes[i32Idx].ui16FIFOAddr, + g_sUSBHCD.psUSBINPipes[i32Idx].ui8FIFOSize, + USB_EP_HOST_IN); + } + + // + // Set the function address for this endpoint. + // + MAP_USBHostAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + psDevice->ui32Address, USB_EP_HOST_IN); + + // + // Set the hub and port address for the endpoint. + // + ui32HubAddr = psDevice->ui8Hub | (psDevice->ui8HubPort << 8); + USBHostHubAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + ui32HubAddr, (USB_EP_HOST_IN | + psDevice->ui32Speed)); + + // + // Reset the state of the pipe to idle. + // + g_sUSBHCD.psUSBINPipes[i32Idx].iState = ePipeIdle; + + break; + } + } + } + + // + // Did not find a free pipe. + // + if(i32Idx == MAX_NUM_PIPES) + { + return(0); + } + + // + // Return the pipe index and type that was allocated. + // + return(ui32EndpointType | i32Idx); +} + +//***************************************************************************** +// +//! This function is used to allocate a USB HCD pipe. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32EndpointType is the type of endpoint that this pipe will be +//! communicating with. +//! \param psDevice is the device instance associated with this endpoint. +//! \param pfnCallback is the function that will be called when events occur on +//! this USB Pipe. +//! +//! Since there are a limited number of USB HCD pipes that can be used in the +//! host controller, this function is used to temporarily or permanently +//! acquire one of the endpoints. It also provides a method to register a +//! callback for status changes on this endpoint. If no callbacks are desired +//! then the \e pfnCallback function should be set to 0. The callback should +//! be used when using the USBHCDPipeSchedule() function so that the caller is +//! notified when the action is complete. +//! +//! \return This function returns a value indicating which pipe was reserved. +//! If the value is 0 then there were no pipes currently available. This value +//! should be passed to any USBHCDPipe APIs to indicate which pipe is being +//! accessed. +// +//***************************************************************************** +uint32_t +USBHCDPipeAlloc(uint32_t ui32Index, uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, tHCDPipeCallback pfnCallback) +{ + // + // The old API allocated only 64 bytes to each endpoint. + // + return(USBHCDPipeAllocSize(ui32Index, ui32EndpointType, psDevice, 64, + pfnCallback)); +} + +//***************************************************************************** +// +//! This function is used to configure a USB HCD pipe. +//! +//! This should be called after allocating a USB pipe with a call to +//! USBHCDPipeAlloc(). It is used to set the configuration associated with an +//! endpoint like the max payload and target endpoint. The \e ui32MaxPayload +//! parameter is typically read directly from the devices endpoint descriptor +//! and is expressed in bytes. +//! +//! Setting the \e ui32Interval parameter depends on the type of endpoint being +//! configured. For endpoints that do not need to use the \e ui32Interval +//! parameter \e ui32Interval should be set to 0. For Bulk \e ui32Interval is +//! a value from 2-16 and will set the NAK timeout value as +//! 2^(\e ui32Interval-1) frames. For interrupt endpoints \e ui32Interval is a +//! value from 1-255 and is the count in frames between polling the endpoint. +//! For isochronous endpoints \e ui32Interval ranges from 1-16 and is the +//! polling interval in frames represented as 2^(\e ui32Interval-1) frames. +//! +//! \param ui32Pipe is the allocated endpoint to modify. +//! \param ui32MaxPayload is maximum data that can be handled per transaction. +//! \param ui32Interval is the polling interval for data transfers expressed in +//! frames. +//! \param ui32TargetEndpoint is the target endpoint on the device to +//! communicate with. +//! +//! \return If the call was successful, this function returns zero any other +//! value indicates an error. +// +//***************************************************************************** +uint32_t +USBHCDPipeConfig(uint32_t ui32Pipe, uint32_t ui32MaxPayload, + uint32_t ui32Interval, uint32_t ui32TargetEndpoint) +{ + uint32_t ui32Flags; + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + // + // Set the direction. + // + if(ui32Pipe & EP_PIPE_TYPE_OUT) + { + // + // Set the mode for this endpoint. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type & EP_PIPE_TYPE_BULK) + { + ui32Flags = USB_EP_MODE_BULK; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type & + EP_PIPE_TYPE_INTR) + { + ui32Flags = USB_EP_MODE_INT; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type & + EP_PIPE_TYPE_ISOC) + { + ui32Flags = USB_EP_MODE_ISOC; + } + else + { + ui32Flags = USB_EP_MODE_CTRL; + } + + ui32Flags |= USB_EP_HOST_OUT; + + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8EPNumber = + (uint8_t)ui32TargetEndpoint; + + // + // Save the interval and the next tick to trigger a scheduler event. + // + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Interval = ui32Interval; + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32NextEventTick = + ui32Interval + g_ui32CurrentTick; + + // + // Set the device speed. + // + ui32Flags |= (g_sUSBHCD.psUSBOUTPipes[ui32Index].psDevice->ui32Speed); + + // + // Set up the appropriate flags if uDMA is used. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + ui32Flags |= USB_EP_DMA_MODE_0 | USB_EP_AUTO_SET; + } + } + else + { + // + // Set the mode for this endpoint. + // + if(g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type & EP_PIPE_TYPE_BULK) + { + ui32Flags = USB_EP_MODE_BULK; + } + else if(g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type & EP_PIPE_TYPE_INTR) + { + ui32Flags = USB_EP_MODE_INT; + } + else if(g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type & EP_PIPE_TYPE_ISOC) + { + ui32Flags = USB_EP_MODE_ISOC; + } + else + { + ui32Flags = USB_EP_MODE_CTRL; + } + ui32Flags |= USB_EP_HOST_IN; + + g_sUSBHCD.psUSBINPipes[ui32Index].ui8EPNumber = + (uint8_t)ui32TargetEndpoint; + + // + // Save the interval and the next tick to trigger a scheduler event. + // + g_sUSBHCD.psUSBINPipes[ui32Index].ui32Interval = ui32Interval; + g_sUSBHCD.psUSBINPipes[ui32Index].ui32NextEventTick = + ui32Interval + g_ui32CurrentTick; + + // + // Set the device speed. + // + ui32Flags |= g_sUSBHCD.psUSBINPipes[ui32Index].psDevice->ui32Speed; + // + // Set up the appropriate flags if uDMA is used. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + ui32Flags |= USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR | + USB_EP_AUTO_REQUEST; + } + } + + + // + // Configure the endpoint according to the flags determined above. + // + USBHostEndpointConfig(USB0_BASE, + IndexToUSBEP((ui32Pipe & EP_PIPE_IDX_M) + 1), + ui32MaxPayload, ui32Interval, ui32TargetEndpoint, + ui32Flags); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to return the current status of a USB HCD pipe. +//! +//! This function will return the current status for a given USB pipe. If +//! there is no status to report this call will simply return +//! \b USBHCD_PIPE_NO_CHANGE. +//! +//! \param ui32Pipe is the USB pipe for this status request. +//! +//! \return This function returns the current status for the given endpoint. +//! This will be one of the \b USBHCD_PIPE_* values. +// +//***************************************************************************** +uint32_t +USBHCDPipeStatus(uint32_t ui32Pipe) +{ + return(0); +} + +//***************************************************************************** +// +//! This function is used to write data to a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to put data into. +//! \param pui8Data is a pointer to the data to send. +//! \param ui32Size is the amount of data to send. +//! +//! This function will block until it has sent as much data as was +//! requested using the USB pipe's FIFO. The caller should have registered a +//! callback with the USBHCDPipeAlloc() call in order to be informed when the +//! data has been transmitted. The value returned by this function can be less +//! than the \e ui32Size requested if the USB pipe has less space available +//! than this request is making. +//! +//! \return This function returns the number of bytes that were scheduled to +//! be sent on the given USB pipe. +// +//***************************************************************************** +uint32_t +USBHCDPipeWrite(uint32_t ui32Pipe, uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Endpoint, ui32RemainingBytes, ui32ByteToSend, ui32PipeIdx; + bool bUseDMA; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + // + // Get index used for looking up pipe data + // + ui32PipeIdx = ui32Pipe & EP_PIPE_IDX_M; + + // + // Set the total number of bytes to send out. + // + ui32RemainingBytes = ui32Size; + + // + // Default to using DMA. + // + bUseDMA = false; + + // + // Initialize the bytes to send to all of the remaining bytes. + // + ui32ByteToSend = ui32RemainingBytes; + + // + // Send all of the requested data. + // + while(ui32RemainingBytes != 0) + { + // + // If uDMA is not enabled for this pipe, or if the uDMA workaround + // is applied, then don't use uDMA for this transfer. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + // + // Start the DMA transfer. + // + if(USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32RemainingBytes) != 0) + { + if(ui32RemainingBytes < 64) + { + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = + ePipeWriteDMASend; + } + else if((ui32RemainingBytes % 64) == 0) + { + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = + ePipeWriteDMA; + } + else + { + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = + ePipeWriteDMASend; + } + + bUseDMA = true; + } + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + } + + if(bUseDMA == false) + { + // + // Only send 64 bytes at a time if not using DMA. + // + if(ui32ByteToSend > 64) + { + ui32ByteToSend = 64; + } + else + { + // + // Send the requested number of bytes. + // + ui32ByteToSend = ui32RemainingBytes; + } + + // + // Start a write request. + // + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = ePipeWriting; + + // + // Disable uDMA on the USB endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ui32Endpoint, + USB_EP_HOST_OUT); + + // + // Put the data in the buffer. + // + MAP_USBEndpointDataPut(USB0_BASE, ui32Endpoint, pui8Data, + ui32ByteToSend); + + // + // Schedule the data to be sent. + // + MAP_USBEndpointDataSend(USB0_BASE, ui32Endpoint, USB_TRANS_OUT); + } + + // + // Wait for a status change. + // + while(1) + { + // + // If an error event occurs then exit out of the loop. + // + if(g_sUSBHCD.ui32IntEvents & (INT_EVENT_DISCONNECT | + INT_EVENT_VBUS_ERR | + INT_EVENT_POWER_FAULT)) + { + // + // Set the pipe state to error. + // + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = ePipeError; + + // + // Needs to be set to exit out of large while loop. + // + ui32RemainingBytes = 0; + + break; + } + // + // If the data was successfully sent then decrement the count and + // continue. + // + else if(g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState == + ePipeDataSent) + { + // + // Decrement the remaining data and advance the pointer. + // + ui32RemainingBytes -= ui32ByteToSend; + pui8Data += ui32ByteToSend; + + // + // If there are less than 64 bytes to send then this is the + // last of the data to go out. + // + if(ui32RemainingBytes < 64) + { + ui32ByteToSend = ui32RemainingBytes; + } + break; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState == + ePipeStalled) + { + // + // Zero out the size so that the caller knows that no data was + // written. + // + ui32Size = 0; + + // + // Needs to be set to exit out of large while loop. + // + ui32RemainingBytes = 0; + + // + // If DMA is being used, then disable the channel. + // + if(bUseDMA == true) + { + // + // Disable the DMA channel. + // + USBLibDMAChannelDisable(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].ui8DMAChannel); + } + + // + // This is the actual endpoint number. + // + USBHCDClearFeature( + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].psDevice->ui32Address, + ui32Pipe, USB_FEATURE_EP_HALT); + + // + // If there was a stall, then no more data is coming so break + // out. + // + break; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState == ePipeError) + { + // + // An error occurred so stop this transaction and set the + // number of bytes to zero. + // + ui32Size = 0; + + // + // Needs to be set to exit out of large while loop. + // + ui32RemainingBytes = 0; + + break; + } + } + } + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = ePipeIdle; + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to schedule and IN transaction on a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to read data from. +//! \param pui8Data is a pointer to store the data that is received. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Data. +//! +//! This function will not block depending on the type of pipe passed in will +//! schedule either a send of data to the device or a read of data from the +//! device. In either case the amount of data will be limited to what will +//! fit in the FIFO for a given endpoint. +//! +//! \return This function returns the number of bytes that were sent in the +//! case of a transfer of data or it will return 0 for a request on a USB IN +//! pipe. +// +//***************************************************************************** +uint32_t +USBHCDPipeSchedule(uint32_t ui32Pipe, uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Endpoint, ui32PipeIdx; + + // + // Get index used for looking up pipe data + // + ui32PipeIdx = ui32Pipe & EP_PIPE_IDX_M; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + if(ui32Pipe & EP_PIPE_TYPE_OUT) + { + // + // Check if uDMA is enabled on this pipe. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + // + // Start a write request. + // + g_sUSBHCD.psUSBOUTPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeWriteDMASend; + + USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32Size); + } + else + { + // + // Start a write request. + // + g_sUSBHCD.psUSBOUTPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeWriting; + + // + // Put the data in the buffer. + // + MAP_USBEndpointDataPut(USB0_BASE, ui32Endpoint, pui8Data, + ui32Size); + + // + // Schedule the data to be sent. + // + MAP_USBEndpointDataSend(USB0_BASE, ui32Endpoint, USB_TRANS_OUT); + } + } + else + { + // + // If uDMA is not enabled for this pipe, or if the uDMA workaround + // is applied, then do not use uDMA for this transfer. + // + if((ui32Pipe & EP_PIPE_USE_UDMA) == 0) + { + // + // Start a read request. + // + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeReading; + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ui32Endpoint, USB_EP_HOST_IN); + } + // + // Otherwise, uDMA should be used for this transfer, so set up + // the uDMA channel in advance of triggering the IN request. + // + else + { + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeReadDMA; + + USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32Size); + } + + // + // Remember details of the buffer into which the data will be read. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].pui8ReadPtr = pui8Data; + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui32ReadSize = ui32Size; + + // + // Trigger a request for data from the device. + // + MAP_USBHostRequestIN(USB0_BASE, ui32Endpoint); + + // + // No data was put into or read from the buffer. + // + ui32Size = 0; + } + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to read data from a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to read data from. +//! \param pui8Data is a pointer to store the data that is received. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Data. +//! +//! This function will not block and will only read as much data as requested +//! or as much data is currently available from the USB pipe. The caller +//! should have registered a callback with the USBHCDPipeAlloc() call in order +//! to be informed when the data has been received. The value returned by this +//! function can be less than the \e ui32Size requested if the USB pipe has +//! less data available than was requested. +//! +//! \return This function returns the number of bytes that were returned in the +//! \e pui8Data buffer. +// +//***************************************************************************** +uint32_t +USBHCDPipeReadNonBlocking(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size) +{ + uint32_t ui32Endpoint; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + // + // Read the data out of the USB endpoint interface. + // + MAP_USBEndpointDataGet(USB0_BASE, ui32Endpoint, pui8Data, &ui32Size); + + // + // Acknowledge that the data was read from the endpoint. + // + MAP_USBHostEndpointDataAck(USB0_BASE, ui32Endpoint); + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = ePipeIdle; + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function acknowledges data received via an interrupt IN pipe. +//! +//! \param ui32Pipe is the USB INT pipe whose last packet is to be +//! acknowledged. +//! +//! This function is used to acknowledge reception of data on an interrupt IN +//! pipe. A transfer on an interrupt IN endpoint is scheduled via a call to +//! USBHCDPipeSchedule() and the application is notified when data is received +//! using a \b USB_EVENT_RX_AVAILABLE event. In the handler for this event, +//! the application must call USBHCDPipeDataAck() to have the USB controller +//! ACK the data from the device and complete the transaction. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDPipeDataAck(uint32_t ui32Pipe) +{ + uint32_t ui32Endpoint; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + // + // Acknowledge that the data was read from the endpoint. + // + USBHostEndpointDataAck(USB0_BASE, ui32Endpoint); + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = ePipeIdle; +} + +//***************************************************************************** +// +//! This function is used to read data from a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to read data from. +//! \param pui8Data is a pointer to store the data that is received. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Data. +//! +//! This function will block and will only return when it has read as much data +//! as requested from the USB pipe. The caller must register a callback with +//! the USBHCDPipeAlloc() call in order to be informed when the data has been +//! received. If the caller provides a non-zero pointer in the \e pui8Data +//! parameter then the data is copied into the buffer before the callback +//! occurs. If the caller provides a zero in \e pui8Data parameter +//! then the caller is responsible for reading the data out of the FIFO when +//! the \b USB_EVENT_RX_AVAILABLE callback event occurs. The value returned +//! by this function can be less than the \e ui32Size requested if the USB pipe +//! has less data available than was requested. +//! +//! \return This function returns the number of bytes that were returned in the +//! \e pui8Data buffer. +// +//***************************************************************************** +uint32_t +USBHCDPipeRead(uint32_t ui32Pipe, uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Endpoint, ui32RemainingBytes, ui32BytesRead, ui32PipeIdx; + bool bUseDMA; + + // + // Get index used for looking up pipe data + // + ui32PipeIdx = ui32Pipe & EP_PIPE_IDX_M; + + // + // Initialized the number of bytes read. + // + ui32BytesRead = 0; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP(ui32PipeIdx + 1); + + // + // Set the remaining bytes to received. + // + ui32RemainingBytes = ui32Size; + + // + // Default to using DMA. + // + bUseDMA = true; + + // + // Continue until all data requested has been received. + // + while(ui32RemainingBytes != 0) + { + // + // Start a read request. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeReading; + + // + // Try the DMA transfer should be used for this transfer, so set up + // the uDMA channel in advance of triggering the IN request. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + if(USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32Size) != 0) + { + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeReadDMA; + + ui32BytesRead = ui32Size; + } + else + { + bUseDMA = false; + } + + // + // Disable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + } + + // + // If unable to use DMA then get ready to transfer without DMA. + // + if(bUseDMA == false) + { + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ui32Endpoint, USB_EP_HOST_IN); + + // + // Set up for the next transaction. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].pui8ReadPtr = pui8Data; + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui32ReadSize = + (ui32RemainingBytes < 64) ? ui32RemainingBytes : 64; + } + + // + // Trigger a request for data from the device. + // + MAP_USBHostRequestIN(USB0_BASE, ui32Endpoint); + + // + // Wait for a status change. + // + while(1) + { + // + // Check if the device stalled the request. + // + if(g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == ePipeStalled) + { + // + // Zero out the size so that the caller knows that no data was + // read. + // + ui32Size = 0; + + // + // There are also no remaining bytes to read. + // + ui32RemainingBytes = 0; + + // + // If DMA is being used, then disable the channel. + // + if(bUseDMA == true) + { + USBLibDMAChannelDisable( + g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel); + } + + // + // This is the actual endpoint number. + // + USBHCDClearFeature( + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].psDevice->ui32Address, + ui32Pipe, USB_FEATURE_EP_HALT); + + // + // If there was a stall, then no more data is coming so break + // out. + // + break; + } + + // + // If any error event occurs then exit out of the loop. + // + if(g_sUSBHCD.ui32IntEvents & (INT_EVENT_DISCONNECT | + INT_EVENT_VBUS_ERR | + INT_EVENT_POWER_FAULT)) + { + // + // Set the pipe state to error. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeError; + break; + } + + // + // If data is ready then return it. + // + if(g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == ePipeDataReady) + { + // + // If not using DMA then read the data from the USB. Otherwise + // the data will already be in the buffer. + // + if(bUseDMA == false) + { + // + // Compute bytes to transfer and set up transfer + // + ui32BytesRead = + ui32RemainingBytes > 64 ? 64 : ui32RemainingBytes; + + // + // Acknowledge that the data was read from the endpoint. + // + MAP_USBHostEndpointDataAck(USB0_BASE, ui32Endpoint); + } + + // + // Subtract the number of bytes read from the bytes remaining. + // + ui32RemainingBytes -= ui32BytesRead; + + // + // If there were less than 64 bytes read, then this was a short + // packet and no more data will be returned. + // + if(ui32BytesRead < 64) + { + // + // Subtract off the bytes that were not received and exit + // the loop. + // + ui32Size = ui32Size - ui32RemainingBytes; + break; + } + else + { + // + // Move the buffer ahead to receive more data into the + // buffer. + // + pui8Data += 64; + } + break; + } + else if(g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == ePipeError) + { + // + // An error occurred so stop this transaction and set the + // number of bytes to zero. + // + ui32Size = 0; + ui32RemainingBytes = 0; + + break; + } + else if((g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == + ePipeReadDMAWait) && + (USBLibDMAChannelStatus(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel) & + USBLIBSTATUS_DMA_COMPLETE)) + { + break; + } + } + } + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeIdle; + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to release a USB pipe. +//! +//! \param ui32Pipe is the allocated USB pipe to release. +//! +//! This function is used to release a USB pipe that was allocated by a call to +//! USBHCDPipeAlloc() for use by some other device endpoint in the system. +//! Freeing an unallocated or invalid pipe will not generate an error and will +//! instead simply return. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDPipeFree(uint32_t ui32Pipe) +{ + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + if(ui32Pipe & EP_PIPE_TYPE_OUT) + { + // + // Clear the address and type for this endpoint to free it up. + // + g_sUSBHCD.psUSBOUTPipes[ui32Index].psDevice = 0; + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type = 0; + g_sUSBHCD.psUSBOUTPipes[ui32Index].pfnCallback = 0; + + // + // Check if this pipe has allocated a DMA channel. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8DMAChannel != + USBHCD_DMA_UNUSED) + { + // + // Release the DMA channel associated with this endpoint. + // + USBLibDMAChannelRelease(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8DMAChannel); + + // + // Clear out the current channel in use by this pipe. + // + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8DMAChannel = + USBHCD_DMA_UNUSED; + } + + // + // Free up the FIFO memory used by this endpoint. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8FIFOSize) + { + FIFOFree(&g_sUSBHCD.psUSBOUTPipes[ui32Index]); + } + + // + // Set the function address for this endpoint back to zero. + // + USBHostAddrSet(USB0_BASE, IndexToUSBEP(ui32Index + 1), + 0, USB_EP_HOST_OUT); + + // + // Set the hub and port address for the endpoint back to zero and the + // speed back to LOW. + // + USBHostHubAddrSet(USB0_BASE, IndexToUSBEP(ui32Index + 1), + 0, (USB_EP_HOST_OUT | USB_EP_SPEED_LOW)); + } + else if(ui32Pipe & EP_PIPE_TYPE_IN) + { + // + // Clear the address and type for this endpoint to free it up. + // + g_sUSBHCD.psUSBINPipes[ui32Index].psDevice = 0; + g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type = 0; + g_sUSBHCD.psUSBINPipes[ui32Index].pfnCallback = 0; + + // + // Check if this pipe has allocated a DMA channel. + // + if(g_sUSBHCD.psUSBINPipes[ui32Index].ui8DMAChannel != + USBHCD_DMA_UNUSED) + { + // + // Release the DMA channel associated with this endpoint. + // + USBLibDMAChannelRelease(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Index].ui8DMAChannel); + + // + // Clear out the current channel in use by this pipe. + // + g_sUSBHCD.psUSBINPipes[ui32Index].ui8DMAChannel = + USBHCD_DMA_UNUSED; + } + + // + // Free up the FIFO memory used by this endpoint. + // + if(g_sUSBHCD.psUSBINPipes[ui32Pipe & EP_PIPE_IDX_M].ui8FIFOSize) + { + FIFOFree(&g_sUSBHCD.psUSBINPipes[ui32Pipe & EP_PIPE_IDX_M]); + } + + // + // Set the function address for this endpoint back to zero. + // + USBHostAddrSet(USB0_BASE, IndexToUSBEP(ui32Index + 1), + 0, USB_EP_HOST_IN); + + // + // Set the hub and port address for the endpoint back to zero and the + // speed back to LOW. + // + USBHostHubAddrSet(USB0_BASE, IndexToUSBEP(ui32Index + 1), + 0, (USB_EP_HOST_IN | USB_EP_SPEED_LOW)); + + // + // Clear any pending IN transactions. + // + USBHostRequestINClear(USB0_BASE, IndexToUSBEP(ui32Index + 1)); + } +} + +//***************************************************************************** +// +// This internal function initializes the HCD code. +// +// \param ui32Index specifies which USB controller to use. +// \param pvPool is a pointer to the data to use as a memory pool for this +// controller. +// \param ui32PoolSize is the size in bytes of the buffer passed in as pvPool. +// +// This function will perform all the necessary operations to allow the USB +// host controller to begin enumeration and communication with a device. This +// function should typically be called once at the start of an application +// before any other calls are made to the host controller. +// +// \return None. +// +//***************************************************************************** +static void +USBHCDInitInternal(uint32_t ui32Index, void *pvPool, uint32_t ui32PoolSize) +{ + int32_t i32Idx; + + ASSERT(ui32Index == 0); + + // + // Get the number of endpoints supported by this device. + // + g_sUSBHCD.ui32NumEndpoints = USBNumEndpointsGet(USB0_BASE); + + // + // The first 64 Bytes are allocated to endpoint 0. + // + g_pui32Alloc[0] = 1; + g_pui32Alloc[1] = 0; + + // + // Save the base address for this controller. + // + g_sUSBHCD.ui32USBBase = USB0_BASE; + + // + // Save the USB interrupt number. + // + g_sUSBHCD.ui32IntNum = INT_USB0_TM4C123; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_sUSBHCD.ui32IntNum = INT_USB0_TM4C129; + } + + // + // All Pipes are unused at start. + // + for(i32Idx = 0; i32Idx < MAX_NUM_PIPES; i32Idx++) + { + g_sUSBHCD.psUSBINPipes[i32Idx].psDevice = 0; + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + g_sUSBHCD.psUSBINPipes[i32Idx].ui8DMAChannel = USBHCD_DMA_UNUSED; + g_sUSBHCD.psUSBOUTPipes[i32Idx].psDevice = 0; + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8DMAChannel = USBHCD_DMA_UNUSED; + } + + // + // Make sure that the hub driver is initialized since it is called even + // if it is not present in the system. + // + USBHHubInit(); + + // + // Initialize the DMA interface. + // + g_sUSBHCD.psDMAInstance = USBLibDMAInit(g_sUSBHCD.ui32USBBase); + + // + // Initialized the device structures. + // + for(i32Idx = 0; i32Idx <= MAX_USB_DEVICES; i32Idx++) + { + // + // Clear the configuration descriptor and state. + // + g_sUSBHCD.piDeviceState[i32Idx] = eHCDIdle; + g_sUSBHCD.psUSBDevice[i32Idx].psConfigDescriptor = 0; + g_sUSBHCD.psUSBDevice[i32Idx].bConfigRead = false; + + // + // Initialize the device descriptor. + // + g_sUSBHCD.psUSBDevice[i32Idx].sDeviceDescriptor.bLength = 0; + g_sUSBHCD.psUSBDevice[i32Idx].sDeviceDescriptor.bMaxPacketSize0 = 0; + + // + // Initialize the device address. + // + g_sUSBHCD.psUSBDevice[i32Idx].ui32Address = 0; + + // + // Set the current interface to 0. + // + g_sUSBHCD.psUSBDevice[i32Idx].ui32Interface = 0; + + // + // Clear the active driver for the device. + // + g_pi32USBHActiveDriver[i32Idx] = -1; + + // + // Initialize the device flags. + // + g_sUSBHCD.psUSBDevice[i32Idx].ui32Flags = 0; + } + + // + // Allocate the memory needed for reading descriptors. + // + g_sUSBHCD.pvPool = pvPool; + g_sUSBHCD.ui32PoolSize = ui32PoolSize; + + // + // Initialize the device class. + // + g_sUSBHCD.ui32Class = USB_CLASS_EVENTS; + + // + // Default enable connect, disconnect, unknown device and power fault + // event notifications. + // + g_sUSBHCD.ui32EventEnables = USBHCD_EVFLAG_CONNECT | + USBHCD_EVFLAG_UNKCNCT | + USBHCD_EVFLAG_DISCNCT | + USBHCD_EVFLAG_PWRFAULT | + USBHCD_EVFLAG_PWREN | + USBHCD_EVFLAG_PWRDIS; + + // + // Initialize the USB tick module. + // + InternalUSBTickInit(); + + // + // Only do hardware update if the stack is in Host mode, do not touch the + // hardware for OTG mode operation. + // + if((g_iUSBMode == eUSBModeHost) || (g_iUSBMode == eUSBModeForceHost)) + { + // + // Configure the End point 0. + // + USBHostEndpointConfig(USB0_BASE, USB_EP_0, 64, 0, 0, + (USB_EP_MODE_CTRL | USB_EP_SPEED_FULL | + USB_EP_HOST_OUT)); + + // + // Enable USB Interrupts. + // + MAP_USBIntEnableControl(USB0_BASE, USB_INTCTRL_RESET | + USB_INTCTRL_DISCONNECT | + USB_INTCTRL_SOF | + USB_INTCTRL_SESSION | + USB_INTCTRL_BABBLE | + USB_INTCTRL_CONNECT | + USB_INTCTRL_RESUME | + USB_INTCTRL_SUSPEND | + USB_INTCTRL_VBUS_ERR | + USB_INTCTRL_POWER_FAULT); + + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + + // + // There is no automatic power in pure host mode. + // + USBHCDPowerConfigSet(ui32Index, (g_ui32PowerConfig & + ~USB_HOST_PWREN_AUTO)); + + // + // Force the power on as well as this point. + // + MAP_USBHostPwrEnable(USB0_BASE); + + // + // This is required to get into host mode on some parts. + // + USBOTGSessionRequest(USB0_BASE, true); + } + + // + // Configure LPM if it is enabled. + // + if(g_sUSBHCD.ui32Features & USBLIB_FEATURE_LPM_EN) + { + if(g_sUSBHCD.ui32Features & USBLIB_FEATURE_LPM_RMT_WAKE) + { + USBHostLPMConfig(USB0_BASE, g_sUSBHCD.ui32LPMHIRD, + USB_DEV_LPM_LS_L1 | USB_DEV_LPM_LS_RMTWAKE); + } + else + { + USBHostLPMConfig(USB0_BASE, g_sUSBHCD.ui32LPMHIRD, + USB_DEV_LPM_LS_L1); + } + + // + // Enable USB interrupts for LPM mode, these enables have no effect on + // devices that do not support LPM. + // + USBLPMIntEnable(USB0_BASE, USB_INTLPM_ERROR | USB_INTLPM_RESUME | + USB_INTLPM_INCOMPLETE | USB_INTLPM_ACK | + USB_INTLPM_NYET | USB_INTLPM_STALL); + } +} + +//***************************************************************************** +// +//! This function is used to set the power pin and power fault configuration. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32PwrConfig is the power configuration to use for the application. +//! +//! This function must be called before HCDInit() is called so that the power +//! pin configuration can be set before power is enabled. The \e ui32PwrConfig +//! flags specify the power fault level sensitivity, the power fault action, +//! and the power enable pin level and source. +//! +//! One of the following can be selected as the power fault level sensitivity: +//! +//! - \b USBHCD_FAULT_LOW - An external power fault is indicated by the pin +//! being driven low. +//! - \b USBHCD_FAULT_HIGH - An external power fault is indicated by the pin +//! being driven high. +//! +//! One of the following can be selected as the power fault action: +//! +//! - \b USBHCD_FAULT_VBUS_NONE - No automatic action when power fault +//! detected. +//! - \b USBHCD_FAULT_VBUS_TRI - Automatically Tri-state the USBnEPEN pin on a +//! power fault. +//! - \b USBHCD_FAULT_VBUS_DIS - Automatically drive the USBnEPEN pin to it's +//! inactive state on a power fault. +//! +//! One of the following can be selected as the power enable level and source: +//! +//! - \b USBHCD_VBUS_MANUAL - Power control is completely managed by the +//! application, the USB library will provide a +//! power callback to request power state changes. +//! - \b USBHCD_VBUS_AUTO_LOW - USBEPEN is driven low by the USB controller +//! automatically if USBOTGSessionRequest() has +//! enabled a session. +//! - \b USBHCD_VBUS_AUTO_HIGH - USBEPEN is driven high by the USB controller +//! automatically if USBOTGSessionRequest() has +//! enabled a session. +//! +//! If \b USBHCD_VBUS_MANUAL is used then the application must provide an +//! event driver to receive the \b USB_EVENT_POWER_ENABLE and +//! \b USB_EVENT_POWER_DISABLE events and enable and disable power to VBUS when +//! requested by the USB library. The application should respond to a power +//! control callback by enabling or disabling VBUS as soon as possible and +//! before returning from the callback function. +//! +//! \note The following values should no longer be used with the USB +//! library: \b USB_HOST_PWRFLT_LOW, \b USB_HOST_PWRFLT_HIGH, +//! \b USB_HOST_PWRFLT_EP_NONE, \b USB_HOST_PWRFLT_EP_TRI, +//! \b USB_HOST_PWRFLT_EP_LOW, \b USB_HOST_PWRFLT_EP_HIGH, +//! \b USB_HOST_PWREN_LOW, \b USB_HOST_PWREN_HIGH, \b USB_HOST_PWREN_VBLOW, and +//! \b USB_HOST_PWREN_VBHIGH. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDPowerConfigInit(uint32_t ui32Index, uint32_t ui32PwrConfig) +{ + ASSERT(ui32Index == 0); + + // + // Save the value as it will be used later. + // + g_ui32PowerConfig = ui32PwrConfig; +} + +//***************************************************************************** +// +//! This function is used to get the power pin and power fault configuration. +//! +//! \param ui32Index specifies which USB controller to use. +//! +//! This function will return the current power control pin configuration as +//! set by the USBHCDPowerConfigInit() function or the defaults if not yet set. +//! See the USBHCDPowerConfigInit() documentation for the meaning of the bits +//! that are returned by this function. +//! +//! \return The configuration of the power control pins. +//! +//***************************************************************************** +uint32_t +USBHCDPowerConfigGet(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Save the value as it will be used later. + // + return(g_ui32PowerConfig); +} + +//***************************************************************************** +// +//! This function is used to set the power pin and power fault configuration. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32Config specifies which USB power configuration to use. +//! +//! This function will set the current power control pin configuration as +//! set by the USBHCDPowerConfigInit() function or the defaults if not yet set. +//! See the USBHCDPowerConfigInit() documentation for the meaning of the bits +//! that are set by this function. +//! +//! \return Returns zero to indicate the power setting is now active. +//! +//***************************************************************************** +uint32_t +USBHCDPowerConfigSet(uint32_t ui32Index, uint32_t ui32Config) +{ + ASSERT(ui32Index == 0); + + // + // Remember the current setting. + // + g_ui32PowerConfig = ui32Config; + + // + // Clear out the two flag bits. + // + ui32Config = g_ui32PowerConfig & ~(USBHCD_VBUS_MANUAL | + USBHCD_FAULT_VBUS_DIS); + + // + // If there is an automatic disable power action specified then set the + // polarity of the signal to match EPEN. + // + if(g_ui32PowerConfig & USBHCD_FAULT_VBUS_DIS) + { + // + // Insure that the assumption below is true. + // + ASSERT((USBHCD_VBUS_AUTO_HIGH & 1) == 1); + ASSERT((USBHCD_VBUS_AUTO_LOW & 1) == 0); + + // + // This is taking advantage of the difference between + // USBHCD_VBUS_AUTO_LOW and USBHCD_VBUS_AUTO_HIGH being that bit + // one is set when EPEN is active high. + // + if(g_ui32PowerConfig & 1) + { + g_ui32PowerConfig |= USB_HOST_PWRFLT_EP_LOW; + ui32Config |= USB_HOST_PWRFLT_EP_LOW; + } + else + { + g_ui32PowerConfig |= USB_HOST_PWRFLT_EP_HIGH; + ui32Config |= USB_HOST_PWRFLT_EP_HIGH; + } + } + + // + // Initialize the power configuration. + // + MAP_USBHostPwrConfig(USB0_BASE, ui32Config); + + // + // If not in manual mode then just turn on power. + // + if((g_ui32PowerConfig & USBHCD_VBUS_MANUAL) == 0) + { + // + // Power the USB bus. + // + MAP_USBHostPwrEnable(USB0_BASE); + } + + // + // Return success. + // + return(0); +} + +//***************************************************************************** +// +//! This function returns if the current power settings will automatically +//! handle enabling and disabling VBUS power. +//! +//! \param ui32Index specifies which USB controller to query. +//! +//! This function returns if the current power control pin configuration will +//! automatically apply power or whether it will be left to the application +//! to turn on power when it is notified. +//! +//! \return A non-zero value indicates that power is automatically applied and +//! a value of zero indicates that the application must manually apply power. +//! +//***************************************************************************** +uint32_t +USBHCDPowerAutomatic(uint32_t ui32Index) +{ + // + // Check if the controller is automatically applying power or not. + // + if(g_ui32PowerConfig & USBHCD_VBUS_MANUAL) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is used to initialize the HCD code. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param pvPool is a pointer to the data to use as a memory pool for this +//! controller. +//! \param ui32PoolSize is the size in bytes of the buffer passed in as +//! \e pvPool. +//! +//! This function will perform all the necessary operations to allow the USB +//! host controller to begin enumeration and communication with devices. This +//! function should typically be called once at the start of an application +//! once all of the device and class drivers are ready for normal operation. +//! This call will start up the USB host controller and any connected device +//! will immediately start the enumeration sequence. +//! +//! The USBStackModeSet() function can be called with eUSBModeHost in order to +//! cause the USB library to force the USB operating mode to a host controller. +//! This allows the application to used the USBVBUS and USBID pins as GPIOs on +//! devices that support forcing OTG to operate as a host only controller. By +//! default the USB library will assume that the USBVBUS and USBID pins are +//! configured as USB pins and not GPIOs. +//! +//! The memory pool passed to this function must be at least as large as a +//! typical configuration descriptor for devices that are to be supported. +//! This value is application-dependent however it should never be less than 32 +//! bytes and, in most cases, should be at least 64 bytes. If there is not +//! sufficient memory to load a configuration descriptor from a device, the +//! device will not be recognized by the USB library's host controller driver. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDInit(uint32_t ui32Index, void *pvPool, uint32_t ui32PoolSize) +{ + int32_t i32Driver; + + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + + // + // Make sure there is at least enough to read the configuration descriptor. + // + ASSERT(ui32PoolSize >= sizeof(tConfigDescriptor)); + + // + // Should not call this if the stack is in device mode. + // + ASSERT(g_iUSBMode != eUSBModeDevice); + ASSERT(g_iUSBMode != eUSBModeForceDevice); + + // + // If the mode was not set then default to eUSBModeHost. + // + if(g_iUSBMode == eUSBModeNone) + { + g_iUSBMode = eUSBModeHost; + } + + // + // Reset the USB controller. + // + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Set the PLL to USB clock divider. + // + if(g_ui32PLLDiv == 0) + { + USBClockEnable(USB0_BASE, g_ui32PLLDiv, USB_CLOCK_EXTERNAL); + } + else + { + USBClockEnable(USB0_BASE, g_ui32PLLDiv, USB_CLOCK_INTERNAL); + } + + // + // Configure ULPI support. + // + if(g_ui32ULPISupport != USBLIB_FEATURE_ULPI_NONE) + { + USBULPIEnable(USB0_BASE); + + if(g_ui32ULPISupport & USBLIB_FEATURE_ULPI_HS) + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_HS); + } + else + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_FS); + } + } + else + { + USBULPIDisable(USB0_BASE); + } + + // + // If the application not requesting OTG mode then set the mode to forced + // host mode. If the mode is actually eUSBModeHost, this will be switched + // off when ID pin detection is complete and the ID is no longer in use. + // + if(g_iUSBMode != eUSBModeOTG) + { + // + // Force Host mode on devices that support force host mode. + // + MAP_USBHostMode(USB0_BASE); + } + + // + // Call our internal function to perform the initialization. + // + USBHCDInitInternal(ui32Index, pvPool, ui32PoolSize); + + // + // No event driver is present by default. + // + g_sUSBHCD.i32EventDriver = -1; + + // + // Search through the Host Class driver list for the devices class. + // + for(i32Driver = 0; i32Driver < g_sUSBHCD.ui32NumClassDrivers; i32Driver++) + { + if(g_sUSBHCD.ppsClassDrivers[i32Driver]->ui32InterfaceClass == + USB_CLASS_EVENTS) + { + // + // Event driver was found so remember it. + // + g_sUSBHCD.i32EventDriver = i32Driver; + } + } + + // + // Get the number of ticks per millisecond, this is only used by blocking + // delays using the SysCtlDelay() function. + // + if(g_ui32Tickms == 0) + { + if(CLASS_IS_TM4C129) + { + g_ui32Tickms = 120000000 / 3000; + } + else + { + g_ui32Tickms = 80000000 / 3000; + } + } +} + +//***************************************************************************** +// +//! This function is used to initialize the HCD class driver list. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ppsHClassDrvs is an array of host class drivers that are +//! supported on this controller. +//! \param ui32NumDrivers is the number of entries in the \e pHostClassDrivers +//! array. +//! +//! This function will set the host classes supported by the host controller +//! specified by the \e ui32Index parameter. This function should be called +//! before enabling the host controller driver with the USBHCDInit() function. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDRegisterDrivers(uint32_t ui32Index, + const tUSBHostClassDriver * const *ppsHClassDrvs, + uint32_t ui32NumDrivers) +{ + ASSERT(ui32Index == 0); + + // + // Save the class drivers. + // + g_sUSBHCD.ppsClassDrivers = ppsHClassDrvs; + + // + // Save the number of class drivers. + // + g_sUSBHCD.ui32NumClassDrivers = ui32NumDrivers; +} + +//***************************************************************************** +// +//! This function is used to terminate the HCD code. +//! +//! \param ui32Index specifies which USB controller to release. +//! +//! This function will clean up the USB host controller and disable it in +//! preparation for shutdown or a switch to USB device mode. Once this call is +//! made, \e USBHCDInit() may be called to reinitialize the controller and +//! prepare for host mode operation. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDTerm(uint32_t ui32Index) +{ + int32_t i32Idx; + + ASSERT(ui32Index == 0); + + // + // End the session. + // + USBOTGSessionRequest(USB0_BASE, false); + + // + // Remove power from the USB bus. + // + MAP_USBHostPwrDisable(USB0_BASE); + + // + // Disable USB interrupts. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL); + + MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Set the host controller state back to it's initial values. + // + for(i32Idx = 0; i32Idx < MAX_NUM_PIPES; i32Idx++) + { + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + } + + // + // Free the memory used by the configuration descriptor. + // + ConfigDescFree(&g_sUSBHCD.psUSBDevice[0]); + + g_sUSBHCD.piDeviceState[0] = eHCDIdle; + g_sUSBHCD.psUSBDevice[0].psConfigDescriptor = 0; + g_sUSBHCD.psUSBDevice[0].bConfigRead = false; + g_sUSBHCD.psUSBDevice[0].sDeviceDescriptor.bLength = 0; + g_sUSBHCD.psUSBDevice[0].sDeviceDescriptor.bMaxPacketSize0 = 0; + g_sUSBHCD.psUSBDevice[0].ui32Address = 0; + g_sUSBHCD.psUSBDevice[0].ui32Interface = 0; + g_sUSBHCD.pvPool = 0; + g_sUSBHCD.ui32PoolSize = 0; +} + +//***************************************************************************** +// +//! This function generates reset signaling on the USB bus. +//! +//! \param ui32Index specifies which USB controller to use. +//! +//! This function handles sending out reset signaling on the USB bus. After +//! returning from this function, any attached device on the USB bus should +//! have returned to it's reset state. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDReset(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Start the reset signaling. + // + MAP_USBHostReset(USB0_BASE, 1); + + // + // Wait 20ms + // + OS_DELAY(g_ui32Tickms * 20); + + // + // End reset signaling on the bus. + // + MAP_USBHostReset(USB0_BASE, 0); + + // + // Need to wait at least 10ms to let the device recover from + // the reset. This is the delay specified in the USB 2.0 spec. + // We will hold the reset for 20ms. + // + OS_DELAY(g_ui32Tickms * 20); +} + +//***************************************************************************** +// +//! This function will generate suspend signaling on the USB bus. +//! +//! \param ui32Index specifies which USB controller to use. +//! +//! This function is used to generate suspend signaling on the USB bus. In +//! order to leave the suspended state, the application should call +//! USBHCDResume(). +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSuspend(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Start the suspend signaling. + // + MAP_USBHostSuspend(USB0_BASE); +} + +//***************************************************************************** +// +//! This function will generate resume signaling on the USB bus. +//! +//! \param ui32Index specifies which USB controller to use. +//! +//! This function is used to generate resume signaling on the USB bus in order +//! to cause USB devices to leave their suspended state. This call should +//! not be made unless a preceding call to USBHCDSuspend() has been made. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDResume(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Start the resume signaling. + // + MAP_USBHostResume(USB0_BASE, 1); + + // + // Wait 100ms + // + OS_DELAY(g_ui32Tickms * 100); + + // + // End reset signaling on the bus. + // + MAP_USBHostResume(USB0_BASE, 0); +} + +//***************************************************************************** +// +//! This function issues a request for the current configuration descriptor +//! from a device. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param psDevice is a pointer to the device structure that holds the buffer +//! to store the configuration descriptor. +//! +//! This function will request the configuration descriptor from the device. +//! The \e psDevice->ConfigDescriptor member variable is used to hold the data +//! for this request. This buffer will be allocated from the pool provided by +//! the HCDInit() function. \e psDevice->sDeviceDescriptor.bMaxPacketSize0 +//! should be valid prior to this call in order to correctly receive the +//! configuration descriptor. If this variable is not valid then this call +//! will not return accurate data. +//! +//! \return The number of bytes returned due to the request. This value can be +//! zero if the device did not respond. +// +//***************************************************************************** +static uint32_t +USBHCDGetConfigDescriptor(uint32_t ui32Index, tUSBHostDevice *psDevice) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Bytes; + tConfigDescriptor sConfigDescriptor; + + ASSERT(ui32Index == 0); + + ui32Bytes = 0; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = USB_DTYPE_CONFIGURATION << 8; + + // + // Index is always 0 for device configurations requests. + // + sSetupPacket.wIndex = 0; + + // + // Only ask for the configuration header first to see how big the + // whole thing is. + // + if(!psDevice->bConfigRead) + { + // + // Only request the space available. + // + sSetupPacket.wLength = sizeof(tConfigDescriptor); + + // + // Put the setup packet in the buffer. + // + ui32Bytes = + USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (uint8_t *)&sConfigDescriptor, + sizeof(tConfigDescriptor), + psDevice->sDeviceDescriptor.bMaxPacketSize0); + } + + // + // If the Configuration header was successfully returned then get the + // full configuration descriptor. + // + if(ui32Bytes == sizeof(tConfigDescriptor)) + { + // + // Save the total size and request the full configuration descriptor. + // + sSetupPacket.wLength = sConfigDescriptor.wTotalLength; + + // + // Not enough space to hold this configuration descriptor. + // + if(ConfigDescAlloc(psDevice, sConfigDescriptor.wTotalLength) == 0) + { + return(0); + } + + // + // Don't allow the buffer to be larger than was allocated. + // + if(sSetupPacket.wLength > psDevice->ui32ConfigDescriptorSize) + { + return(0); + } + + // + // Put the setup packet in the buffer. + // + ui32Bytes = + USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (uint8_t *)psDevice->psConfigDescriptor, + sSetupPacket.wLength, + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // If we read the descriptor, remember the fact. + // + if(ui32Bytes) + { + psDevice->bConfigRead = true; + } + } + + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function issues a request for a device descriptor from a device. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param psDevice is a pointer to the device structure that holds the buffer +//! to store the device descriptor into. +//! +//! This function will request the device descriptor from the device. The +//! \e psDevice->sDeviceDescriptor descriptor is used to hold the data for this +//! request. \e psDevice->sDeviceDescriptor.bMaxPacketSize0 should be +//! initialized to zero or to the valid maximum packet size if it is known. If +//! this variable is not set to zero, then this call will determine the maximum +//! packet size for endpoint 0 and save it in the structure member +//! bMaxPacketSize0. +//! +//! \return The number of bytes returned due to the request. This value can be +//! zero if the device did not respond. +// +//***************************************************************************** +static uint32_t +USBHCDGetDeviceDescriptor(uint32_t ui32Index, tUSBHostDevice *psDevice) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Bytes; + + ASSERT(ui32Index == 0); + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = USB_DTYPE_DEVICE << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // All devices must have at least an 8 byte max packet size so just ask + // for 8 bytes to start with. + // + sSetupPacket.wLength = sizeof(tDeviceDescriptor); + + ui32Bytes = 0; + + // + // Discover the max packet size for endpoint 0. + // + if(psDevice->sDeviceDescriptor.bMaxPacketSize0 == 0) + { + // + // Put the setup packet in the buffer. + // + ui32Bytes = + USBHCDControlTransfer(ui32Index, &sSetupPacket, psDevice, + (uint8_t *)&(psDevice->sDeviceDescriptor), + sizeof(tDeviceDescriptor), 8); + } + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + if(ui32Bytes < sizeof(tDeviceDescriptor)) + { + sSetupPacket.wLength = (uint16_t)sizeof(tDeviceDescriptor); + + ui32Bytes = + USBHCDControlTransfer(ui32Index, &sSetupPacket, psDevice, + (uint8_t *)&(psDevice->sDeviceDescriptor), + sizeof(tDeviceDescriptor), + psDevice->sDeviceDescriptor.bMaxPacketSize0); + } + + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function issues a request for a string descriptor from a device. +//! +//! \param psDevice is the device for this request. +//! \param pui8Buffer is the pointer to the buffer to store the requested +//! string descriptor. +//! \param ui32Size is the size of the buffer passed in the buffer that will +//! be used for this request. +//! \param ui32LangID is the ID of the language for the requested string. +//! \param ui32StringIndex is the index for the request. +//! +//! This function will request a string descriptor from the device of the type +//! specified in the \e ui32DescriptorType parameter. The \e pui8Descriptor +//! pointer is the location where the request results will be stored. The +//! \e ui32Size should be passed in to indicate the size of the +//! \e pui8Descriptor buffer. The \e ui32DevAddress parameter is used to +//! specify the device address to communicate with on the USB bus. This value +//! should be specified as 0 for any non-configured device on the USB bus and +//! be changed to the address set by a call to USBHCDSetAddress(). +//! +//! \return The number of bytes returned in the \e pui8Buffer due to the +//! request. This value can be zero if the device did not respond. +// +//***************************************************************************** +uint32_t +USBHCDStringDescriptorGet(tUSBHostDevice *psDevice, uint8_t *pui8Buffer, + uint32_t ui32Size, uint32_t ui32LangID, + uint32_t ui32StringIndex) +{ + uint32_t ui32BytesReturned; + tUSBRequest sSetupPacket; + + // + // Default the number of bytes to zero. + // + ui32BytesReturned = 0; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + + // + // Request for a string descriptor. + // + sSetupPacket.wValue = (USB_DTYPE_STRING << 8) | + (uint16_t)ui32StringIndex; + + // + // Set the language ID. + // + sSetupPacket.wIndex = ui32LangID; + + // + // Only request the space available. + // + sSetupPacket.wLength = (uint16_t)ui32Size; + + // + // Put the setup packet in the buffer. + // + ui32BytesReturned = + USBHCDControlTransfer(0, &sSetupPacket, psDevice, pui8Buffer, ui32Size, + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Return the number of bytes in the string. + // + return(ui32BytesReturned); +} + +//***************************************************************************** +// +//! This function is used to send the set address command to a device. +//! +//! \param ui32DevIndex is the index of the device whose address is to be +//! set. This value must be 0 to indicate that the device is connected +//! directly to the host controller. Higher values indicate devices connected +//! via a hub. +//! \param ui32DevAddress is the new device address to use for a device. +//! +//! The USBHCDSetAddress() function is used to set the USB device address, once +//! a device has been discovered on the bus. This call is typically issued +//! following a USB reset triggered by a call the USBHCDReset(). The +//! address passed into this function via the \e ui32DevAddress parameter is +//! used for all further communications with the device after this function +//! returns. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSetAddress(uint32_t ui32DevIndex, uint32_t ui32DevAddress) +{ + tUSBRequest sSetupPacket; + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_ADDRESS; + sSetupPacket.wValue = ui32DevAddress; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // Only request the space available. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, + &g_sUSBHCD.psUSBDevice[ui32DevIndex], 0, + 0, MAX_PACKET_SIZE_EP0); + + // + // Must delay 2ms after setting the address. + // + OS_DELAY(g_ui32Tickms * 2); +} + +//***************************************************************************** +// +//! This function is used to send a Clear Feature request to a device. +//! +//! \param ui32DevAddress is the USB bus address of the device that will +//! receive this request. +//! \param ui32Pipe is the pipe that will be used to send the request. +//! \param ui32Feature is one of the USB_FEATURE_* definitions. +//! +//! This function will issue a Clear Feature request to the device indicated +//! by the \e ui32DevAddress parameter. The \e ui32Pipe parameter is the USB +//! pipe that should be used to send this request. The \e ui32Feature +//! parameter should be one of the following values: +//! +//! * \b USB_FEATURE_EP_HALT is used to end a HALT condition on a devices +//! endpoint. +//! * \b USB_FEATURE_REMOTE_WAKE is used to disable a device's remote wake +//! feature. +//! * \b USB_FEATURE_TEST_MODE is used take the USB device out of test mode. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDClearFeature(uint32_t ui32DevAddress, uint32_t ui32Pipe, + uint32_t ui32Feature) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_ENDPOINT; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + sSetupPacket.wValue = ui32Feature; + + // + // Set the endpoint to access. + // + if(ui32Pipe & EP_PIPE_TYPE_IN) + { + sSetupPacket.wIndex = g_sUSBHCD.psUSBINPipes[ui32Index].ui8EPNumber | + 0x80; + } + else + { + sSetupPacket.wIndex = g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8EPNumber; + } + + // + // This is always 0. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, + &g_sUSBHCD.psUSBDevice[ui32DevAddress - 1], 0, 0, + MAX_PACKET_SIZE_EP0); + + // + // Set the endpoint to access. + // + if(ui32Pipe & EP_PIPE_TYPE_IN) + { + MAP_USBEndpointDataToggleClear(USB0_BASE, + IndexToUSBEP(ui32Index + 1), + USB_EP_HOST_IN); + } + else + { + MAP_USBEndpointDataToggleClear(USB0_BASE, + IndexToUSBEP(ui32Index + 1), + USB_EP_HOST_OUT); + } + + // + // Must delay 2ms after clearing the feature. + // + OS_DELAY(g_ui32Tickms * 2); +} + +//***************************************************************************** +// +//! This function is used to set the current configuration for a device. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32Device is the USB device for this function. +//! \param ui32Configuration is one of the devices valid configurations. +//! +//! This function is used to set the current device configuration for a USB +//! device. The \e ui32Configuration value must be one of the configuration +//! indexes that was returned in the configuration descriptor from the device, +//! or a value of 0. If 0 is passed in, the device will return to it's +//! addressed state and no longer be in a configured state. If the value is +//! non-zero then the device will change to the requested configuration. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSetConfig(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Configuration) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + ASSERT(ui32Index == 0); + + psDevice = (tUSBHostDevice *)ui32Device; + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_CONFIG; + sSetupPacket.wValue = ui32Configuration; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // Only request the space available. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + MAX_PACKET_SIZE_EP0); +} + +//***************************************************************************** +// +//! This function is used to set the current interface and alternate setting +//! for an interface on a device. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32Device is the USB device for this function. +//! \param ui32Interface is one of the valid interface numbers for a device. +//! \param ui32AltSetting is one of the valid alternate interfaces for the +//! \e ui32Interface number. +//! +//! This function is used to change the alternate setting for one of the valid +//! interfaces on a USB device. The \e ui32Device specifies the device +//! instance that was returned when the device was connected. This call will +//! set the USB device's interface based on the \e ui32Interface and +//! \e ui32AltSetting. +//! +//! \b Example: Set the USB device interface 2 to alternate setting 1. +//! +//! \verbatim +//! USBHCDSetInterface(0, ui32Device, 2, 1); +//! \endverbatim +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSetInterface(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Interface, uint32_t ui32AltSetting) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + ASSERT(ui32Index == 0); + + psDevice = (tUSBHostDevice *)ui32Device; + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_INTERFACE; + + // + // Index is the interface to access. + // + sSetupPacket.wIndex = ui32Interface; + + // + // wValue is the alternate setting. + // + sSetupPacket.wValue = ui32AltSetting; + + + // + // Only request the space available. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + MAX_PACKET_SIZE_EP0); +} + +//***************************************************************************** +// +// The internal function to see if a new schedule event should occur. +// +// This function is called by the main interrupt handler due to start of frame +// interrupts to determine if a new scheduler event should be sent to the USB +// pipe. +// +// \return None. +// +//***************************************************************************** +void +USBHostCheckPipes(void) +{ + int32_t i32Idx; + + g_ui32CurrentTick++; + + for(i32Idx = 0; i32Idx < g_sUSBHCD.ui32NumEndpoints; i32Idx++) + { + // + // Skip unused pipes. + // + if(g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type == USBHCD_PIPE_UNUSED) + { + continue; + } + + // + // If the tick has expired and it has an interval then update it. + // + if((g_sUSBHCD.psUSBINPipes[i32Idx].ui32Interval != 0) && + (g_sUSBHCD.psUSBINPipes[i32Idx].ui32NextEventTick == + g_ui32CurrentTick)) + { + // + // Schedule the next event. + // + g_sUSBHCD.psUSBINPipes[i32Idx].ui32NextEventTick += + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Interval; + + // + // If the pipe is IDLE and there is a callback, let the higher + // level drivers know that a new transfer can be scheduled. + // + if((g_sUSBHCD.psUSBINPipes[i32Idx].iState == ePipeIdle) && + (g_sUSBHCD.psUSBINPipes[i32Idx].pfnCallback)) + { + g_sUSBHCD.psUSBINPipes[i32Idx].pfnCallback( + IN_PIPE_HANDLE(i32Idx), + USB_EVENT_SCHEDULER); + } + } + } +} + +//***************************************************************************** +// +// The internal USB host mode interrupt handler. +// +// \param ui32Index is the USB controller associated with this interrupt. +// \param ui32Status is the current interrupt status as read via a call to +// \e USBIntStatusControl(). +// +// This the main USB interrupt handler called when operating in host mode. +// This handler will branch the interrupt off to the appropriate handlers +// depending on the current status of the USB controller. +// +// The two-tiered structure for the interrupt handler ensures that it is +// possible to use the same handler code in both host and OTG modes and +// means that device code can be excluded from applications that only require +// support for USB host mode operation. +// +// \return None. +// +//***************************************************************************** +void +USBHostIntHandlerInternal(uint32_t ui32Index, uint32_t ui32Status) +{ + uint32_t ui32EPStatus, ui32DMAIntStatus, ui32Idx, ui32DevIndex; + static uint32_t ui32SOFDivide = 0; + int32_t i32ClassDrvr; + + // + // By default, assume we are dealing with the device directly connected + // to the host controller and that we need to notify its class driver of + // this interrupt. + // + g_sUSBHCD.psUSBDevice[0].ui32Flags |= USBHDEV_FLAG_NOTIFYINT; + + if(ui32Status & USB_INTCTRL_SOF) + { + // + // Indicate that a start of frame has occurred. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_SOF; + } + + // + // A power fault has occurred so notify the application. + // + if(ui32Status & USB_INTCTRL_POWER_FAULT) + { + // + // Indicate that a power fault has occurred. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_POWER_FAULT; + + // + // Turn off power to the bus. + // + MAP_USBHostPwrDisable(USB0_BASE); + + // + // Disable USB interrupts. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + return; + } + + // + // In the event of a USB VBUS error, end the session and remove power to + // the device. + // + if(ui32Status & USB_INTCTRL_VBUS_ERR) + { + // + // Set the VBUS error event. We deliberately clear all other events + // since this one means anything else that is outstanding is + // irrelevant. + // + g_sUSBHCD.ui32IntEvents = INT_EVENT_VBUS_ERR; + return; + } + + // + // Received a reset from the host. + // + if(ui32Status & USB_INTCTRL_BABBLE) + { + } + + // + // Suspend was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_SUSPEND) + { + } + + // + // Start the session. + // + if(ui32Status & USB_INTCTRL_SESSION) + { + // + // Power the USB bus. + // + MAP_USBHostPwrEnable(USB0_BASE); + + USBOTGSessionRequest(USB0_BASE, true); + } + + // + // Resume was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_RESUME) + { + } + + // + // Device connected so tell the main routine to issue a reset. + // + if(ui32Status & USB_INTCTRL_CONNECT) + { + // + // Set the connect flag and clear disconnect if it happens to be set. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_CONNECT; + g_sUSBHCD.ui32IntEvents &= ~INT_EVENT_DISCONNECT; + + // + // Power the USB bus. + // + MAP_USBHostPwrEnable(USB0_BASE); + } + + // + // Handle the ID detection so that the ID pin can be used as a + // GPIO in eUSBModeHost. + // + if(ui32Status & USB_INTCTRL_MODE_DETECT) + { + // + // If in eUSBModeHost mode then switch back to OTG detection + // so that VBUS can be monitored but free up the ID pin. + // + if(g_iUSBMode == eUSBModeHost) + { + USBOTGMode(USB0_BASE); + } + } + + // + // Device was unplugged. + // + if(ui32Status & USB_INTCTRL_DISCONNECT) + { + // + // Set the disconnect flag and clear connect if it happens to be set. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_DISCONNECT; + g_sUSBHCD.ui32IntEvents &= ~INT_EVENT_CONNECT; + } + + // + // Start of Frame was received. + // + if(ui32Status & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ui32USBSOFCount++; + + // + // Increment our SOF divider. + // + ui32SOFDivide++; + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ui32SOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ui32SOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Handle the LPM interrupt + // + ui32Status = USBLPMIntStatus(USB0_BASE); + + if(ui32Status) + { + // + // Set the LPM interrupt event and clear the pending event. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_LPM; + g_sUSBHCD.ui32IntEvents &= ~INT_EVENT_LPM_PEND; + + // + // Anything other than and acknowledge means that the transfer + // was not complete for some reason. + // + for(ui32Idx = 0; ui32Idx < (MAX_USB_DEVICES + 1); ui32Idx++) + { + if((ui32Status != USB_INTLPM_ACK) && + (g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags & + USBHDEV_FLAG_LPMPEND)) + { + g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags |= + USBHDEV_FLAG_LPMERROR; + } + g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags &= ~USBHDEV_FLAG_LPMPEND; + } + } + + // + // Check to see if any DMA transfers are pending + // + ui32DMAIntStatus = USBLibDMAIntStatus(g_sUSBHCD.psDMAInstance); + + if(ui32DMAIntStatus) + { + // + // Handle any DMA interrupt processing. + // + USBLibDMAIntHandler(g_sUSBHCD.psDMAInstance, ui32DMAIntStatus); + + for(ui32Idx = 0; ui32Idx < MAX_NUM_PIPES; ui32Idx++) + { + if((g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReadDMAWait) || + (g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReadDMA)) + { + // + // If the DMA channel transfer is complete, send an ack. + // + if(USBLibDMAChannelStatus(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel) == + USBLIBSTATUS_DMA_COMPLETE) + { + // + // Acknowledge the IN request. + // + MAP_USBHostEndpointDataAck(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // If using uDMA then the endpoint status interrupt will + // not occur. So process the data ready event here. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeDataReady; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), USB_EVENT_RX_AVAILABLE); + } + + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].psDevice->ui32Flags |= + USBHDEV_FLAG_NOTIFYINT; + } + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == + ePipeWriteDMASend) + { + // + // If the uDMA channel transfer is complete, then tell + // the USB controller to go ahead and send the data + // + if(USBLibDMAChannelStatus(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32Idx].ui8DMAChannel) & + USBLIBSTATUS_DMA_COMPLETE) + { + MAP_USBEndpointDataSend(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_TRANS_OUT); + + // + // Now waiting on the final endpoint interrupt. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = + ePipeWriteDMAWait; + } + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == ePipeWriteDMA) + { + // + // Data was transmitted successfully. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeDataSent; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that its last transaction was completed. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_TX_COMPLETE); + } + } + } + } + + // + // Get the current endpoint interrupt status. + // + ui32Status = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ui32Status & USB_INTEP_0) + { + // + // Indicate that a start of frame has occurred. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_ENUM; + } + + for(ui32Idx = 0; ui32Idx < MAX_NUM_PIPES; ui32Idx++) + { + // + // Check the next pipe, the first time through this will clear out + // any interrupts dealing with endpoint zero since it was handled + // above. + // + ui32Status >>= 1; + + // + // Break out if there are no more pending interrupts. + // + if(ui32Status == 0) + { + break; + } + + // + // Check the status of the receive(IN) pipes. + // + if(ui32Status & 0x10000) + { + // + // Clear the status flag for the IN Pipe. + // + ui32Status &= ~0x10000; + + // + // Read the status of the endpoint connected to this pipe. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // Check if the device stalled the request. + // + if(ui32EPStatus & USB_HOST_IN_STALL) + { + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_IN_STALL); + + // + // Save the STALLED state. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeStalled; + + // + // Notify the pipe that it was stalled. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), + USB_EVENT_STALL); + } + } + else if(ui32EPStatus & USB_HOST_IN_ERROR) + { + // + // We can no longer communicate with this device for some + // reason. It may have been disconnected from a hub, for + // example. Merely clear the status and continue. + // + USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_IN_ERROR); + + // + // Save the STALLED state. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeError; + + // + // Notify the pipe that it was stalled. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), + USB_EVENT_ERROR); + } + } + // + // Handle the case where the pipe is reading a single packet. + // + else if(g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReadDMA) + { + void *pvAddr; + + // + // Enable the DMA channel and wait for it to complete. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeReadDMAWait; + + pvAddr = USBLibDMAAddrGet(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel); + + // + // Save the amount of data available. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead = + USBEndpointDataAvail(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // Only request what is available. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead <= + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32ReadSize) + { + // + // Reset the transfer size. + // + USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel, + pvAddr, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead); + } + else + { + // + // The transfer size did not change, this leaves some + // data in the FIFO. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead = + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32ReadSize; + } + + USBLibDMAChannelEnable( + g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel); + } + else if(g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReading) + { + // + // Data is available. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeDataReady; + + // + // Read the data out of the USB endpoint interface into the + // buffer provided by the caller to USBHCDPipeRead() or + // USBHCDPipeSchedule() if a buffer was provided already. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pui8ReadPtr) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead = + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32ReadSize; + + USBEndpointDataGet(USB0_BASE, IndexToUSBEP(ui32Idx + 1), + g_sUSBHCD.psUSBINPipes[ui32Idx].pui8ReadPtr, + &g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead); + } + + // + // Notify the pipe that its last transaction was completed. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), + USB_EVENT_RX_AVAILABLE); + } + + } + + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].psDevice->ui32Flags |= + USBHDEV_FLAG_NOTIFYINT; + } + + // + // Check the status of the transmit(OUT) pipes. + // + if(ui32Status & 1) + { + // + // Read the status of the endpoint connected to this pipe. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // Check if the device stalled the request. + // + if(ui32EPStatus & USB_HOST_OUT_STALL) + { + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_OUT_STALL); + + // + // Save the STALLED state. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeStalled; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that it was stalled. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_STALL); + } + } + else if(ui32EPStatus & USB_HOST_OUT_ERROR) + { + // + // Clear the error condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_OUT_ERROR); + + // + // Save the Pipes error state. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeError; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that had an error. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_ERROR); + } + } + else if((g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == + ePipeWriting) || + (g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == + ePipeWriteDMAWait)) + { + // + // Data was transmitted successfully. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeDataSent; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that its last transaction was completed. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_TX_COMPLETE); + } + } + + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + ui32EPStatus & USB_HOST_OUT_STATUS); + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].psDevice) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].psDevice->ui32Flags |= + USBHDEV_FLAG_NOTIFYINT; + } + } + } + + // + // Send back notifications to any class driver whose endpoint required + // service during the handler. + // + for(ui32DevIndex = 0; ui32DevIndex <= MAX_USB_DEVICES; ui32DevIndex++) + { + // + // Which class driver does this device use? + // + i32ClassDrvr = g_pi32USBHActiveDriver[ui32DevIndex]; + + // + // If a class driver is in use, and one of its endpoints was serviced + // and the class driver has an interrupt callback... + // + if((i32ClassDrvr >= 0) && + (g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags & + USBHDEV_FLAG_NOTIFYINT) && + (g_sUSBHCD.ppsClassDrivers[i32ClassDrvr]->pfnIntHandler)) + { + // + // ...call the class driver's interrupt notification callback. + // + g_sUSBHCD.ppsClassDrivers[i32ClassDrvr]->pfnIntHandler( + g_ppvDriverInstance[ui32DevIndex]); + } + } +} + +//***************************************************************************** +// +//! The USB host mode interrupt handler for controller index 0. +//! +//! This the main USB interrupt handler entry point. This handler will branch +//! the interrupt off to the appropriate handlers depending on the current +//! status of the USB controller. This function must be placed in the +//! interrupt table in order for the USB Library host stack to function. +//! +//! \return None. +// +//***************************************************************************** +void +USB0HostIntHandler(void) +{ + uint32_t ui32Status; + + // + // Get the control interrupt status. + // + ui32Status = MAP_USBIntStatusControl(USB0_BASE); + + // + // Call the internal handler to process the interrupts. + // + USBHostIntHandlerInternal(0, ui32Status); +} + +//***************************************************************************** +// +//! This function opens the class driver. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32DeviceNum is the device number for the driver to load. +//! +//! This function opens the driver needed based on the class value found in +//! the device's interface descriptor. +//! +//! \return This function returns -1 if no driver is found, or it returns the +//! index of the driver found in the list of host class drivers. +// +//***************************************************************************** +static int32_t +USBHCDOpenDriver(uint32_t ui32Index, uint32_t ui32DeviceNum) +{ + int32_t i32Driver; + uint32_t ui32Class; + tInterfaceDescriptor *psInterface; + tEventInfo sEvent; + + ASSERT(ui32Index == 0); + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DeviceNum].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DeviceNum].ui32Interface, + USB_DESC_ANY); + + // + // Read the interface class. + // + ui32Class = psInterface->bInterfaceClass; + + // + // Search through the Host Class driver list for the devices class. + // + for(i32Driver = 0; i32Driver < g_sUSBHCD.ui32NumClassDrivers; i32Driver++) + { + // + // If a driver was found call the open for this driver and save which + // driver is in use. + // + if(g_sUSBHCD.ppsClassDrivers[i32Driver]->ui32InterfaceClass == + ui32Class) + { + // + // Call the open function for the class driver. + // + g_ppvDriverInstance[ui32DeviceNum] = + g_sUSBHCD.ppsClassDrivers[i32Driver]->pfnOpen( + &g_sUSBHCD.psUSBDevice[ui32DeviceNum]); + + // + // If the driver was successfully loaded then break out of the + // loop. + // + if(g_ppvDriverInstance[ui32DeviceNum] != 0) + { + break; + } + } + } + + // + // If no drivers were found then return -1 to indicate an invalid + // driver instance. + // + if(i32Driver == g_sUSBHCD.ui32NumClassDrivers) + { + // + // Send an unknown connection event. + // + SendUnknownConnect(ui32Index, (ui32Index << 16) | ui32DeviceNum); + + // + // Indicate that no driver was found. + // + i32Driver = -1; + } + else + { + // + // If the connect event is enabled then send the event. + // + sEvent.ui32Event = USB_EVENT_CONNECTED; + sEvent.ui32Instance = (ui32Index << 16) | ui32DeviceNum; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_CONNECT); + } + + return(i32Driver); +} + +//***************************************************************************** +// +// This function will send an event to a registered event driver. +// +// \param ui32Index specifies which USB controller to use. +// \param psEvent is a pointer to the event structure to send. +// +// This function is only used internally to the USB library and will check +// if an event driver is registered and send on the event. +// +// Note: This function should not be called outside of the USB library. +// +// \return None. +// +//***************************************************************************** +void +InternalUSBHCDSendEvent(uint32_t ui32Index, tEventInfo *psEvent, + uint32_t ui32EvFlag) +{ + // + // Make sure that an event driver has been registered. + // + if((g_sUSBHCD.i32EventDriver != -1) && + (g_sUSBHCD.ppsClassDrivers[g_sUSBHCD.i32EventDriver]->pfnIntHandler) && + (g_sUSBHCD.ui32EventEnables & ui32EvFlag)) + { + g_sUSBHCD.ppsClassDrivers[g_sUSBHCD.i32EventDriver]->pfnIntHandler( + psEvent); + } +} + +//***************************************************************************** +// +// This function handles the necessary clean up for device disconnect. +// +// \param ui32Index is the device number for the device that was disconnected. +// +// This function handles all of the necessary clean up after a device +// disconnect has been detected by the stack. This includes calling back the +// appropriate driver if necessary. +// +// \return None. +// +//***************************************************************************** +static void +USBHCDDeviceDisconnected(uint32_t ui32Index, uint32_t ui32DevIndex) +{ + tEventInfo sEvent; + + ASSERT(ui32Index == 0); + ASSERT(ui32DevIndex <= MAX_USB_DEVICES); + + // + // If there is an event driver with a valid event handler and the + // USBHCD_EVFLAG_DISCNCT is enabled, then call the registered event + // handler. + // + sEvent.ui32Event = USB_EVENT_DISCONNECTED; + sEvent.ui32Instance = (ui32Index << 16) | ui32DevIndex; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_DISCNCT); + + // + // Reset the class. + // + g_sUSBHCD.ui32Class = USB_CLASS_EVENTS; + + // + // Free the memory used by the configuration descriptor. + // + ConfigDescFree(&g_sUSBHCD.psUSBDevice[ui32DevIndex]); + + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags = 0; + g_sUSBHCD.psUSBDevice[ui32DevIndex].bConfigRead = false; + + // + // Reset the max packet size so that this will be re-read from new devices. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bMaxPacketSize0 = 0; + + // + // No longer have a device descriptor. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bLength = 0; + + // + // No longer addressed. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address = 0; + + // + // If this was an active driver then close it out. + // + if(g_pi32USBHActiveDriver[ui32DevIndex] >= 0) + { + // + // Call the driver Close entry point. + // + g_sUSBHCD.ppsClassDrivers[g_pi32USBHActiveDriver[ui32DevIndex]]-> + pfnClose(g_ppvDriverInstance[ui32DevIndex]); + + // + // No active driver now present. + // + g_pi32USBHActiveDriver[ui32DevIndex] = -1; + g_ppvDriverInstance[ui32DevIndex] = 0; + } + + // + // This call is necessary for OTG controllers to know that the host + // stack has completed handling the disconnect of the device before + // removing power and returning to a state that can allow OTG + // negotiations once again. + // We only do this if the disconnected device + // was attached directly to us (device index 0). + // + if((ui32DevIndex == 0) && (g_iUSBMode == eUSBModeOTG)) + { + OTGDeviceDisconnect(0); + } +} + +//***************************************************************************** +// +//! This function is the main routine for the Host Controller Driver. +//! +//! This function is the main routine for the host controller driver, and must +//! be called periodically by the main application outside of a callback +//! context. This allows for a simple cooperative system to access the the +//! host controller driver interface without the need for an RTOS. All time +//! critical operations are handled in interrupt context but all blocking +//! operations are run from the this function to allow them to block and wait +//! for completion without holding off other interrupts. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDMain(void) +{ + tUSBHDeviceState iOldState; + int32_t i32Dev; + tEventInfo sEvent; + + // + // Save the old state to detect changes properly. + // + iOldState = g_sUSBHCD.piDeviceState[0]; + + // + // Fix up the state if any important interrupt events occurred. + // + if(g_sUSBHCD.ui32IntEvents) + { + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_POWER_FAULT) + { + // + // A power fault has occurred so notify the application if there + // is an event handler and the event has been enabled. + // + sEvent.ui32Event = USB_EVENT_POWER_FAULT; + sEvent.ui32Instance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWRFAULT); + + g_sUSBHCD.piDeviceState[0] = eHCDPowerFault; + } + else if(g_sUSBHCD.ui32IntEvents & INT_EVENT_VBUS_ERR) + { + // + // A VBUS error has occurred. This event trumps connect and + // disconnect since it will cause a controller reset. + // + g_sUSBHCD.piDeviceState[0] = eHCDVBUSError; + } + else + { + // + // Has a device connected? + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_CONNECT) + { + g_sUSBHCD.piDeviceState[0] = eHCDDevReset; + g_sUSBHCD.psUSBDevice[0].ui8Hub = 0; + g_sUSBHCD.psUSBDevice[0].ui8HubPort = 0; + } + else + { + // + // Has a device disconnected? + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_DISCONNECT) + { + g_sUSBHCD.piDeviceState[0] = eHCDDevDisconnected; + } + } + + // + // Handle the start of frame event + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_SOF) + { + // + // If the connect event is enabled then send the event. + // + sEvent.ui32Event = USB_EVENT_SOF; + sEvent.ui32Instance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_SOF); + + USBHostCheckPipes(); + + // + // Call the hub driver to have it perform any necessary + // processing to handle downstream devices. + // + USBHHubMain(); + } + + // + // Handle LPM interrupt events. + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_LPM) + { + // + // There should be a pending LPM request. + // + ASSERT((g_sUSBHCD.ui32IntEvents & INT_EVENT_LPM_PEND) != 0); + + for(i32Dev = 0; i32Dev < MAX_USB_DEVICES + 1; i32Dev++) + { + // + // Find the device with the pending LPM request. + // + if(g_sUSBHCD.psUSBDevice[i32Dev].ui32Flags & + USBHDEV_FLAG_LPMPEND) + { + // + // Clear the pending event at the device level, this + // leaves the error set if it was already set. + // + g_sUSBHCD.psUSBDevice[i32Dev].ui32Flags &= + ~USBHDEV_FLAG_LPMPEND; + + // + // Clear the pending request and event at the host + // controller level. + // + g_sUSBHCD.ui32IntEvents &= ~(INT_EVENT_LPM_PEND | + INT_EVENT_LPM); + } + } + } + } + + // + // Clear the flags. + // + g_sUSBHCD.ui32IntEvents = 0; + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + } + + // + // Process the state machine for each connected device. Yes, the exit + // condition for this loop is correct since we support (MAX_USB_DEVICES+1) + // devices (the hub counts as one). + // + for(i32Dev = 0; i32Dev <= MAX_USB_DEVICES; i32Dev++) + { + // + // If this is not the first device (i.e. the one directly connected to + // the host controller) then set the old state to the current state + // since we won't have mucked with it in any of the previous code. + // + if(i32Dev != 0) + { + iOldState = g_sUSBHCD.piDeviceState[i32Dev]; + } + + // + // Process the state machine for this device. + // + ProcessUSBDeviceStateMachine(iOldState, i32Dev); + } +} + +static void +ProcessUSBDeviceStateMachine(tUSBHDeviceState iOldState, + uint32_t ui32DevIndex) +{ + switch(g_sUSBHCD.piDeviceState[ui32DevIndex]) + { + // + // There was a power fault condition so shut down and wait for the + // application to re-initialized the system. + // + case eHCDPowerFault: + { + break; + } + + // + // There was a VBUS error so handle it. + // + case eHCDVBUSError: + { + // + // Disable USB interrupts. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + // + // If there was a device in any state of connection then indicate + // that it has been disconnected. + // + if((iOldState != eHCDIdle) && (iOldState != eHCDPowerFault)) + { + // + // Handle device disconnect. + // + USBHCDDeviceDisconnected(0, ui32DevIndex); + } + + // + // Reset the controller. + // + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + + // + // Wait for 100ms before trying to re-power the device. + // + OS_DELAY(g_ui32Tickms * 100); + + // + // Re-initialize the HCD. + // + USBHCDInitInternal(0, g_sUSBHCD.pvPool, g_sUSBHCD.ui32PoolSize); + + break; + } + // + // Trigger a reset to the connected device. + // + case eHCDDevReset: + { + if(!ui32DevIndex) + { + // + // Trigger a Reset. This is only ever done for devices + // attached directly to the controller. + // + DEBUG_OUTPUT("USB reset.\n"); + USBHCDReset(0); + } + + // + // The state moves to connected but not configured. + // + g_sUSBHCD.piDeviceState[0] = eHCDDevConnected; + + // + // Remember that we don't have a valid configuration descriptor + // yet. + // + g_sUSBHCD.psUSBDevice[0].bConfigRead = false; + + break; + } + // + // Device connection has been established now start enumerating + // the device. + // + case eHCDDevConnected: + case eHCDDevConnectedHub: + { + // + // First check if we have read the device descriptor at all + // before proceeding. + // + if(g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bLength == + 0) + { + // + // Initialize a request for the device descriptor. + // + DEBUG_OUTPUT("Connection %d - getting device descriptor\n", + ui32DevIndex); + + // + // Hub enumeration has already set the speed so do not + // override the setting here. + // + if(g_sUSBHCD.piDeviceState[ui32DevIndex] == eHCDDevConnected) + { + // + // Remember the speed of this device to ensure endpoints + // are properly configured. + // + switch(USBHostSpeedGet(USB0_BASE)) + { + case USB_HIGH_SPEED: + { + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = + USB_EP_SPEED_HIGH; + break; + } + case USB_FULL_SPEED: + { + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = + USB_EP_SPEED_FULL; + break; + } + default: + { + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = + USB_EP_SPEED_LOW; + break; + } + } + } + + if(USBHCDGetDeviceDescriptor(0, + &g_sUSBHCD.psUSBDevice[ui32DevIndex]) == 0) + { + // + // If the device descriptor cannot be read then the device + // will be treated as unknown. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevError; + + DEBUG_OUTPUT("Connection %d - failed to get descriptor\n", + ui32DevIndex); + + // + // Send an unknown connection event to let the application + // know that there is a device connected but return no + // zero for the instance. + // + SendUnknownConnect(0, 0); + + // + // If the device is connected via a hub, tell the hub + // driver that we experienced an error enumerating the + // device. + // + if(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub) + { + USBHHubEnumerationError( + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); + } + } + } + // + // If we have the device descriptor then move on to setting + // the address of the device. + // + else if(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address == 0) + { + DEBUG_OUTPUT("Connection %d - setting address %d\n", + ui32DevIndex, ui32DevIndex + 1); + + // + // Send the set address command. + // + USBHCDSetAddress(ui32DevIndex, (ui32DevIndex + 1)); + + // + // Save the address. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address = + (ui32DevIndex + 1); + + // + // Move on to the addressed state. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevAddressed; + } + break; + } + case eHCDDevAddressed: + { + // + // First check if we have read the configuration descriptor. + // + if(!g_sUSBHCD.psUSBDevice[ui32DevIndex].bConfigRead) + { + DEBUG_OUTPUT("Connection %d - getting config descriptor\n", + ui32DevIndex); + + // + // Initialize a request for the configuration descriptor. + // + if(USBHCDGetConfigDescriptor(0, + &g_sUSBHCD.psUSBDevice[ui32DevIndex]) == 0) + { + // + // If the device descriptor cannot be read then the device + // will be treated as unknown. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevError; + + DEBUG_OUTPUT("Connection %d - failed to get descriptor\n", + ui32DevIndex); + + // + // Send an unknown connection event to let the application + // know that there is a device connected but return no + // zero for the instance. + // + SendUnknownConnect(0, 0); + + // + // If the device is connected via a hub, tell the hub + // driver that we experienced an error enumerating the + // device. + // + if(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub) + { + USBHHubEnumerationError( + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); + } + } + } + // + // Now have addressed and received the device configuration, + // so get ready to set the device configuration. + // + else + { + DEBUG_OUTPUT("Connection %d - setting configuration.\n", + ui32DevIndex); + + // + // Use the first configuration to set the device + // configuration. + // + USBHCDSetConfig(0, + (uint32_t)&g_sUSBHCD.psUSBDevice[ui32DevIndex], 1); + + // + // Move on to the configured state. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevConfigured; + + // + // Open the driver for the device. + // + g_pi32USBHActiveDriver[ui32DevIndex] = USBHCDOpenDriver(0, + ui32DevIndex); + + // + // If the device is connected via a hub, tell the hub + // driver that enumeration is complete. + // + if(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub) + { + USBHHubEnumerationComplete( + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); + } + } + break; + } + // + // The device was making a request and is now complete. + // + case eHCDDevRequest: + { + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevConnected; + break; + } + // + // The strings are currently not accessed. + // + case eHCDDevGetStrings: + { + break; + } + // + // Basically Idle at this point. + // + case eHCDDevDisconnected: + { + DEBUG_OUTPUT("Connection %d - disconnected.\n", + ui32DevIndex); + + // + // Handle device disconnect. + // + USBHCDDeviceDisconnected(0, ui32DevIndex); + + // + // Return to the Idle state. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDIdle; + break; + } + + // + // Connection and enumeration is complete so allow this function + // to exit. + // + case eHCDDevConfigured: + { + break; + } + + // + // Poorly behaving device are in limbo in this state until removed. + // + case eHCDDevError: + { + DEBUG_OUTPUT("Connection %d - Error!\n", ui32DevIndex); + + // + // If this device is connected directly to us, tidy up and ignore + // it until it is removed. If the device is connected to a hub, + // we just leave it in the error state until it is removed. + // + if(ui32DevIndex == 0) + { + g_sUSBHCD.ui32IntEvents |= INT_EVENT_DISCONNECT; + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDIdle; + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +//! This function completes a control transaction to a device. +//! +//! \param ui32Index is the controller index to use for this transfer. +//! \param psSetupPacket is the setup request to be sent. +//! \param psDevice is the device instance pointer for this request. +//! \param pui8Data is the data to send for OUT requests or the receive buffer +//! for IN requests. +//! \param ui32Size is the size of the buffer in \e pui8Data. +//! \param ui32MaxPacketSize is the maximum packet size for the device for this +//! request. +//! +//! This function handles the state changes necessary to send a control +//! transaction to a device. This function should not be called from within +//! an interrupt callback as it is a blocking function. +//! +//! \return The number of bytes of data that were sent or received as a result +//! of this request. +// +//***************************************************************************** +uint32_t +USBHCDControlTransfer(uint32_t ui32Index, tUSBRequest *psSetupPacket, + tUSBHostDevice *psDevice, uint8_t *pui8Data, + uint32_t ui32Size, uint32_t ui32MaxPacketSize) +{ + uint32_t ui32Remaining; + uint32_t ui32DataSize; + + // + // Debug sanity check. + // + ASSERT(g_sUSBHEP0State.iState == eEP0StateIdle); + ASSERT(ui32Index == 0); + + // + // Initialize the state of the data for this request. + // + g_sUSBHEP0State.pui8Data = pui8Data; + g_sUSBHEP0State.ui32BytesRemaining = ui32Size; + g_sUSBHEP0State.ui32DataSize = ui32Size; + + // + // Set the maximum packet size. + // + g_sUSBHEP0State.ui32MaxPacketSize = ui32MaxPacketSize; + + // + // Save the current address. + // + g_sUSBHEP0State.ui32DevAddress = psDevice->ui32Address; + + // + // Set the address the host will used to communicate with the device. + // + MAP_USBHostAddrSet(USB0_BASE, USB_EP_0, g_sUSBHEP0State.ui32DevAddress, + USB_EP_HOST_OUT); + + // + // Make sure that endpoint 0 has the proper speed setting. + // + USBHostEndpointConfig(USB0_BASE, USB_EP_0, 64, 0, 0, + (USB_EP_MODE_CTRL | psDevice->ui32Speed | + USB_EP_HOST_OUT)); + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (uint8_t *)psSetupPacket, + sizeof(tUSBRequest)); + + // + // If this is an IN request, change to that state. + // + if(psSetupPacket->bmRequestType & USB_RTYPE_DIR_IN) + { + g_sUSBHEP0State.iState = eEP0StateSetupIN; + } + else + { + // + // If there is no data then this is not an OUT request. + // + if(ui32Size != 0) + { + // + // Since there is data, this is an OUT request. + // + g_sUSBHEP0State.iState = eEP0StateSetupOUT; + } + else + { + // + // Otherwise this request has no data and just a status phase. + // + g_sUSBHEP0State.iState = eEP0StateStatusIN; + } + } + + // + // Make sure we are talking to the correct device. + // + if(psDevice->ui8Hub == 0) + { + USBHostHubAddrSet(USB0_BASE, USB_EP_0, 0, + USB_EP_HOST_OUT | psDevice->ui32Speed); + } + else + { + USBHostHubAddrSet(USB0_BASE, USB_EP_0, + (psDevice->ui8Hub | (psDevice->ui8HubPort << 8)), + USB_EP_HOST_OUT | psDevice->ui32Speed); + } + + // + // Send the Setup packet. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_SETUP); + + // + // Block until endpoint 0 returns to the IDLE state. + // + while(g_sUSBHEP0State.iState != eEP0StateIdle) + { + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + if((g_sUSBHCD.ui32IntEvents & (INT_EVENT_ENUM | INT_EVENT_SOF)) == + (INT_EVENT_ENUM | INT_EVENT_SOF)) + { + g_sUSBHCD.ui32IntEvents &= ~(INT_EVENT_ENUM | INT_EVENT_SOF); + + USBHCDEnumHandler(); + } + + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + + if(g_sUSBHEP0State.iState == eEP0StateError) + { + return(0xffffffff); + } + + // + // If we aborted the transfer due to an error, tell the caller + // that no bytes were transferred. + // + if(g_sUSBHCD.ui32IntEvents & (INT_EVENT_VBUS_ERR | + INT_EVENT_DISCONNECT)) + { + return(0xffffffff); + } + } + + // + // Calculate and return the number of bytes that were sent or received. + // The extra copy into local variables is required to prevent some + // compilers from warning about undefined order of volatile access. + // + ui32DataSize = g_sUSBHEP0State.ui32DataSize; + ui32Remaining = g_sUSBHEP0State.ui32BytesRemaining; + + return(ui32DataSize - ui32Remaining); +} + +//***************************************************************************** +// +// Starts enumerating a new device connected via the hub. +// +// \param ui32Index is the index of the USB controller to use. +// \param ui32Hub is the hub address from which the connection is being made. +// \param ui32Port is the hub port number that the new device is connected to. +// \param pui8ConfigPool is memory to be used to store the device's +// configuration descriptor. +// \param ui32ConfigSize is the number of bytes available in the buffer pointed +// to by pui8ConfigPool. +// +// This function is called by the hub class driver after it has detected a new +// device connection and reset the device. +// +// \return Returns the index of the device allocated or 0 if no resources are +// available. Device index 0 is the hub itself. +// +//***************************************************************************** +uint32_t +USBHCDHubDeviceConnected(uint32_t ui32Index, uint8_t ui8Hub, + uint8_t ui8Port, uint32_t ui32Speed) +{ + uint32_t ui32DevIndex; + + // + // Debug sanity checks. + // + ASSERT(ui32Index == 0); + ASSERT(ui8Port); + + DEBUG_OUTPUT("Connection from hub %d, port %d.\n", ui8Hub, ui8Port); + + // + // Look for a free slot in the device table. + // + for(ui32DevIndex = 1; ui32DevIndex <= MAX_USB_DEVICES; ui32DevIndex++) + { + if((g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags & + USBHDEV_FLAG_ALLOCATED) == 0) + { + // + // We found one. Set the state to ensure that it gets enumerated. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags = + USBHDEV_FLAG_ALLOCATED; + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor->bLength = 0; + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub = ui8Hub; + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort = ui8Port; + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = ui32Speed; + g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bLength = 0; + + // + // Set the state to ensure enumeration begins. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevConnectedHub; + + DEBUG_OUTPUT("Allocating device %d\n", ui32DevIndex); + + // + // Pass the device index back to the hub driver. + // + return(ui32DevIndex); + } + } + + // + // If we get here, there are device slots available so send back an invalid + // device index to tell the caller to ignore this device. + // + return(0); +} + +//***************************************************************************** +// +// Called when a device is disconnected from a hub. +// +// \param ui32Index is the index of the USB controller to use. +// \param ui32DevIndex is the device index for the USB device that was +// disconnected. +// +//***************************************************************************** +void +USBHCDHubDeviceDisconnected(uint32_t ui32Index, uint32_t ui32DevIndex) +{ + // + // Debug sanity checks. + // + ASSERT(ui32Index == 0); + ASSERT(ui32DevIndex && (ui32DevIndex <= MAX_USB_DEVICES)); + + DEBUG_OUTPUT("Disconnection from hub %d, port %d, device %d\n", + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort, ui32DevIndex); + + // + // Set the device state to ensure that USBHCDMain cleans it up. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevDisconnected; +} + +//***************************************************************************** +// +// This is the endpoint 0 interrupt handler. +// +// \return None. +// +//***************************************************************************** +static void +USBHCDEnumHandler(void) +{ + uint32_t ui32EPStatus; + uint32_t ui32DataSize; + + // + // Get the end point 0 status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + // + // If there was an error then go to the error state. + // + if(ui32EPStatus == USB_HOST_EP0_ERROR) + { + // + // Clear this status indicating that the status packet was + // received. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0, + USB_HOST_EP0_ERROR); + MAP_USBFIFOFlush(USB0_BASE, USB_EP_0, 0); + + // + // Just go back to the idle state. + // + g_sUSBHEP0State.iState = eEP0StateError; + + return; + } + + switch(g_sUSBHEP0State.iState) + { + // + // Handle the status state, this is a transitory state from + // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE. + // + case eEP0StateStatus: + { + // + // Handle the case of a received status packet. + // + if(ui32EPStatus & (USB_HOST_EP0_RXPKTRDY | USB_HOST_EP0_STATUS)) + { + // + // Clear this status indicating that the status packet was + // received. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0, + (USB_HOST_EP0_RXPKTRDY | + USB_HOST_EP0_STATUS)); + } + + // + // Just go back to the idle state. + // + g_sUSBHEP0State.iState = eEP0StateIdle; + + break; + } + + // + // This state triggers a STATUS IN request from the device. + // + case eEP0StateStatusIN: + { + // + // Generate an IN request from the device. + // + MAP_USBHostRequestStatus(USB0_BASE); + + // + // Change to the status phase and wait for the response. + // + g_sUSBHEP0State.iState = eEP0StateStatus; + + break; + } + + // + // In the IDLE state the code is waiting to receive data from the host. + // + case eEP0StateIdle: + { + break; + } + + // + // Data is still being sent to the host so handle this in the + // EP0StateTx() function. + // + case eEP0StateSetupOUT: + { + // + // Send remaining data if necessary. + // + USBHCDEP0StateTx(); + + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint 0. + // + case eEP0StateSetupIN: + { + // + // Generate a new IN request to the device. + // + MAP_USBHostRequestIN(USB0_BASE, USB_EP_0); + + // + // Proceed to the RX state to receive the requested data. + // + g_sUSBHEP0State.iState = eEP0StateRx; + + break; + } + + // + // The endpoint remains in this state until all requested data has + // been received. + // + case eEP0StateRx: + { + // + // There was a stall on endpoint 0 so go back to the idle state + // as this command has been terminated. + // + if(ui32EPStatus & USB_HOST_EP0_RX_STALL) + { + g_sUSBHEP0State.iState = eEP0StateIdle; + + // + // Clear the stalled state on endpoint 0. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0, + ui32EPStatus & USB_HOST_IN_STATUS); + break; + } + + // + // Set the number of bytes to get out of this next packet. + // + ui32DataSize = g_sUSBHEP0State.ui32BytesRemaining; + if(ui32DataSize > g_sUSBHEP0State.ui32MaxPacketSize) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ui32DataSize = MAX_PACKET_SIZE_EP0; + } + + if(ui32DataSize != 0) + { + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, + g_sUSBHEP0State.pui8Data, + &ui32DataSize); + } + + // + // Advance the pointer. + // + g_sUSBHEP0State.pui8Data += ui32DataSize; + + // + // Decrement the number of bytes that are being waited on. + // + g_sUSBHEP0State.ui32BytesRemaining -= ui32DataSize; + + // + // Need to ack the data on end point 0 in this case + // without setting data end. + // + MAP_USBHostEndpointDataAck(USB0_BASE, USB_EP_0); + + // + // If there was not more than the maximum packet size bytes of data + // the this was a int16_t packet and indicates that this transfer + // is complete. If there were exactly + // g_sUSBHEP0State.ui32MaxPacketSize remaining then there still + // needs to be null packet sent before this transfer is complete. + // + if((ui32DataSize < g_sUSBHEP0State.ui32MaxPacketSize) || + (g_sUSBHEP0State.ui32BytesRemaining == 0)) + { + // + // Return to the idle state. + // + g_sUSBHEP0State.iState = eEP0StateStatus; + + // + // No more data. + // + g_sUSBHEP0State.pui8Data = 0; + + // + // Send a null packet to acknowledge that all data was + // received. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_STATUS); + } + else + { + // + // Request more data. + // + MAP_USBHostRequestIN(USB0_BASE, USB_EP_0); + } + break; + } + + // + // The device stalled endpoint zero so check if the stall needs to be + // cleared once it has been successfully sent. + // + case eEP0StateStall: + { + // + // Reset the global end point 0 state to IDLE. + // + g_sUSBHEP0State.iState = eEP0StateIdle; + + break; + } + + // + // Halt on an unknown state, but only in DEBUG builds. + // + default: + { + ASSERT(0); + break; + } + } +} + +//***************************************************************************** +// +// This internal function handles sending data on endpoint 0. +// +// \return None. +// +//***************************************************************************** +static void +USBHCDEP0StateTx(void) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + + // + // In the TX state on endpoint 0. + // + g_sUSBHEP0State.iState = eEP0StateSetupOUT; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = g_sUSBHEP0State.ui32BytesRemaining; + + // + // Limit individual transfers to 64 bytes. + // + if(ui32NumBytes > 64) + { + ui32NumBytes = 64; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pui8Data = (uint8_t *)g_sUSBHEP0State.pui8Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_sUSBHEP0State.ui32BytesRemaining -= ui32NumBytes; + g_sUSBHEP0State.pui8Data += ui32NumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pui8Data, ui32NumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ui32NumBytes == 64) + { + // + // There is more data to send or exactly 64 bytes were sent, this + // means that there is either more data coming or a null packet needs + // to be sent to complete the transaction. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_OUT); + } + else + { + // + // Send the last bit of data. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_OUT); + + // + // Now go to the status state and wait for the transmit to complete. + // + g_sUSBHEP0State.iState = eEP0StateStatusIN; + } +} + +//***************************************************************************** +// +//! This function returns the USB hub port for the requested device instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! +//! This function returns the USB hub port for the device that is associated +//! with the \e ui32Instance parameter. The caller must use the value for +//! \e ui32Instance was passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function returns the USB hub port for +//! the interface number specified by the \e ui32Interface parameter. +//! +//! \return The USB hub port for the requested interface. +// +//***************************************************************************** +uint8_t +USBHCDDevHubPort(uint32_t ui32Instance) +{ + uint32_t ui32DevIndex; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + if(ui32DevIndex == 0xff) + { + return(ui32DevIndex); + } + + return(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); +} + +//***************************************************************************** +// +//! This function will return the USB address for the requested device +//! instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! +//! This function returns the USB address for the device that is associated +//! with the \e ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB address for +//! the interface number specified by the \e ui32Interface parameter. +//! +//! \return The USB address for the requested interface. +// +//***************************************************************************** +uint8_t +USBHCDDevAddress(uint32_t ui32Instance) +{ + uint32_t ui32DevIndex; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + if(ui32DevIndex == 0xff) + { + return(ui32DevIndex); + } + + return(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address); +} + +//***************************************************************************** +// +//! This function will return the USB class for the requested device +//! instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! \param ui32Interface is the interface number to query for the USB class. +//! +//! This function returns the USB class for the device that is associated +//! with the \e ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB class for +//! the interface number specified by the \e ui32Interface parameter. If +//! \e ui32Interface is set to 0xFFFFFFFF then the function will return the USB +//! class for the first interface that is found in the device's USB +//! descriptors. +//! +//! \return The USB class for the requested interface. +// +//***************************************************************************** +uint8_t +USBHCDDevClass(uint32_t ui32Instance, uint32_t ui32Interface) +{ + uint32_t ui32DevIndex; + tInterfaceDescriptor *psInterface; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + // + // If the instance was not valid return an undefined class. + // + if(ui32DevIndex == 0xff) + { + return(USB_CLASS_DEVICE); + } + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Interface, + ui32Interface); + + // + // Make sure that the interface requested actually exists. + // + if(psInterface) + { + // + // Return the interface class. + // + return(psInterface->bInterfaceClass); + } + + // + // No valid interface so return an undefined class. + // + return(USB_CLASS_DEVICE); +} + +//***************************************************************************** +// +//! This function will return the USB subclass for the requested device +//! instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! \param ui32Interface is the interface number to query for the USB subclass. +//! +//! This function returns the USB subclass for the device that is associated +//! with the \e ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB subclass +//! for the interface number specified by the \e ui32Interface parameter. If +//! \e ui32Interface is set to 0xFFFFFFFF then the function will return the USB +//! subclass for the first interface that is found in the device's USB +//! descriptors. +//! +//! \return The USB subclass for the requested interface. +// +//***************************************************************************** +uint8_t +USBHCDDevSubClass(uint32_t ui32Instance, uint32_t ui32Interface) +{ + uint32_t ui32DevIndex; + tInterfaceDescriptor *psInterface; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + // + // If the instance was not valid return an undefined subclass. + // + if(ui32DevIndex == 0xff) + { + return(USB_SUBCLASS_UNDEFINED); + } + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Interface, + ui32Interface); + + // + // Make sure that the interface requested actually exists. + // + if(psInterface) + { + // + // Return the interface subclass. + // + return(psInterface->bInterfaceSubClass); + } + + // + // No valid interface so return an undefined subclass. + // + return(USB_SUBCLASS_UNDEFINED); +} + +//***************************************************************************** +// +//! This function returns the USB protocol for the requested device instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! \param ui32Interface is the interface number to query for the USB protocol. +//! +//! This function returns the USB protocol for the device that is associated +//! with the \e ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB protocol +//! for the interface number specified by the \e ui32Interface parameter. If +//! \e ui32Interface is set to 0xFFFFFFFF then the function will return the USB +//! protocol for the first interface that is found in the device's USB +//! descriptors. +//! +//! \return The USB protocol for the requested interface. +// +//***************************************************************************** +uint8_t +USBHCDDevProtocol(uint32_t ui32Instance, uint32_t ui32Interface) +{ + uint32_t ui32DevIndex; + tInterfaceDescriptor *psInterface; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + // + // If the instance was not valid return an undefined protocol. + // + if(ui32DevIndex == 0xff) + { + return(USB_PROTOCOL_UNDEFINED); + } + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Interface, + ui32Interface); + + // + // Make sure that the interface requested actually exists. + // + if(psInterface) + { + // + // Return the interface protocol. + // + return(psInterface->bInterfaceProtocol); + } + + // + // No valid interface so return an undefined protocol. + // + return(USB_PROTOCOL_UNDEFINED); +} + +//***************************************************************************** +// +//! This function sets one of the \b USBLIB_FEATURE_ requests. +//! +//! \param ui32Index is the index of the USB controller to access. +//! \param ui32Feature is one of the \b USBLIB_FEATURE_ defines. +//! \param pvFeature is a pointer to the data for the \b USBLIB_FEATURE +//! request. +//! +//! This function sends the requested feature request to the USB library. +//! Not all features are supported by all devices so see the documentation +//! for the \b USBLIB_FEATURE_ to determine if the feature is supported. +//! +//! \return Returns \b true if the feature was set and \b false if the feature +//! is not supported or could not be changed to the requested value. +// +//***************************************************************************** +bool +USBHCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, + void *pvFeature) +{ + bool bRetCode; + tLPMFeature *psLPMFeature; + + bRetCode = true; + + switch(ui32Feature) + { + case USBLIB_FEATURE_CPUCLK: + { + // + // Set the ticks per millisecond. + // + g_ui32Tickms = (*(uint32_t *)pvFeature / 3000); + break; + } + case USBLIB_FEATURE_LPM: + { + psLPMFeature = (tLPMFeature *)pvFeature; + + if(psLPMFeature->ui32Features & USBLIB_FEATURE_LPM_EN) + { + g_sUSBHCD.ui32Features |= USBLIB_FEATURE_LPM_EN; + + if(psLPMFeature->ui32Features & USBLIB_FEATURE_LPM_RMT_WAKE) + { + g_sUSBHCD.ui32Features |= USBLIB_FEATURE_LPM_RMT_WAKE; + } + g_sUSBHCD.ui32LPMHIRD = psLPMFeature->ui32HIRD; + } + else + { + psLPMFeature->ui32Features &= ~USBLIB_FEATURE_LPM_EN; + } + break; + } + case USBLIB_FEATURE_USBPLL: + { + // + // If the PLL rate is not evenly divisible by 60MHz then + // do not set it. + // + if((*(uint32_t *)pvFeature % 60000000) != 0) + { + bRetCode = false; + } + else + { + // + // Save the new PLL rate. + // + g_ui32PLLDiv = (*(uint32_t *)pvFeature / 60000000); + } + break; + } + case USBLIB_FEATURE_USBULPI: + { + // + // Save the ULPI support level. + // + g_ui32ULPISupport = *(uint32_t *)pvFeature; + + break; + } + default: + { + bRetCode = false; + break; + } + } + return(bRetCode); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psDevice is the device to query. +//! +//! 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 +USBHCDLPMStatus(tUSBHostDevice *psDevice) +{ + uint32_t ui32Ret; + + ASSERT(psDevice != 0); + + // + // Should never have both USBHDEV_FLAG_LPMERROR and USBHDEV_FLAG_LPMPEND + // set at the same time. + // + ASSERT((psDevice->ui32Flags & + (USBHDEV_FLAG_LPMERROR | USBHDEV_FLAG_LPMPEND)) != + (USBHDEV_FLAG_LPMERROR | USBHDEV_FLAG_LPMPEND)); + + // + // Default to no pending transfers or errors. + // + ui32Ret = USBHCD_LPM_AVAIL; + + if(psDevice->ui32Flags & USBHDEV_FLAG_LPMERROR) + { + // + // An error occurred after the last call to send an LPM command. + // + ui32Ret = USBHCD_LPM_ERROR; + } + else if(psDevice->ui32Flags & USBHDEV_FLAG_LPMPEND) + { + // + // Still have a pending transfer. + // + ui32Ret = USBHCD_LPM_PENDING; + } + + return(ui32Ret); +} + +//***************************************************************************** +// +//! This function generates an LPM request for a device to enter L1 sleep +//! state. +//! +//! \param psDevice is the device to query. +//! +//! This function sends a request to a device to 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 another +//! device this function will return \b USBHCD_LPM_PENDING or +//! \b USBHCD_LPM_AVAIL if the LPM request was scheduled to be sent. The +//! caller should check the USBHCDLPMStatus() function to determine if the +//! request has completed. +//! +//! \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 +USBHCDLPMSleep(tUSBHostDevice *psDevice) +{ + uint32_t ui32Ret; + + ASSERT(psDevice != 0); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + // + // If there is no current LPM pending then send the request. + // + if((g_sUSBHCD.ui32IntEvents & INT_EVENT_LPM_PEND) || + (psDevice->ui32Flags & USBHDEV_FLAG_LPMPEND)) + { + ui32Ret = USBHCD_LPM_PENDING; + } + else + { + // + // New pending LPM transfer at the host controller level. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_LPM_PEND; + + // + // New pending request and clear any previous error for this + // device in case it was already set. + // + psDevice->ui32Flags |= USBHDEV_FLAG_LPMPEND; + psDevice->ui32Flags &= ~USBHDEV_FLAG_LPMERROR; + + USBHostLPMSend(USB0_BASE, psDevice->ui32Address, USB_EP_0); + + ui32Ret = USBHCD_LPM_AVAIL; + } + + // + // Disable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + + return(ui32Ret); +} + +//***************************************************************************** +// +//! This function generates an LPM request for a device to exit L1 sleep state. +//! +//! \param ui32Index specifies which USB controller to use. +//! +//! This function will start LPM resume signaling on the USB bus. This wakes +//! all devices and is similar to USBHCDResume() call but is triggered by an +//! LPM request. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDLPMResume(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + USBHostLPMResume(USB0_BASE); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhostpriv.h b/usblib/host/usbhostpriv.h new file mode 100644 index 0000000..1128749 --- /dev/null +++ b/usblib/host/usbhostpriv.h @@ -0,0 +1,201 @@ +//***************************************************************************** +// +// usbhostpriv.h - Internal header file for USB host functions. +// +// Copyright (c) 2011-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. +// +//***************************************************************************** + +#ifndef __USBHOSTPRIV_H__ +#define __USBHOSTPRIV_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The states a hub port can be in during device connection. +// +//***************************************************************************** +typedef enum +{ + // + // The port has no device connected. + // + ePortIdle, + + // + // The port has a device present and is waiting for the enumeration + // sequence to begin. + // + ePortConnected, + + // + // A device connection notification has been received and we have initiated + // a reset to the port. We are waiting for the reset to complete. + // + ePortResetActive, + + // + // The Port reset has completed but now the hub is waiting the required + // 10ms before accessing the device. + // + ePortResetWait, + + // + // A device is connected and the port has been reset. Control has been + // passed to the main host handling portion of USBLib to enumerate the + // device. + // + ePortActive, + + // + // A device has completed enumeration. + // + ePortEnumerated, + + // + // A device is attached to the port but enumeration failed. + // + ePortError +} +tHubPortState; + +//***************************************************************************** +// +// The list of valid event flags in the g_sUSBHCD.ui32EventEnables member +// variable. +// +//***************************************************************************** +#define USBHCD_EVFLAG_SOF 0x00000001 +#define USBHCD_EVFLAG_CONNECT 0x00000002 +#define USBHCD_EVFLAG_UNKCNCT 0x00000004 +#define USBHCD_EVFLAG_DISCNCT 0x00000008 +#define USBHCD_EVFLAG_PWRFAULT 0x00000010 +#define USBHCD_EVFLAG_PWRDIS 0x00000020 +#define USBHCD_EVFLAG_PWREN 0x00000040 + +//***************************************************************************** +// +// This is the structure that holds all of the information for devices +// that are enumerated in the system. It is passed in to Open function of +// USB host class drivers so that they can allocate any endpoints and parse +// out other information that the device class needs to complete enumeration. +// +//***************************************************************************** +struct tUSBHostDevice +{ + // + // The current device address for this device. + // + uint32_t ui32Address; + + // + // The current interface for this device. + // + uint32_t ui32Interface; + + // + // A flag used to record whether this is a low-speed or a full-speed + // device. + // + bool bLowSpeed; + + // + // The USB connection speed for this device. + // + uint32_t ui32Speed; + + // + // A flag indicating whether or not we have read the device's + // configuration descriptor yet. + // + bool bConfigRead; + + // + // The hub number to which this device is attached. + // + uint8_t ui8Hub; + + // + // The hub port number to which the device is attached. + // + uint8_t ui8HubPort; + + // + // The device descriptor for this device. + // + tDeviceDescriptor sDeviceDescriptor; + + // + // A pointer to the configuration descriptor for this device. + // + tConfigDescriptor *psConfigDescriptor; + + // + // The size of the buffer allocated to psConfigDescriptor. + // + uint32_t ui32ConfigDescriptorSize; + + // + // Internal flags used by the host controller driver. + // + uint32_t ui32Flags; +}; + +//***************************************************************************** +// +// Functions within the host controller that are called by the hub class driver +// +//***************************************************************************** +extern uint32_t USBHCDHubDeviceConnected(uint32_t ui32Index, uint8_t ui8Hub, + uint8_t ui8Port, uint32_t ui32Speed); +extern void USBHCDHubDeviceDisconnected(uint32_t ui32Index, + uint32_t ui32DevIndex); + +//***************************************************************************** +// +// Functions in the hub class driver that are called by the host controller. +// +//***************************************************************************** +extern void USBHHubMain(void); +extern void USBHHubInit(void); +extern void USBHHubEnumerationComplete(uint8_t ui8Hub, uint8_t ui8Port); +extern void USBHHubEnumerationError(uint8_t ui8Hub, uint8_t ui8Port); +extern uint32_t USBHCDLPMSleep(tUSBHostDevice *psDevice); +extern uint32_t USBHCDLPMStatus(tUSBHostDevice *psDevice); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHOSTPRIV_H__ diff --git a/usblib/host/usbhscsi.c b/usblib/host/usbhscsi.c new file mode 100644 index 0000000..407dc2d --- /dev/null +++ b/usblib/host/usbhscsi.c @@ -0,0 +1,777 @@ +//***************************************************************************** +// +// usbhscsi.c - USB host SCSI layer used by the USB host MSC 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 "usblib/usblib.h" +#include "usblib/usbmsc.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhmsc.h" +#include "usblib/host/usbhscsi.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the data verify tag passed between requests. +// +//***************************************************************************** +#define CBW_TAG_VALUE 0x54231990 + +//***************************************************************************** +// +//! This function is used to issue SCSI commands via USB. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param psSCSICmd is the SCSI command structure to send. +//! \param pui8Data is pointer to the command data to be sent. +//! \param pui32Size is the number of bytes is the number of bytes expected or +//! sent by the command. +//! +//! This internal function is used to handle SCSI commands sent by other +//! functions. It serves as a layer between the SCSI command and the USB +//! interface being used to send the command. The \e pSCSI parameter contains +//! the SCSI command to send. For commands that expect data back, the +//! \e pui8Data is the buffer to store the data into and \e pui32Size is used +//! to store the amount of data to request as well as used to indicate how many +//! bytes were filled into the \e pui8Data buffer on return. For commands that +//! are sending data, \e pui8Data is the data to be sent and \e pui32Size is +//! the number of bytes to send. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +static uint32_t +USBHSCSISendCommand(uint32_t ui32InPipe, uint32_t ui32OutPipe, + tMSCCBW *psSCSICmd, uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCSW sCmdStatus; + uint32_t ui32Bytes; + + // + // Initialize the command status. + // + sCmdStatus.dCSWSignature = 0; + sCmdStatus.dCSWTag = 0; + sCmdStatus.bCSWStatus = SCSI_CMD_STATUS_FAIL; + + // + // Set the CBW signature and tag. + // + psSCSICmd->dCBWSignature = CBW_SIGNATURE; + psSCSICmd->dCBWTag = CBW_TAG_VALUE; + + // + // Set the size of the data to be returned by the device. + // + psSCSICmd->dCBWDataTransferLength = *pui32Size; + + // + // Send the command. + // + ui32Bytes = USBHCDPipeWrite(ui32OutPipe, (uint8_t*)psSCSICmd, + sizeof(tMSCCBW)); + + // + // If no bytes went out then the command failed. + // + if(ui32Bytes == 0) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Only request data if there is data to request. + // + if(psSCSICmd->dCBWDataTransferLength != 0) + { + // + // See if this is a read or a write. + // + if(psSCSICmd->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Read the data back. + // + *pui32Size = USBHCDPipeRead(ui32InPipe, pui8Data, *pui32Size); + } + else + { + // + // Write the data out. + // + *pui32Size = USBHCDPipeWrite(ui32OutPipe, pui8Data, *pui32Size); + } + } + + // + // Get the status of the command. + // + ui32Bytes = USBHCDPipeRead(ui32InPipe, (uint8_t *)&sCmdStatus, + sizeof(tMSCCSW)); + + + // + // If the status was invalid or did not have the correct signature then + // indicate a failure. + // + if((ui32Bytes == 0) || (sCmdStatus.dCSWSignature != CSW_SIGNATURE) || + (sCmdStatus.dCSWTag != CBW_TAG_VALUE)) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Return the status. + // + return((uint32_t)sCmdStatus.bCSWStatus); +} + +//***************************************************************************** +// +//! This will issue the SCSI inquiry command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size is the size of buffer that was passed in on entry and the +//! number of bytes returned. +//! +//! This function should be used to issue a SCSI Inquiry command to a mass +//! storage device. To allow for multiple devices, the \e ui32InPipe and +//! \e ui32OutPipe parameters indicate which USB pipes to use for this call. +//! +//! \note The \e pui8Data buffer pointer should have at least +//! \b SCSI_INQUIRY_DATA_SZ bytes of data or this function will overflow the +//! buffer. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSIInquiry(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // The number of bytes of data that the host expects to transfer on the + // Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during + // the execution of this command. If this field is zero, the device and + // the host shall transfer no data between the CBW and the associated CSW, + // and the device shall ignore the value of the Direction bit in + // bmCBWFlags. + // + *pui32Size = SCSI_INQUIRY_DATA_SZ; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // This is the length of the command itself. + // + sSCSICmd.bCBWCBLength = 6; + + // + // Send Inquiry command with no request for vital product data. + // + pui32Data[0] = SCSI_INQUIRY_CMD; + + // + // Allocation length. + // + pui32Data[1] = SCSI_INQUIRY_DATA_SZ; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacity command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size is the size of buffer that was passed in on entry and the +//! number of bytes returned. +//! +//! This function should be used to issue a SCSI Read Capacity command +//! to a mass storage device that is connected. To allow for multiple devices, +//! the \e ui32InPipe and \e ui32OutPipe parameters indicate which USB pipes to +//! use for this call. +//! +//! \note The \e pui8Data buffer pointer should have at least +//! \b SCSI_READ_CAPACITY_SZ bytes of data or this function will overflow the +//! buffer. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSIReadCapacity(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // Set the size of the command data. + // + *pui32Size = SCSI_READ_CAPACITY_SZ; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + sSCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pui32Data[0] = SCSI_READ_CAPACITY; + pui32Data[1] = 0; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacities command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size is the size of buffer that was passed in on entry and the +//! number of bytes returned. +//! +//! This function should be used to issue a SCSI Read Capacities command +//! to a mass storage device that is connected. To allow for multiple devices, +//! the \e ui32InPipe and \e ui32OutPipe parameters indicate which USB pipes to +//! use for this call. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSIReadCapacities(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + sSCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pui32Data[0] = SCSI_READ_CAPACITIES; + pui32Data[1] = 0; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This will issue the SCSI Mode Sense(6) command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param ui32Flags is a combination of flags defining the exact query that is +//! to be made. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size is the size of the buffer on entry and number of bytes +//! read on exit. +//! +//! This function should be used to issue a SCSI Mode Sense(6) command +//! to a mass storage device. To allow for multiple devices,the \e ui32InPipe +//! and \e ui32OutPipe parameters indicate which USB pipes to use for this +//! call. The call will return at most the number of bytes in the \e pui32Size +//! parameter, however it can return less and change the \e pui32Size parameter +//! to the number of valid bytes in the \e *pui32Size buffer. +//! +//! The \e ui32Flags parameter is a combination of the following three sets of +//! definitions: +//! +//! One of the following values must be specified: +//! +//! - \b SCSI_MS_PC_CURRENT request for current settings. +//! - \b SCSI_MS_PC_CHANGEABLE request for changeable settings. +//! - \b SCSI_MS_PC_DEFAULT request for default settings. +//! - \b SCSI_MS_PC_SAVED request for the saved values. +//! +//! One of these following values must also be specified to determine the page +//! code for the request: +//! +//! - \b SCSI_MS_PC_VENDOR is the vendor specific page code. +//! - \b SCSI_MS_PC_DISCO is the disconnect/reconnect page code. +//! - \b SCSI_MS_PC_CONTROL is the control page code. +//! - \b SCSI_MS_PC_LUN is the protocol specific LUN page code. +//! - \b SCSI_MS_PC_PORT is the protocol specific port page code. +//! - \b SCSI_MS_PC_POWER is the power condition page code. +//! - \b SCSI_MS_PC_INFORM is the informational exceptions page code. +//! - \b SCSI_MS_PC_ALL will request all pages codes supported by the device. +//! +//! The last value is optional and supports the following global flag: +//! - \b SCSI_MS_DBD disables returning block descriptors. +//! +//! Example: Request for all current settings. +//! +//! \verbatim +//! SCSIModeSense6(ui32InPipe, ui32OutPipe, +//! SCSI_MS_PC_CURRENT | SCSI_MS_PC_ALL, +//! pui8Data, pui32Size); +//! \endverbatim +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSIModeSense6(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32Flags, uint8_t *pui8Data, + uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 6; + + // + // Set the options for the Mode Sense Command (6). + // + pui32Data[0] = (SCSI_MODE_SENSE_6 | ui32Flags); + pui32Data[1] = (uint8_t)*pui32Size; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Test Unit Ready command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! +//! This function is used to issue a SCSI Test Unit Ready command to a device. +//! This call will simply return the results of issuing this command. +//! +//! \return This function returns the results of the SCSI Test Unit Ready +//! command. The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSITestUnitReady(uint32_t ui32InPipe, uint32_t ui32OutPipe) +{ + tMSCCBW sSCSICmd; + uint32_t ui32Size; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // No data in this command. + // + ui32Size = 0; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 6; + + // + // Set the parameter options. + // + pui32Data[0] = SCSI_TEST_UNIT_READY; + pui32Data[1] = 0; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, 0, + &ui32Size)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Request Sense command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size is the size of the buffer on entry and number of bytes +//! read on exit. +//! +//! This function is used to issue a SCSI Request Sense command to a device. +//! It will return the data in the buffer pointed to by \e pui8Data. The +//! parameter \e pui32Size should have the allocation size in bytes of the +//! buffer pointed to by \e pui8Data. +//! +//! \return This function returns the results of the SCSI Request Sense +//! command. The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSIRequestSense(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 12; + + // + // Set the parameter options. + // + pui32Data[0] = SCSI_REQUEST_SENSE; + pui32Data[1] = 18; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Read(10) command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param ui32LBA is the logical block address to read. +//! \param pui8Data is the data buffer to return the data. +//! \param pui32Size is the size of the buffer on entry and number of bytes +//! read on exit. +//! \param ui32NumBlocks is the number of contiguous blocks to read from the +//! device. +//! +//! This function is used to issue a SCSI Read(10) command to a device. The +//! \e ui32LBA parameter specifies the logical block address to read from the +//! device. The data from this block will be returned in the buffer pointed to +//! by \e pui8Data. The parameter \e pui32Size should indicate enough space to +//! hold a full block size, or only the first \e pui32Size bytes of the LBA are +//! returned. +//! +//! \return This function returns the results of the SCSI Read(10) command. +//! The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSIRead10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks) +{ + tMSCCBW sSCSICmd; + int32_t i32Idx; + + // + // Zero out the response data. + // + for(i32Idx = 0; i32Idx < sizeof(sSCSICmd.CBWCB); i32Idx++) + { + sSCSICmd.CBWCB[i32Idx] = 0; + } + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + sSCSICmd.CBWCB[0] = SCSI_READ_10; + + // + // LBA starts at offset 2. + // + sSCSICmd.CBWCB[2] = (uint8_t)(ui32LBA >> 24); + sSCSICmd.CBWCB[3] = (uint8_t)(ui32LBA >> 16); + sSCSICmd.CBWCB[4] = (uint8_t)(ui32LBA >> 8); + sSCSICmd.CBWCB[5] = (uint8_t)ui32LBA; + + // + // Transfer length in blocks starts at offset 7. + // + sSCSICmd.CBWCB[7] = (uint8_t)(ui32NumBlocks >> 8); + sSCSICmd.CBWCB[8] = (uint8_t)ui32NumBlocks; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Write(10) command to a device. +//! +//! This function is used to issue a SCSI Write(10) command to a device. The +//! \e ui32LBA parameter specifies the logical block address on the device. +//! The data to write to this block should be in the buffer pointed to by +//! \e pui8Data parameter. The parameter \e pui32Size should indicate the +//! amount of data to write to the specified LBA. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param ui32LBA is the logical block address to read. +//! \param pui8Data is the data buffer to write out. +//! \param pui32Size is the size of the buffer. +//! \param ui32NumBlocks is the number of contiguous blocks to write to the +//! device. +//! +//! \return This function returns the results of the SCSI Write(10) command. +//! The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +uint32_t +USBHSCSIWrite10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_OUT; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + sSCSICmd.CBWCB[0] = SCSI_WRITE_10; + + // + // Clear the reserved field. + // + sSCSICmd.CBWCB[1] = 0; + + // + // LBA starts at offset 2. + // + sSCSICmd.CBWCB[2] = (uint8_t)(ui32LBA >> 24); + sSCSICmd.CBWCB[3] = (uint8_t)(ui32LBA >> 16); + sSCSICmd.CBWCB[4] = (uint8_t)(ui32LBA >> 8); + sSCSICmd.CBWCB[5] = (uint8_t)ui32LBA; + + // + // Clear the reserved field. + // + sSCSICmd.CBWCB[6] = 0; + + // + // Set the transfer length in blocks. + // This also sets the Control value to 0 at offset 9. + // + sSCSICmd.CBWCB[7] = (ui32NumBlocks & 0xFF00) >> 8; + + // + // The blocks go into is byte offset 8 or word address 2. + // + pui32Data[2] = (ui32NumBlocks & 0xFF); + + // + // The blocks go into is byte offset 12 or word address 3. + // + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhscsi.h b/usblib/host/usbhscsi.h new file mode 100644 index 0000000..29291e1 --- /dev/null +++ b/usblib/host/usbhscsi.h @@ -0,0 +1,87 @@ +//***************************************************************************** +// +// usbhscsi.h - Definitions for the USB host SCSI layer. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHSCSI_H__ +#define __USBHSCSI_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for the APIs exported by the USB SCSI layer. +// +//***************************************************************************** +extern uint32_t USBHSCSIInquiry(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Buffer, uint32_t *pui32Size); +extern uint32_t USBHSCSIReadCapacity(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size); +extern uint32_t USBHSCSIReadCapacities(uint32_t ui32InPipe, + uint32_t ui32OutPipe, uint8_t *pui8Data, + uint32_t *pui32Size); +extern uint32_t USBHSCSIModeSense6(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32Flags, uint8_t *pui8Data, + uint32_t *pui32Size); +extern uint32_t USBHSCSITestUnitReady(uint32_t ui32InPipe, + uint32_t ui32OutPipe); +extern uint32_t USBHSCSIRequestSense(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size); +extern uint32_t USBHSCSIRead10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks); +extern uint32_t USBHSCSIWrite10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHSCSI_H__ diff --git a/usblib/readme.txt b/usblib/readme.txt new file mode 100644 index 0000000..54f56a3 --- /dev/null +++ b/usblib/readme.txt @@ -0,0 +1,21 @@ +This project will build the Tiva USB Library. + +------------------------------------------------------------------------------- + +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. diff --git a/usblib/rvmdk/usblib.lib b/usblib/rvmdk/usblib.lib new file mode 100644 index 0000000..56ffd6e Binary files /dev/null and b/usblib/rvmdk/usblib.lib differ diff --git a/usblib/usb-ids.h b/usblib/usb-ids.h new file mode 100644 index 0000000..31f4ae4 --- /dev/null +++ b/usblib/usb-ids.h @@ -0,0 +1,57 @@ +//***************************************************************************** +// +// usb-ids.h - Definitions of VIDs and PIDs used by USB library. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBIDS_H__ +#define __USBIDS_H__ + +//***************************************************************************** +// +// TI Vendor ID for devices that use VID as 0x1CBE. +// +//***************************************************************************** +#define USB_VID_TI_1CBE 0x1cbe + +//***************************************************************************** +// +// Product IDs. +// +//***************************************************************************** +#define USB_PID_MOUSE 0x0000 +#define USB_PID_KEYBOARD 0x0001 +#define USB_PID_SERIAL 0x0002 +#define USB_PID_BULK 0x0003 +#define USB_PID_SCOPE 0x0004 +#define USB_PID_MSC 0x0005 +#define USB_PID_AUDIO 0x0006 +#define USB_PID_COMP_SERIAL 0x0007 +#define USB_PID_COMP_AUDIO_HID 0x0008 +#define USB_PID_COMP_HID_SER 0x0009 +#define USB_PID_COMP_HID_DFU 0x000A +#define USB_PID_DATA_LOGGER 0x000B +#define USB_PID_COMP_HID_HID 0x000D +#define USB_PID_GAMEPAD 0x000F +#define USB_PID_LP_CGAMEPAD 0x0010 +#define USB_PID_DFU 0x00FF + +#endif /* __USBIDS_H__ */ diff --git a/usblib/usbaudio.h b/usblib/usbaudio.h new file mode 100644 index 0000000..eee61bc --- /dev/null +++ b/usblib/usbaudio.h @@ -0,0 +1,718 @@ +//***************************************************************************** +// +// usbaudio.h - Definitions used by Audio Class devices. +// +// Copyright (c) 2009-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. +// +//***************************************************************************** + +#ifndef __USBAUDIO_H__ +#define __USBAUDIO_H__ + + +//***************************************************************************** +// +// Standard Audio descriptor sub types. +// +//***************************************************************************** +#define USB_AI_UNDEFINED 0 +#define USB_AI_HEADER 1 +#define USB_AI_INPUT_TERMINAL 2 +#define USB_AI_OUTPUT_TERMINAL 3 +#define USB_AI_MIXER_UNIT 4 +#define USB_AI_SELECTOR_UNIT 5 +#define USB_AI_FEATURE_UNIT 6 +#define USB_AI_PROCESSING_UNIT 7 +#define USB_AI_EXTENSION_UNIT 8 + +//***************************************************************************** +// +// Standard Audio Streaming Interface descriptor types. +// +//***************************************************************************** +#define USB_AS_UNDEFINED 0 +#define USB_AS_GENERAL 1 +#define USB_AS_FORMAT_TYPE 2 +#define USB_AS_FORMAT_SPECIFIC 3 + +//***************************************************************************** +// +// Standard USB terminal types used with audio terminal descriptors. These +// are defined in the "Universal Serial Bus Device Class Definition for +// Terminal Types" version 1.0 released March 18, 1998. +// +//***************************************************************************** +#define USB_TTYPE_UNDEFINED 0x0100 +#define USB_TTYPE_STREAMING 0x0101 +#define USB_TTYPE_VENDOR 0x01ff + +#define USB_TTYPE_OUT_UNDEF 0x0300 +#define USB_TTYPE_OUT_SPEAKER 0x0301 +#define USB_TTYPE_OUT_HEADPHONE 0x0302 +#define USB_TTYPE_OUT_DESK_SPKR 0x0304 +#define USB_TTYPE_OUT_ROOM_SPKR 0x0305 +#define USB_TTYPE_OUT_COMM_SPKR 0x0306 +#define USB_TTYPE_OUT_LFE 0x0307 + +#define USB_TTYPE_EXT_UNDEF 0x0600 +#define USB_TTYPE_EXT_ANALOG 0x0601 +#define USB_TTYPE_EXT_DIGITAL 0x0602 +#define USB_TTYPE_EXT_LINE 0x0603 +#define USB_TTYPE_EXT_LEGACY 0x0604 +#define USB_TTYPE_EXT_SPDIF 0x0605 +#define USB_TTYPE_EXT_1394_DA 0x0606 +#define USB_TTYPE_EXT_1394_DV 0x0607 + +//***************************************************************************** +// +// Audio Interface Subclass Codes +// +//***************************************************************************** +#define USB_ASC_UNDEFINED 0x00 +#define USB_ASC_AUDIO_CONTROL 0x01 +#define USB_ASC_AUDIO_STREAMING 0x02 +#define USB_ASC_MIDI_STREAMING 0x03 + +//***************************************************************************** +// +// Audio Class-Specific Descriptor Types +// (Table A-4) +// +//***************************************************************************** +#define USB_ACSDT_UNDEFINED 0x20 +#define USB_ACSDT_DEVICE 0x21 +#define USB_ACSDT_CONFIGURATION 0x22 +#define USB_ACSDT_STRING 0x23 +#define USB_ACSDT_INTERFACE 0x24 +#define USB_ACSDT_ENDPOINT 0x25 + +//***************************************************************************** +// +// Audio Class-Specific AC Interface Descriptor Subtypes +// (Table A-5) +// +//***************************************************************************** +#define USB_ACDSTYPE_UNDEFINED 0x00 +#define USB_ACDSTYPE_HEADER 0x01 +#define USB_ACDSTYPE_IN_TERMINAL 0x02 +#define USB_ACDSTYPE_OUT_TERMINAL 0x03 +#define USB_ACDSTYPE_MIXER_UNIT 0x04 +#define USB_ACDSTYPE_SELECTOR_UNIT 0x05 +#define USB_ACDSTYPE_FEATURE_UNIT 0x06 +#define USB_ACDSTYPE_PROCESSING_UNIT 0x07 +#define USB_ACDSTYPE_EXTENSION_UNIT 0x08 + +//***************************************************************************** +// +// Audio Class-Specific AS Interface Descriptor Subtypes +// (Table A-6) +// +//***************************************************************************** +#define USB_ASDSTYPE_UNDEFINED 0x00 +#define USB_ASDSTYPE_GENERAL 0x01 +#define USB_ASDSTYPE_FORMAT_TYPE 0x02 +#define USB_ASDSTYPE_FORMAT_SPECIFIC 0x03 + +//***************************************************************************** +// +// Audio Data Format Type I Codes. +// +//***************************************************************************** +#define USB_ADF_UNDEFINED 0x0000 +#define USB_ADF_PCM 0x0001 +#define USB_ADF_PCM8 0x0002 +#define USB_ADF_IEEE_FLOAT 0x0003 +#define USB_ADF_ALAW 0x0004 +#define USB_ADF_MULAW 0x0005 + +//***************************************************************************** +// +// Audio Format Type Codes +// +//***************************************************************************** +#define USB_AF_TYPE_UNDEFINED 0x00 +#define USB_AF_TYPE_TYPE_I 0x01 +#define USB_AF_TYPE_TYPE_II 0x02 +#define USB_AF_TYPE_TYPE_III 0x03 + +//***************************************************************************** +// +// Audio Class-Specific controls used with bmaControls values. +// +//***************************************************************************** +#define USB_ACONTROL_MUTE 0x0001 // Mute +#define USB_ACONTROL_VOLUME 0x0002 // Volume +#define USB_ACONTROL_BASS 0x0004 // Bass +#define USB_ACONTROL_MID 0x0008 // Mid +#define USB_ACONTROL_TREBLE 0x0010 // Treble +#define USB_ACONTROL_EQ 0x0020 // Graphic Equalizer +#define USB_ACONTROL_AGC 0x0040 // Automatic Gain +#define USB_ACONTROL_DELAY 0x0080 // Delay +#define USB_ACONTROL_BASS_BOOST 0x0100 // Bass Boost +#define USB_ACONTROL_LOUD 0x0200 // Loudness + +//***************************************************************************** +// +// Audio Class-Specific Output terminal types. +// +//***************************************************************************** +#define USB_ATTYPE_UNDEFINED 0x0300 // Output Terminal, undefined Type. +#define USB_ATTYPE_SPEAKER 0x0301 // A generic speaker. +#define USB_ATTYPE_HEADPHONES 0x0302 // A head-mounted audio output device. +#define USB_ATTYPE_HMD 0x0303 // The audio part of a VR head mounted + // display. +#define USB_ATTYPE_SPEAKER_DT 0x0304 // Desktop or Monitor speaker(s). +#define USB_ATTYPE_SPEAKER_RM 0x0305 // Larger room speaker(s). +#define USB_ATTYPE_SPEAKER_COM 0x0306 // Communications Speaker (phone). +#define USB_ATTYPE_SPEAKER_LFE 0x0307 // Speaker designed for low + // frequencies. + +//***************************************************************************** +// +// USB Audio channel configuration bits for wChannelConfig values. +// wChannelConfig: a bit field that indicates which spatial locations are +// present in the cluster. The bit allocations are as follows: +// +//***************************************************************************** +#define USB_CHANNEL_L 0x0001 // Left Front (L) +#define USB_CHANNEL_R 0x0002 // Right Front (R) +#define USB_CHANNEL_C 0x0004 // Center Front (C) +#define USB_CHANNEL_LFE 0x0008 // Low Frequency Enhancement (LFE) +#define USB_CHANNEL_LS 0x0010 // Left Surround (LS) +#define USB_CHANNEL_RS 0x0020 // Right Surround (RS) +#define USB_CHANNEL_LC 0x0040 // Left of Center (LC) +#define USB_CHANNEL_RC 0x0080 // Right of Center (RC) +#define USB_CHANNEL_S 0x0100 // Surround (S) +#define USB_CHANNEL_SL 0x0200 // Side Left (SL) +#define USB_CHANNEL_SR 0x0400 // Side Right (SR) +#define USB_CHANNEL_T 0x0800 // Top (T) + +//***************************************************************************** +// +// Endpoint attributes for Audio Class General type. +// +//***************************************************************************** +#define USB_EP_ATTR_ACG_SAMPLING 0x01 // Sampling Frequency +#define USB_EP_ATTR_ACG_PITCH 0x02 // Pitch +#define USB_EP_ATTR_ACG_MAXPACKET 0x80 // MaxPacketsOnly + +//***************************************************************************** +// +// Indicates the units used for the wLockDelay field for Audio Class General +// type. +// +//***************************************************************************** +#define USB_EP_LOCKDELAY_UNDEF 0x00 // Undefined +#define USB_EP_LOCKDELAY_MS 0x01 // Milliseconds +#define USB_EP_LOCKDELAY_PCM 0x02 // Decoded PCM samples + +//***************************************************************************** +// +// Audio Class-Specific Request Codes +// +//***************************************************************************** +#define USB_AC_SET_CUR 0x01 +#define USB_AC_SET_MIN 0x02 +#define USB_AC_SET_MAX 0x03 +#define USB_AC_SET_RES 0x04 +#define USB_AC_SET_MEM 0x05 +#define USB_AC_GET_CUR 0x81 +#define USB_AC_GET_MIN 0x82 +#define USB_AC_GET_MAX 0x83 +#define USB_AC_GET_RES 0x84 +#define USB_AC_GET_MEM 0x85 +#define USB_AC_GET_STAT 0xff + +#define USB_CS_CONTROL_M 0xff00 +#define USB_CS_CHANNEL_M 0x00ff + +//***************************************************************************** +// +// Endpoint Control Selectors +// +//***************************************************************************** +#define EP_CONTROL_UNDEFINED 0x0000 +#define SAMPLING_FREQ_CONTROL 0x0100 +#define PITCH_CONTROL 0x0200 + +//***************************************************************************** +// +// Feature Unit Control Selectors +// +//***************************************************************************** +#define FU_CONTROL_UNDEFINED 0x0000 +#define MUTE_CONTROL 0x0100 +#define VOLUME_CONTROL 0x0200 +#define BASS_CONTROL 0x0300 +#define MID_CONTROL 0x0400 +#define TREBLE_CONTROL 0x0500 +#define EQUALIZER_CONTROL 0x0600 +#define AUTOMATIC_GAIN_CONTROL 0x0700 +#define DELAY_CONTROL 0x0800 +#define BASS_BOOST_CONTROL 0x0900 +#define LOUDNESS_CONTROL 0x0A00 + +//***************************************************************************** +// +// All structures defined in this section of the header require byte packing of +// fields. This is usually accomplished using the PACKED macro but, for IAR +// Embedded Workbench, this requires a pragma. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +//! This structure describes the Class-Specific Audio Class Interface Header +//! Descriptor as defined in Universal Serial Bus Device Class Definition +//! for Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_DSUBTYPE_HEADER for the header. + // + uint8_t bDescriptorSubtype; + + // + //! Audio Device Class Specification Release Number in Binary-Coded + //! Decimal. + // + uint16_t bcdADC; + + // + //! Total number of bytes returned for the class-specific AudioControl + //! interface descriptor. Includes the combined length of this descriptor + //! header and all Unit and Terminal descriptors. + // + uint16_t wTotai32Length; + + // + //! The number of AudioStreaming and MIDIStreaming interfaces in the Audio + //! Interface Collection to which this AudioControl interface belongs. + // + uint8_t bInCollection; + + // + //! Interface number of the first AudioStreaming or MIDIStreaming interface + //! in the Collection. + // + uint8_t baInterfaceNr; +} +PACKED tACHeader; + +//***************************************************************************** +// +// These are the possible bits set in the tACInputTerminal.wChannelConfig +// value. +// +//***************************************************************************** +#define USB_AC_CC_LEFT 0x0001 +#define USB_AC_CC_RIGHT 0x0002 +#define USB_AC_CC_CENTER 0x0004 +#define USB_AC_CC_LFE 0x0008 +#define USB_AC_CC_LEFTSURROUND 0x0010 +#define USB_AC_CC_RIGHTSURROUND 0x0020 +#define USB_AC_CC_LEFT_CENTER 0x0040 +#define USB_AC_CC_RIGHT_CENTER 0x0080 +#define USB_AC_CC_SURROUND 0x0100 +#define USB_AC_CC_SIDE_LEFT 0x0200 +#define USB_AC_CC_SIDE_RIGHT 0x0400 +#define USB_AC_CC_TOP 0x0800 + +//***************************************************************************** +// +//! This structure describes the Feature Unit Descriptor as defined in +//! Universal Serial Bus Device Class Definition for Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_DSUBTYPE_IN_TERM for the header. + // + uint8_t bDescriptorSubtype; + + // + //! Constant uniquely identifying the Unit within the audio function. This + //! value is used in all requests to address this Unit. + // + uint8_t bUnitID; + + // + //! ID of the Unit or Terminal to which this Feature Unit is connected. + // + uint8_t bSourceID; + + // + //! ID of the Output Terminal to which this Input Terminal is associated. + // + uint8_t bControlSize; + + // + //! A bit set to 1 indicates that the mentioned Control is supported for + //! a given channel. See the USB_FU_* defines for the bit definitions. + //! This actually an array of elements of size bControlSize so be + //! careful when using this value directly. + // + uint16_t bmaControls; +} +PACKED tACFeatureUnit; + +//***************************************************************************** +// +//! This structure describes the Output Terminal Descriptor as defined in +//! Universal Serial Bus Device Class Definition for Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_DSUBTYPE_OUT_TERM for the header. + // + uint8_t bDescriptorSubtype; + + // + //! Constant uniquely identifying the Terminal within the audio function. + //! This value is used in all requests to address this Terminal. + // + uint8_t bTerminalID; + + // + //! Constant characterizing the type of Terminal. See USB Audio Terminal + //! Types. + // + uint16_t wTerminalType; + + // + //! Constant, identifying the Input Terminal to which this Output Terminal + //! is associated. + // + uint8_t bAssocTerminal; + + // + //! ID of the Unit or Terminal to which this Terminal is connected. + // + uint8_t bSourceID; + + // + //! Index of a string descriptor, describing the Output Terminal. + // + uint8_t iTerminal; +} +PACKED tACOutputTerminal; + +//***************************************************************************** +// +//! This structure describes the Input Terminal Descriptor as defined in +//! Universal Serial Bus Device Class Definition for Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_DSUBTYPE_OUT_TERM for the header. + // + uint8_t bDescriptorSubtype; + + // + //! Constant uniquely identifying the Terminal within the audio function. + //! This value is used in all requests to address this Terminal. + // + uint8_t bTerminalID; + + // + //! Constant characterizing the type of Terminal. See USB Audio Terminal + //! Types. + // + uint16_t wTerminalType; + + // + //! Constant, identifying the Input Terminal to which this Output Terminal + //! is associated. + // + uint8_t bAssocTerminal; + + // + //! Number of logical output channels in the Terminal's output audio + //! channel cluster. + // + uint8_t bNrChannels; + + // + //! Describes the spatial location of the logical channels. + // + uint16_t wChannelConfig; + + // + //! Index of a string descriptor, describing the name of the first logical + //! channel. + // + uint8_t iChannelNames; + + // + //! Index of a string descriptor, describing the Output Terminal. + // + uint8_t iTerminal; +} +PACKED tACInputTerminal; + +//***************************************************************************** +// +//! This structure describes the Mixer Descriptor as defined in Universal +//! Serial Bus Device Class Definition for Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_AI_MIXER_UNIT for the header. + // + uint8_t bDescriptorSubtype; + + // + //! Constant uniquely identifying the Unit within the audio function. This + //! value is used in all requests to address this Unit. + // + uint8_t bUnitID; + + // + //! Number of Input Pins of this Unit. + // + uint8_t bNrInPins; + + // + //! ID of the Unit or Terminal to which the first Input Pin of this Mixer + //! Unit is connected. + // + uint8_t baSourceID; +} +PACKED tACMixer; + +//***************************************************************************** +// +//! This structure describes the Selector Descriptor as defined in Universal +//! Serial Bus Device Class Definition for Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_AI_MIXER_UNIT for the header. + // + uint8_t bDescriptorSubtype; + + // + //! Constant uniquely identifying the Unit within the audio function. This + //! value is used in all requests to address this Unit. + // + uint8_t bUnitID; + + // + //! Number of Input Pins of this Unit. + // + uint8_t bNrInPins; + + // + //! ID of the Unit or Terminal to which the first Input Pin of this Mixer + //! Unit is connected. + // + uint8_t baSourceID; +} +PACKED tACSelector; + +//***************************************************************************** +// +//! This structure describes the Output Terminal Descriptor as defined in +//! Universal Serial Bus Device Class Definition for Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_DSUBTYPE_GENERAL for the header. + // + uint8_t bDescriptorSubtype; + + // + //! The Terminal ID of the Terminal to which the endpoint of this + //! interface is connected. + // + uint8_t bTerminalLink; + + // + //! Delay introduced by the data path. Expressed in number of frames. + // + uint8_t bDelay; + + // + //! The Audio Data Format that has to be used to communicate with this + //! interface. + // + uint16_t wFormatTag; +} +PACKED tACGeneral; + +//***************************************************************************** +// +//! This structure describes the Type I Audio format descriptors defined in +//! USB Audio Devices Release 1.0. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + uint8_t bDescriptorType; + + // + //! This will be USB_AS_FORMAT_TYPE. + // + uint8_t bDescriptorSubtype; + + // + //! This will be USB_AS_FORMAT_TYPE_I. + // + uint8_t bFormatType; + + // + //! Number of channels on this streaming interface. + // + uint8_t bNrChannels; + + // + //! Number of bytes per audio sub-frame or channel. + // + uint8_t bSubFrameSize; + + // + //! Number of bits per sample. + // + uint8_t bBitResolution; + + // + //! Number of sample rates that are supported. + // + uint8_t bSamFreqType; + + // + //! Number of bits per sample. + // + uint8_t tSamFreq; +} +PACKED tASFormat; + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +#endif + diff --git a/usblib/usbbuffer.c b/usblib/usbbuffer.c new file mode 100644 index 0000000..acc74e3 --- /dev/null +++ b/usblib/usbbuffer.c @@ -0,0 +1,1192 @@ +//***************************************************************************** +// +// usbbuffer.c - USB buffer object. +// +// 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/debug.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup usblib_buffer_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Workspace variables required by each buffer instance. This structure is +// overlaid on the pvWorkspace memory provided in the tUSBBuffer structure +// passed to USBBufferInit(). +// +//***************************************************************************** +typedef struct +{ + tUSBRingBufObject sRingBuf; + uint32_t ui32LastSent; + uint32_t ui32Flags; +} +tUSBBufferVars; + +//***************************************************************************** +// +// Flags which may be set in the tUSBBufferVars ui32Flags field. +// +//***************************************************************************** +#define USB_BUFFER_FLAG_SEND_ZLP 0x00000001 + +//***************************************************************************** +// +// Schedule the next packet transmission to the host if data remains to be +// sent. +// +// \param psBuffer points to the buffer from which a packet transmission is +// to be scheduled. +// +// This function checks to determine whether the lower layer is capable of +// accepting a new packet for transmission and, if so, schedules the next +// packet transmission if data remains in the buffer. +// +// \return None. +// +//***************************************************************************** +static void +ScheduleNextTransmission(const tUSBBuffer *psBuffer) +{ + tUSBBufferVars *psBufVars; + uint32_t ui32Packet, ui32Space, ui32Total, ui32Sent; + + // + // Get a pointer to our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Ask the lower layer if it has space to accept another packet of data. + // + ui32Packet = psBuffer->pfnAvailable(psBuffer->pvHandle); + + // + // If we were returned something other than zero, we can write that number + // of bytes to the lower layer. + // + if(ui32Packet) + { + // + // How much contiguous data do we have in the buffer? + // + ui32Space = USBRingBufContigUsed(&psBufVars->sRingBuf); + + // + // How much total data do we have in the buffer? + // + ui32Total = USBRingBufUsed(&psBufVars->sRingBuf); + + // + // How much data will we be sending as a result of this call? + // + ui32Sent = (ui32Packet < ui32Total) ? ui32Packet : ui32Total; + + // + // Write the contiguous bytes to the lower layer assuming there is + // something to send. + // + if(ui32Space) + { + // + // There is data available to send. Update our state to indicate + // the amount we will be sending in this packet. + // + psBufVars->ui32LastSent = ui32Sent; + + // + // Determine the maximum sized block we can send in this transfer. + // + ui32Space = (ui32Space < ui32Packet) ? ui32Space : ui32Packet; + // + // Call the lower layer to send the new packet. If the current + // data spans the buffer wrap, tell the lower layer that it can + // expect a second call to fill the whole packet before it + // transmits it. + // + psBuffer->pfnTransfer(psBuffer->pvHandle, + (psBufVars->sRingBuf.pui8Buf + + psBufVars->sRingBuf.ui32ReadIndex), + ui32Space, + (((ui32Space < ui32Packet) && + (ui32Space < ui32Total)) ? false : true)); + + // + // Do we need to send a second part to fill out the packet? This + // will occur if the current packet spans the buffer wrap. + // + if((ui32Space < ui32Packet) && (ui32Space < ui32Total)) + { + // + // The packet straddled the wrap. How much space remains in + // the packet? + // + ui32Packet -= ui32Space; + + // + // How much data can we actually send? + // + ui32Space = ui32Total - ui32Space; + ui32Space = (ui32Space > ui32Packet) ? ui32Packet : ui32Space; + + psBuffer->pfnTransfer(psBuffer->pvHandle, + psBufVars->sRingBuf.pui8Buf, ui32Space, + true); + } + } + else + { + // + // There is no data to send. Did we last send a full packet? + // + if(psBufVars->ui32LastSent == ui32Packet) + { + // + // Yes - if necessary, send a zero-length packet back to the + // host to complete the last transaction. + // + if(psBufVars->ui32Flags & USB_BUFFER_FLAG_SEND_ZLP) + { + psBufVars->ui32LastSent = 0; + psBuffer->pfnTransfer(psBuffer->pvHandle, + psBufVars->sRingBuf.pui8Buf, 0, + true); + } + } + } + + // + // Don't update the ring buffer read index yet. We do this once we are + // sure the packet was correctly transmitted. + // + } +} + +//***************************************************************************** +// +// Handles USB_EVENT_RX_AVAILABLE for a receive buffer. +// +// \param psBuffer points to the buffer which is receiving the event. +// \param ui32Size is the size reported in the event. +// \param pui8Data is the pointer provided in the event. +// +// This function is responsible for reading data from the lower layer into +// the buffer or, if we had previously passed a section of the buffer to the +// lower layer for it to write into directly, updating the buffer write pointer +// to add the new data to the buffer. +// +// If the pointer provided is NULL, we call the low level pfnTransfer function +// to get the new data. If the pointer is not NULL and not within the existing +// ring buffer, we copy the data directly from the pointer to the buffer and +// return the number of bytes read. +// +// \return Returns the number of bytes read from the lower layer. +// +//***************************************************************************** +static uint32_t +HandleRxAvailable(tUSBBuffer *psBuffer, uint32_t ui32Size, uint8_t *pui8Data) +{ + tUSBBufferVars *psBufVars; + uint32_t ui32Avail, ui32Read, ui32Packet, ui32RetCount; + + // + // Get a pointer to our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Has the data already been read into memory? + // + if(pui8Data) + { + // + // Yes - is it already in our ring buffer? + // + if((pui8Data >= psBuffer->pui8Buffer) && + (pui8Data < psBuffer->pui8Buffer + psBuffer->ui32BufferSize)) + { + // + // The data is already in our ring buffer so merely update the + // write pointer to add the new data. + // + USBRingBufAdvanceWrite(&psBufVars->sRingBuf, ui32Size); + + // + // In this case, we pass back 0 to indicate that the lower layer + // doesn't need to make any buffer pointer updates. + // + ui32RetCount = 0; + } + else + { + // + // The data is not within our buffer so we need to copy it into + // the buffer. + // + // How much space does the buffer have available? + // + ui32Avail = USBRingBufFree(&psBufVars->sRingBuf); + + // + // How much should we copy? + // + ui32Read = (ui32Avail < ui32Size) ? ui32Avail : ui32Size; + + // + // Copy the data into the buffer. + // + USBRingBufWrite(&psBufVars->sRingBuf, pui8Data, ui32Read); + + // + // We need to return the number of bytes we read in this case + // since the buffer supplied to us was owned by the lower layer and + // it may need to update its read pointer. + // + ui32RetCount = ui32Read; + } + } + else + { + // + // We were passed a NULL pointer so the low level driver has not read + // the data into memory yet. We need to call the transfer function to + // get the packet. + // + // How big is the packet that we need to receive? + // + ui32Packet = psBuffer->pfnAvailable(psBuffer->pvHandle); + + // + // How much contiguous space do we have in the buffer? + // + ui32Avail = USBRingBufContigFree(&psBufVars->sRingBuf); + + // + // Get as much of the packet as we can in the available space. + // + ui32Read = psBuffer->pfnTransfer(psBuffer->pvHandle, + (psBufVars->sRingBuf.pui8Buf + + psBufVars->sRingBuf.ui32WriteIndex), + ui32Avail, true); + + // + // Advance the ring buffer write pointer to add our new data. + // + if(ui32Read) + { + USBRingBufAdvanceWrite(&psBufVars->sRingBuf, ui32Read); + } + + // + // Did we get the whole packet? + // + if(ui32Read < ui32Packet) + { + // + // No - how much space do we have in the buffer? + // + ui32Avail = USBRingBufContigFree(&psBufVars->sRingBuf); + + // + // If there is any space left, read as much of the remainder of + // the packet as we can. + // + if(ui32Avail) + { + ui32Packet = + psBuffer->pfnTransfer(psBuffer->pvHandle, + (psBufVars->sRingBuf.pui8Buf + + psBufVars->sRingBuf.ui32WriteIndex), + ui32Avail, true); + + // + // Update the write pointer after we read more data into the + // buffer. + // + if(ui32Packet) + { + USBRingBufAdvanceWrite(&psBufVars->sRingBuf, ui32Packet); + } + } + } + + // + // We need to return 0 in this case to indicate that the lower layer + // need not perform any buffer maintenance as a result of the callback. + // + ui32RetCount = 0; + } + + // + // How much data do we have in the buffer? + // + ui32Avail = USBRingBufUsed(&psBufVars->sRingBuf); + + // + // Pass the event on to the client with the current read pointer and + // available data size. The client is expected to understand the ring + // structure and be able to deal with wrap if it wants to read the data + // directly from the buffer. + // + ui32Read = psBuffer->pfnCallback(psBuffer->pvCBData, + USB_EVENT_RX_AVAILABLE, ui32Avail, + (psBufVars->sRingBuf.pui8Buf + + psBufVars->sRingBuf.ui32ReadIndex)); + + // + // If the client read anything from the buffer, update the read pointer. + // + USBRingBufAdvanceRead(&psBufVars->sRingBuf, ui32Read); + + // + // Return the correct value to the low level driver. + // + return(ui32RetCount); +} + +//***************************************************************************** +// +// Handles USB_EVENT_DATA_REMAINING for a receive buffer. +// +// \param psBuffer points to the buffer which is receiving the event. +// +// This function determines the total number of bytes of data that remain +// unprocessed in the client and buffer and reports this back to the caller. +// +// \return Returns the number of bytes remaining to be processed. +// +//***************************************************************************** +static uint32_t +HandleDataRemaining(tUSBBuffer *psBuffer) +{ + uint32_t ui32BufData, ui32ClientData; + tUSBBufferVars *psBufVars; + + // + // Get a pointer to our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // How much data does the client currently have buffered? + // + ui32ClientData = psBuffer->pfnCallback(psBuffer->pvCBData, + USB_EVENT_DATA_REMAINING, 0, + (void *)0); + + // + // How much data do we have in the buffer? + // + ui32BufData = USBRingBufUsed(&psBufVars->sRingBuf); + + // + // Return the total number of bytes of unprocessed data to the lower layer. + // + return(ui32BufData + ui32ClientData); +} + +//***************************************************************************** +// +// Handles USB_EVENT_TX_COMPLETE for a transmit buffer. +// +// \param psBuffer points to the buffer which is receiving the event. +// \param ui32Size is the number of bytes that have been transmitted and +// acknowledged. +// +// This function informs us that data written to the lower layer from a +// transmit buffer has been successfully transmitted. We use this to update +// the buffer read pointer and attempt to schedule the next transmission if +// data remains in the buffer. +// +// \return Returns the number of bytes remaining to be processed. +// +//***************************************************************************** +static uint32_t +HandleTxComplete(tUSBBuffer *psBuffer, uint32_t ui32Size) +{ + tUSBBufferVars *psBufVars; + + // + // Get a pointer to our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Update the transmit buffer read pointer to remove the data that has + // now been transmitted. + // + USBRingBufAdvanceRead(&psBufVars->sRingBuf, ui32Size); + + // + // Try to schedule the next packet transmission if data remains to be + // sent. + // + ScheduleNextTransmission(psBuffer); + + // + // The return code from this event is ignored. + // + return(0); +} + +//***************************************************************************** +// +// Handles USB_EVENT_REQUEST_BUFFER for a receive buffer. +// +// \param psBuffer points to the buffer which is receiving the event. +// \param ui32Size is the size of the buffer requested. +// \param ppui8Buffer is a pointer which is to be written with a pointer to +// the returned buffer. +// +// This function is called by a low level driver that wishes to receive data +// automatically and write it directly to a memory buffer, either using +// software or DMA prior to issuing USB_EVENT_RX_AVAILABLE. The event is sent +// in advance of receiving data to provide storage for whatever is received +// next. +// +// If we have a contiguous block of space in the buffer of at least ui32Size +// bytes immediately in front of the current write pointer, we pass this back +// otherwise we send NULL indicating that the next packet should be notified +// using a standard USB_EVENT_RX_AVAILABLE event without being received +// automatically. Note that the USB_EVENT_REQUEST_BUFFER protocol allows us to +// return less than \e ui32Size bytes if we know how much data is expected next +// but this is not possible here since the USBBuffer knows nothing about the +// protocol whose data it is handling. +// +// \return Returns the number of bytes remaining to be processed. +// +//***************************************************************************** +static uint32_t +HandleRequestBuffer(tUSBBuffer *psBuffer, uint32_t ui32Size, + uint8_t **ppui8Buffer) +{ + tUSBBufferVars *psBufVars; + uint32_t ui32Space; + + // + // Get a pointer to our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // How much contiguous space do we have available? + // + ui32Space = USBRingBufContigFree(&psBufVars->sRingBuf); + + // + // Is there enough space available to satisfy the request? + // + if(ui32Space >= ui32Size) + { + // + // Yes - return the current write pointer + // + *ppui8Buffer = psBufVars->sRingBuf.pui8Buf + + psBufVars->sRingBuf.ui32WriteIndex; + return(ui32Size); + } + else + { + // + // We do not have enough contiguous space following the current write + // pointer to satisfy the request so do not provide a buffer. + // + *ppui8Buffer = (uint8_t *)0; + return(0); + } +} + +//***************************************************************************** +// +//! Initializes a USB buffer object to be used with a given USB controller and +//! device or host class driver. +//! +//! \param psBuffer points to a structure containing information on the buffer +//! memory to be used and the underlying device or host class driver whose data +//! is to be buffered. This structure must remain accessible for as long as +//! the buffer is in use. +//! +//! This function is used to initialize a USB buffer object and insert it +//! into the function and callback interfaces between an underlying driver +//! and the application. The caller supplies information on both the RAM +//! to be used to buffer data, the type of buffer to be created (transmit or +//! receive) and the functions to be called in the lower layer to transfer +//! data to or from the USB controller. +//! +//! \return Returns the original buffer structure pointer if successful or +//! NULL if an error is detected. +// +//***************************************************************************** +const tUSBBuffer * +USBBufferInit(const tUSBBuffer *psBuffer) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer && psBuffer->pvWorkspace && psBuffer->pui8Buffer && + psBuffer->ui32BufferSize && psBuffer->pfnAvailable && + psBuffer->pfnTransfer && psBuffer->pfnCallback); + + // + // Get a pointer to the buffer workspace and initialize the variables it + // contains. + // + psBufVars = psBuffer->pvWorkspace; + psBufVars->ui32Flags = 0; + USBRingBufInit(&psBufVars->sRingBuf, psBuffer->pui8Buffer, + psBuffer->ui32BufferSize); + + // + // If all is well, return the same pointer we were originally passed. + // + return(psBuffer); +} + +//***************************************************************************** +// +//! Enables or disables zero-length packet insertion. +//! +//! \param psBuffer is the pointer to the buffer instance whose information +//! is being queried. +//! \param bSendZLP is \b true to send zero-length packets or \b false to +//! prevent them from being sent. +//! +//! This function allows the use of zero-length packets to be controlled by +//! an application. In cases where the USB buffer has sent a full (64 byte) +//! packet and then discovers that the transmit buffer is empty, the default +//! behavior is to do nothing. Some protocols, however, require that a zero- +//! length packet be inserted to signal the end of the data. When using such +//! a protocol, this function should be called with \e bSendZLP set to \b true +//! to enable the desired behavior. +//! +//! \return None. +// +//***************************************************************************** +void +USBBufferZeroLengthPacketInsert(const tUSBBuffer *psBuffer, bool bSendZLP) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Set the flag telling us whether or not to send a zero-length packet + // after sending a 64 bytes packet and finding no more data to send. + // + if(bSendZLP) + { + // + // Enable ZLP transmission. + // + psBufVars->ui32Flags |= USB_BUFFER_FLAG_SEND_ZLP; + } + else + { + // + // Disable ZLP transmission. + // + psBufVars->ui32Flags &= ~ USB_BUFFER_FLAG_SEND_ZLP; + } +} + +//***************************************************************************** +// +//! Returns the current ring buffer indices for this USB buffer. +//! +//! \param psBuffer is the pointer to the buffer instance whose information +//! is being queried. +//! \param psRingBuf is a pointer to storage that will be written with the +//! current ring buffer control structure for this USB buffer. +//! +//! This function is provided to aid a client wishing to write data directly +//! into the USB buffer rather than using the USBBufferWrite() function. This +//! may be necessary to control when the USBBuffer starts transmission of a +//! large block of data, for example. +//! +//! A transmit buffer will immediately send a new packet on any call to +//! USBBufferWrite() if the underlying layer indicates that a transmission can +//! be started. In some cases this is not desirable and a client may wish to +//! wishes to write more data to the buffer in advance of starting transmission +//! to the lower layer. In such cases, this function may be called to retrieve +//! the current ring buffer indices and the buffer accessed directly. Once the +//! client has written all data it wishes to send, it should call function +//! USBBufferDataWritten() to indicate that transmission may begin. +//! +//! \return None. +// +//***************************************************************************** +void +USBBufferInfoGet(const tUSBBuffer *psBuffer, tUSBRingBufObject *psRingBuf) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer && psRingBuf); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Copy the current ring buffer settings to the clients storage. + // + psRingBuf->pui8Buf = psBufVars->sRingBuf.pui8Buf; + psRingBuf->ui32ReadIndex = psBufVars->sRingBuf.ui32ReadIndex; + psRingBuf->ui32Size = psBufVars->sRingBuf.ui32ReadIndex; + psRingBuf->ui32WriteIndex = psBufVars->sRingBuf.ui32WriteIndex; +} + +//***************************************************************************** +// +//! Indicates that a client has written data directly into the buffer and +//! wishes to start transmission. +//! +//! \param psBuffer is the pointer to the buffer instance into which data has +//! been written. +//! \param ui32Length is the number of bytes of data that the client has +//! written. +//! +//! This function updates the USB buffer write pointer and starts transmission +//! of the data in the buffer assuming the lower layer is ready to receive a +//! new packet. The function is provided to aid a client wishing to write +//! data directly into the USB buffer rather than using the USBBufferWrite() +//! function. This may be necessary to control when the USB buffer starts +//! transmission of a large block of data, for example. +//! +//! A transmit buffer will immediately send a new packet on any call to +//! USBBufferWrite() if the underlying layer indicates that a transmission can +//! be started. In some cases this is not desirable and a client may wish to +//! write more data to the buffer in advance of starting transmission +//! to the lower layer. In such cases, USBBufferInfoGet() may be called to +//! retrieve the current ring buffer indices and the buffer accessed directly. +//! Once the client has written all data it wishes to send (taking care to +//! handle the ring buffer wrap), it should call this function to indicate that +//! transmission may begin. +//! +//! \return None. +// +//***************************************************************************** +void +USBBufferDataWritten(const tUSBBuffer *psBuffer, uint32_t ui32Length) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Advance the ring buffer write pointer to include the newly written + // data. + // + if(ui32Length) + { + USBRingBufAdvanceWrite(&psBufVars->sRingBuf, ui32Length); + } + + // + // Try to schedule a new packet transmission. + // + ScheduleNextTransmission(psBuffer); +} + +//***************************************************************************** +// +//! Indicates that a client has read data directly out of the buffer. +//! +//! \param psBuffer is the pointer to the buffer instance from which data has +//! been read. +//! \param ui32Length is the number of bytes of data that the client has read. +//! +//! This function updates the USB buffer read pointer to remove data that +//! the client has read directly rather than via a call to USBBufferRead(). +//! The function is provided to aid a client wishing to minimize data copying. +//! To read directly from the buffer, a client must call USBBufferInfoGet() to +//! retrieve the current buffer inpsBufVarsdices. With this information, the +//! data following the current read index can be read. Once the client has +//! processed much data as it needs, USBBufferDataRemoved() must be called to +//! advance the read pointer past the data that has been read and free up that +//! section of the buffer. The client must take care to correctly handle the +//! wrap point if accessing the buffer directly. +//! +//! \return None. +// +//***************************************************************************** +void +USBBufferDataRemoved(const tUSBBuffer *psBuffer, uint32_t ui32Length) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Advance the ring buffer write pointer to include the newly written + // data. + // + if(ui32Length) + { + USBRingBufAdvanceRead(&psBufVars->sRingBuf, ui32Length); + } +} + +//***************************************************************************** +// +//! Sets the callback pointer supplied to clients of this buffer. +//! +//! \param psBuffer is the pointer to the buffer instance whose callback data +//! is to be changed. +//! \param pvCBData is the pointer the client wishes to receive on all future +//! callbacks from this buffer. +//! +//! This function sets the callback pointer which this buffer will supply +//! to clients as the \e pvCBData parameter in all future calls to the +//! event callback. +//! +//! \note If this function is to be used, the application must ensure that the +//! tUSBBuffer structure used to describe this buffer is held in RAM rather +//! than flash. The \e pvCBData value passed is written directly into this +//! structure. +//! +//! \return Returns the previous callback pointer set for the buffer. +// +//***************************************************************************** +void * +USBBufferCallbackDataSet(tUSBBuffer *psBuffer, void *pvCBData) +{ + void *pvOldData; + + // + // Keep a copy of the old callback data. + // + pvOldData = psBuffer->pvCBData; + + // + // Replace the callback data with the new value. + // + psBuffer->pvCBData = pvCBData; + + // + // Give the caller the old value back. + // + return(pvOldData); +} + +//***************************************************************************** +// +//! Writes a block of data to the transmit buffer and queues it for +//! transmission to the USB controller. +//! +//! \param psBuffer points to the pointer instance into which data is to be +//! written. +//! \param pui8Data points to the first byte of data which is to be written. +//! \param ui32Length is the number of bytes of data to write to the buffer. +//! +//! This function copies the supplied data into the transmit buffer. The +//! transmit buffer data will be packetized according to the constraints +//! imposed by the lower layer in use and sent to the USB controller as soon as +//! possible. Once a packet is transmitted and acknowledged, a +//! \b USB_EVENT_TX_COMPLETE event will be sent to the application callback +//! indicating the number of bytes that have been sent from the buffer. +//! +//! Attempts to send more data than there is space for in the transmit buffer +//! will result in fewer bytes than expected being written. The value returned +//! by the function indicates the actual number of bytes copied to the buffer. +//! +//! \return Returns the number of bytes actually written. +// +//***************************************************************************** +uint32_t +USBBufferWrite(const tUSBBuffer *psBuffer, const uint8_t *pui8Data, + uint32_t ui32Length) +{ + uint32_t ui32Space; + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer && pui8Data); + ASSERT(psBuffer->bTransmitBuffer == true); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // How much space is left in the buffer? + // + ui32Space = USBRingBufFree(&psBufVars->sRingBuf); + + // + // How many bytes will we write? + // + ui32Length = (ui32Length > ui32Space) ? ui32Space : ui32Length; + + // + // Write the data to the buffer. + // + if(ui32Length) + { + USBRingBufWrite(&psBufVars->sRingBuf, pui8Data, ui32Length); + } + + // + // Try to transmit the next packet to the host. + // + ScheduleNextTransmission(psBuffer); + + // + // Tell the caller how many bytes we wrote to the buffer. + // + return(ui32Length); +} + +//***************************************************************************** +// +//! Flushes a USB buffer, discarding any data that it contains. +//! +//! \param psBuffer is the pointer to the buffer instance which is to be +//! flushed. +//! +//! This function discards all data currently in the supplied buffer without +//! processing (transmitting it via the USB controller or passing it to the +//! client depending upon the buffer mode). +//! +//! \return None. +// +//***************************************************************************** +void +USBBufferFlush(const tUSBBuffer *psBuffer) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Flush the ring buffer. + // + USBRingBufFlush(&psBufVars->sRingBuf); +} + +//***************************************************************************** +// +//! Reads a block of data from a USB receive buffer into storage supplied by +//! the caller. +//! +//! \param psBuffer is the pointer to the buffer instance from which data is +//! to be read. +//! \param pui8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by pui8Data. +//! +//! This function reads up to \e ui32Length bytes of data received from the USB +//! host into the supplied application buffer. If the receive buffer +//! contains fewer than \e ui32Length bytes of data, the data that is present +//! will be copied and the return code will indicate the actual number of bytes +//! copied to \e pui8Data. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +uint32_t +USBBufferRead(const tUSBBuffer *psBuffer, uint8_t *pui8Data, + uint32_t ui32Length) +{ + tUSBBufferVars *psBufVars; + uint32_t ui32Avail, ui32Read; + + // + // Check parameter validity. + // + ASSERT(psBuffer && pui8Data && ui32Length); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // How much data is in the buffer? + // + ui32Avail = USBRingBufUsed(&psBufVars->sRingBuf); + + // + // Determine how many bytes we can actually read. + // + ui32Read = (ui32Avail < ui32Length) ? ui32Avail : ui32Length; + + // + // Read the data from the buffer assuming there is some to read. + // + if(ui32Read) + { + USBRingBufRead(&psBufVars->sRingBuf, pui8Data, ui32Read); + } + + // + // Tell the caller how many bytes we wrote to their buffer. + // + return(ui32Read); +} + +//***************************************************************************** +// +//! Returns the number of bytes of data available in the buffer. +//! +//! \param psBuffer is the pointer to the buffer instance which is to be +//! queried. +//! +//! This function may be used to determine the number of bytes of data in a +//! buffer. For a receive buffer, this indicates the number of bytes that the +//! client can read from the buffer using USBBufferRead(). For a transmit +//! buffer, this indicates the amount of data that remains to be sent to the +//! USB controller. +//! +//! \return Returns the number of bytes of data in the buffer. +// +//***************************************************************************** +uint32_t +USBBufferDataAvailable(const tUSBBuffer *psBuffer) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Return the amount of data in the buffer. + // + return(USBRingBufUsed(&psBufVars->sRingBuf)); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the buffer. +//! +//! \param psBuffer is the pointer to the buffer instance which is to be +//! queried. +//! +//! This function returns the number of free bytes in the buffer. For a +//! transmit buffer, this indicates the maximum number of bytes that can be +//! passed on a call to USBBufferWrite() and accepted for transmission. For a +//! receive buffer, it indicates the number of bytes that can be read from the +//! USB controller before the buffer will be full. +//! +//! \return Returns the number of free bytes in the buffer. +// +//***************************************************************************** +uint32_t +USBBufferSpaceAvailable(const tUSBBuffer *psBuffer) +{ + tUSBBufferVars *psBufVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psBufVars = psBuffer->pvWorkspace; + + // + // Return the amount of space available in the buffer. + // + return(USBRingBufFree(&psBufVars->sRingBuf)); +} + +//***************************************************************************** +// +//! Called by the USB buffer to notify the client of asynchronous events. +//! +//! \param pvCBData is the client-supplied callback pointer associated with +//! this buffer instance. +//! \param ui32Event is the identifier of the event being sent. This will be +//! a general event identifier of the form \b USBD_EVENT_xxxx or a device +//! class-dependent event of the form \b USBD_CDC_EVENT_xxx or +//! \b USBD_HID_EVENT_xxx. +//! \param ui32MsgValue is an event-specific parameter value. +//! \param pvMsgData is an event-specific data pointer. +//! +//! This function is the USB buffer event handler that applications should +//! register with the USB device class driver as the callback for the channel +//! which is to be buffered using this buffer. +//! +//! \note This function will never be called by an application. It is the +//! handler that allows the USB buffer to be inserted above the device class +//! driver or host pipe driver and below the application to offer buffering +//! support. +//! +//! \return The return value is dependent upon the event being processed. +// +//***************************************************************************** +uint32_t +USBBufferEventCallback(void *pvCBData, uint32_t ui32Event, + uint32_t ui32MsgValue, void *pvMsgData) +{ + tUSBBuffer *psBuffer; + + // + // Get our instance data pointers from the callback data. + // + psBuffer = (tUSBBuffer *)pvCBData; + ASSERT(psBuffer); + + // + // Which event have we been sent? + // + switch(ui32Event) + { + // + // Data is available from the lower layer. + // + case USB_EVENT_RX_AVAILABLE: + { + // + // This event is only relevant to us if we are a receive buffer. + // + if(!psBuffer->bTransmitBuffer) + { + return(HandleRxAvailable(psBuffer, ui32MsgValue, pvMsgData)); + } + break; + } + + // + // We are being asked how much data remains to be processed. + // + case USB_EVENT_DATA_REMAINING: + { + return(HandleDataRemaining(psBuffer)); + } + + // + // A previous transmission has completed. + // + case USB_EVENT_TX_COMPLETE: + { + // + // This event is only relevant to us if we are a transmit buffer. + // + if(psBuffer->bTransmitBuffer) + { + // + // Handle the message then drop out of the switch so that the + // event is echoed to the layer above. + // + HandleTxComplete(psBuffer, ui32MsgValue); + } + break; + } + + // + // We are being asked to provide a buffer into which the next packet + // can be received. + // + case USB_EVENT_REQUEST_BUFFER: + { + // + // This event is only relevant to us if we are a receive buffer. + // + if(!psBuffer->bTransmitBuffer) + { + return(HandleRequestBuffer(psBuffer, ui32MsgValue, pvMsgData)); + } + break; + } + + // + // All other events are merely passed through to the client. + // + default: + { + break; + } + } + + // + // If we drop out of the switch, we need to pass the event on to the client + // unmodified and return the relevant return code back to the lower layer. + // + return(psBuffer->pfnCallback(psBuffer->pvCBData, ui32Event, ui32MsgValue, + pvMsgData)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbcdc.h b/usblib/usbcdc.h new file mode 100644 index 0000000..a59ac91 --- /dev/null +++ b/usblib/usbcdc.h @@ -0,0 +1,947 @@ +//***************************************************************************** +// +// usbhid.h - Definitions used by Communication Device Class devices. +// +// Copyright (c) 2007-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. +// +//***************************************************************************** + +//***************************************************************************** +// +// Note: This header contains definitions related to the USB Communication +// Device Class specification. The header is complete for ACM model +// devices but request and notification definitions specific to other +// modem types, ISDN, ATM and Ethernet are currently incomplete or +// omitted. +// +//***************************************************************************** + +#ifndef __USBCDC_H__ +#define __USBCDC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup cdc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Generic macros to read an 8-bit, 16-bit or 32-bit value from a character +// pointer. +// +//***************************************************************************** +#define BYTE(pui8Data) (*(uint8_t *)(pui8Data)) +#define SHORT(pui8Data) (*(uint16_t *)(pui8Data)) +#define LONG(pui8Data) (*(uint32_t *)(pui8Data)) + +//***************************************************************************** +// +// USB CDC subclass codes. Used in interface descriptor, bInterfaceClass +// +//***************************************************************************** +#define USB_CDC_SUBCLASS_DIRECT_LINE_MODEL \ + 0x01 +#define USB_CDC_SUBCLASS_ABSTRACT_MODEL \ + 0x02 +#define USB_CDC_SUBCLASS_TELEPHONE_MODEL \ + 0x03 +#define USB_CDC_SUBCLASS_MULTI_CHANNEL_MODEL \ + 0x04 +#define USB_CDC_SUBCLASS_CAPI_MODEL \ + 0x05 +#define USB_CDC_SUBCLASS_ETHERNET_MODEL \ + 0x06 +#define USB_CDC_SUBCLASS_ATM_MODEL \ + 0x07 + +//***************************************************************************** +// +// USB CDC control interface protocols. Used in control interface descriptor, +// bInterfaceProtocol +// +//***************************************************************************** +#define USB_CDC_PROTOCOL_NONE 0x00 +#define USB_CDC_PROTOCOL_V25TER 0x01 +#define USB_CDC_PROTOCOL_VENDOR 0xFF + +//***************************************************************************** +// +// USB CDC data interface protocols. Used in data interface descriptor, +// bInterfaceProtocol +// +//***************************************************************************** +#define USB_CDC_PROTOCOL_NONE 0x00 +#define USB_CDC_PROTOCOL_I420 0x30 +#define USB_CDC_PROTOCOL_TRANSPARENT \ + 0x32 +#define USB_CDC_PROTOCOL_Q921M 0x50 +#define USB_CDC_PROTOCOL_Q921 0x51 +#define USB_CDC_PROTOCOL_Q921TM 0x52 +#define USB_CDC_PROTOCOL_V42BIS 0x90 +#define USB_CDC_PROTOCOL_Q921EURO \ + 0x91 +#define USB_CDC_PROTOCOL_V120 0x92 +#define USB_CDC_PROTOCOL_CAPI20 0x93 +#define USB_CDC_PROTOCOL_HOST_DRIVER \ + 0xFD +#define USB_CDC_PROTOCOL_CDC_SPEC \ + 0xFE +#define USB_CDC_PROTOCOL_VENDOR 0xFF + +//***************************************************************************** +// +// Functional descriptor definitions +// +//***************************************************************************** + +//***************************************************************************** +// +// Functional descriptor types +// +//***************************************************************************** +#define USB_CDC_CS_INTERFACE 0x24 +#define USB_CDC_CS_ENDPOINT 0x25 + +//***************************************************************************** +// +// Functional descriptor subtypes +// +//***************************************************************************** +#define USB_CDC_FD_SUBTYPE_HEADER \ + 0x00 +#define USB_CDC_FD_SUBTYPE_CALL_MGMT \ + 0x01 +#define USB_CDC_FD_SUBTYPE_ABSTRACT_CTL_MGMT \ + 0x02 +#define USB_CDC_FD_SUBTYPE_DIRECT_LINE_MGMT \ + 0x03 +#define USB_CDC_FD_SUBTYPE_TELEPHONE_RINGER \ + 0x04 +#define USB_CDC_FD_SUBTYPE_LINE_STATE_CAPS \ + 0x05 +#define USB_CDC_FD_SUBTYPE_UNION \ + 0x06 +#define USB_CDC_FD_SUBTYPE_COUNTRY \ + 0x07 +#define USB_CDC_FD_SUBTYPE_TELEPHONE_MODES \ + 0x08 +#define USB_CDC_FD_SUBTYPE_USB_TERMINAL \ + 0x09 +#define USB_CDC_FD_SUBTYPE_NETWORK_TERMINAL \ + 0x0A +#define USB_CDC_FD_SUBTYPE_PROTOCOL_UNIT \ + 0x0B +#define USB_CDC_FD_SUBTYPE_EXTENSION_UNIT \ + 0x0C +#define USB_CDC_FD_SUBTYPE_MULTI_CHANNEL_MGMT \ + 0x0D +#define USB_CDC_FD_SUBTYPE_CAPI_MGMT \ + 0x0E +#define USB_CDC_FD_SUBTYPE_ETHERNET \ + 0x0F +#define USB_CDC_FD_SUBTYPE_ATM 0x10 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_CALL_MGMT, Header functional descriptor, bmCapabilities +// +//***************************************************************************** +#define USB_CDC_CALL_MGMT_VIA_DATA \ + 0x02 +#define USB_CDC_CALL_MGMT_HANDLED \ + 0x01 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_ABSTRACT_CTL_MGMT, Abstract Control Management functional +// descriptor, bmCapabilities +// +//***************************************************************************** +#define USB_CDC_ACM_SUPPORTS_NETWORK_CONNECTION \ + 0x08 +#define USB_CDC_ACM_SUPPORTS_SEND_BREAK \ + 0x04 +#define USB_CDC_ACM_SUPPORTS_LINE_PARAMS \ + 0x02 +#define USB_CDC_ACM_SUPPORTS_COMM_FEATURE \ + 0x01 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_DIRECT_LINE_MGMT, Direct Line Management functional +// descriptor, bmCapabilities +// +//***************************************************************************** +#define USB_CDC_DLM_NEEDS_EXTRA_PULSE_SETUP \ + 0x04 +#define USB_CDC_DLM_SUPPORTS_AUX \ + 0x02 +#define USB_CDC_DLM_SUPPORTS_PULSE \ + 0x01 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_TELEPHONE_MODES, Telephone Operational Modes functional +// descriptor, bmCapabilities +// +//***************************************************************************** +#define USB_CDC_TELEPHONE_SUPPORTS_COMPUTER \ + 0x04 +#define USB_CDC_TELEPHONE_SUPPORTS_STANDALONE \ + 0x02 +#define USB_CDC_TELEPHONE_SUPPORTS_SIMPLE \ + 0x01 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_LINE_STATE_CAPS, Telephone Call and Line State Reporting +// Capabilities descriptor +// +//***************************************************************************** +#define USB_CDC_LINE_STATE_CHANGES_NOTIFIED \ + 0x20 +#define USB_CDC_LINE_STATE_REPORTS_DTMF \ + 0x10 +#define USB_CDC_LINE_STATE_REPORTS_DIST_RING \ + 0x08 +#define USB_CDC_LINE_STATE_REPORTS_CALLERID \ + 0x04 +#define USB_CDC_LINE_STATE_REPORTS_BUSY \ + 0x02 +#define USB_CDC_LINE_STATE_REPORTS_INT_DIALTONE \ + 0x01 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_USB_TERMINAL, USB Terminal functional descriptor, +// bmOptions +// +//***************************************************************************** +#define USB_CDC_TERMINAL_NO_WRAPPER_USED \ + 0x00 +#define USB_CDC_TERMINAL_WRAPPER_USED \ + 0x01 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_MULTI_CHANNEL_MGMT, Multi-Channel Management functional +// descriptor, bmCapabilities +// +//***************************************************************************** +#define USB_CDC_MCM_SUPPORTS_SET_UNIT_PARAM \ + 0x04 +#define USB_CDC_MCM_SUPPORTS_CLEAR_UNIT_PARAM \ + 0x02 +#define USB_CDC_MCM_UNIT_PARAMS_NON_VOLATILE \ + 0x01 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_CAPI_MGMT, CAPI Control Management functional descriptor, +// bmCapabilities +// +//***************************************************************************** +#define USB_CDC_CAPI_INTELLIGENT \ + 0x01 +#define USB_CDC_CAPI_SIMPLE 0x00 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_ETHERNET, Ethernet Networking functional descriptor, +// bmEthernetStatistics +// +//***************************************************************************** +#define USB_CDC_ETHERNET_XMIT_OK \ + 0x01000000 +#define USB_CDC_ETHERNET_RCV_OK 0x02000000 +#define USB_CDC_ETHERNET_XMIT_ERROR \ + 0x04000000 +#define USB_CDC_ETHERNET_RCV_ERROR \ + 0x08000000 +#define USB_CDC_ETHERNET_RCV_NO_BUFFER \ + 0x10000000 +#define USB_CDC_ETHERNET_DIRECTED_BYTES_XMIT \ + 0x20000000 +#define USB_CDC_ETHERNET_DIRECTED_FRAMES_XMIT \ + 0x40000000 +#define USB_CDC_ETHERNET_MULTICAST_BYTES_XMIT \ + 0x80000000 +#define USB_CDC_ETHERNET_MULTICAST_FRAMES_XMIT \ + 0x00010000 +#define USB_CDC_ETHERNET_BROADCAST_BYTES_XMIT \ + 0x00020000 +#define USB_CDC_ETHERNET_BROADCAST_FRAMES_XMIT \ + 0x00040000 +#define USB_CDC_ETHERNET_DIRECTED_BYTES_RCV \ + 0x00080000 +#define USB_CDC_ETHERNET_DIRECTED_FRAMES_RCV \ + 0x00100000 +#define USB_CDC_ETHERNET_MULTICAST_BYTES_RCV \ + 0x00200000 +#define USB_CDC_ETHERNET_MULTICAST_FRAMES_RCV \ + 0x00400000 +#define USB_CDC_ETHERNET_BROADCAST_BYTES_RCV \ + 0x00800000 +#define USB_CDC_ETHERNET_BROADCAST_FRAMES_RCV \ + 0x00000100 +#define USB_CDC_ETHERNET_RCV_CRC_ERROR \ + 0x00000200 +#define USB_CDC_ETHERNET_TRANSMIT_QUEUE_LENGTH \ + 0x00000400 +#define USB_CDC_ETHERNET_RCV_ERROR_ALIGNMENT \ + 0x00000800 +#define USB_CDC_ETHERNET_XMIT_ONE_COLLISION \ + 0x00001000 +#define USB_CDC_ETHERNET_XMIT_MORE_COLLISIONS \ + 0x00002000 +#define USB_CDC_ETHERNET_XMIT_DEFERRED \ + 0x00004000 +#define USB_CDC_ETHERNET_XMIT_MAX_COLLISIONS \ + 0x00008000 +#define USB_CDC_ETHERNET_RCV_OVERRUN \ + 0x00000001 +#define USB_CDC_ETHERNET_XMIT_UNDERRUN \ + 0x00000002 +#define USB_CDC_ETHERNET_XMIT_HEARTBEAT_FAILURE \ + 0x00000004 +#define USB_CDC_ETHERNET_XMIT_TIMES_CRS_LOST \ + 0x00000010 + +//***************************************************************************** +// +// USB_CDC_FD_SUBTYPE_ATM, ATM Networking functional descriptor, +// bmDataCapabilities +// +//***************************************************************************** +#define USB_CDC_ATM_TYPE_3 0x08 +#define USB_CDC_ATM_TYPE_2 0x04 +#define USB_CDC_ATM_TYPE_1 0x02 + +//***************************************************************************** +// +// bmATMDeviceStatistics +// +//***************************************************************************** +#define USB_CDC_ATM_VC_US_CELLS_SENT \ + 0x10 +#define USB_CDC_ATM_VC_US_CELLS_RECEIVED \ + 0x08 +#define USB_CDC_ATM_DS_CELLS_HEC_ERR_CORRECTED \ + 0x04 +#define USB_CDC_ATM_US_CELLS_SENT \ + 0x02 +#define USB_CDC_ATM_US_CELLS_RECEIVED \ + 0x01 + +//***************************************************************************** +// +// Management Element Requests (provided in tUSBRequest.bRequest) +// +//***************************************************************************** +#define USB_CDC_SEND_ENCAPSULATED_COMMAND \ + 0x00 +#define USB_CDC_GET_ENCAPSULATED_RESPONSE \ + 0x01 +#define USB_CDC_SET_COMM_FEATURE \ + 0x02 +#define USB_CDC_GET_COMM_FEATURE \ + 0x03 +#define USB_CDC_CLEAR_COMM_FEATURE \ + 0x04 +#define USB_CDC_SET_AUX_LINE_STATE \ + 0x10 +#define USB_CDC_SET_HOOK_STATE 0x11 +#define USB_CDC_PULSE_SETUP 0x12 +#define USB_CDC_SEND_PULSE 0x13 +#define USB_CDC_SET_PULSE_TIME 0x14 +#define USB_CDC_RING_AUX_JACK 0x15 +#define USB_CDC_SET_LINE_CODING 0x20 +#define USB_CDC_GET_LINE_CODING 0x21 +#define USB_CDC_SET_CONTROL_LINE_STATE \ + 0x22 +#define USB_CDC_SEND_BREAK 0x23 +#define USB_CDC_SET_RINGER_PARMS \ + 0x30 +#define USB_CDC_GET_RINGER_PARMS \ + 0x31 +#define USB_CDC_SET_OPERATION_PARMS \ + 0x32 +#define USB_CDC_GET_OPERATION_PARMS \ + 0x33 +#define USB_CDC_SET_LINE_PARMS 0x34 +#define USB_CDC_GET_LINE_PARMS 0x35 +#define USB_CDC_DIAL_DIGITS 0x36 +#define USB_CDC_SET_UNIT_PARAMETER \ + 0x37 +#define USB_CDC_GET_UNIT_PARAMETER \ + 0x38 +#define USB_CDC_CLEAR_UNIT_PARAMETER \ + 0x39 +#define USB_CDC_GET_PROFILE 0x3A +#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS \ + 0x40 +#define USB_CDC_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER \ + 0x41 +#define USB_CDC_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER \ + 0x42 +#define USB_CDC_SET_ETHERNET_PACKET_FILTER \ + 0x43 +#define USB_CDC_GET_ETHERNET_STATISTIC \ + 0x44 +#define USB_CDC_SET_ATM_DATA_FORMAT \ + 0x50 +#define USB_CDC_GET_ATM_DEVICE_STATISTICS \ + 0x51 +#define USB_CDC_SET_ATM_DEFAULT_VC \ + 0x52 +#define USB_CDC_GET_ATM_VC_STATISTICS \ + 0x53 + +//***************************************************************************** +// +// In cases where a request defined above results in the return of a fixed size +// data block, the following group of labels define the size of that block. In +// each of these cases, an access macro is also provided to write the response +// data into an appropriately-sized array of 8-bit characters. +// +//***************************************************************************** +#define USB_CDC_SIZE_COMM_FEATURE \ + 2 +#define USB_CDC_SIZE_LINE_CODING \ + 7 +#define USB_CDC_SIZE_RINGER_PARMS \ + 4 +#define USB_CDC_SIZE_OPERATION_PARMS \ + 2 +#define USB_CDC_SIZE_UNIT_PARAMETER \ + 2 +#define USB_CDC_SIZE_PROFILE 64 +#define USB_CDC_SIZE_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER \ + 2 +#define USB_CDC_SIZE_ETHERNET_STATISTIC \ + 4 +#define USB_CDC_SIZE_ATM_DEVICE_STATISTICS \ + 4 +#define USB_CDC_SIZE_ATM_VC_STATISTICS \ + 4 +#define USB_CDC_SIZE_LINE_PARMS \ + 10 + +//***************************************************************************** +// +// NB: USB_CDC_SIZE_LINE_PARAMS assumes only a single call. For multiple +// calls, add 4 bytes per additional call. +// +//***************************************************************************** + +//***************************************************************************** +// +// USB_CDC_GET_COMM_FEATURE & USB_CDC_SET_COMM_FEATURE +// +//***************************************************************************** + +//***************************************************************************** +// +// wValue (Feature Selector) +// +//***************************************************************************** +#define USB_CDC_ABSTRACT_STATE 0x0001 +#define USB_CDC_COUNTRY_SETTING 0x0002 + +//***************************************************************************** +// +// Data when feature selector is USB_DCD_ABSTRACT_STATE +// +//***************************************************************************** +#define USB_CDC_ABSTRACT_CALL_DATA_MULTIPLEXED \ + 0x0002 +#define USB_CDC_ABSTRACT_ENDPOINTS_IDLE \ + 0x0001 + +//***************************************************************************** +// +// Macros to populate the response data buffer (whose size in bytes is defined +// by USB_CDC_SIZE_COMM_FEATURE). +// +//***************************************************************************** +#define SetResponseCommFeature(pi8Buf, ui16Data) \ + do \ + { \ + (*(uint16_t *)(pi8Buf)) = ui16Data; \ + } \ + while(0) + +//***************************************************************************** +// +// USB_CDC_SET_AUX_LINE_STATE, wValue +// +//***************************************************************************** +#define USB_CDC_AUX_DISCONNECT 0x0000 +#define USB_CDC_AUX_CONNECT 0x0001 + +//***************************************************************************** +// +// USB_CDC_SET_HOOK_STATE, wValue +// +//***************************************************************************** +#define USB_CDC_ON_HOOK 0x0000 +#define USB_CDC_OFF_HOOK 0x0001 +#define USB_CDC_SNOOPING 0x0002 + +//***************************************************************************** +// +// USB_CDC_GET_LINE_CODING +// +//***************************************************************************** +#define USB_CDC_STOP_BITS_1 0x00 +#define USB_CDC_STOP_BITS_1_5 0x01 +#define USB_CDC_STOP_BITS_2 0x02 + +#define USB_CDC_PARITY_NONE 0x00 +#define USB_CDC_PARITY_ODD 0x01 +#define USB_CDC_PARITY_EVEN 0x02 +#define USB_CDC_PARITY_MARK 0x03 +#define USB_CDC_PARITY_SPACE 0x04 + +//***************************************************************************** +// +// Macro to populate the response data buffer (whose size in bytes is defined +// by USB_CDC_SIZE_LINE_CODING). +// +//***************************************************************************** +#define SetResponseLineCoding(pi8Buf, ui8Rate, ui8Stop, ui8Parity, \ + ui8Databits) \ + do \ + { \ + (*(uint32_t *)(pi8Buf)) = ui8Rate; \ + (*((uint8_t *)(pi8Buf) + 4)) = ui8Stop; \ + (*((uint8_t *)(pi8Buf) + 5)) = ui8Parity; \ + (*((uint8_t *)(pi8Buf) + 6)) = ui8Databits; \ + } \ + while(0) + +//***************************************************************************** +// +// USB_CDC_SET_CONTROL_LINE_STATE, wValue +// +//***************************************************************************** +#define USB_CDC_DEACTIVATE_CARRIER \ + 0x00 +#define USB_CDC_ACTIVATE_CARRIER \ + 0x02 +#define USB_CDC_DTE_NOT_PRESENT 0x00 +#define USB_CDC_DTE_PRESENT 0x01 + +//***************************************************************************** +// +// USB_CDC_SET_RINGER_PARMS, USB_CDC_GET_RINGER_PARMS and +// USB_CDC_GET_LINE_PARMS (ui32RingerBmp) +// +//***************************************************************************** +#define USB_CDC_RINGER_EXISTS 0x80000000 +#define USB_CDC_RINGER_DOES_NOT_EXIST \ + 0x00000000 + +//***************************************************************************** +// +// Macro to populate the response data buffer to USB_CDC_GET_RINGER_PARMS. +// Parameter buf points to a buffer of size USB_CDC_SIZE_RINGER_PARMS bytes. +// +//***************************************************************************** +#define SetResponseRingerParms(pi8Buf, ui8Pattern, ui8Volume, ui32Exists) \ + do \ + { \ + *(uint32_t *)(pi8Buf) = ((ui8Pattern) + \ + ((ui8Volume & 0xFF) << 8) + \ + (ui32Exists & USB_CDC_RINGER_EXISTS)); \ + } \ + while(0) + +//***************************************************************************** +// +// Macros to extract fields from the USB_CDC_SET_RINGER_PARMS data +// +//***************************************************************************** +#define GetRingerVolume(pi8Data) \ + (BYTE((pi8Data) + 1)) +#define GetRingerPattern(pi8Data) \ + (BYTE(pi8Data)) +#define GetRingerExists(pi8Data) \ + ((LONG(pi8Data)) & USB_CDC_RINGER_EXISTS) + +//***************************************************************************** +// +// USB_CDC_SET_OPERATION_PARMS, wValue +// +//***************************************************************************** +#define USB_CDC_SIMPLE_MODE 0x0000 +#define USB_CDC_STANDALONE_MODE 0x0001 +#define USB_CDC_HOST_CENTRIC_MODE \ + 0x0002 + +//***************************************************************************** +// +// Macro to populate the response data buffer to USB_CDC_GET_OPERATION_PARMS. +// Parameter buf points to a buffer of size USB_CDC_SIZE_OPERATION_PARMS +// bytes. +// +//***************************************************************************** +#define SetResponseOperationParms(pi8Bbuf, ui16Data) \ + do \ + { \ + WORD(pi8Buf) = ui16Data; \ + } \ + while(0) + +//***************************************************************************** +// +// USB_CDC_SET_LINE_PARMS, wParam - Line State Change +// +//***************************************************************************** +#define USB_CDC_DROP_ACTIVE_CALL \ + 0x0000 +#define USB_CDC_START_NEW_CALL 0x0001 +#define USB_CDC_APPLY_RINGING 0x0002 +#define USB_CDC_REMOVE_RINGING 0x0003 +#define USB_CDC_SWITCH_CALL 0x0004 + +//***************************************************************************** +// +// Line state bitmap in USB_CDC_GET_LINE_PARMS response +// +//***************************************************************************** +#define USB_CDC_LINE_IS_ACTIVE 0x80000000 +#define USB_CDC_LINE_IS_IDLE 0x00000000 +#define USB_CDC_LINE_NO_ACTIVE_CALL \ + 0x000000FF + +#define USB_CDC_CALL_ACTIVE 0x80000000 + +//***************************************************************************** +// +// Call state value definitions +// +//***************************************************************************** +#define USB_CDC_CALL_IDLE 0x00000000 +#define USB_CDC_CALL_TYPICAL_DIALTONE \ + 0x00000001 +#define USB_CDC_CALL_INTERRUPTED_DIALTONE \ + 0x00000002 +#define USB_CDC_CALL_DIALING 0x00000003 +#define USB_CDC_CALL_RINGBACK 0x00000004 +#define USB_CDC_CALL_CONNECTED 0x00000005 +#define USB_CDC_CALL_INCOMING 0x00000006 + +//***************************************************************************** +// +// Call state change value definitions +// +//***************************************************************************** +#define USB_CDC_CALL_STATE_IDLE 0x01 +#define USB_CDC_CALL_STATE_DIALING \ + 0x02 +#define USB_CDC_CALL_STATE_RINGBACK \ + 0x03 +#define USB_CDC_CALL_STATE_CONNECTED \ + 0x04 +#define USB_CDC_CALL_STATE_INCOMING \ + 0x05 + +//***************************************************************************** +// +// Extra byte of data describing the connection type for +// USB_CDC_CALL_STATE_CONNECTED. +// +//***************************************************************************** +#define USB_CDC_VOICE 0x00 +#define USB_CDC_ANSWERING_MACHINE \ + 0x01 +#define USB_CDC_FAX 0x02 +#define USB_CDC_MODEM 0x03 +#define USB_CDC_UNKNOWN 0xFF + +//***************************************************************************** +// +// Macro to extract call index from request in cases where wParam is +// USB_CDC_SWITCH_CALL. +// +//***************************************************************************** +#define GetCallIndex(pi8Data) (BYTE(pi8Data)) + +//***************************************************************************** +// +// Macro to populate the CallState entries in response to request +// USB_CDC_GET_LINE_PARMS. The ui8Index parameter is a zero based index +// indicating which call entry in the pi8Buf response buffer to fill in. Note +// that pi8Buf points to the first byte of the buffer (the wLength field). +// +//***************************************************************************** +#define SetResponseCallState(pi8Buf, ui8Index, ui32Active, ui8StateChange, \ + ui8State) \ + do \ + { \ + (LONG((uint8_t *)(pi8Buf) + (10 + (4 * (ui8Index))))) = \ + (((ui32Active) & USB_CDC_CALL_IS_ACTIVE) + \ + (((ui8StateChange) & 0xFF) << 8) + \ + ((ui8State) & 0xFF)); \ + } \ + while(0) + +//***************************************************************************** +// +// Macro to populate the response data buffer (whose size in bytes is defined +// by USB_CDC_SIZE_LINE_PARMS). Note that this macro only populates fields for +// a single call. If multiple calls are being managed, additional 4 byte +// fields must be appended to provide call state for each call after the first. +// This may be done using the SetResponseCallState macro with the appropriate +// call index supplied. +// +//***************************************************************************** +#define SetResponseLineParms(pi8Buf, ui16Length, \ + ui8RingPattern, ui8RingVolume, ui32RingExists, \ + ui32LineActive, ui8LineCallIndex, \ + ui32CallActive, ui8CallStateChange, \ + ui8CallState) \ + do \ + { \ + (WORD(pi8Buf)) = ui16Length; \ + SetResponseRingerParams(((uint8_t *)(pi8Buf) + 2), \ + ui8RingPattern, ui8RingVolume, \ + ui32RingExists); \ + (LONG((uint8_t *)(pi8Buf) + 6)) = \ + (((ui32LineActive) & USB_CDC_LINE_IS_ACTIVE) + \ + ((ui8LineCallIndex) & 0xFF)) ; \ + SetResponseCallState(pi8Buf, 0, ui32CallActive, \ + ui8CallStateChange, ui8CallState); \ + } \ + while(0) + +//***************************************************************************** +// +// Notification Element definitions +// +//***************************************************************************** +#define USB_CDC_NOTIFY_NETWORK_CONNECTION \ + 0x00 +#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE \ + 0x01 +#define USB_CDC_NOTIFY_AUX_JACK_HOOK_STATE \ + 0x08 +#define USB_CDC_NOTIFY_RING_DETECT \ + 0x09 +#define USB_CDC_NOTIFY_SERIAL_STATE \ + 0x20 +#define USB_CDC_NOTIFY_CALL_STATE_CHANGE \ + 0x28 +#define USB_CDC_NOTIFY_LINE_STATE_CHANGE \ + 0x29 +#define USB_CDC_NOTIFY_CONNECTION_SPEED_CHANGE \ + 0x2A + +//***************************************************************************** +// +// USB_CDC_NOTIFY_NETWORK_CONNECTION, wValue +// +//***************************************************************************** +#define USB_CDC_NETWORK_DISCONNECTED \ + 0x0000 +#define USB_CDC_NETWORK_CONNECTED \ + 0x0001 + +//***************************************************************************** +// +// USB_CDC_NOTIFY_AUX_JACK_HOOK_STATE, wValue +// +//***************************************************************************** +#define USB_CDC_AUX_JACK_ON_HOOK \ + 0x0000 +#define USB_CDC_AUX_JACK_OFF_HOOK \ + 0x0001 + +//***************************************************************************** +// +// USB_CDC_NOTIFY_SERIAL_STATE, Data +// +//***************************************************************************** + +//***************************************************************************** +// +// Number of bytes of data returned alongside this notification. +// +//***************************************************************************** +#define USB_CDC_NOTIFY_SERIAL_STATE_SIZE \ + 2 + +#define USB_CDC_SERIAL_STATE_OVERRUN \ + 0x0040 +#define USB_CDC_SERIAL_STATE_PARITY \ + 0x0020 +#define USB_CDC_SERIAL_STATE_FRAMING \ + 0x0010 +#define USB_CDC_SERIAL_STATE_RING_SIGNAL \ + 0x0008 +#define USB_CDC_SERIAL_STATE_BREAK \ + 0x0004 +#define USB_CDC_SERIAL_STATE_TXCARRIER \ + 0x0002 +#define USB_CDC_SERIAL_STATE_RXCARRIER \ + 0x0001 + +//***************************************************************************** +// +// USB_CDC_NOTIFY_CALL_STATE_CHANGE, wValue +// +// Call state values are defined above in the group beginning +// USB_CDC_CALL_STATE_IDLE. Note that the data returned alongside this +// notification are heavily dependent upon the call state being reported so no +// specific lengths or access macros are provided here. +// +// Macro to construct the correct wValue for this notification given a state +// and call index. +// +//***************************************************************************** +#define SetNotifyCallStatewValue(pi16Result, ui8CallState, ui8Index) \ + do \ + { \ + (WORD(pi16Result)) = (((ui8CallState) & 0xFF) + \ + (((ui8Index) & 0xFF) << 8)); \ + } \ + while(0) + +//***************************************************************************** +// +// USB_CDC_NOTIFY_LINE_STATE_CHANGE, wValue +// +// Note that the data returned alongside this notification are heavily +// dependent upon the call state being reported so no specific lengths or +// access macros are provided here. +// +//***************************************************************************** +#define USB_CDC_LINE_STATE_IDLE 0x0000 +#define USB_CDC_LINE_STATE_HOLD 0x0001 +#define USB_CDC_LINE_STATE_OFF_HOOK \ + 0x0002 +#define USB_CDC_LINE_STATE_ON_HOOK \ + 0x0003 + +//***************************************************************************** +// +// USB_CDC_NOTIFY_CONNECTION_SPEED_CHANGE, Data +// +// Macro to populate the 8 byte data structure returned alongside this +// notification. +// +//***************************************************************************** +#define SetNotifyConnectionSpeedChange(pi8Buf, ui32USBitRate, ui32DSBitRate) \ + do \ + { \ + LONG(pi8Buf) = ui32USBitRate; \ + LONG((uint8_t *)(pi8Buf) + 4) = ui32DSBitRate; \ + } \ + while(0) + +//***************************************************************************** +// +// Packed structure definitions for request/response data blocks +// +//***************************************************************************** + +//***************************************************************************** +// +// All structures defined in this section of the header require byte packing of +// fields. This is usually accomplished using the PACKED macro but, for IAR +// Embedded Workbench, this requires a pragma. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +//! USB_CDC_GET/SET_LINE_CODING request-specific data. +// +//***************************************************************************** +typedef struct +{ + // + //! The data terminal rate in bits per second. + // + uint32_t ui32Rate; + + // + //! The number of stop bits. Valid values are USB_CDC_STOP_BITS_1, + //! USB_CDC_STOP_BITS_1_5 or USB_CDC_STOP_BITS_2 + // + uint8_t ui8Stop; + + // + //! The parity setting. Valid values are USB_CDC_PARITY_NONE, + //! USB_CDC_PARITY_ODD, USB_CDC_PARITY_EVEN, USB_CDC_PARITY_MARK and + //! USB_CDC_PARITY_SPACE. + // + uint8_t ui8Parity; + + // + //! The number of data bits per character. Valid values are 5, 6, 7 and 8 + //! in this implementation. + // + uint8_t ui8Databits; +} +PACKED tLineCoding; + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBCDC_H__ diff --git a/usblib/usbdesc.c b/usblib/usbdesc.c new file mode 100644 index 0000000..ac88082 --- /dev/null +++ b/usblib/usbdesc.c @@ -0,0 +1,480 @@ +//***************************************************************************** +// +// usbdesc.c - USB descriptor parsing functions. +// +// 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 "usblib/usblib.h" + +//***************************************************************************** +// +// Assumptions: +// ------------ +// +// The following assumptions are made in this module. From reading chapter 9 +// of the USB 2.0 specification, these appear to be perfectly valid. +// +// 1. The interface number, bInterfaceNumber in the interface descriptor, is +// a zero based index and takes values between 0 and +// (pConfigDescriptor->bNumInterfaces - 1) inclusive. +// 2. Similarly, the alternate setting number, bAlternateSetting in the +// interface descriptor, is a zero based index. +// 3. Interface descriptors are ordered by interface number in the +// configuration descriptor. +// 4. If alternate settings are available for an interface, the interface +// descriptors are ordered by alternate setting value bAlternateSetting. +// 5. Although the endpoints associated with a given interface must follow +// their associated interface descriptor, it is possible for other, +// device specific descriptors to be found between an interface descriptor +// and its endpoints or between endpoint descriptors for the same +// interface. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup general_usblib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Determines the number of individual descriptors of a particular type within +//! a supplied buffer. +//! +//! \param psDesc points to the first byte of a block of standard USB +//! descriptors. +//! \param ui32Size is the number of bytes of descriptor data found at pointer +//! \e psDesc. +//! \param ui32Type identifies the type of descriptor that is to be counted. +//! If the value is \b USB_DESC_ANY, the function returns the total number of +//! descriptors regardless of type. +//! +//! This function can be used to count the number of descriptors of a +//! particular type within a block of descriptors. The caller can provide a +//! specific type value which the function matches against the second byte of +//! each descriptor or, alternatively, can specify \b USB_DESC_ANY to have the +//! function count all descriptors regardless of their type. +//! +//! \return Returns the number of descriptors found in the supplied block of +//! data. +// +//***************************************************************************** +uint32_t +USBDescGetNum(tDescriptorHeader *psDesc, uint32_t ui32Size, + uint32_t ui32Type) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32TotLength; + uint32_t ui32Count; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = psDesc; + ui32TotLength = 0; + ui32Count = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ui32TotLength < ui32Size) + { + // + // Does this descriptor match the type passed (if a specific type + // has been specified)? + // + if((ui32Type == USB_DESC_ANY) || + (psDescCheck->bDescriptorType == (uint8_t)(ui32Type & 0xFF))) + { + ui32Count++; + } + + // + // Move on to the next descriptor. + // + ui32TotLength += (uint32_t)psDescCheck->bLength; + psDescCheck = NEXT_USB_DESCRIPTOR(psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ui32Count); +} + +//***************************************************************************** +// +//! Determines the number of individual descriptors of a particular type within +//! a supplied buffer. +//! +//! \param psDesc points to the first byte of a block of standard USB +//! descriptors. +//! \param ui32Size is the number of bytes of descriptor data found at pointer +//! \e psDesc. +//! \param ui32Type identifies the type of descriptor that is to be found. If +//! the value is \b USB_DESC_ANY, the function returns a pointer to the n-th +//! descriptor regardless of type. +//! \param ui32Index is the zero based index of the descriptor whose pointer is +//! to be returned. For example, passing value 1 in \e ui32Index returns the +//! second matching descriptor. +//! +//! Return a pointer to the n-th descriptor of a particular type found in the +//! block of \e ui32Size bytes starting at \e psDesc. +//! +//! \return Returns a pointer to the header of the required descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tDescriptorHeader * +USBDescGet(tDescriptorHeader *psDesc, uint32_t ui32Size, + uint32_t ui32Type, uint32_t ui32Index) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32TotLength; + uint32_t ui32Count; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = psDesc; + ui32TotLength = 0; + ui32Count = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ui32TotLength < ui32Size) + { + // + // Does this descriptor match the type passed (if a specific type + // has been specified)? + // + if((ui32Type == USB_DESC_ANY) || + (psDescCheck->bDescriptorType == (uint8_t)(ui32Type & 0xFF))) + { + // + // We found a matching descriptor. If our count matches the + // supplied index, we are done so return the pointer. + // + if(ui32Count == ui32Index) + { + return(psDescCheck); + } + + // + // We have not found enough descriptors yet to satisfy the supplied + // index so increment our count and continue. + // + ui32Count++; + } + + // + // Move on to the next descriptor. + // + ui32TotLength += (uint32_t)psDescCheck->bLength; + psDescCheck = NEXT_USB_DESCRIPTOR(psDescCheck); + } + + // + // If we get here, we reached the end of the data without finding the + // required descriptor. Return NULL. + // + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +//! Determines the number of different alternate configurations for a given +//! interface within a configuration descriptor. +//! +//! \param psConfig points to the first byte of a standard USB configuration +//! descriptor. +//! \param ui8InterfaceNumber is the interface number for which the number of +//! alternate configurations is to be counted. +//! +//! This function can be used to count the number of alternate settings for a +//! specific interface within a configuration. +//! +//! \return Returns the number of alternate versions of the specified interface +//! or 0 if the interface number supplied cannot be found in the config +//! descriptor. +// +//***************************************************************************** +uint32_t +USBDescGetNumAlternateInterfaces(tConfigDescriptor *psConfig, + uint8_t ui8InterfaceNumber) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32TotLength; + uint32_t ui32Count; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig; + ui32TotLength = 0; + ui32Count = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ui32TotLength < (uint32_t)psConfig->wTotalLength) + { + // + // Is this an interface descriptor with the required interface number? + // + if((psDescCheck->bDescriptorType == USB_DTYPE_INTERFACE) && + (((tInterfaceDescriptor *)psDescCheck)->bInterfaceNumber == + ui8InterfaceNumber)) + { + // + // Yes - increment our count. + // + ui32Count++; + } + + // + // Move on to the next descriptor. + // + ui32TotLength += (uint32_t)psDescCheck->bLength; + psDescCheck = NEXT_USB_DESCRIPTOR(psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ui32Count); +} + +//***************************************************************************** +// +//! Returns a pointer to the n-th interface descriptor in a config descriptor +//! with the supplied interface number. +//! +//! \param psConfig points to the first byte of a standard USB configuration +//! descriptor. +//! \param ui8InterfaceNumber is the interface number of the descriptor that is +//! being queried. +//! \param ui32Index is the zero based index of the descriptor to return. +//! +//! This function returns a pointer to the n-th interface descriptor in the +//! supplied configuration which has the requested interface number. It may be +//! used by a client to retrieve the descriptors for each alternate setting +//! of a given interface within the configuration passed. +//! +//! \return Returns a pointer to the n-th interface descriptor with interface +//! number as specified or NULL of this descriptor does not exist. +// +//***************************************************************************** +static tInterfaceDescriptor * +USBDescGetAlternateInterface(tConfigDescriptor *psConfig, + uint8_t ui8InterfaceNumber, + uint32_t ui32Index) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32TotLength; + uint32_t ui32Count; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig; + ui32TotLength = 0; + ui32Count = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ui32TotLength < (uint32_t)psConfig->wTotalLength) + { + // + // Does this descriptor match the type passed (if a specific type + // has been specified)? + // + if((psDescCheck->bDescriptorType == USB_DTYPE_INTERFACE) && + (((tInterfaceDescriptor *)psDescCheck)->bInterfaceNumber == + ui8InterfaceNumber)) + { + // + // This is an interface descriptor for interface + // ui8InterfaceNumber. Determine if this is the n-th one we have + // found and, if so, return its pointer. + // + if(ui32Count == ui32Index) + { + // + // Found it - return the pointer. + // + return((tInterfaceDescriptor *)psDescCheck); + } + + // + // Increment our count of matching descriptors found and go back + // to look for another since we have not yet reached the n-th + // match. + // + ui32Count++; + } + + // + // Move on to the next descriptor. + // + ui32TotLength += (uint32_t)psDescCheck->bLength; + psDescCheck = NEXT_USB_DESCRIPTOR(psDescCheck); + } + + // + // If we drop out the end of the loop, we did not find the requested + // descriptor so return NULL. + // + return((tInterfaceDescriptor *)0); +} + +//***************************************************************************** +// +//! Returns a pointer to the n-th interface descriptor in a configuration +//! descriptor that applies to the supplied alternate setting number. +//! +//! \param psConfig points to the first byte of a standard USB configuration +//! descriptor. +//! \param ui32Index is the zero based index of the interface that is to be +//! found. If \e ui32Alt is set to a value other than \b USB_DESC_ANY, this +//! will be equivalent to the interface number being searched for. +//! \param ui32Alt is the alternate setting number which is to be +//! searched for. If this value is \b USB_DESC_ANY, the alternate setting +//! is ignored and all interface descriptors are considered in the search. +//! +//! Return a pointer to the n-th interface descriptor found in the supplied +//! configuration descriptor. If \e ui32Alt is not \b USB_DESC_ANY, only +//! interface descriptors which are part of the supplied alternate setting are +//! considered in the search otherwise all interface descriptors are +//! considered. +//! +//! Note that, although alternate settings can be applied on an interface-by- +//! interface basis, the number of interfaces offered is fixed for a given +//! config descriptor. Hence, this function will correctly find the unique +//! interface descriptor for that interface's alternate setting number +//! \e ui32Alt if \e ui32Index is set to the required interface number and +//! \e ui32Alt is set to a valid alternate setting number for that interface. +//! +//! \return Returns a pointer to the required interface descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tInterfaceDescriptor * +USBDescGetInterface(tConfigDescriptor *psConfig, uint32_t ui32Index, + uint32_t ui32Alt) +{ + // + // If we are being told to ignore the alternate configuration, this boils + // down to a very simple query. + // + if(ui32Alt == USB_DESC_ANY) + { + // + // Return the ui32Index-th interface descriptor we find in the + // configuration descriptor. + // + return((tInterfaceDescriptor *)USBDescGet( + (tDescriptorHeader *)psConfig, + (uint32_t)psConfig->wTotalLength, + USB_DTYPE_INTERFACE, ui32Index)); + } + else + { + // + // In this case, a specific alternate setting number is required. + // Given that interface numbers are zero based indices, we can + // pass the supplied ui32Index parameter directly as the interface + // number to USBDescGetAlternateInterface to retrieve the requested + // interface descriptor pointer. + // + return(USBDescGetAlternateInterface(psConfig, ui32Index, ui32Alt)); + } +} + +//***************************************************************************** +// +//! Return a pointer to the n-th endpoint descriptor in the supplied +//! interface descriptor. +//! +//! \param psInterface points to the first byte of a standard USB interface +//! descriptor. +//! \param ui32Index is the zero based index of the endpoint that is to be +//! found. +//! \param ui32Size contains the maximum number of bytes that the function may +//! search beyond \e psInterface while looking for the requested endpoint +//! descriptor. +//! +//! Return a pointer to the n-th endpoint descriptor found in the supplied +//! interface descriptor. If the \e ui32Index parameter is invalid (greater +//! than or equal to the bNumEndpoints field of the interface descriptor) or +//! the endpoint cannot be found within \e ui32Size bytes of the interface +//! descriptor pointer, the function will return NULL. +//! +//! Note that, although the USB 2.0 specification states that endpoint +//! descriptors must follow the interface descriptor that they relate to, it +//! also states that device specific descriptors should follow any standard +//! descriptor that they relate to. As a result, we cannot assume that each +//! interface descriptor will be followed by nothing but an ordered list of +//! its own endpoints and, hence, the function needs to be provided \e ui32Size +//! to limit the search range. +//! +//! \return Returns a pointer to the requested endpoint descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tEndpointDescriptor * +USBDescGetInterfaceEndpoint(tInterfaceDescriptor *psInterface, + uint32_t ui32Index, uint32_t ui32Size) +{ + // + // Is the index passed valid? + // + if(ui32Index >= psInterface->bNumEndpoints) + { + // + // It's out of bounds so return a NULL. + // + return((tEndpointDescriptor *)0); + } + else + { + // + // Endpoint index is valid so find the descriptor. + // + return((tEndpointDescriptor *)USBDescGet( + (tDescriptorHeader *)psInterface, + ui32Size, USB_DTYPE_ENDPOINT, ui32Index)); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbdfu.h b/usblib/usbdfu.h new file mode 100644 index 0000000..9bde75d --- /dev/null +++ b/usblib/usbdfu.h @@ -0,0 +1,504 @@ +//***************************************************************************** +// +// usbdfu.h - Definitions related to the USB Device Firmware Upgrade class. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBDFU_H__ +#define __USBDFU_H__ + +//***************************************************************************** +// +// DFU attributes as published in the functional descriptor. +// +//***************************************************************************** +#define DFU_ATTR_WILL_DETACH 0x08 +#define DFU_ATTR_MANIFEST_TOLERANT \ + 0x04 +#define DFU_ATTR_CAN_UPLOAD 0x02 +#define DFU_ATTR_CAN_DOWNLOAD 0x01 + +//***************************************************************************** +// +// The states that the DFU device can be in. These values are reported to +// the host in response to a USBD_DFU_REQUEST_GETSTATE request. +// +//***************************************************************************** +typedef enum +{ + eDFUStateAppIdle = 0, + eDFUStateAppDetach, + eDFUStateIdle, + eDFUStateDnloadSync, + eDFUStateDnBusy, + eDFUStateDnloadIdle, + eDFUStateManifestSync, + eDFUStateManifest, + eDFUStateManifestWaitReset, + eDFUStateUploadIdle, + eDFUStateError +} +tDFUState; + +//***************************************************************************** +// +// The current error status of the DFU device. These values are reported to +// the host in response to a USBD_DFU_REQUEST_GETSTATUS request. +// +//***************************************************************************** +typedef enum +{ + eDFUStatusOk = 0, + eDFUStatusErrTarget, + eDFUStatusErrFile, + eDFUStatusErrWrite, + eDFUStatusErrErase, + eDFUStatusErrCheckErased, + eDFUStatusErrProg, + eDFUStatusErrVerify, + eDFUStatusErrAddress, + eDFUStatusErrNotDone, + eDFUStatusErrFirmware, + eDFUStatusErrVendor, + eDFUStatusErrUSBR, + eDFUStatusErrPOR, + eDFUStatusErrUnknown, + eDFUStatusErrStalledPkt +} +tDFUStatus; + +//***************************************************************************** +// +// The descriptor type for the DFU functional descriptor. +// +//***************************************************************************** +#define USB_DFU_FUNC_DESCRIPTOR_TYPE \ + 0x21 + +//***************************************************************************** +// +// The subclass identifier for DFU as reported to the host in the +// bInterfaceSubClass field of the DFU interface descriptor. +// +//***************************************************************************** +#define USB_DFU_SUBCLASS 0x01 + +//***************************************************************************** +// +// The protocol identifier for DFU as reported to the host in the +// bInterfaceProtocol field of the DFU interface descriptor. +// +//***************************************************************************** +#define USB_DFU_PROTOCOL 0x02 +#define USB_DFU_RUNTIME_PROTOCOL \ + 0x01 + +//***************************************************************************** +// +// DFU class-specific request identifiers. +// +//***************************************************************************** +#define USBD_DFU_REQUEST_DETACH 0 +#define USBD_DFU_REQUEST_DNLOAD 1 +#define USBD_DFU_REQUEST_UPLOAD 2 +#define USBD_DFU_REQUEST_GETSTATUS \ + 3 +#define USBD_DFU_REQUEST_CLRSTATUS \ + 4 +#define USBD_DFU_REQUEST_GETSTATE \ + 5 +#define USBD_DFU_REQUEST_ABORT 6 + +//***************************************************************************** +// +// Request 1KB blocks from the host. This value is published in the USB +// functional descriptor. +// +//***************************************************************************** +#define DFU_TRANSFER_SIZE 1024 + +//***************************************************************************** +// +// USBLib-specific request identifier. This is used to determine whether +// the target device supports our DFU command protocol. It is expected that +// a device not supporting our extensions will stall this request. This +// request is only supported while the DFU device is in eDFUStateIdle. +// +// An IN request containing the following parameters will result in the device +// sending back a tDFUQueryTivaProtocol structure indicating that +// USBLib extensions are supported. The actual values in wValue and wIndex +// have no meaning other than to act as markers in the unlikely event that +// another DFU device also chooses to use request ID 0x42 for some other +// purpose. +// +// wValue - 0x23(REQUEST_TIVA_VALUE) +// wIndex - Interface number +// wLength - sizeof(tDFUQueryTivaProtocol) +// +//***************************************************************************** +#define USBD_DFU_REQUEST_TIVA 0x42 +#define REQUEST_TIVA_VALUE 0x23 + +#define DFU_PROTOCOL_TIVA_MARKER \ + 0x4C4D +#define DFU_PROTOCOL_TIVA_VERSION_1 \ + 0x0001 + +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// The structure sent to the host when a valid USBD_DFU_REQUEST_TIVA is +// received while the DFU device is in idle state. +// +//***************************************************************************** +typedef struct +{ + // + // The protocol marker(DFU_PROTOCOL_TIVA_MARKER) + // + uint16_t ui16Marker; + + // + // The protocol version(DFU_PROTOCOL_TIVA_VERSION_1) + // + uint16_t ui16Version; +} +PACKED tDFUQueryTivaProtocol; + +//***************************************************************************** +// +// Structure sent to the host in response to USBD_DFU_REQUEST_GETSTATUS. +// +//***************************************************************************** +typedef struct +{ + uint8_t bStatus; + uint8_t bwPollTimeout[3]; + uint8_t bState; + uint8_t iString; +} +PACKED tDFUGetStatusResponse; + +//***************************************************************************** +// +// Firmware Download Commands +// +// The data passed on a USBD_DFU_REQUEST_DNLOAD request is comprised of a +// header which instructs the boot loader how to interpret the block and +// block-specific data. The following definitions relate to the download +// block headers. +// +//***************************************************************************** + +//***************************************************************************** +// +// Supported command identifiers +// +//***************************************************************************** +#define DFU_CMD_PROG 0x01 +#define DFU_CMD_READ 0x02 +#define DFU_CMD_CHECK 0x03 +#define DFU_CMD_ERASE 0x04 +#define DFU_CMD_INFO 0x05 +#define DFU_CMD_BIN 0x06 +#define DFU_CMD_RESET 0x07 + +//***************************************************************************** +// +// Generic download command header. +// +//***************************************************************************** +typedef struct +{ + // + // Command identifier. + // + uint8_t ui8Command; + + // + // Command-specific data elements. + // + uint8_t pui8Data[7]; +} +PACKED tDFUDownloadHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_PROG command. +// +// This command is used to program a section of the flash with the binary data +// which immediately follows the header. The start address of the data is +// expressed as a 1KB block number so 0 would represent the bottom of flash +// (which, incidentally, the USB boot loader will not let you program) and 0x10 +// would represent address 16KB or 16384 (0x4000). The ui32Length field +// contains the total number of bytes of data in the following programming +// operation. The DFU device will not look for any command header on following +// USBD_DFU_REQUEST_DNLOAD requests until the operation is completed or +// aborted. +// +// By using this protocol, the DFU_CMD_PROG command header may be used as a +// simple header on the binary files to be sent to the DFU device for +// programming. If we enforce the requirement that the DFU_CMD_PROG header is +// applied to each USBD_DFU_REQUEST_DNLOAD (one per block), this means that the +// host-side DFU application must be aware of the underlying protocol and +// insert these headers dynamically during programming operations. This could +// be handled by post processing the binary to insert the headers at the +// appropriate points but this would then tie the binary structure to the +// chosen transfer size and break the operation if the transfer size were to +// change in the future. +// +//***************************************************************************** +typedef struct +{ + // + // DFU_CMD_PROG + // + uint8_t ui8Command; + + // + // Reserved - set to 0x00. + // + uint8_t ui8Reserved; + + // + // Block start address / 1024 + // + uint16_t ui16StartAddr; + + // + // Total length, in bytes, of following data for the complete download + // operation. + // + uint32_t ui32Length; +} +PACKED tDFUDownloadProgHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_READ and DFU_CMD_CHECK commands. +// +// This command may be used to set the address range whose content will be +// returned on subsequent USBD_DFU_REQUEST_UPLOAD requests from the host. +// +// To read back a the contents of a region of flash, the host should send +// USBD_DFU_REQUEST_DNLOAD with ui8Command DFU_CMD_READ, ui16StartAddr set to +// the 1KB block start address and ui32Length set to the number of bytes to +// read. The host should then send one or more USBD_DFU_REQUEST_UPLOAD +// requests to receive the current flash contents from the configured +// addresses. Data returned will include an 8 byte DFU_CMD_PROG prefix +// structure unless the prefix has been disabled by sending a DFU_CMD_BIN +// command with the bBinary parameter set to 1. +// +// To check that a region of flash is erased, the DFU_CMD_CHECK command should +// be sent with ui16StartAddr and ui32Length set to describe the region to +// check. The host should then send a USBD_DFU_REQUEST_GETSTATUS. If the +// erase check was successful, the returned bStatus value will be STATUS_OK, +// otherwise it will be STATUS_ERR_CHECK_ERASED. Note that ui32Length passed +// must be a multiple of 4. If this is not the case, the value will be +// truncated before the check is performed. +// +//***************************************************************************** +typedef struct +{ + // + // DFU_CMD_READ or DFU_CMD_CHECK + // + uint8_t ui8Command; + + // + // Reserved - write to 0 + // + uint8_t ui8Reserved; + + // + // Block start address / 1024 + // + uint16_t ui16StartAddr; + + // + // The number of bytes of data to read back or check. + // + uint32_t ui32Length; +} +PACKED tDFUDownloadReadCheckHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_ERASE command. +// +// This command may be used to erase a number of flash blocks. The address of +// the first block to be erased is passed in ui16StartAddr with ui16NumBlocks +// containing the number of blocks to be erased from this address. The block +// size of the device may be determined using the DFU_CMD_INFO command. +// +//***************************************************************************** +typedef struct +{ + // + // DFU_CMD_ERASE + // + uint8_t ui8Command; + + // + // Reserved - set to 0. + // + uint8_t ui8Reserved; + + // + // Block start address / 1024 + // + uint16_t ui16StartAddr; + + // + // The number of blocks to erase. + // + uint16_t ui16NumBlocks; + + // + // Reserved - set to 0. + // + uint8_t pui8Reserved2[2]; +} +PACKED tDFUDownloadEraseHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_INFO command. +// +// This command may be used to query information about the connected device. +// After sending the command, the information is returned on the next +// USBD_DFU_REQUEST_UPLOAD request. +// +//***************************************************************************** +typedef struct +{ + // + // DFU_CMD_INFO + // + uint8_t ui8Command; + + // + // Reserved - set to 0. + // + uint8_t pui8Reserved[7]; +} +PACKED tDFUDownloadInfoHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_BIN command. +// +// This command may be used to set the format of uploaded data. By default, +// images read using USBD_DFU_REQUEST_UPLOAD are formatted with the appropriate +// header to allow the same image to be flashed back to the device and have it +// located at the address from which it originated. This is a requirement of +// the DFU class specification (section 6.2 "the uploaded image must be +// usable in a subsequent download") but may not be helpful in some cases where +// the application wishes to receive only the binary image from flash. To +// instruct the DFU device to omit the position and size header, send this +// command with the bBinary field set to \b true prior to issuing a +// USBD_DFU_REQUEST_UPLOAD for image data. The format choice remains in effect +// until the command is sent once again with bBinary set to \b false. +// +// Note that the format choice affects only image data sent and not responses +// read via USBD_DFU_REQUEST_UPLOAD following USBLib-specific commands such +// as DFU_CMD_INFO. +// +//***************************************************************************** +typedef struct +{ + // + // DFU_CMD_BIN + // + uint8_t ui8Command; + + // + // Set to true to omit image header or false to include it (the default). + // + uint8_t ui8Binary; + + // + // Reserved - set to 0. + // + uint8_t pui8Reserved[6]; +} +PACKED tDFUDownloadBinHeader; + +//***************************************************************************** +// +// The DFU_CMD_RESET command uses a tDFUDownloadHeader structure since +// only the ui8Command field is important. This command causes an immediate +// reset of the the target board. +// +//***************************************************************************** + +//***************************************************************************** +// +//! Payload returned in response to the DFU_CMD_INFO command. +//! +//! This is structure is returned in response to the first +//! USBD_DFU_REQUEST_UPLOAD request following a DFU_CMD_INFO command. +// +//***************************************************************************** +typedef struct +{ + // + //! The size of a flash block in bytes. + // + uint16_t ui16FlashBlockSize; + + // + //! The number of blocks of flash in the device. Total flash size is + //! ui16NumFlashBlocks * ui16FlashBlockSize. + // + uint16_t ui16NumFlashBlocks; + + // + //! Information on the part number, family, version and package as + //! read from SYSCTL register DID1. + // + uint32_t ui32PartInfo; + + // + //! Information on the part class and revision as read from SYSCTL DID0. + // + uint32_t ui32ClassInfo; + + // + //! Address 1 byte above the highest location the boot loader can access. + // + uint32_t ui32FlashTop; + + // + //! Lowest address the boot loader can write or erase. + // + uint32_t ui32AppStartAddr; +} +PACKED tDFUDeviceInfo; + +#ifdef ewarm +#pragma pack() +#endif + +#endif // __USBDFU_H__ diff --git a/usblib/usbdma.c b/usblib/usbdma.c new file mode 100644 index 0000000..4ae9e2b --- /dev/null +++ b/usblib/usbdma.c @@ -0,0 +1,1453 @@ +//***************************************************************************** +// +// usbdma.c - USB Library DMA handling functions. +// +// Copyright (c) 2012-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_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_ints.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_udma.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/rtos_bindings.h" +#include "driverlib/usb.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/udma.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup usblib_dma_api Internal USB DMA functions +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// There are two sets of functions in this file, one is used with uDMA while +// the other is used with USB controllers with an integrated DMA controller. +// The functions with the IDMA prefix are for the integrated DMA controller and +// the functions that are specific to the uDMA controller are prefixed with +// uDMA. Any common functions are have just the DMA prefix. +// +//***************************************************************************** +static tUSBDMAInstance g_psUSBDMAInst[1]; + +//***************************************************************************** +// +// Macros used to determine if a uDMA endpoint configuration is used for +// receive or transmit. +// +//***************************************************************************** +#define UDMAConfigIsRx(ui32Config) \ + ((ui32Config & UDMA_SRC_INC_NONE) == UDMA_SRC_INC_NONE) +#define UDMAConfigIsTx(ui32Config) \ + ((ui32Config & UDMA_DEST_INC_NONE) == UDMA_DEST_INC_NONE) + +//***************************************************************************** +// +// USBLibDMAChannelStatus() for USB controllers that use the uDMA for DMA. +// +//***************************************************************************** +static uint32_t +uDMAUSBChannelStatus(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + uint32_t ui32Status; + + // + // Initialize the current status to no events. + // + ui32Status = USBLIBSTATUS_DMA_IDLE; + + // + // Check if there is a pending DMA transfer. + // + if(psUSBDMAInst->ui32Complete & (1 << (ui32Channel - 1))) + { + // + // Return that the DMA transfer has completed and clear the + // DMA pending flag. + // + ui32Status = USBLIBSTATUS_DMA_COMPLETE; + } + else if(psUSBDMAInst->ui32Pending & (1 << (ui32Channel - 1))) + { + // + // DMA transfer is still pending. + // + ui32Status = USBLIBSTATUS_DMA_PENDING; + } + else + { + // + // DMA transfer is still pending. + // + ui32Status = USBLIBSTATUS_DMA_IDLE; + } + + return(ui32Status); +} + +//***************************************************************************** +// +// USBLibDMAChannelStatus() for USB controllers with an integrated DMA +// controller. +// +//***************************************************************************** +static uint32_t +iDMAUSBChannelStatus(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + uint32_t ui32Status; + + // + // Initialize the current status to no events. + // + ui32Status = USBLIBSTATUS_DMA_IDLE; + + // + // Check if an error has occurred. + // + if(USBDMAChannelStatus(psUSBDMAInst->ui32Base, ui32Channel) == + USB_DMA_STATUS_ERROR) + { + ui32Status = USBLIBSTATUS_DMA_ERROR; + } + // + // Otherwise check if there a pending DMA transfer has completed. + // + else if(psUSBDMAInst->ui32Complete & (1 << (ui32Channel - 1))) + { + // + // Return that the DMA transfer has completed and clear the + // DMA pending flag. + // + ui32Status = USBLIBSTATUS_DMA_COMPLETE; + } + else if(psUSBDMAInst->ui32Pending & (1 << (ui32Channel - 1))) + { + // + // DMA transfer is still pending. + // + ui32Status = USBLIBSTATUS_DMA_PENDING; + } + else + { + // + // DMA Channel is idle. + // + ui32Status = USBLIBSTATUS_DMA_IDLE; + } + + return(ui32Status); +} + +//***************************************************************************** +// +// USBLibDMAIntStatus() for USB controllers that use uDMA. +// +//***************************************************************************** +static uint32_t +uDMAUSBIntStatus(tUSBDMAInstance *psUSBDMAInst) +{ + uint32_t ui32Status, ui32Pending; + int32_t i32Channel; + + // + // Initialize the current status to no events. + // + ui32Status = 0; + + // + // No pending interrupts by default. + // + ui32Status = 0; + + // + // Save the pending channels. + // + ui32Pending = psUSBDMAInst->ui32Pending; + + // + // Loop through channels to find out if any pending DMA transfers have + // completed. + // + for(i32Channel = 0; i32Channel < USB_MAX_DMA_CHANNELS; i32Channel++) + { + // + // If pending and stopped then the DMA completed. + // + if((ui32Pending & 1) && + (MAP_uDMAChannelModeGet(i32Channel) == UDMA_MODE_STOP)) + { + ui32Status |= (1 << i32Channel); + } + ui32Pending >>= 1; + + // + // Done if this is zero. + // + if(ui32Pending == 0) + { + break; + } + } + + return(ui32Status); +} + +//***************************************************************************** +// +// USBLibDMAIntStatus() for USB controllers with an integrated DMA controller. +// +//***************************************************************************** +static uint32_t +iDMAUSBIntStatus(tUSBDMAInstance *psUSBDMAInst) +{ + // + // Read the current DMA status, unfortunately this clears the + // pending interrupt status. + // + return(USBDMAChannelIntStatus(psUSBDMAInst->ui32Base)); +} + +//***************************************************************************** +// +// USBLibDMAIntStatusClear() for USB controllers that use uDMA for DMA or have +// an integrated DMA controller. +// +//***************************************************************************** +static void +DMAUSBIntStatusClear(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Status) +{ + // + // Clear out the requested interrupts. Since the USB interface does not + // have a true interrupt clear, this clears the current completed + // status for the requested channels. + // + psUSBDMAInst->ui32Complete &= ~ui32Status; + + return; +} + +//***************************************************************************** +// +// USBLibDMAIntHandler() for USB controllers that use uDMA for DMA or have an +// integrated DMA controller. +// +//***************************************************************************** +static void +DMAUSBIntHandler(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32DMAIntStatus) +{ + uint32_t ui32Channel; + + if(ui32DMAIntStatus == 0) + { + return; + } + + // + // Determine if the uDMA is used or the USB DMA controller. + // + for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS; ui32Channel++) + { + // + // Mark any pending interrupts as completed. + // + if(ui32DMAIntStatus & 1) + { + psUSBDMAInst->ui32Pending &= ~(1 << ui32Channel); + psUSBDMAInst->ui32Complete |= (1 << ui32Channel); + } + + // + // Check the next channel. + // + ui32DMAIntStatus >>= 1; + + // + // Break if there are no more pending DMA interrupts. + // + if(ui32DMAIntStatus == 0) + { + break; + } + } +} + +//***************************************************************************** +// +// USBLibDMAChannelEnable() for USB controllers that use uDMA. +// +//***************************************************************************** +static void +uDMAUSBChannelEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + uint32_t ui32IntEnabled; + + // + // Save if the interrupt was enabled or not. + // + ui32IntEnabled = IntIsEnabled(psUSBDMAInst->ui32IntNum); + + // + // Disable the USB interrupt if it was enabled. + // + if(ui32IntEnabled) + { + OS_INT_DISABLE(psUSBDMAInst->ui32IntNum); + } + + // + // Mark this channel as pending and not complete. + // + psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); + + // + // Enable DMA for the endpoint. + // + if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1])) + { + MAP_USBEndpointDMAEnable(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_DEV_OUT | USB_EP_HOST_IN); + } + else + { + MAP_USBEndpointDMAEnable(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_DEV_IN | USB_EP_HOST_OUT); + } + + // + // Enable the DMA in the uDMA controller. + // + MAP_uDMAChannelEnable(ui32Channel - 1); + + // + // Enable the USB interrupt if it was enabled before. + // + if(ui32IntEnabled) + { + OS_INT_ENABLE(psUSBDMAInst->ui32IntNum); + } +} + +//***************************************************************************** +// +// USBLibDMAChannelEnable() for USB controllers with an integrated DMA +// controller. +// +//***************************************************************************** +static void +iDMAUSBChannelEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + uint32_t ui32IntEnabled; + + // + // Save if the interrupt was enabled or not. + // + ui32IntEnabled = IntIsEnabled(psUSBDMAInst->ui32IntNum); + + // + // Disable the USB interrupt if it was enabled. + // + if(ui32IntEnabled) + { + OS_INT_DISABLE(psUSBDMAInst->ui32IntNum); + } + + // + // Mark this channel as pending and not complete. + // + psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); + + // + // Enable the interrupt for this DMA channel. + // + USBDMAChannelIntEnable(psUSBDMAInst->ui32Base, ui32Channel - 1); + + // + // Enable the DMA channel. + // + USBDMAChannelEnable(psUSBDMAInst->ui32Base, ui32Channel - 1); + + // + // Enable the USB interrupt if it was enabled before. + // + if(ui32IntEnabled) + { + OS_INT_ENABLE(psUSBDMAInst->ui32IntNum); + } +} + +//***************************************************************************** +// +// USBLibDMAChannelDisable() for USB controllers that use uDMA. +// +//***************************************************************************** +static void +uDMAUSBChannelDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + // + // Disable DMA for the endpoint. + // + if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1])) + { + MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_DEV_OUT); + } + else + { + MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_DEV_IN); + } + + // + // Disable the DMA channel in the uDMA controller. + // + MAP_uDMAChannelDisable(ui32Channel - 1); + + // + // Clear out any pending or complete flag set for this DMA channel. + // + psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); +} + +//***************************************************************************** +// +// USBLibDMAChannelDisable() for USB controllers with an integrated DMA +// controller. +// +//***************************************************************************** +static void +iDMAUSBChannelDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + // + // Disable the DMA channel. + // + USBDMAChannelDisable(psUSBDMAInst->ui32Base, ui32Channel - 1); + + // + // Disable the interrupt for this DMA channel. + // + USBDMAChannelIntDisable(psUSBDMAInst->ui32Base, ui32Channel - 1); + + // + // Clear out any pending or complete flag set for this DMA channel. + // + psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); +} + +//***************************************************************************** +// +// USBLibDMAChannelIntEnable() for USB controllers that use uDMA. +// +//***************************************************************************** +static void +uDMAUSBChannelIntEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + // + // There is no way to Enable channel interrupts when using uDMA. + // +} + +//***************************************************************************** +// +// USBLibDMAChannelIntEnable() for USB controllers with an integrated DMA +// controller. +// +//***************************************************************************** +static void +iDMAUSBChannelIntEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + // + // Enable the interrupt for this DMA channel. + // + USBDMAChannelIntEnable(psUSBDMAInst->ui32Base, ui32Channel - 1); +} + +//***************************************************************************** +// +// USBLibDMAChannelIntDisable() for USB controllers that use uDMA. +// +//***************************************************************************** +static void +uDMAUSBChannelIntDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + // + // There is no way to Disable channel interrupts when using uDMA. + // +} + +//***************************************************************************** +// +// USBLibDMAChannelIntDisable() for USB controllers with an integrated DMA +// controller. +// +//***************************************************************************** +static void +iDMAUSBChannelIntDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + // + // Disable the interrupt for this DMA channel. + // + USBDMAChannelIntDisable(psUSBDMAInst->ui32Base, ui32Channel - 1); +} + +//***************************************************************************** +// +// USBLibDMATransfer() for USB controllers that use the uDMA controller. +// +//***************************************************************************** +static uint32_t +uDMAUSBTransfer(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel, + void *pvBuffer, uint32_t ui32Size) +{ + void *pvFIFO; + uint32_t uluDMAChannel; + uint32_t ui32PacketCount; + uint32_t ui32TransferCount; + + if((ui32Size < 64) || ((uint32_t)pvBuffer & 0x3)) + { + return(0); + } + + // + // Mark this channel as pending and not complete. + // + psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); + + // + // Save the pointer to the data and the byte count. + // + psUSBDMAInst->ppui32Data[ui32Channel - 1] = pvBuffer; + psUSBDMAInst->pui32Count[ui32Channel - 1] = ui32Size; + + // + // Need the address of the FIFO. + // + pvFIFO = (void *)USBFIFOAddrGet(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1]); + + // + // Calculate the uDMA channel for this RX channel. + // + uluDMAChannel = UDMA_CHANNEL_USBEP1RX + ui32Channel - 1; + + ui32TransferCount = ui32Size; + + if((psUSBDMAInst->pui32Config[ui32Channel - 1] & UDMA_SIZE_32) == + UDMA_SIZE_32) + { + ui32TransferCount >>= 2; + } + else if((psUSBDMAInst->pui32Config[ui32Channel - 1] & UDMA_SIZE_32) == + UDMA_SIZE_32) + { + ui32TransferCount >>= 1; + } + + // + // If source increment is none this is an RX transfer. + // + if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1])) + { + MAP_uDMAChannelTransferSet(uluDMAChannel, UDMA_MODE_BASIC, pvFIFO, + pvBuffer, ui32TransferCount); + } + else + { + MAP_uDMAChannelTransferSet(uluDMAChannel, UDMA_MODE_BASIC, pvBuffer, + pvFIFO, ui32TransferCount); + } + + // + // Set the mode based on the size of the transfer. More than one + // packet requires mode 1. + // + if(ui32Size > psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1]) + { + // + // Calculate the number of packets required for this transfer. + // + ui32PacketCount = ((ui32Size / + psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1])); + + // + // Set the packet count so that the last packet does not generate + // another IN request. + // + USBEndpointPacketCountSet(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + ui32PacketCount); + + // + // Configure the USB endpoint in mode 1 for this DMA transfer. + // + USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + psUSBDMAInst->pui32EPDMAMode1[ui32Channel - 1]); + } + else + { + // + // Configure the USB endpoint in mode 0 for this DMA transfer. + // + USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + psUSBDMAInst->pui32EPDMAMode0[ui32Channel -1]); + } + + // + // Enable the uDMA channel to start the transfer + // + uDMAUSBChannelEnable(psUSBDMAInst, ui32Channel); + + return(ui32Size); +} + +//***************************************************************************** +// +// USBLibDMATransfer() for USB controllers with an integrated DMA controller. +// +//***************************************************************************** +static uint32_t +iDMAUSBTransfer(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel, + void *pvBuffer, uint32_t ui32Size) +{ + uint32_t ui32PacketCount; + + if((uint32_t)pvBuffer & 0x3) + { + return(0); + } + + // + // Mark this channel as pending and not complete. + // + psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); + + // + // Save the pointer to the data and the byte count. + // + psUSBDMAInst->ppui32Data[ui32Channel - 1] = pvBuffer; + psUSBDMAInst->pui32Count[ui32Channel - 1] = ui32Size; + + // + // Set the address. + // + USBDMAChannelAddressSet(psUSBDMAInst->ui32Base, ui32Channel - 1, pvBuffer); + + // + // Set the number of transfers. + // + USBDMAChannelCountSet(psUSBDMAInst->ui32Base, ui32Channel - 1, ui32Size); + + // + // Set the mode based on the size of the transfer. More than one + // packet requires mode 1. + // + if(ui32Size > psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1]) + { + // + // Calculate the number of packets required for this transfer. + // + ui32PacketCount = ui32Size / + psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1]; + + if(ui32Size % psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1]) + { + ui32PacketCount += 1; + } + + USBEndpointPacketCountSet(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + ui32PacketCount); + + // + // Configure the USB DMA controller for mode 1. + // + USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + psUSBDMAInst->pui32EPDMAMode1[ui32Channel - 1]); + + USBDMAChannelConfigSet(psUSBDMAInst->ui32Base, ui32Channel - 1, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + psUSBDMAInst->pui32Config[ui32Channel - 1] | + USB_DMA_CFG_MODE_1); + + // + // Enable DMA on the endpoint. + // + if(psUSBDMAInst->pui32Config[ui32Channel - 1] & USB_DMA_CFG_DIR_TX) + { + // + // Make sure that DMA is enabled on the endpoint. + // + MAP_USBEndpointDMAEnable( + psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_HOST_OUT); + } + else + { + // + // Make sure that DMA is enabled on the endpoint. + // + MAP_USBEndpointDMAEnable( + psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_HOST_IN); + } + + // + // Enable the DMA channel. + // + USBDMAChannelEnable(psUSBDMAInst->ui32Base, ui32Channel - 1); + } + else + { + // + // Configure the USB DMA controller for mode 0. + // + USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + psUSBDMAInst->pui32EPDMAMode0[ui32Channel -1]); + + USBDMAChannelConfigSet(psUSBDMAInst->ui32Base, ui32Channel -1, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + psUSBDMAInst->pui32Config[ui32Channel - 1] | + USB_DMA_CFG_MODE_0); + + // + // In mode 0 only enable DMA transfer for mode 0. + // + if(psUSBDMAInst->pui32Config[ui32Channel - 1] & USB_DMA_CFG_DIR_TX) + { + // + // Make sure that DMA is enabled on the endpoint. + // + MAP_USBEndpointDMAEnable( + psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_HOST_OUT); + // + // Enable the DMA channel. + // + USBDMAChannelEnable(psUSBDMAInst->ui32Base, ui32Channel - 1); + } + else + { + // + // Make sure that DMA is disabled on the endpoint, it will + // be enabled when the endpoint interrupt occurs. + // + MAP_USBEndpointDMADisable( + psUSBDMAInst->ui32Base, + psUSBDMAInst->pui8Endpoint[ui32Channel - 1], + USB_EP_HOST_IN); + + } + } + + return(ui32Size); +} + +//***************************************************************************** +// +// USBLibDMAChannelAllocate() for USB controllers that use uDMA for DMA. +// +//***************************************************************************** +static uint32_t +uDMAUSBChannelAllocate(tUSBDMAInstance *psUSBDMAInst, uint8_t ui8Endpoint, + uint32_t ui32MaxPacketSize, uint32_t ui32Config) +{ + uint32_t ui32Channel; + + // + // The DMA channels are organized in pairs on this controller and the + // transmit channels are 1, 3, and 5 while receive are 0, 2, and 4. + // + if(ui32Config & USB_DMA_EP_RX) + { + ui32Channel = 0; + } + else + { + ui32Channel = 1; + } + + // + // Search for an available DMA channel to use. + // + for(; ui32Channel < USB_MAX_DMA_CHANNELS_0; ui32Channel += 2) + { + // + // If the current endpoint value is zero then this channel is + // available. + // + if(psUSBDMAInst->pui8Endpoint[ui32Channel] == 0) + { + // + // Save the endpoint for this DMA channel. + // + psUSBDMAInst->pui8Endpoint[ui32Channel] = ui8Endpoint; + + // + // Save the maximum packet size for the endpoint. + // + psUSBDMAInst->pui32MaxPacketSize[ui32Channel] = ui32MaxPacketSize; + + // + // Set the channel configuration based on the direction. + // + if(ui32Config & USB_DMA_EP_RX) + { + psUSBDMAInst->pui32Config[ui32Channel] = + UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | + UDMA_ARB_64; + + // + // If in device mode and Isochronous. + // + if(((ui32Config & USB_DMA_EP_HOST) == 0) && + ((ui32Config & USB_DMA_EP_TYPE_M) == USB_DMA_EP_TYPE_ISOC)) + { + // + // USB_EP_AUTO_REQUEST is required for device + // Isochronous endpoints. + // + psUSBDMAInst->pui32EPDMAMode0[ui32Channel] = + USB_EP_DMA_MODE_0 | + USB_EP_AUTO_REQUEST | + USB_EP_HOST_IN; + } + else + { + psUSBDMAInst->pui32EPDMAMode0[ui32Channel] = + USB_EP_DMA_MODE_0 | + USB_EP_AUTO_CLEAR | + USB_EP_HOST_IN; + } + + // + // Do not set auto request in device mode unless it is an + // isochronous endpoint. + // + if(((ui32Config & USB_DMA_EP_HOST) == 0) && + ((ui32Config & USB_DMA_EP_TYPE_M) != USB_DMA_EP_TYPE_ISOC)) + { + psUSBDMAInst->pui32EPDMAMode1[ui32Channel] = + USB_EP_DMA_MODE_1 | + USB_EP_HOST_IN | + USB_EP_AUTO_CLEAR; + } + else + { + psUSBDMAInst->pui32EPDMAMode1[ui32Channel] = + USB_EP_DMA_MODE_1 | + USB_EP_HOST_IN | + USB_EP_AUTO_REQUEST | + USB_EP_AUTO_CLEAR; + } + } + else + { + psUSBDMAInst->pui32Config[ui32Channel] = + UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | + UDMA_ARB_64; + + psUSBDMAInst->pui32EPDMAMode0[ui32Channel] = USB_EP_DMA_MODE_0 | + USB_EP_HOST_OUT; + psUSBDMAInst->pui32EPDMAMode1[ui32Channel] = USB_EP_DMA_MODE_1 | + USB_EP_HOST_OUT | + USB_EP_AUTO_SET; + } + + // + // Map the uDMA channel to the given endpoint. + // + MAP_USBEndpointDMAChannel(psUSBDMAInst->ui32Base, ui8Endpoint, + ui32Channel); + + // + // Clear out the attributes on this channel. + // + MAP_uDMAChannelAttributeDisable(ui32Channel, UDMA_ATTR_ALL); + + // + // Configure the uDMA channel for the pipe + // + MAP_uDMAChannelControlSet(ui32Channel, + psUSBDMAInst->pui32Config[ui32Channel]); + + if(ui32Config & USB_DMA_EP_RX) + { + MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, ui8Endpoint, + USB_EP_DEV_OUT); + } + else + { + MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, ui8Endpoint, + USB_EP_DEV_IN); + } + + // + // Outside of this function all channels are 1 based as + // zero is not a valid channel. + // + return(ui32Channel + 1); + } + } + return(0); +} + +//***************************************************************************** +// +// USBLibDMAChannelAllocate() for USB controllers with an integrated DMA +// controller. +// +//***************************************************************************** +static uint32_t +iDMAUSBChannelAllocate(tUSBDMAInstance *psUSBDMAInst, uint8_t ui8Endpoint, + uint32_t ui32MaxPacketSize, uint32_t ui32Config) +{ + uint32_t ui32Channel; + + // + // Search for an available DMA channel to use. + // + for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS_0; ui32Channel++) + { + // + // If the current endpoint value is zero then this channel is + // available. + // + if(psUSBDMAInst->pui8Endpoint[ui32Channel] == 0) + { + // + // Clear out the attributes on this channel. + // + USBDMAChannelDisable(psUSBDMAInst->ui32Base, ui32Channel); + + // + // Save the endpoint for this DMA channel. + // + psUSBDMAInst->pui8Endpoint[ui32Channel] = ui8Endpoint; + + // + // Save the maximum packet size for the endpoint. + // + psUSBDMAInst->pui32MaxPacketSize[ui32Channel] = ui32MaxPacketSize; + + // + // Assign the endpoint to the channel and set the direction. + // + if(ui32Config & USB_DMA_EP_RX) + { + psUSBDMAInst->pui32Config[ui32Channel] = + USB_DMA_CFG_DIR_RX | + USB_DMA_CFG_BURST_NONE | + USB_DMA_CFG_INT_EN; + + // + // If in device mode and Isochronous. + // + if(((ui32Config & USB_DMA_EP_HOST) == 0) && + ((ui32Config & USB_DMA_EP_TYPE_M) == USB_DMA_EP_TYPE_ISOC)) + { + // + // USB_EP_AUTO_REQUEST is required for device + // Isochronous endpoints. + // + psUSBDMAInst->pui32EPDMAMode0[ui32Channel] = + USB_EP_DMA_MODE_0 | + USB_EP_AUTO_REQUEST | + USB_EP_HOST_IN; + } + else + { + psUSBDMAInst->pui32EPDMAMode0[ui32Channel] = + USB_EP_DMA_MODE_0 | + USB_EP_AUTO_CLEAR | + USB_EP_HOST_IN; + } + + // + // Do not set auto request in device mode unless it is an + // isochronous endpoint. + // + if(((ui32Config & USB_DMA_EP_HOST) == 0) && + ((ui32Config & USB_DMA_EP_TYPE_M) != USB_DMA_EP_TYPE_ISOC)) + { + psUSBDMAInst->pui32EPDMAMode1[ui32Channel] = + USB_EP_DMA_MODE_1 | + USB_EP_HOST_IN | + USB_EP_AUTO_CLEAR; + } + else + { + psUSBDMAInst->pui32EPDMAMode1[ui32Channel] = + USB_EP_DMA_MODE_1 | + USB_EP_HOST_IN | + USB_EP_AUTO_REQUEST | + USB_EP_AUTO_CLEAR; + } + } + else + { + psUSBDMAInst->pui32Config[ui32Channel] = + USB_DMA_CFG_DIR_TX | + USB_DMA_CFG_BURST_NONE | + USB_DMA_CFG_INT_EN; + + psUSBDMAInst->pui32EPDMAMode0[ui32Channel] = + USB_EP_DMA_MODE_0 | + USB_EP_HOST_OUT; + psUSBDMAInst->pui32EPDMAMode1[ui32Channel] = + USB_EP_DMA_MODE_1 | + USB_EP_HOST_OUT | + USB_EP_AUTO_SET; + } + + // + // Outside of this function all channels are 1 based as + // zero is not a valid channel. + // + return(ui32Channel + 1); + } + } + return(0); +} + +//***************************************************************************** +// +// USBLibDMAChannelRelease() for USB controllers that use uDMA for DMA. +// +//***************************************************************************** +static void +uDMAUSBChannelRelease(tUSBDMAInstance *psUSBDMAInst, uint8_t ui32Channel) +{ + ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS_0); + + // + // Clear out the attributes on this channel. + // + MAP_uDMAChannelAttributeDisable(ui32Channel - 1, UDMA_ATTR_ALL); + + if(psUSBDMAInst->pui8Endpoint[ui32Channel] & USB_DMA_EP_RX) + { + MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, + (psUSBDMAInst->pui8Endpoint[ui32Channel] & ~USB_DMA_EP_RX), + USB_EP_DEV_OUT); + } + else + { + MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, + (psUSBDMAInst->pui8Endpoint[ui32Channel] & ~USB_DMA_EP_RX), + USB_EP_DEV_IN); + } + + // + // Clear out the state for this endpoint. + // + psUSBDMAInst->pui8Endpoint[ui32Channel - 1] = 0; + psUSBDMAInst->pui32Config[ui32Channel - 1] = 0; + psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); + psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1] = 0; +} + +//***************************************************************************** +// +// DMAChannelRelease() for USB controllers with an integrated DMA controller. +// +//***************************************************************************** +static void +iDMAUSBChannelRelease(tUSBDMAInstance *psUSBDMAInst, uint8_t ui32Channel) +{ + ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS); + + // + // Clear out the attributes on this channel. + // + USBDMAChannelDisable(psUSBDMAInst->ui32Base, ui32Channel); + + // + // Clear out the state for this endpoint. + // + psUSBDMAInst->pui8Endpoint[ui32Channel - 1] = 0; + psUSBDMAInst->pui32Config[ui32Channel - 1] = 0; + psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1)); + psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1)); + psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1] = 0; +} + +//***************************************************************************** +// +// USBLibDMAUnitSizeSet() for USB controllers that use uDMA for DMA. +// +//***************************************************************************** +static void +uDMAUSBUnitSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel, + uint32_t ui32BitSize) +{ + uint32_t ui32Value; + + ASSERT((ui32BitSize == 8) || (ui32BitSize == 16) || (ui32BitSize == 32)); + + ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS_0); + + if(ui32BitSize == 8) + { + ui32Value = UDMA_SIZE_8; + + if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1])) + { + // + // Receive increments destination and not source. + // + ui32Value |= UDMA_DST_INC_8 | UDMA_SRC_INC_NONE; + } + else + { + // + // Transmit increments source and not destination. + // + ui32Value |= UDMA_SRC_INC_8 | UDMA_DST_INC_NONE; + } + } + else if(ui32BitSize == 16) + { + ui32Value = UDMA_SIZE_16; + + if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1])) + { + // + // Receive increments destination and not source. + // + ui32Value |= UDMA_DST_INC_16 | UDMA_SRC_INC_NONE; + } + else + { + // + // Transmit increments source and not destination. + // + ui32Value |= UDMA_SRC_INC_16 | UDMA_DST_INC_NONE; + } + } + else + { + ui32Value = UDMA_SIZE_32; + + if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1])) + { + // + // Receive increments destination and not source. + // + ui32Value |= (UDMA_DST_INC_32 | UDMA_SRC_INC_NONE); + } + else + { + // + // Transmit increments source and not destination. + // + ui32Value |= (UDMA_SRC_INC_32 | UDMA_DST_INC_NONE); + } + } + + // + // Keep the current arbitration size and or in the size. + // + psUSBDMAInst->pui32Config[ui32Channel - 1] &= 0x00ffffff; + psUSBDMAInst->pui32Config[ui32Channel - 1] |= ui32Value; + MAP_uDMAChannelControlSet(ui32Channel - 1, + psUSBDMAInst->pui32Config[ui32Channel - 1]); +} + +//***************************************************************************** +// +// USBLibDMAUnitSizeSet() for USB controllers that have an integrated DMA +// controller. +// +//***************************************************************************** +static void +iDMAUSBUnitSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel, + uint32_t ui32BitSize) +{ +} + +//***************************************************************************** +// +// USBLibDMAArbSizeSet() for USB controllers that use uDMA for DMA. +// +//***************************************************************************** +static void +uDMAUSBArbSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel, + uint32_t ui32ArbSize) +{ + uint32_t ui32Value; + + ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS_0); + + // + // Get the arbitration size value. + // + if(ui32ArbSize == 2) + { + ui32Value = UDMA_ARB_2; + } + else if(ui32ArbSize == 4) + { + ui32Value = UDMA_ARB_4; + } + else if(ui32ArbSize == 8) + { + ui32Value = UDMA_ARB_8; + } + else if(ui32ArbSize == 16) + { + ui32Value = UDMA_ARB_16; + } + else if(ui32ArbSize == 32) + { + ui32Value = UDMA_ARB_32; + } + else if(ui32ArbSize == 64) + { + ui32Value = UDMA_ARB_64; + } + else if(ui32ArbSize == 128) + { + ui32Value = UDMA_ARB_128; + } + else if(ui32ArbSize == 256) + { + ui32Value = UDMA_ARB_256; + } + else + { + // + // Default to arbitration size of 1. + // + ui32Value = UDMA_ARB_1; + } + + // + // Keep the current size and or in the new arbitration size. + // + psUSBDMAInst->pui32Config[ui32Channel - 1] &= 0xff000000; + psUSBDMAInst->pui32Config[ui32Channel - 1] |= ui32Value; + + // + // Set the uDMA channel control, remember its channel starts at 0 and + // not 1. + // + MAP_uDMAChannelControlSet(ui32Channel - 1, + psUSBDMAInst->pui32Config[ui32Channel - 1]); +} + +//***************************************************************************** +// +// USBLibDMAArbSizeSet() for USB controllers that have an integrated DMA +// controller. +// +//***************************************************************************** +static void +iDMAUSBArbSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel, + uint32_t ui32ArbSize) +{ +} + +//***************************************************************************** +// +// USBLibDMAStatus() for USB controllers that use uDMA for DMA or have an +// integrated DMA controller. +// +//***************************************************************************** +static uint32_t +DMAUSBStatus(tUSBDMAInstance *psUSBDMAInst) +{ + return(0); +} + +//***************************************************************************** +// +//! This function is used to return the current DMA pointer for a given +//! DMA channel. +//! +//! \param psUSBDMAInst is a generic instance pointer that can be used to +//! distinguish between different hardware instances. +//! \param ui32Channel is the DMA channel number for this request. +//! +//! This function returns the address that is in use by the DMA channel passed +//! in via the \e ui32Channel parameter. This is not the real-time pointer, +//! but the starting address of the DMA transfer for this DMA channel. +//! +//! \return The current DMA address for the given DMA channel. +// +//***************************************************************************** +void * +USBLibDMAAddrGet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + return(psUSBDMAInst->ppui32Data[ui32Channel - 1]); +} + +//***************************************************************************** +// +//! This function is used to return the current DMA transfer size for a given +//! DMA channel. +//! +//! \param psUSBDMAInst is a generic instance pointer that can be used to +//! distinguish between different hardware instances. +//! \param ui32Channel is the DMA channel number for this request. +//! +//! This function returns the DMA transfer size that is in use by the DMA +//! channel passed in via the \e ui32Channel parameter. +//! +//! \return The current DMA transfer size for the given DMA channel. +// +//***************************************************************************** +uint32_t +USBLibDMASizeGet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel) +{ + return(psUSBDMAInst->pui32Count[ui32Channel - 1]); +} + +//***************************************************************************** +// +//! This function is used to initialize the DMA interface for a USB instance. +//! +//! \param ui32Index is the index of the USB controller for this instance. +//! +//! This function performs any initialization and configuration of the DMA +//! portions of the USB controller. This function returns a pointer that +//! is used with the remaining USBLibDMA APIs or the function returns zero +//! if the requested controller cannot support DMA. If this function is called +//! when already initialized it will not reinitialize the DMA controller and +//! will instead return the previously initialized DMA instance. +//! +//! \return A pointer to use with USBLibDMA APIs. +// +//***************************************************************************** +tUSBDMAInstance * +USBLibDMAInit(uint32_t ui32Index) +{ + uint32_t ui32Channel; + + ASSERT(ui32Index == 0); + + // + // Make sure that the DMA has not already been initialized. + // + if(g_psUSBDMAInst[0].ui32Base == USB0_BASE) + { + return(&g_psUSBDMAInst[0]); + } + + // + // Save the base address of the USB controller. + // + g_psUSBDMAInst[0].ui32Base = USB0_BASE; + + // + // Save the interrupt number for the USB controller. + // + g_psUSBDMAInst[0].ui32IntNum = INT_USB0_TM4C123; + + // + // Initialize the function pointers. + // + g_psUSBDMAInst[0].pfnArbSizeSet = uDMAUSBArbSizeSet; + g_psUSBDMAInst[0].pfnChannelAllocate = uDMAUSBChannelAllocate; + g_psUSBDMAInst[0].pfnChannelDisable = uDMAUSBChannelDisable; + g_psUSBDMAInst[0].pfnChannelEnable = uDMAUSBChannelEnable; + g_psUSBDMAInst[0].pfnChannelIntEnable = uDMAUSBChannelIntEnable; + g_psUSBDMAInst[0].pfnChannelIntDisable = uDMAUSBChannelIntDisable; + g_psUSBDMAInst[0].pfnChannelRelease = uDMAUSBChannelRelease; + g_psUSBDMAInst[0].pfnChannelStatus = uDMAUSBChannelStatus; + g_psUSBDMAInst[0].pfnIntHandler = DMAUSBIntHandler; + g_psUSBDMAInst[0].pfnIntStatus = uDMAUSBIntStatus; + g_psUSBDMAInst[0].pfnIntStatusClear = DMAUSBIntStatusClear; + g_psUSBDMAInst[0].pfnStatus = DMAUSBStatus; + g_psUSBDMAInst[0].pfnTransfer = uDMAUSBTransfer; + g_psUSBDMAInst[0].pfnUnitSizeSet = uDMAUSBUnitSizeSet; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_psUSBDMAInst[0].ui32IntNum = INT_USB0_TM4C129; + } + + // + // Initialize the function pointers for the integrated USB DMA controller. + // + if(USBControllerVersion(g_psUSBDMAInst[0].ui32Base) == USB_CONTROLLER_VER_1) + { + g_psUSBDMAInst[0].pfnArbSizeSet = iDMAUSBArbSizeSet; + g_psUSBDMAInst[0].pfnChannelAllocate = iDMAUSBChannelAllocate; + g_psUSBDMAInst[0].pfnChannelStatus = iDMAUSBChannelStatus; + g_psUSBDMAInst[0].pfnIntStatus = iDMAUSBIntStatus; + g_psUSBDMAInst[0].pfnChannelIntEnable = iDMAUSBChannelIntEnable; + g_psUSBDMAInst[0].pfnChannelIntDisable = iDMAUSBChannelIntDisable; + g_psUSBDMAInst[0].pfnTransfer = iDMAUSBTransfer; + g_psUSBDMAInst[0].pfnChannelRelease = iDMAUSBChannelRelease; + g_psUSBDMAInst[0].pfnChannelEnable = iDMAUSBChannelEnable; + g_psUSBDMAInst[0].pfnChannelDisable = iDMAUSBChannelDisable; + g_psUSBDMAInst[0].pfnUnitSizeSet = iDMAUSBUnitSizeSet; + } + + // + // Clear out the endpoint and the current configuration. + // + for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS; ui32Channel++) + { + g_psUSBDMAInst[0].pui8Endpoint[ui32Channel] = 0; + g_psUSBDMAInst[0].pui32Config[ui32Channel] = 0; + g_psUSBDMAInst[0].ui32Pending = 0; + g_psUSBDMAInst[0].ui32Complete = 0; + } + return(&g_psUSBDMAInst[0]); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbhid.h b/usblib/usbhid.h new file mode 100644 index 0000000..b89a24f --- /dev/null +++ b/usblib/usbhid.h @@ -0,0 +1,667 @@ +//***************************************************************************** +// +// usbhid.h - Definitions used by HID class devices and hosts. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHID_H__ +#define __USBHID_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// HID Interface descriptor Subclasses. +// +//***************************************************************************** +#define USB_HID_SCLASS_NONE 0x00 +#define USB_HID_SCLASS_BOOT 0x01 + +//***************************************************************************** +// +// USB Interface descriptor HID protocols. +// +//***************************************************************************** +#define USB_HID_PROTOCOL_NONE 0 +#define USB_HID_PROTOCOL_KEYB 1 +#define USB_HID_PROTOCOL_MOUSE 2 + +//***************************************************************************** +// +// HID Class descriptor types. +// +//***************************************************************************** +#define USB_HID_DTYPE_HID 0x21 +#define USB_HID_DTYPE_REPORT 0x22 +#define USB_HID_DTYPE_PHYSICAL 0x23 + +//***************************************************************************** +// +// HID USB requests. +// +//***************************************************************************** +#define USBREQ_GET_REPORT 0x01 +#define USBREQ_GET_IDLE 0x02 +#define USBREQ_GET_PROTOCOL 0x03 +#define USBREQ_SET_REPORT 0x09 +#define USBREQ_SET_IDLE 0x0a +#define USBREQ_SET_PROTOCOL 0x0b + +//***************************************************************************** +// +// GET_REPORT or SET_REPORT Definitions. +// +//***************************************************************************** +#define USB_HID_REPORT_IN 0x01 +#define USB_HID_REPORT_OUTPUT 0x02 +#define USB_HID_REPORT_FEATURE 0x03 + +//***************************************************************************** +// +// GET_PROTOCOL or SET_PROTOCOL Definitions. +// +//***************************************************************************** +#define USB_HID_PROTOCOL_BOOT 0 +#define USB_HID_PROTOCOL_REPORT 1 + +//***************************************************************************** +// +// Report Values used with the Report macros. +// +//***************************************************************************** +#define USB_HID_GENERIC_DESKTOP 0x01 +#define USB_HID_BUTTONS 0x09 +#define USB_HID_X 0x30 +#define USB_HID_Y 0x31 +#define USB_HID_Z 0x32 +#define USB_HID_RX 0x33 +#define USB_HID_RY 0x34 +#define USB_HID_RZ 0x35 + +#define USB_HID_POINTER 0x01 +#define USB_HID_MOUSE 0x02 +#define USB_HID_JOYSTICK 0x04 +#define USB_HID_GAME_PAD 0x05 +#define USB_HID_KEYBOARD 0x06 + +#define USB_HID_PHYSICAL 0x00 +#define USB_HID_APPLICATION 0x01 +#define USB_HID_LOGICAL 0x02 + +#define USB_HID_USAGE_POINTER 0x0109 +#define USB_HID_USAGE_BUTTONS 0x0509 +#define USB_HID_USAGE_LEDS 0x0508 +#define USB_HID_USAGE_KEYCODES 0x0507 + +//***************************************************************************** +// +// HID mouse button definitions as used in the first byte of the output report +// used in the BIOS mouse protocol. +// +//***************************************************************************** +#define HID_MOUSE_BUTTON_1 0x01 +#define HID_MOUSE_BUTTON_2 0x02 +#define HID_MOUSE_BUTTON_3 0x04 + +//***************************************************************************** +// +// HID Keyboard LED definitions as used in the first byte of the output report +// used in the BIOS keyboard protocol. +// +//***************************************************************************** +#define HID_KEYB_NUM_LOCK 0x01 +#define HID_KEYB_CAPS_LOCK 0x02 +#define HID_KEYB_SCROLL_LOCK 0x04 +#define HID_KEYB_COMPOSE 0x08 +#define HID_KEYB_KANA 0x10 + +//***************************************************************************** +// +// HID Keyboard key modifiers as provided in the first byte of the input report +// used in the BIOS keyboard protocol. +// +//***************************************************************************** +#define HID_KEYB_LEFT_CTRL 0x01 +#define HID_KEYB_LEFT_SHIFT 0x02 +#define HID_KEYB_LEFT_ALT 0x04 +#define HID_KEYB_LEFT_GUI 0x08 +#define HID_KEYB_RIGHT_CTRL 0x10 +#define HID_KEYB_RIGHT_SHIFT 0x20 +#define HID_KEYB_RIGHT_ALT 0x40 +#define HID_KEYB_RIGHT_GUI 0x80 + +//***************************************************************************** +// +// A subset of the HID keyboard usage IDs. +// +//***************************************************************************** +#define HID_KEYB_USAGE_RESERVED 0x00 +#define HID_KEYB_USAGE_ROLLOVER 0x01 +#define HID_KEYB_USAGE_A 0x04 +#define HID_KEYB_USAGE_B 0x05 +#define HID_KEYB_USAGE_C 0x06 +#define HID_KEYB_USAGE_D 0x07 +#define HID_KEYB_USAGE_E 0x08 +#define HID_KEYB_USAGE_F 0x09 +#define HID_KEYB_USAGE_G 0x0A +#define HID_KEYB_USAGE_H 0x0B +#define HID_KEYB_USAGE_I 0x0C +#define HID_KEYB_USAGE_J 0x0D +#define HID_KEYB_USAGE_K 0x0E +#define HID_KEYB_USAGE_L 0x0F +#define HID_KEYB_USAGE_M 0x10 +#define HID_KEYB_USAGE_N 0x11 +#define HID_KEYB_USAGE_O 0x12 +#define HID_KEYB_USAGE_P 0x13 +#define HID_KEYB_USAGE_Q 0x14 +#define HID_KEYB_USAGE_R 0x15 +#define HID_KEYB_USAGE_S 0x16 +#define HID_KEYB_USAGE_T 0x17 +#define HID_KEYB_USAGE_U 0x18 +#define HID_KEYB_USAGE_V 0x19 +#define HID_KEYB_USAGE_W 0x1A +#define HID_KEYB_USAGE_X 0x1B +#define HID_KEYB_USAGE_Y 0x1C +#define HID_KEYB_USAGE_Z 0x1D +#define HID_KEYB_USAGE_1 0x1E +#define HID_KEYB_USAGE_2 0x1F +#define HID_KEYB_USAGE_3 0x20 +#define HID_KEYB_USAGE_4 0x21 +#define HID_KEYB_USAGE_5 0x22 +#define HID_KEYB_USAGE_6 0x23 +#define HID_KEYB_USAGE_7 0x24 +#define HID_KEYB_USAGE_8 0x25 +#define HID_KEYB_USAGE_9 0x26 +#define HID_KEYB_USAGE_0 0x27 +#define HID_KEYB_USAGE_ENTER 0x28 +#define HID_KEYB_USAGE_ESCAPE 0x29 +#define HID_KEYB_USAGE_BACKSPACE \ + 0x2A +#define HID_KEYB_USAGE_TAB 0x2B +#define HID_KEYB_USAGE_SPACE 0x2C +#define HID_KEYB_USAGE_MINUS 0x2D +#define HID_KEYB_USAGE_EQUAL 0x2E +#define HID_KEYB_USAGE_LBRACKET 0x2F +#define HID_KEYB_USAGE_RBRACKET 0x30 +#define HID_KEYB_USAGE_BSLASH 0x31 +#define HID_KEYB_USAGE_SEMICOLON \ + 0x33 +#define HID_KEYB_USAGE_FQUOTE 0x34 +#define HID_KEYB_USAGE_BQUOTE 0x35 +#define HID_KEYB_USAGE_COMMA 0x36 +#define HID_KEYB_USAGE_PERIOD 0x37 +#define HID_KEYB_USAGE_FSLASH 0x38 +#define HID_KEYB_USAGE_CAPSLOCK 0x39 +#define HID_KEYB_USAGE_F1 0x3A +#define HID_KEYB_USAGE_F2 0x3B +#define HID_KEYB_USAGE_F3 0x3C +#define HID_KEYB_USAGE_F4 0x3D +#define HID_KEYB_USAGE_F5 0x3E +#define HID_KEYB_USAGE_F6 0x3F +#define HID_KEYB_USAGE_F7 0x40 +#define HID_KEYB_USAGE_F8 0x41 +#define HID_KEYB_USAGE_F9 0x42 +#define HID_KEYB_USAGE_F10 0x43 +#define HID_KEYB_USAGE_F11 0x44 +#define HID_KEYB_USAGE_F12 0x45 +#define HID_KEYB_USAGE_SCROLLOCK \ + 0x47 +#define HID_KEYB_USAGE_PAGE_UP 0x4B +#define HID_KEYB_USAGE_PAGE_DOWN \ + 0x4E +#define HID_KEYB_USAGE_RIGHT_ARROW \ + 0x4F +#define HID_KEYB_USAGE_LEFT_ARROW \ + 0x50 +#define HID_KEYB_USAGE_DOWN_ARROW \ + 0x51 +#define HID_KEYB_USAGE_UP_ARROW 0x52 +#define HID_KEYB_USAGE_NUMLOCK 0x53 +#define HID_KEYB_USAGE_KEYPAD_SLASH \ + 0x54 +#define HID_KEYB_USAGE_KEYPAD_STAR \ + 0x55 +#define HID_KEYB_USAGE_KEYPAD_MINUS \ + 0x56 +#define HID_KEYB_USAGE_KEYPAD_PLUS \ + 0x57 +#define HID_KEYB_USAGE_KEPAD_ENTER \ + 0x58 +#define HID_KEYB_USAGE_KEYPAD_1 0x59 +#define HID_KEYB_USAGE_KEYPAD_2 0x5A +#define HID_KEYB_USAGE_KEYPAD_3 0x5B +#define HID_KEYB_USAGE_KEYPAD_4 0x5C +#define HID_KEYB_USAGE_KEYPAD_5 0x5D +#define HID_KEYB_USAGE_KEYPAD_6 0x5E +#define HID_KEYB_USAGE_KEYPAD_7 0x5F +#define HID_KEYB_USAGE_KEYPAD_8 0x60 +#define HID_KEYB_USAGE_KEYPAD_9 0x61 +#define HID_KEYB_USAGE_KEYPAD_0 0x62 +#define HID_KEYB_USAGE_KEPAD_PERIOD \ + 0x63 + +//***************************************************************************** +// +// HID descriptor country codes (most of these are described as "countries" in +// the HID specification even though they are really languages). +// +//***************************************************************************** +#define USB_HID_COUNTRY_NONE 0x00 +#define USB_HID_COUNTRY_ARABIC 0x01 +#define USB_HID_COUNTRY_BELGIAN 0x02 +#define USB_HID_COUNTRY_CANADA_BI \ + 0x03 +#define USB_HID_COUNTRY_CANADA_FR \ + 0x04 +#define USB_HID_COUNTRY_CZECH_REPUBLIC \ + 0x05 +#define USB_HID_COUNTRY_DANISH 0x06 +#define USB_HID_COUNTRY_FINNISH 0x07 +#define USB_HID_COUNTRY_FRENCH 0x08 +#define USB_HID_COUNTRY_GERMAN 0x09 +#define USB_HID_COUNTRY_GREEK 0x0A +#define USB_HID_COUNTRY_HEBREW 0x0B +#define USB_HID_COUNTRY_HUNGARY 0x0C +#define USB_HID_COUNTRY_INTERNATIONAL_ISO \ + 0x0D +#define USB_HID_COUNTRY_ITALIAN 0x0E +#define USB_HID_COUNTRY_JAPAN_KATAKANA \ + 0x0F +#define USB_HID_COUNTRY_KOREAN 0x10 +#define USB_HID_COUNTRY_LATIN_AMERICAN \ + 0x11 +#define USB_HID_COUNTRY_NETHERLANDS \ + 0x12 +#define USB_HID_COUNTRY_NORWEGIAN \ + 0x13 +#define USB_HID_COUNTRY_PERSIAN 0x14 +#define USB_HID_COUNTRY_POLAND 0x15 +#define USB_HID_COUNTRY_PORTUGUESE \ + 0x16 +#define USB_HID_COUNTRY_RUSSIA 0x17 +#define USB_HID_COUNTRY_SLOVAKIA \ + 0x18 +#define USB_HID_COUNTRY_SPANISH 0x19 +#define USB_HID_COUNTRY_SWEDISH 0x1A +#define USB_HID_COUNTRY_SWISS_FRENCH \ + 0x1B +#define USB_HID_COUNTRY_SWISS_GERMAN \ + 0x1C +#define USB_HID_COUNTRY_SWITZERLAND \ + 0x1D +#define USB_HID_COUNTRY_TAIWAN 0x1E +#define USB_HID_COUNTRY_TURKISH_Q \ + 0x1F +#define USB_HID_COUNTRY_UK 0x20 +#define USB_HID_COUNTRY_US 0x21 +#define USB_HID_COUNTRY_YUGOSLAVIA \ + 0x22 +#define USB_HID_COUNTRY_TURKISH_F \ + 0x23 + +//***************************************************************************** +// +// Data flags used in Input item tags within report descriptors. +// +//***************************************************************************** +#define USB_HID_INPUT_DATA 0x0000 +#define USB_HID_INPUT_CONSTANT 0x0001 +#define USB_HID_INPUT_ARRAY 0x0000 +#define USB_HID_INPUT_VARIABLE 0x0002 +#define USB_HID_INPUT_ABS 0x0000 +#define USB_HID_INPUT_RELATIVE 0x0004 +#define USB_HID_INPUT_NOWRAP 0x0000 +#define USB_HID_INPUT_WRAP 0x0008 +#define USB_HID_INPUT_LINEAR 0x0000 +#define USB_HID_INPUT_NONLINEAR 0x0010 +#define USB_HID_INPUT_PREFER 0x0000 +#define USB_HID_INPUT_NONPREFER 0x0020 +#define USB_HID_INPUT_NONULL 0x0000 +#define USB_HID_INPUT_NULL 0x0040 +#define USB_HID_INPUT_BITF 0x0100 +#define USB_HID_INPUT_BYTES 0x0000 + +//***************************************************************************** +// +// Data flags used in Feature item tags within report descriptors. +// +//***************************************************************************** +#define USB_HID_FEATURE_DATA 0x0000 +#define USB_HID_FEATURE_CONSTANT \ + 0x0001 +#define USB_HID_FEATURE_ARRAY 0x0000 +#define USB_HID_FEATURE_VARIABLE \ + 0x0002 +#define USB_HID_FEATURE_ABS 0x0000 +#define USB_HID_FEATURE_RELATIVE \ + 0x0004 +#define USB_HID_FEATURE_NOWRAP 0x0000 +#define USB_HID_FEATURE_WRAP 0x0008 +#define USB_HID_FEATURE_LINEAR 0x0000 +#define USB_HID_FEATURE_NONLINEAR \ + 0x0010 +#define USB_HID_FEATURE_PREFER 0x0000 +#define USB_HID_FEATURE_NONPREFER \ + 0x0020 +#define USB_HID_FEATURE_NONULL 0x0000 +#define USB_HID_FEATURE_NULL 0x0040 +#define USB_HID_FEATURE_BITF 0x0100 +#define USB_HID_FEATURE_BYTES 0x0000 + +//***************************************************************************** +// +// Data flags used in Output item tags within report descriptors. +// +//***************************************************************************** +#define USB_HID_OUTPUT_DATA 0x0000 +#define USB_HID_OUTPUT_CONSTANT 0x0001 +#define USB_HID_OUTPUT_ARRAY 0x0000 +#define USB_HID_OUTPUT_VARIABLE 0x0002 +#define USB_HID_OUTPUT_ABS 0x0000 +#define USB_HID_OUTPUT_RELATIVE 0x0004 +#define USB_HID_OUTPUT_NOWRAP 0x0000 +#define USB_HID_OUTPUT_WRAP 0x0008 +#define USB_HID_OUTPUT_LINEAR 0x0000 +#define USB_HID_OUTPUT_NONLINEAR \ + 0x0010 +#define USB_HID_OUTPUT_PREFER 0x0000 +#define USB_HID_OUTPUT_NONPREFER \ + 0x0020 +#define USB_HID_OUTPUT_NONULL 0x0000 +#define USB_HID_OUTPUT_NULL 0x0040 +#define USB_HID_OUTPUT_BITF 0x0100 +#define USB_HID_OUTPUT_BYTES 0x0000 + +//***************************************************************************** +// +// Physical descriptor bias values. +// +//***************************************************************************** +#define USB_HID_BIAS_NOT_APPLICABLE \ + 0x00 +#define USB_HID_BIAS_RIGHT_HAND 0x01 +#define USB_HID_BIAS_LEFT_HAND 0x02 +#define USB_HID_BIAS_BOTH_HANDS 0x03 +#define USB_HID_BIAS_EITHER_HAND \ + 0x04 + +//***************************************************************************** +// +// Physical descriptor designator values. +// +//***************************************************************************** +#define USB_HID_DESIGNATOR_NONE 0x00 +#define USB_HID_DESIGNATOR_HAND 0x01 +#define USB_HID_DESIGNATOR_EYEBALL \ + 0x02 +#define USB_HID_DESIGNATOR_EYEBROW \ + 0x03 +#define USB_HID_DESIGNATOR_EYELID \ + 0x04 +#define USB_HID_DESIGNATOR_EAR 0x05 +#define USB_HID_DESIGNATOR_NOSE 0x06 +#define USB_HID_DESIGNATOR_MOUTH \ + 0x07 +#define USB_HID_DESIGNATOR_UPPER_LIP \ + 0x08 +#define USB_HID_DESIGNATOR_LOWER_LIP \ + 0x09 +#define USB_HID_DESIGNATOR_JAW 0x0A +#define USB_HID_DESIGNATOR_NECK 0x0B +#define USB_HID_DESIGNATOR_UPPER_ARM \ + 0x0C +#define USB_HID_DESIGNATOR_ELBOW \ + 0x0D +#define USB_HID_DESIGNATOR_FOREARM \ + 0x0E +#define USB_HID_DESIGNATOR_WRIST \ + 0x0F +#define USB_HID_DESIGNATOR_PALM 0x10 +#define USB_HID_DESIGNATOR_THUMB \ + 0x11 +#define USB_HID_DESIGNATOR_INDEX_FINGER \ + 0x12 +#define USB_HID_DESIGNATOR_MIDDLE_FINGER \ + 0x13 +#define USB_HID_DESIGNATOR_RING_FINGER \ + 0x14 +#define USB_HID_DESIGNATOR_LITTLE_FINGER \ + 0x15 +#define USB_HID_DESIGNATOR_HEAD 0x16 +#define USB_HID_DESIGNATOR_SHOULDER \ + 0x17 +#define USB_HID_DESIGNATOR_HIP 0x18 +#define USB_HID_DESIGNATOR_WAIST \ + 0x19 +#define USB_HID_DESIGNATOR_THIGH \ + 0x1A +#define USB_HID_DESIGNATOR_KNEE 0x1B +#define USB_HID_DESIGNATOR_CALF 0x1C +#define USB_HID_DESIGNATOR_ANKLE \ + 0x1D +#define USB_HID_DESIGNATOR_FOOT 0x1E +#define USB_HID_DESIGNATOR_HEEL 0x1F +#define USB_HID_DESIGNATOR_BALL_OF_FOOT \ + 0x20 +#define USB_HID_DESIGNATOR_BIG_TOE \ + 0x21 +#define USB_HID_DESIGNATOR_SECOND_TOE \ + 0x22 +#define USB_HID_DESIGNATOR_THIRD_TOE \ + 0x23 +#define USB_HID_DESIGNATOR_FOURTH_TOE \ + 0x24 +#define USB_HID_DESIGNATOR_LITTLE_TOE \ + 0x25 +#define USB_HID_DESIGNATOR_BROW 0x26 +#define USB_HID_DESIGNATOR_CHEEK \ + 0x27 + +//***************************************************************************** +// +// Physical descriptor qualifier values. +// +//***************************************************************************** +#define USB_HID_QUALIFIER_NOT_APPLICABLE \ + (0x00 << 5) +#define USB_HID_QUALIFIER_RIGHT (0x01 << 5) +#define USB_HID_QUALIFIER_LEFT (0x02 << 5) +#define USB_HID_QUALIFIER_BOTH (0x03 << 5) +#define USB_HID_QUALIFIER_EITHER \ + (0x04 << 5) +#define USB_HID_QUALIFIER_CENTER \ + (0x05 << 5) + +//***************************************************************************** +// +// This is the maximum value for a usage code. +// +//***************************************************************************** +#define USBH_HID_MAX_USAGE 256 +#define USBH_HID_CAPS_ARRAY_SZ (USBH_HID_MAX_USAGE/sizeof(uint32_t)) + +//***************************************************************************** +// +// All structures defined in this section of the header require byte packing of +// fields. This is usually accomplished using the PACKED macro but, for IAR +// Embedded Workbench, this requires a pragma. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +//! The class descriptor information structure is used to announce the presence +//! of HID-specific class descriptors within the HID descriptor. +// +//***************************************************************************** +typedef struct +{ + // + //! The type of HID class descriptor. This will be \b USB_HID_DTYPE_REPORT + //! or \b USB_HID_DTYPE_PHYSICAL. + // + uint8_t bDescriptorType; + + // + //! The total length of the HID class descriptor. + // + uint16_t wDescriptorLength; +} +PACKED tHIDClassDescriptorInfo; + +//***************************************************************************** +// +//! The HID descriptor is inserted following the interface descriptor and +//! before the endpoint descriptors for a HID class device. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a HID descriptor, this will be + //! \b USB_HID_DTYPE_HID. + // + uint8_t bDescriptorType; + + // + //! A BCD value identifying the HID Class specification release supported + //! by the device. For version 1.11, for example, this value would be + //! 0x0111. + // + uint16_t bcdHID; + + // + //! The country code for which this hardware is localized or 0 if no + //! localization has been performed. Valid country (or language) codes are + //! in labels of the form \b USB_HID_COUNTRY_xxx. + uint8_t bCountryCode; + + // + //! The number of class-specific descriptors that exist for this device. + //! This indicates the number of class descriptor information structures + //! that are appended to this structure and must be at least 1 (since all + //! HID devices must publish at least 1 report descriptor). + // + uint8_t bNumDescriptors; + + // + //! A table announcing each of the class-specific descriptors that this + //! device publishes. The actual number of entries in the array is given + //! by the bNumDescriptors field. + // + tHIDClassDescriptorInfo sClassDescriptor[1]; +} +PACKED tHIDDescriptor; + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +//! This structure defines the mapping of USB usage identifiers to printable +//! characters. The structure has three members that hold this information. +//! The ui8BytesPerChar, indicates the number of bytes per character in +//! the table. The pui32CapsLock array holds a packed bit array of usage +//! identifiers that can be modified by the Caps Lock key. The pCharMapping +//! array is treated as a double indexed array with two "columns". In the case +//! of a single byte character it is treated as pairs of 8 bit values for +//! unshifted and shifted values. In the case of a double byte characters it +//! is treated as pairs of 16 bit values. +// +//***************************************************************************** +typedef struct +{ + // + //! Number of bytes per character in the pCharMapping table of this + //! structure. + // + uint8_t ui8BytesPerChar; + + // + //! This is a packed bitmasked structure with a one bit flags that + //! indicates if the corresponding Usage ID is affected by the Caps Lock + //! key. + // + uint32_t pui32CapsLock[USBH_HID_CAPS_ARRAY_SZ]; + + // + //! This is the indexed table of Usage ID to character value. It must be + //! at least ui8BytesPerChar * 2 * \b USBH_HID_MAX_USAGE bytes in size as + //! it is treated as a double indexed array. + // + void *pvCharMapping; +} +tHIDKeyboardUsageTable; + +//***************************************************************************** +// +// The US Keyboard mapping used by USB keyboard usage ID to character mapping. +// +//***************************************************************************** +extern const tHIDKeyboardUsageTable g_sUSKeyboardMap; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHID_H__ diff --git a/usblib/usbkeyboardmap.c b/usblib/usbkeyboardmap.c new file mode 100644 index 0000000..ae5fd20 --- /dev/null +++ b/usblib/usbkeyboardmap.c @@ -0,0 +1,145 @@ +//***************************************************************************** +// +// usbkeyboardmap.c - This file holds the table to enable USB keyboard usage +// identifiers to be mapped to printable characters. +// +// 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 "usblib/usblib.h" +#include "usblib/usbhid.h" + +//***************************************************************************** +// +// This is the array that hold the unshifted and shifted ASCII character for +// each usage ID. +// +//***************************************************************************** +const uint8_t g_pui8KeyBoardMap[USBH_HID_MAX_USAGE][2] = +{ +// +// Usage ID to character mapping Usage ID CAPS Lock +// + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 0 - 3 0 + {'a', 'A'}, {'b', 'B'}, {'c', 'C'}, {'d', 'D'}, // 4 - 7 f + {'e', 'E'}, {'f', 'F'}, {'g', 'G'}, {'h', 'H'}, // 8 - 11 f + {'i', 'I'}, {'j', 'J'}, {'k', 'K'}, {'l', 'L'}, // 12 - 15 f + {'m', 'M'}, {'n', 'N'}, {'o', 'O'}, {'p', 'P'}, // 16 - 19 f + {'q', 'Q'}, {'r', 'R'}, {'s', 'S'}, {'t', 'T'}, // 20 - 23 f + {'u', 'U'}, {'v', 'V'}, {'w', 'W'}, {'x', 'X'}, // 24 - 27 f + {'y', 'Y'}, {'z', 'Z'}, {'1', '!'}, {'2', '@'}, // 28 - 31 3 + + {'3', '#'}, {'4', '$'}, {'5', '%'}, {'6', '^'}, // 32 - 35 0 + {'7', '&'}, {'8', '*'}, {'9', '('}, {'0', ')'}, // 36 - 39 0 + {'\n', '\n'}, {0, 0}, {0, 0}, {'\t', '\t'}, // 40 - 43 0 + {' ', ' '}, {'-', '_'}, {'=', '+'}, {'[', '{'}, // 44 - 47 0 + {']', '}'}, {'\\', '|'},{'`', '~'}, {';', ':'}, // 48 - 51 0 + {'\'', '"'}, {'`', '~'}, {',', '<'}, {'.', '>'},// 52 - 55 0 + {'/', '?'}, {0, 0}, {0, 0}, {0, 0}, // 56 - 59 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 60 - 63 0 + + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 64 - 67 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 68 - 71 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 72 - 75 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 76 - 79 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 80 - 83 0 + {'/', '/'}, {'*', '*'}, {'-', '-'}, {'+', '+'}, // 84 - 87 0 + {'\n', '\n'}, {'1', 0}, {'2', 0}, {'3', 0}, // 88 - 91 0 + {'4', 0}, {'5', 0}, {'6', 0}, {'7', 0}, // 92 - 95 0 + + {'8', 0}, {'9', 0}, {'0', 0}, {'.', 0}, // 96 - 99 0 + {'\\', '|'},{0, 0}, {0, 0}, {'=', '+'}, // 100 - 103 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 104 - 107 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 108 - 111 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 112 - 115 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 116 - 119 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 120 - 123 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 124 - 127 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 128 - 131 0 + + {0, 0}, {',', ','}, {'=', '='}, {0, 0}, // 132 - 135 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 136 - 139 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 140 - 143 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 144 - 147 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 148 - 151 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 152 - 155 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 156 - 159 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 160 - 163 0 + + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 164 - 167 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 168 - 171 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 172 - 175 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 174 - 179 0 + {0, 0}, {0, 0}, {'(', '('}, {')', ')'}, // 180 - 183 0 + {'{', '{'}, {'}', '}'}, {'\t', '\t'}, {0, 0}, // 184 - 187 0 + {'A', 'A'}, {'B', 'B'}, {'C', 'C'}, {'D', 'D'}, // 188 - 191 0 + {'E', 'E'}, {'F', 'F'}, {0, 0}, {'^', '^'}, // 192 - 195 0 + + {'%', '%'}, {'<', '<'}, {'>', '>'}, {'&', '&'}, // 196 - 199 0 + {'&', '&'}, {'|', '|'}, {'|', '|'}, {':', ':'}, // 200 - 203 0 + {'#', '#'}, {' ', ' '}, {'@', '@'}, {'!', '!'}, // 204 - 207 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 208 - 211 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 212 - 215 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 216 - 219 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 220 - 223 0 + + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 224 - 227 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 228 - 231 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 232 - 235 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 236 - 239 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 240 - 243 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 244 - 247 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 248 - 251 0 + {0, 0}, {0, 0}, {0, 0}, {0, 0}, // 252 - 255 0 +}; + +//***************************************************************************** +// +// This is the structure that defines the mapping of USB usage IDs to ASCII +// values for printing. +// +//***************************************************************************** +const tHIDKeyboardUsageTable g_sUSKeyboardMap = +{ + // + // One byte per character. + // + 1, + // + // Packed bit array of usages codes that are effected by Caps Lock state. + // + { + 0x3ffffff0, // Alpha characters are only one affected by CAPS LOCK + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + }, + // + // The large table of the direct mapping of usage id's to ascii characters. + // + (void *)g_pui8KeyBoardMap +}; diff --git a/usblib/usblib.ewp b/usblib/usblib.ewp new file mode 100644 index 0000000..70afea7 --- /dev/null +++ b/usblib/usblib.ewp @@ -0,0 +1,860 @@ + + + + 1 + + Debug + + ARM + + 1 + + General + 3 + + 14 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 19 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 7 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 5 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Source + + $PROJ_DIR$\usbbuffer.c + + + $PROJ_DIR$\device\usbdaudio.c + + + $PROJ_DIR$\device\usbdbulk.c + + + $PROJ_DIR$\device\usbdcdc.c + + + $PROJ_DIR$\device\usbdcdesc.c + + + $PROJ_DIR$\device\usbdcomp.c + + + $PROJ_DIR$\device\usbdconfig.c + + + $PROJ_DIR$\device\usbddfu-rt.c + + + $PROJ_DIR$\device\usbdenum.c + + + $PROJ_DIR$\usbdesc.c + + + $PROJ_DIR$\device\usbdhandler.c + + + $PROJ_DIR$\device\usbdhid.c + + + $PROJ_DIR$\device\usbdhidgamepad.c + + + $PROJ_DIR$\device\usbdhidkeyb.c + + + $PROJ_DIR$\device\usbdhidmouse.c + + + $PROJ_DIR$\usbdma.c + + + $PROJ_DIR$\device\usbdmsc.c + + + $PROJ_DIR$\host\usbhaudio.c + + + $PROJ_DIR$\host\usbhhid.c + + + $PROJ_DIR$\host\usbhhidkeyboard.c + + + $PROJ_DIR$\host\usbhhidmouse.c + + + $PROJ_DIR$\host\usbhhub.c + + + $PROJ_DIR$\host\usbhmsc.c + + + $PROJ_DIR$\host\usbhostenum.c + + + $PROJ_DIR$\host\usbhscsi.c + + + $PROJ_DIR$\usbkeyboardmap.c + + + $PROJ_DIR$\usbmode.c + + + $PROJ_DIR$\usbringbuf.c + + + $PROJ_DIR$\usbtick.c + + + $PROJ_DIR$\usbulpi.c + + + diff --git a/usblib/usblib.h b/usblib/usblib.h new file mode 100644 index 0000000..3c96a19 --- /dev/null +++ b/usblib/usblib.h @@ -0,0 +1,1951 @@ +//***************************************************************************** +// +// usblib.h - Main header file for the USB Library. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBLIB_H__ +#define __USBLIB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// This is the maximum number of devices we can support when in host mode and +// using a hub. By default, we support up to 4 devices (plus 1 internally for +// the hub itself). +// +//***************************************************************************** +#ifndef MAX_USB_DEVICES +#define MAX_USB_DEVICES 5 +#endif + +//***************************************************************************** +// +// This is the maximum number of endpoints supported by the usblib. +// +//***************************************************************************** +#ifndef USBLIB_NUM_EP +#define USBLIB_NUM_EP 8 // Number of supported endpoints. +#endif + +//***************************************************************************** +// +// The following macro allows compiler-independent syntax to be used to +// define packed structures. A typical structure definition using these +// macros will look similar to the following example: +// +// #ifdef ewarm +// #pragma pack(1) +// #endif +// +// typedef struct _PackedStructName +// { +// uint32_t ui32FirstField; +// int8_t i8CharMember; +// uint16_t ui16Short; +// } +// PACKED tPackedStructName; +// +// #ifdef ewarm +// #pragma pack() +// #endif +// +// The conditional blocks related to ewarm include the #pragma pack() lines +// only if the IAR Embedded Workbench compiler is being used. Unfortunately, +// it is not possible to emit a #pragma from within a macro definition so this +// must be done explicitly. +// +//***************************************************************************** +#if defined(ccs) || \ + defined(codered) || \ + defined(gcc) || \ + defined(rvmdk) || \ + defined(__ARMCC_VERSION) || \ + defined(sourcerygxx) +#define PACKED __attribute__ ((packed)) +#elif defined(ewarm) +#define PACKED +#else +#error Unrecognized COMPILER! +#endif +//***************************************************************************** +// +// Assorted language IDs from the document "USB_LANGIDs.pdf" provided by the +// USB Implementers' Forum (Version 1.0). +// +//***************************************************************************** +#define USB_LANG_CHINESE_PRC 0x0804 // Chinese (PRC) +#define USB_LANG_CHINESE_TAIWAN 0x0404 // Chinese (Taiwan) +#define USB_LANG_EN_US 0x0409 // English (United States) +#define USB_LANG_EN_UK 0x0809 // English (United Kingdom) +#define USB_LANG_EN_AUS 0x0C09 // English (Australia) +#define USB_LANG_EN_CA 0x1009 // English (Canada) +#define USB_LANG_EN_NZ 0x1409 // English (New Zealand) +#define USB_LANG_FRENCH 0x040C // French (Standard) +#define USB_LANG_GERMAN 0x0407 // German (Standard) +#define USB_LANG_HINDI 0x0439 // Hindi +#define USB_LANG_ITALIAN 0x0410 // Italian (Standard) +#define USB_LANG_JAPANESE 0x0411 // Japanese +#define USB_LANG_KOREAN 0x0412 // Korean +#define USB_LANG_ES_TRAD 0x040A // Spanish (Traditional) +#define USB_LANG_ES_MODERN 0x0C0A // Spanish (Modern) +#define USB_LANG_SWAHILI 0x0441 // Swahili (Kenya) +#define USB_LANG_URDU_IN 0x0820 // Urdu (India) +#define USB_LANG_URDU_PK 0x0420 // Urdu (Pakistan) + +//***************************************************************************** +// +//! \addtogroup usbchap9_src +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Note: +// +// Structure definitions which are derived directly from the USB specification +// use field names from the specification. Since a somewhat different version +// of Hungarian prefix notation is used from the standard, beware of making +// assumptions about field sizes based on the field prefix when using +// these structures. Of particular note is the difference in the meaning of +// the 'i' prefix. In USB structures, this indicates a single byte index +// whereas in other code, this is an integer or enumeration variable. +// +//***************************************************************************** + +//***************************************************************************** +// +// All structures defined in this section of the header require byte packing of +// fields. This is usually accomplished using the PACKED macro but, for IAR +// Embedded Workbench, this requires a pragma. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// Definitions related to standard USB device requests (sections 9.3 & 9.4) +// +//***************************************************************************** + +//***************************************************************************** +// +//! The standard USB request header as defined in section 9.3 of the USB 2.0 +//! specification. +// +//***************************************************************************** +typedef struct +{ + // + //! Determines the type and direction of the request. + // + uint8_t bmRequestType; + + // + //! Identifies the specific request being made. + // + uint8_t bRequest; + + // + //! Word-sized field that varies according to the request. + // + uint16_t wValue; + + // + //! Word-sized field that varies according to the request; typically used + //! to pass an index or offset. + // + uint16_t wIndex; + + // + //! The number of bytes to transfer if there is a data stage to the + //! request. + // + uint16_t wLength; + +} +PACKED tUSBRequest; + +//***************************************************************************** +// +// The following defines are used with the bmRequestType member of tUSBRequest. +// +// Request types have 3 bit fields: +// 4:0 - Is the recipient type. +// 6:5 - Is the request type. +// 7 - Is the direction of the request. +// +//***************************************************************************** +#define USB_RTYPE_DIR_IN 0x80 +#define USB_RTYPE_DIR_OUT 0x00 + +#define USB_RTYPE_TYPE_M 0x60 +#define USB_RTYPE_VENDOR 0x40 +#define USB_RTYPE_CLASS 0x20 +#define USB_RTYPE_STANDARD 0x00 + +#define USB_RTYPE_RECIPIENT_M 0x1f +#define USB_RTYPE_OTHER 0x03 +#define USB_RTYPE_ENDPOINT 0x02 +#define USB_RTYPE_INTERFACE 0x01 +#define USB_RTYPE_DEVICE 0x00 + +//***************************************************************************** +// +// Standard USB requests IDs used in the bRequest field of tUSBRequest. +// +//***************************************************************************** +#define USBREQ_GET_STATUS 0x00 +#define USBREQ_CLEAR_FEATURE 0x01 +#define USBREQ_SET_FEATURE 0x03 +#define USBREQ_SET_ADDRESS 0x05 +#define USBREQ_GET_DESCRIPTOR 0x06 +#define USBREQ_SET_DESCRIPTOR 0x07 +#define USBREQ_GET_CONFIG 0x08 +#define USBREQ_SET_CONFIG 0x09 +#define USBREQ_GET_INTERFACE 0x0a +#define USBREQ_SET_INTERFACE 0x0b +#define USBREQ_SYNC_FRAME 0x0c + +//***************************************************************************** +// +// Data returned from a USBREQ_GET_STATUS request to a device. +// +//***************************************************************************** +#define USB_STATUS_SELF_PWR 0x0001 // Currently self powered. +#define USB_STATUS_BUS_PWR 0x0000 // Currently bus-powered. +#define USB_STATUS_PWR_M 0x0001 // Mask for power mode. +#define USB_STATUS_REMOTE_WAKE 0x0002 // Remote wake-up is currently enabled. + +//***************************************************************************** +// +// Feature Selectors (tUSBRequest.wValue) passed on USBREQ_CLEAR_FEATURE and +// USBREQ_SET_FEATURE. +// +//***************************************************************************** +#define USB_FEATURE_EP_HALT 0x0000 // Endpoint halt feature. +#define USB_FEATURE_REMOTE_WAKE 0x0001 // Remote wake feature, device only. +#define USB_FEATURE_TEST_MODE 0x0002 // Test mode + +//***************************************************************************** +// +// Endpoint Selectors (tUSBRequest.wIndex) passed on USBREQ_CLEAR_FEATURE, +// USBREQ_SET_FEATURE and USBREQ_GET_STATUS. +// +//***************************************************************************** +#define USB_REQ_EP_NUM_M 0x007F +#define USB_REQ_EP_DIR_M 0x0080 +#define USB_REQ_EP_DIR_IN 0x0080 +#define USB_REQ_EP_DIR_OUT 0x0000 + +//***************************************************************************** +// +// Standard USB descriptor types. These values are passed in the upper bytes +// of tUSBRequest.wValue on USBREQ_GET_DESCRIPTOR and also appear in the +// bDescriptorType field of standard USB descriptors. +// +//***************************************************************************** +#define USB_DTYPE_DEVICE 1 +#define USB_DTYPE_CONFIGURATION 2 +#define USB_DTYPE_STRING 3 +#define USB_DTYPE_INTERFACE 4 +#define USB_DTYPE_ENDPOINT 5 +#define USB_DTYPE_DEVICE_QUAL 6 +#define USB_DTYPE_OSPEED_CONF 7 +#define USB_DTYPE_INTERFACE_PWR 8 +#define USB_DTYPE_OTG 9 +#define USB_DTYPE_INTERFACE_ASC 11 +#define USB_DTYPE_CS_INTERFACE 36 +#define USB_DTYPE_HUB 41 + +//***************************************************************************** +// +// Definitions related to USB descriptors (sections 9.5 & 9.6) +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure describes a generic descriptor header. These fields are to +//! be found at the beginning of all valid USB descriptors. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor (including this length byte) expressed + //! in bytes. + // + uint8_t bLength; + + // + //! The type identifier of the descriptor whose information follows. For + //! standard descriptors, this field could contain, for example, + //! USB_DTYPE_DEVICE to identify a device descriptor or USB_DTYPE_ENDPOINT + //! to identify an endpoint descriptor. + // + uint8_t bDescriptorType; +} +PACKED tDescriptorHeader; + +//***************************************************************************** +// +//! This structure describes the USB device descriptor as defined in USB +//! 2.0 specification section 9.6.1. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. All device descriptors are + //! 18 bytes long. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a device descriptor, this will be + //! USB_DTYPE_DEVICE (1). + // + uint8_t bDescriptorType; + + // + //! The USB Specification Release Number in BCD format. For USB 2.0, this + //! will be 0x0200. + // + uint16_t bcdUSB; + + // + //! The device class code. + // + uint8_t bDeviceClass; + + // + //! The device subclass code. This value qualifies the value found in the + //! bDeviceClass field. + // + uint8_t bDeviceSubClass; + + // + //! The device protocol code. This value is qualified by the values of + //! bDeviceClass and bDeviceSubClass. + // + uint8_t bDeviceProtocol; + + // + //! The maximum packet size for endpoint zero. Valid values are 8, 16, 32 + //! and 64. + // + uint8_t bMaxPacketSize0; + + // + //! The device Vendor ID (VID) as assigned by the USB-IF. + // + uint16_t idVendor; + + // + //! The device Product ID (PID) as assigned by the manufacturer. + // + uint16_t idProduct; + + // + //! The device release number in BCD format. + // + uint16_t bcdDevice; + + // + //! The index of a string descriptor describing the manufacturer. + // + uint8_t iManufacturer; + + // + //! The index of a string descriptor describing the product. + // + uint8_t iProduct; + + // + //! The index of a string descriptor describing the device's serial + //! number. + // + uint8_t iSerialNumber; + + // + //! The number of possible configurations offered by the device. This + //! field indicates the number of distinct configuration descriptors that + //! the device offers. + // + uint8_t bNumConfigurations; +} +PACKED tDeviceDescriptor; + +//***************************************************************************** +// +// USB Device Class codes used in the tDeviceDescriptor.bDeviceClass field. +// Definitions for the bDeviceSubClass and bDeviceProtocol fields are device +// specific and can be found in the appropriate device class header files. +// +//***************************************************************************** +#define USB_CLASS_DEVICE 0x00 +#define USB_CLASS_AUDIO 0x01 +#define USB_CLASS_CDC 0x02 +#define USB_CLASS_HID 0x03 +#define USB_CLASS_PHYSICAL 0x05 +#define USB_CLASS_IMAGE 0x06 +#define USB_CLASS_PRINTER 0x07 +#define USB_CLASS_MASS_STORAGE 0x08 +#define USB_CLASS_HUB 0x09 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_SMART_CARD 0x0b +#define USB_CLASS_SECURITY 0x0d +#define USB_CLASS_VIDEO 0x0e +#define USB_CLASS_HEALTHCARE 0x0f +#define USB_CLASS_DIAG_DEVICE 0xdc +#define USB_CLASS_WIRELESS 0xe0 +#define USB_CLASS_MISC 0xef +#define USB_CLASS_APP_SPECIFIC 0xfe +#define USB_CLASS_VEND_SPECIFIC 0xff +#define USB_CLASS_EVENTS 0xffffffff + +//***************************************************************************** +// +// Generic values for undefined subclass and protocol. +// +//***************************************************************************** +#define USB_SUBCLASS_UNDEFINED 0x00 +#define USB_PROTOCOL_UNDEFINED 0x00 + +//***************************************************************************** +// +// The following are the miscellaneous subclass values. +// +//***************************************************************************** +#define USB_MISC_SUBCLASS_SYNC 0x01 +#define USB_MISC_SUBCLASS_COMMON \ + 0x02 + +//***************************************************************************** +// +// These following are miscellaneous protocol values. +// +//***************************************************************************** +#define USB_MISC_PROTOCOL_IAD 0x01 + +//***************************************************************************** +// +// These following are hub protocol values. +// +//***************************************************************************** +#define USB_HUB_PROTOCOL_FS 0x00 +#define USB_HUB_PROTOCOL_SINGLE 0x01 +#define USB_HUB_PROTOCOL_MULTI 0x02 + +//***************************************************************************** +// +//! This structure describes the USB device qualifier descriptor as defined in +//! the USB 2.0 specification, section 9.6.2. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. All device qualifier + //! descriptors are 10 bytes long. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a device descriptor, this will be + //! USB_DTYPE_DEVICE_QUAL (6). + // + uint8_t bDescriptorType; + + // + //! The USB Specification Release Number in BCD format. For USB 2.0, this + //! will be 0x0200. + // + uint16_t bcdUSB; + + // + //! The device class code. + // + uint8_t bDeviceClass; + + // + //! The device subclass code. This value qualifies the value found in the + //! bDeviceClass field. + // + uint8_t bDeviceSubClass; + + // + //! The device protocol code. This value is qualified by the values of + //! bDeviceClass and bDeviceSubClass. + // + uint8_t bDeviceProtocol; + + // + //! The maximum packet size for endpoint zero when operating at a speed + //! other than high speed. + // + uint8_t bMaxPacketSize0; + + // + //! The number of other-speed configurations supported. + // + uint8_t bNumConfigurations; + + // + //! Reserved for future use. Must be set to zero. + // + uint8_t bReserved; +} +PACKED tDeviceQualifierDescriptor; + +//***************************************************************************** +// +//! This structure describes the USB configuration descriptor as defined in +//! USB 2.0 specification section 9.6.3. This structure also applies to the +//! USB other speed configuration descriptor defined in section 9.6.4. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. All configuration descriptors + //! are 9 bytes long. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a configuration descriptor, this will + //! be USB_DTYPE_CONFIGURATION (2). + // + uint8_t bDescriptorType; + + // + //! The total length of data returned for this configuration. This + //! includes the combined length of all descriptors (configuration, + //! interface, endpoint and class- or vendor-specific) returned for this + //! configuration. + // + uint16_t wTotalLength; + + // + //! The number of interface supported by this configuration. + // + uint8_t bNumInterfaces; + + // + //! The value used as an argument to the SetConfiguration standard request + //! to select this configuration. + // + uint8_t bConfigurationValue; + + // + //! The index of a string descriptor describing this configuration. + // + uint8_t iConfiguration; + + // + //! Attributes of this configuration. + // + uint8_t bmAttributes; + + // + //! The maximum power consumption of the USB device from the bus in this + //! configuration when the device is fully operational. This is expressed + //! in units of 2mA so, for example, 100 represents 200mA. + // + uint8_t bMaxPower; +} +PACKED tConfigDescriptor; + +//***************************************************************************** +// +// Flags used in constructing the value assigned to the field +// tConfigDescriptor.bmAttributes. Note that bit 7 is reserved and must be set +// to 1. +// +//***************************************************************************** +#define USB_CONF_ATTR_PWR_M 0xC0 + +#define USB_CONF_ATTR_SELF_PWR 0xC0 +#define USB_CONF_ATTR_BUS_PWR 0x80 +#define USB_CONF_ATTR_RWAKE 0xA0 + +//***************************************************************************** +// +//! This structure describes the USB interface descriptor as defined in USB +//! 2.0 specification section 9.6.5. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. All interface descriptors + //! are 9 bytes long. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_INTERFACE (4). + // + uint8_t bDescriptorType; + + // + //! The number of this interface. This is a zero based index into the + //! array of concurrent interfaces supported by this configuration. + // + uint8_t bInterfaceNumber; + + // + //! The value used to select this alternate setting for the interface + //! defined in bInterfaceNumber. + // + uint8_t bAlternateSetting; + + // + //! The number of endpoints used by this interface (excluding endpoint + //! zero). + // + uint8_t bNumEndpoints; + + // + //! The interface class code as assigned by the USB-IF. + // + uint8_t bInterfaceClass; + + // + //! The interface subclass code as assigned by the USB-IF. + // + uint8_t bInterfaceSubClass; + + // + //! The interface protocol code as assigned by the USB-IF. + // + uint8_t bInterfaceProtocol; + + // + //! The index of a string descriptor describing this interface. + // + uint8_t iInterface; +} +PACKED tInterfaceDescriptor; + +//***************************************************************************** +// +//! This structure describes the USB endpoint descriptor as defined in USB +//! 2.0 specification section 9.6.6. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. All endpoint descriptors + //! are 7 bytes long. + // + uint8_t bLength; + + // + //! The type of the descriptor. For an endpoint descriptor, this will + //! be USB_DTYPE_ENDPOINT (5). + // + uint8_t bDescriptorType; + + // + //! The address of the endpoint. This field contains the endpoint number + //! ORed with flag USB_EP_DESC_OUT or USB_EP_DESC_IN to indicate the + //! endpoint direction. + // + uint8_t bEndpointAddress; + + // + //! The endpoint transfer type, USB_EP_ATTR_CONTROL, USB_EP_ATTR_ISOC, + //! USB_EP_ATTR_BULK or USB_EP_ATTR_INT and, if isochronous, additional + //! flags indicating usage type and synchronization method. + // + uint8_t bmAttributes; + + // + //! The maximum packet size this endpoint is capable of sending or + //! receiving when this configuration is selected. For high speed + //! isochronous or interrupt endpoints, bits 11 and 12 are used to + //! pass additional information. + // + uint16_t wMaxPacketSize; + + // + //! The polling interval for data transfers expressed in frames or + //! micro frames depending upon the operating speed. + // + uint8_t bInterval; +} +PACKED tEndpointDescriptor; + +//***************************************************************************** +// +// Flags used in constructing the value assigned to the field +// tEndpointDescriptor.bEndpointAddress. +// +//***************************************************************************** +#define USB_EP_DESC_OUT 0x00 +#define USB_EP_DESC_IN 0x80 +#define USB_EP_DESC_NUM_M 0x0f + +//***************************************************************************** +// +// Mask used to extract the maximum packet size (in bytes) from the +// wMaxPacketSize field of the endpoint descriptor. +// +//***************************************************************************** +#define USB_EP_MAX_PACKET_COUNT_M \ + 0x07FF + +//***************************************************************************** +// +// Endpoint attributes used in tEndpointDescriptor.bmAttributes. +// +//***************************************************************************** +#define USB_EP_ATTR_CONTROL 0x00 +#define USB_EP_ATTR_ISOC 0x01 +#define USB_EP_ATTR_BULK 0x02 +#define USB_EP_ATTR_INT 0x03 +#define USB_EP_ATTR_TYPE_M 0x03 + +#define USB_EP_ATTR_ISOC_M 0x0c +#define USB_EP_ATTR_ISOC_NOSYNC 0x00 +#define USB_EP_ATTR_ISOC_ASYNC 0x04 +#define USB_EP_ATTR_ISOC_ADAPT 0x08 +#define USB_EP_ATTR_ISOC_SYNC 0x0c +#define USB_EP_ATTR_USAGE_M 0x30 +#define USB_EP_ATTR_USAGE_DATA 0x00 +#define USB_EP_ATTR_USAGE_FEEDBACK \ + 0x10 +#define USB_EP_ATTR_USAGE_IMPFEEDBACK \ + 0x20 + +//***************************************************************************** +// +//! This structure describes the USB string descriptor for index 0 as defined +//! in USB 2.0 specification section 9.6.7. Note that the number of language +//! IDs is variable and can be determined by examining bLength. The number of +//! language IDs present in the descriptor is given by ((bLength - 2) / 2). +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. This value will vary + //! depending upon the number of language codes provided in the descriptor. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a string descriptor, this will be + //! USB_DTYPE_STRING (3). + // + uint8_t bDescriptorType; + + // + //! The language code (LANGID) for the first supported language. Note that + //! this descriptor may support multiple languages, in which case, the + //! number of elements in the wLANGID array will increase and bLength will + //! be updated accordingly. + // + uint16_t wLANGID[1]; +} +PACKED tString0Descriptor; + +//***************************************************************************** +// +//! This structure describes the USB string descriptor for all string indexes +//! other than 0 as defined in USB 2.0 specification section 9.6.7. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. This value will be 2 greater + //! than the number of bytes comprising the UNICODE string that the + //! descriptor contains. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a string descriptor, this will be + //! USB_DTYPE_STRING (3). + // + uint8_t bDescriptorType; + + // + //! The first byte of the UNICODE string. This string is not NULL + //! terminated. Its length (in bytes) can be computed by subtracting 2 + //! from the value in the bLength field. + // + uint8_t bString; +} +PACKED tStringDescriptor; + +//***************************************************************************** +// +//! Write a 16-bit value to a USB descriptor block. +//! +//! \param ui16Value is the 16-bit value to write to the descriptor. +//! +//! This helper macro is used in descriptor definitions to write two-byte +//! values. Since the configuration descriptor contains all interface and +//! endpoint descriptors in a contiguous block of memory, these descriptors are +//! typically defined using an array of bytes rather than as packed structures. +//! +//! \return Not a function. +// +//***************************************************************************** +#define USBShort(ui16Value) (ui16Value & 0xff), (ui16Value >> 8) + +//***************************************************************************** +// +//! Write a 24-bit value to a USB descriptor block. +//! +//! \param ui32Value is the 24-bit value that to write to the descriptor. +//! +//! This helper macro is used in descriptor definitions to write three-byte +//! values. Since the configuration descriptor contains all interface and +//! endpoint descriptors in a contiguous block of memory, these descriptors are +//! typically defined using an array of bytes rather than as packed structures. +//! +//! \return Not a function. +// +//***************************************************************************** +#define USB3Byte(ui32Value) (ui32Value & 0xff), \ + ((ui32Value >> 8) & 0xff), \ + ((ui32Value >> 16) & 0xff) + +//***************************************************************************** +// +//! Write a 32-bit value to a USB descriptor block. +//! +//! \param ui32Value is the 32-bit value that to write to the descriptor. +//! +//! This helper macro is used in descriptor definitions to write four-byte +//! values. Since the configuration descriptor contains all interface and +//! endpoint descriptors in a contiguous block of memory, these descriptors are +//! typically defined using an array of bytes rather than as packed structures. +//! +//! \return Not a function. +// +//***************************************************************************** +#define USBLong(ui32Value) (ui32Value & 0xff), \ + ((ui32Value >> 8) & 0xff), \ + ((ui32Value >> 16) & 0xff), \ + ((ui32Value >> 24) & 0xff) + +//***************************************************************************** +// +//! Traverse to the next USB descriptor in a block. +//! +//! \param ptr points to the first byte of a descriptor in a block of +//! USB descriptors. +//! +//! This macro aids in traversing lists of descriptors by returning a pointer +//! to the next descriptor in the list given a pointer to the current one. +//! +//! \return Returns a pointer to the next descriptor in the block following +//! \e ptr. +//! +//***************************************************************************** +#define NEXT_USB_DESCRIPTOR(ptr) \ + (tDescriptorHeader *)(((uint8_t *)(ptr)) + \ + *((uint8_t *)(ptr))) + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// Close the usbchap9_src Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Function prototype for any standard USB request. +// +//***************************************************************************** +typedef void (* tStdRequest)(void *pvInstance, tUSBRequest *pUSBRequest); + +//***************************************************************************** +// +// Data callback for receiving data from an endpoint. +// +//***************************************************************************** +typedef void (* tInfoCallback)(void *pvInstance, uint32_t ui32Info); + +//***************************************************************************** +// +// Callback made to indicate that an interface alternate setting change has +// occurred. +// +//***************************************************************************** +typedef void (* tInterfaceCallback)(void *pvInstance, uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting); + +//***************************************************************************** +// +// Generic interrupt handler callbacks. +// +//***************************************************************************** +typedef void (* tUSBIntHandler)(void *pvInstance); + +//***************************************************************************** +// +// Interrupt handler callbacks that have status information. +// +//***************************************************************************** +typedef void (* tUSBEPIntHandler)(void *pvInstance, uint32_t ui32Status); + +//***************************************************************************** +// +//! Generic handler callbacks that are used when the callers needs to call into +//! an instance of class. +// +//***************************************************************************** +typedef void (* tUSBDeviceHandler)(void *pvInstance, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +//! USB event handler functions used during enumeration and operation of the +//! device stack. +// +//***************************************************************************** +typedef struct +{ + // + //! This callback is made whenever the USB host requests a non-standard + //! descriptor from the device. + // + tStdRequest pfnGetDescriptor; + + // + //! This callback is made whenever the USB host makes a non-standard + //! request. + // + tStdRequest pfnRequestHandler; + + // + //! This callback is made in response to a SetInterface request from the + //! host. + // + tInterfaceCallback pfnInterfaceChange; + + // + //! This callback is made in response to a SetConfiguration request from + //! the host. + // + tInfoCallback pfnConfigChange; + + // + //! This callback is made when data has been received following to a call + //! to USBDCDRequestDataEP0. + // + tInfoCallback pfnDataReceived; + + // + //! This callback is made when data has been transmitted following a call + //! to USBDCDSendDataEP0. + // + tInfoCallback pfnDataSent; + + // + //! This callback is made when a USB reset is detected. + // + tUSBIntHandler pfnResetHandler; + + // + //! This callback is made when the bus has been inactive long enough to + //! trigger a suspend condition. + // + tUSBIntHandler pfnSuspendHandler; + + // + //! This is called when resume signaling is detected. + // + tUSBIntHandler pfnResumeHandler; + + // + //! This callback is made when the device is disconnected from the USB bus. + // + tUSBIntHandler pfnDisconnectHandler; + + // + //! This callback is made to inform the device of activity on all endpoints + //! other than endpoint zero. + // + tUSBEPIntHandler pfnEndpointHandler; + + // + //! This generic handler is provided to allow requests based on + //! a given instance to be passed into a device. This is commonly used + //! by a top level composite device that is using multiple instances of + //! a class. + // + tUSBDeviceHandler pfnDeviceHandler; +} +tCustomHandlers; + +//***************************************************************************** +// +//! This structure defines a contiguous block of data which contains a group +//! of descriptors that form part of a configuration descriptor for a device. +//! It is assumed that a config section contains only whole descriptors. It is +//! not valid to split a single descriptor across multiple sections. +//! +//***************************************************************************** +typedef struct +{ + // + //! The number of bytes of descriptor data pointed to by pui8Data. + // + uint16_t ui16Size; + + // + //! A pointer to a block of data containing an integral number of + //! USB descriptors which form part of a larger configuration descriptor. + // + const uint8_t *pui8Data; +} +tConfigSection; + +//***************************************************************************** +// +//! This is the top level structure defining a USB device configuration +//! descriptor. A configuration descriptor contains a collection of device- +//! specific descriptors in addition to the basic config, interface and +//! endpoint descriptors. To allow flexibility in constructing the +//! configuration, the descriptor is described in terms of a list of data +//! blocks. The first block must contain the configuration descriptor itself +//! and the following blocks are appended to this in order to produce the +//! full descriptor sent to the host in response to a GetDescriptor request +//! for the configuration descriptor. +//! +//***************************************************************************** +typedef struct +{ + // + //! The number of sections comprising the full descriptor for this + //! configuration. + // + uint8_t ui8NumSections; + + // + //! A pointer to an array of ui8NumSections section pointers which must + //! be concatenated to form the configuration descriptor. + // + const tConfigSection * const *psSections; +} +tConfigHeader; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup general_usblib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Predeclare of the DMA instance structure. +// +//***************************************************************************** +typedef struct tUSBDMAInstance tUSBDMAInstance; + +//***************************************************************************** +// +// USB descriptor parsing functions found in usbdesc.c +// +//***************************************************************************** + +//***************************************************************************** +// +//! The USB_DESC_ANY label is used as a wild card in several of the descriptor +//! parsing APIs to determine whether or not particular search criteria should +//! be ignored. +// +//***************************************************************************** +#define USB_DESC_ANY 0xFFFFFFFF + +extern uint32_t USBDescGetNum(tDescriptorHeader *psDesc, uint32_t ui32Size, + uint32_t ui32Type); +extern tDescriptorHeader *USBDescGet(tDescriptorHeader *psDesc, + uint32_t ui32Size, uint32_t ui32Type, + uint32_t ui32Index); +extern uint32_t + USBDescGetNumAlternateInterfaces(tConfigDescriptor *psConfig, + uint8_t ui8InterfaceNumber); +extern tInterfaceDescriptor *USBDescGetInterface(tConfigDescriptor *psConfig, + uint32_t ui32Index, + uint32_t ui32AltCfg); +extern tEndpointDescriptor * + USBDescGetInterfaceEndpoint(tInterfaceDescriptor *psInterface, + uint32_t ui32Index, uint32_t ui32Size); + +//***************************************************************************** +// +//! The operating mode required by the USB library client. This type is used +//! by applications which wish to be able to switch between host and device +//! modes by calling the USBStackModeSet() API. +// +//***************************************************************************** +typedef enum +{ + // + //! Operate in USB device mode with active monitoring of VBUS and the + //! ID pin must be pulled to a logic high value. + // + eUSBModeDevice = 0, + + // + //! Operate in USB host mode with active monitoring of VBUS and the ID pin + //! must be pulled to a logic low value. + // + eUSBModeHost, + + // + //! Operate as an On-The-Go device which requires both VBUS and ID to be + //! connected directly to the USB controller from the USB connector. + // + eUSBModeOTG, + + // + //! A marker indicating that no USB mode has yet been set by the + //! application. + // + eUSBModeNone, + + // + //! Force host mode so that the VBUS and ID pins are not used or monitored + //! by the USB controller. + // + eUSBModeForceHost, + + // + //! Forcing device mode so that the VBUS and ID pins are not used or + //! monitored by the USB controller. + // + eUSBModeForceDevice, +} +tUSBMode; + +//***************************************************************************** +// +// A pointer to a USB mode callback function. This function is called by the +// USB library to indicate to the application which operating mode it should +// use, host or device. +// +//***************************************************************************** +typedef void (*tUSBModeCallback)(uint32_t ui32Index, tUSBMode iMode); + +//***************************************************************************** +// +//! USB callback function. +//! +//! \param pvCBData is the callback pointer associated with the instance +//! generating the callback. This is a value provided by the client during +//! initialization of the instance making the callback. +//! \param ui32Event is the identifier of the asynchronous event which is being +//! notified to the client. +//! \param ui32MsgParam is an event-specific parameter. +//! \param pvMsgData is an event-specific data pointer. +//! +//! A function pointer provided to the USB layer by the application +//! which will be called to notify it of all asynchronous events relating to +//! data transmission or reception. This callback is used by device class +//! drivers and host pipe functions. +//! +//! \return Returns an event-dependent value. +// +//***************************************************************************** +typedef uint32_t (* tUSBCallback)(void *pvCBData, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData); + +//***************************************************************************** +// +// Error sources reported via USB_EVENT_ERROR. +// +//***************************************************************************** + +// +//! The host received an invalid PID in a transaction. +// +#define USBERR_HOST_IN_PID_ERROR \ + 0x01000000 + +// +//! The host did not receive a response from a device. +// +#define USBERR_HOST_IN_NOT_COMP 0x00100000 + +// +//! The host received a stall on an IN endpoint. +// +#define USBERR_HOST_IN_STALL 0x00400000 + +// +//! The host detected a CRC or bit-stuffing error (isochronous mode). +// +#define USBERR_HOST_IN_DATA_ERROR \ + 0x00080000 + +// +//! The host received NAK on an IN endpoint for longer than the specified +//! timeout period (interrupt, bulk and control modes). +// +#define USBERR_HOST_IN_NAK_TO 0x00080000 + +// +//! The host failed to communicate with a device via an IN endpoint. +// +#define USBERR_HOST_IN_ERROR 0x00040000 + +// +//! The host receive FIFO is full. +// +#define USBERR_HOST_IN_FIFO_FULL \ + 0x00020000 +// +//! The host received NAK on an OUT endpoint for longer than the specified +//! timeout period (bulk, interrupt and control modes). +// +#define USBERR_HOST_OUT_NAK_TO 0x00000080 + +// +//! The host did not receive a response from a device (isochronous mode). +// +#define USBERR_HOST_OUT_NOT_COMP \ + 0x00000080 + +// +//! The host received a stall on an OUT endpoint. +// +#define USBERR_HOST_OUT_STALL 0x00000020 + +// +//! The host failed to communicate with a device via an OUT endpoint. +// +#define USBERR_HOST_OUT_ERROR 0x00000004 + +// +//! The host received NAK on endpoint 0 for longer than the configured +//! timeout. +// +#define USBERR_HOST_EP0_NAK_TO 0x00000080 + +// +//! The host failed to communicate with a device via an endpoint zero. +// +#define USBERR_HOST_EP0_ERROR 0x00000010 + +// +//! The device detected a CRC error in received data. +// +#define USBERR_DEV_RX_DATA_ERROR \ + 0x00080000 + +// +//! The device was unable to receive a packet from the host since the receive +//! FIFO is full. +// +#define USBERR_DEV_RX_OVERRUN 0x00040000 + +// +//! The device receive FIFO is full. +// +#define USBERR_DEV_RX_FIFO_FULL 0x00020000 + +//***************************************************************************** +// +// Close the general_usblib_api Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup usblib_events +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure is used to return generic event based information to an +//! application. The following events are currently supported: +//! USB_EVENT_CONNECTED, USB_EVENT_DISCONNECTED, USB_EVENT_POWER_FAULT, +//! USB_EVENT_POWER_FAULT, USB_EVENT_POWER_ENABLE, +//! USB_EVENT_POWER_DISABLE and USB_EVENT_SOF. +// +//***************************************************************************** +typedef struct +{ + // + //! One of the USB_EVENT_ values. + // + uint32_t ui32Event; + + // + //! The caller supplied instance value that is passed to event handlers. + // + uint32_t ui32Instance; +} +tEventInfo; + +//***************************************************************************** +// +// Base identifiers for groups of USB events. These are used by both the +// device class drivers and host layer. +// +// USB_CLASS_EVENT_BASE is the lowest identifier that should be used for +// a class-specific event. Individual event bases are defined for each +// of the supported device class drivers. Events with IDs between +// USB_EVENT_BASE and USB_CLASS_EVENT_BASE are reserved for stack use. +// +//***************************************************************************** +#define USB_EVENT_BASE 0x0000 +#define USB_CLASS_EVENT_BASE 0x8000 + +//***************************************************************************** +// +// Event base identifiers for the various device classes supported in host +// and device modes. +// The first 0x800 values of a range are reserved for the device specific +// messages and the second 0x800 values of a range are used for the host +// specific messages for a given class. +// +//***************************************************************************** +#define USBD_CDC_EVENT_BASE (USB_CLASS_EVENT_BASE + 0) +#define USBD_HID_EVENT_BASE (USB_CLASS_EVENT_BASE + 0x1000) +#define USBD_HID_KEYB_EVENT_BASE \ + (USBD_HID_EVENT_BASE + 0x100) +#define USBD_BULK_EVENT_BASE (USB_CLASS_EVENT_BASE + 0x2000) +#define USBD_MSC_EVENT_BASE (USB_CLASS_EVENT_BASE + 0x3000) +#define USBD_AUDIO_EVENT_BASE (USB_CLASS_EVENT_BASE + 0x4000) +#define USBD_DFU_EVENT_BASE (USB_CLASS_EVENT_BASE + 0x5000) + +#define USBH_CDC_EVENT_BASE (USBD_CDC_EVENT_BASE + 0x800) +#define USBH_HID_EVENT_BASE (USBD_HID_EVENT_BASE + 0x800) +#define USBH_BULK_EVENT_BASE (USBD_BULK_EVENT_BASE + 0x800) +#define USBH_MSC_EVENT_BASE (USBD_MSC_EVENT_BASE + 0x800) +#define USBH_AUDIO_EVENT_BASE (USBD_AUDIO_EVENT_BASE + 0x800) + +//***************************************************************************** +// +// General events supported by device classes and host pipes. +// +//***************************************************************************** + +// +//! The device is now attached to a USB host and ready to begin sending +//! and receiving data (used by device classes only). +// +#define USB_EVENT_CONNECTED (USB_EVENT_BASE + 0) + +// +//! The device has been disconnected from the USB host (used by device classes +//! only). +//! +//! \note In device mode, the USB_EVENT_DISCONNECTED will not be reported if +//! the MCU's PB1/USB0VBUS pin is connected to a fixed +5 Volts rather than +//! directly to the VBUS pin on the USB connector. +// +#define USB_EVENT_DISCONNECTED (USB_EVENT_BASE + 1) + +// +//! Data has been received and is in the buffer provided or is ready to be +//! read from the FIFO. If the \e pvMsgData value is 0 then the +//! \e ui32MsgParam value contains the amount of data in bytes ready to be read +//! from the device. If the \e pvMsgData value is not 0 then \e pvMsgData is +//! a pointer to the data that was read and \e ui32MsgParam is the number of +//! valid bytes in the array pointed to by \e pvMsgData. +// +#define USB_EVENT_RX_AVAILABLE (USB_EVENT_BASE + 2) + +// +//! This event is sent by a lower layer to inquire about the amount of +//! unprocessed data buffered in the layers above. It is used in cases +//! where a low level driver needs to ensure that all preceding data has +//! been processed prior to performing some action or making some notification. +//! Clients receiving this event should return the number of bytes of data +//! that are unprocessed or 0 if no outstanding data remains. +// +#define USB_EVENT_DATA_REMAINING \ + (USB_EVENT_BASE + 3) + +// +//! This event is sent by a lower layer supporting DMA to request a buffer in +//! which the next received packet may be stored. The \e ui32MsgValue +//! parameter indicates the maximum size of packet that can be received in this +//! channel and \e pvMsgData points to storage which should be written with the +//! returned buffer pointer. The return value from the callback should be the +//! size of the buffer allocated (which may be less than the maximum size +//! passed in \e ui32MsgValue if the client knows that fewer bytes are expected +//! to be received) or 0 if no buffer is being returned. +// +#define USB_EVENT_REQUEST_BUFFER \ + (USB_EVENT_BASE + 4) + +// +//! Data has been sent and acknowledged. If this event is received via the +//! USB buffer callback, the \e ui32MsgValue parameter indicates the number of +//! bytes from the transmit buffer that have been successfully transmitted +//! and acknowledged. +// +#define USB_EVENT_TX_COMPLETE (USB_EVENT_BASE + 5) + +// +//! An error has been reported on the channel or pipe. The \e ui32MsgValue +//! parameter indicates the source(s) of the error and is the logical OR +//! combination of "USBERR_" flags defined below. +// +#define USB_EVENT_ERROR (USB_EVENT_BASE + 6) + +// +//! The bus has entered suspend state. +// +#define USB_EVENT_SUSPEND (USB_EVENT_BASE + 7) + +// +//! The bus has left suspend state. +// +#define USB_EVENT_RESUME (USB_EVENT_BASE + 8) + +// +//! A scheduler event has occurred. +// +#define USB_EVENT_SCHEDULER (USB_EVENT_BASE + 9) +// +//! A device or host has detected a stall condition. +// +#define USB_EVENT_STALL (USB_EVENT_BASE + 10) + +// +//! The host detected a power fault condition. +// +#define USB_EVENT_POWER_FAULT (USB_EVENT_BASE + 11) + +// +//! The controller has detected a A-Side cable and needs power applied This is +//! only generated on OTG parts if automatic power control is disabled. +// +#define USB_EVENT_POWER_ENABLE (USB_EVENT_BASE + 12) + +// +//! The controller needs power removed, This is only generated on OTG parts +//! if automatic power control is disabled. +// +#define USB_EVENT_POWER_DISABLE (USB_EVENT_BASE + 13) + +// +//! This define is used with a device class's pfnDeviceHandler handler function +//! to indicate that the USB library has changed the interface number. This +//! event is typically due to the class being included in a composite device. +//! +//! The \e pvInstance is a pointer to an instance of the device being accessed. +//! +//! The \e ui32Request is USB_EVENT_COMP_IFACE_CHANGE. +//! +//! The \e pvRequestData is a pointer to a two byte array where the first value +//! is the old interface number and the second is the new interface number. +// +#define USB_EVENT_COMP_IFACE_CHANGE \ + (USB_EVENT_BASE + 14) + +// +//! This define is used with a device class's pfnDeviceHandler handler function +//! to indicate that the USB library has changed the endpoint number. This +//! event is typically due to the class being included in a composite device. +//! +//! The \e pvInstance is a pointer to an instance of the device being accessed. +//! +//! The \e ui32Request is USB_EVENT_COMP_EP_CHANGE. +//! +//! The \e pvRequestData is a pointer to a two byte array where the first value +//! is the old endpoint number and the second is the new endpoint number. The +//! endpoint numbers should be exactly as USB specification defines them and +//! bit 7 set indicates an IN endpoint and bit 7 clear indicates an OUT +//! endpoint. +// +#define USB_EVENT_COMP_EP_CHANGE \ + (USB_EVENT_BASE + 15) + +// +//! This define is used with a device class's pfnDeviceHandler handler function +//! to indicate that the USB library has changed the string index number for a +//! string. This event is typically due to the class being included in a +//! composite device. +//! +//! The \e pvInstance is a pointer to an instance of the device being accessed. +//! +//! The \e ui32Request is USB_EVENT_COMP_STR_CHANGE. +//! +//! The \e pvRequestData is a pointer to a two byte array where the first value +//! is the old string index and the second is the new string index. +// +#define USB_EVENT_COMP_STR_CHANGE \ + (USB_EVENT_BASE + 16) + +// +//! This define is used with a device class's pfnDeviceHandler handler function +//! to indicate that the USB library has changed the configuration descriptor. +//! This allows the class to make final adjustments to the configuration +//! descriptor. This event is typically due to the class being included in a +//! composite device. +//! +//! The \e pvInstance is a pointer to an instance of the device being accessed. +//! +//! The \e ui32Request is USB_EVENT_COMP_CONFIG. +//! +//! The \e pvRequestData is a pointer to the beginning of the configuration +//! descriptor for the device instance. +// +#define USB_EVENT_COMP_CONFIG (USB_EVENT_BASE + 17) + +// +//! An unknown device is now attached to a USB host. This value is only valid +//! for the generic event handler and not other device handlers. It is +//! useful for applications that want to know when an unknown device is +//! connected and what the class is of the unknown device. +//! +//! The \e ui32Instance is the device instance for the unknown device. +// +#define USB_EVENT_UNKNOWN_CONNECTED \ + (USB_EVENT_BASE + 18) + +// +//! A start of frame event has occurred. This event is disabled by default +//! and must be enabled via a call from the application to USBHCDEventEnable(). +// +#define USB_EVENT_SOF (USB_EVENT_BASE + 19) + +// +//! This event occurs when a device enters LPM sleep mode. +// +#define USB_EVENT_LPM_SLEEP (USB_EVENT_BASE + 20) + +// +//! This event occurs when a device is resumed from LPM sleep mode. +// +#define USB_EVENT_LPM_RESUME (USB_EVENT_BASE + 21) + +// +//! This event occurs when a device has responded with a NYET to an LPM request +//! because LPM responses were disabled. +// +#define USB_EVENT_LPM_ERROR (USB_EVENT_BASE + 22) + +//***************************************************************************** +// +// Close the usblib_events Doxygen group. +//! @} +// +//***************************************************************************** +//***************************************************************************** +// +//! \addtogroup usblib_buffer_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! A function pointer type which describes either a class driver packet read +//! or packet write function (both have the same prototype) to the USB +//! buffer object. +// +//***************************************************************************** +typedef uint32_t (* tUSBPacketTransfer)(void *pvHandle, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); + +//***************************************************************************** +// +//! A function pointer type which describes either a class driver transmit +//! or receive packet available function (both have the same prototype) to the +//! USB buffer object. +//! +//! \param pvHandle is the handle of the device. +//! +//! \return None. +// +//***************************************************************************** +typedef uint32_t (* tUSBPacketAvailable)(void *pvHandle); + +//***************************************************************************** +// +//! The number of bytes of workspace that each USB buffer object requires. +//! This workspace memory is provided to the buffer on USBBufferInit() in +//! the \e pvWorkspace field of the \e tUSBBuffer structure. +// +//***************************************************************************** +#define USB_BUFFER_WORKSPACE_SIZE \ + 24 +//***************************************************************************** +// +// The defines used with the USBDCDFeatureSet() or USBHCDFeatureSet() calls. +// +//***************************************************************************** + +//***************************************************************************** +// +//! This feature setting enables or disables LPM support in the USB library in +//! either host or device mode depending on if the USBHCDFeatureSet() or +//! USBDCDFeatureSet() is called. If no action is taken the default behavior of +//! USB library is to not support LPM transactions. The \e pvFeature value +//! is a pointer to a 32-bit value containing the a logical OR of the following +//! values: +//! +//! - \b USBLIB_FEATURE_LPM_EN is used to enable LPM support in host or device +//! mode. +//! - \b USBLIB_FEATURE_LPM_DIS(default) is used to disable LPM support in host +//! or device mode. +//! - \b USBLIB_FEATURE_LPM_RMT_WAKE is used to enable remote wake from an +//! LPM suspended state. +//! +// +//***************************************************************************** +#define USBLIB_FEATURE_LPM 0x00000001 + +//***************************************************************************** +// +// The defines used with the USBDCDFeatureSet() or USBHCDFeatureSet() calls +// for \b USBLIB_FEATURE_LPM feature requests. +// +//***************************************************************************** +#define USBLIB_FEATURE_LPM_RMT_WAKE \ + 0x00000002 +#define USBLIB_FEATURE_LPM_EN 0x00000001 +#define USBLIB_FEATURE_LPM_DIS 0x00000000 + +//***************************************************************************** +// +//! This feature setting allows an application to inform the USB library of the +//! current processor speed that is can use for internal timing when the frame +//! counter is not yet running. The \e pvFeature is a pointer to a 32-bit +//! value that holds the processor frequency in Hz. +// +//***************************************************************************** +#define USBLIB_FEATURE_CPUCLK 0x00000002 + +//***************************************************************************** +// +//! This feature setting allows an application to inform the USB library of the +//! current USB PLL rate in cases where the USB library needs this information +//! for internal configuration. If this feature is not set, then default rate +//! for the USB PLL is 480MHz. The \e pvFeature is a pointer to an 32-bit +//! value that holds the USB PLL speed in Hz. If the application needs to use +//! an external USB clock the PLL value should be set to zero. This is used +//! when connecting to an external USB phy which is providing the 60-MHz clock. +//! +// +//***************************************************************************** +#define USBLIB_FEATURE_USBPLL 0x00000003 + +//***************************************************************************** +//! +//! This feature setting allows an application to disable or configure and +//! enable the ULPI features in the USB library. If this feature is not set, +//! the default behavior is to not support ULPI operation. The \e pvFeature is +//! a pointer to an 32-bit value that holds the USB ULPI configuration. The +//! following are the valid settings for this feature: +//! +//! - USBLIB_FEATURE_ULPI_NONE disables all ULPI support. +//! - USBLIB_FEATURE_ULPI_HS enable ULPI with high speed support. +//! - USBLIB_FEATURE_ULPI_FS enable ULPI with full speed support. +// +//***************************************************************************** +#define USBLIB_FEATURE_USBULPI 0x00000004 + +//***************************************************************************** +// +// The defines used with the USBDCDFeatureSet() or USBHCDFeatureSet() calls +// for \b USBLIB_FEATURE_USBULPI feature requests. +// +//***************************************************************************** +#define USBLIB_FEATURE_ULPI_NONE \ + 0x00000000 +#define USBLIB_FEATURE_ULPI_HS 0x00000010 +#define USBLIB_FEATURE_ULPI_FS 0x00000020 + +//***************************************************************************** +// +//! This feature setting enables or disables various power settings in the USB +//! library in either host or device mode depending on if the +//! USBHCDFeatureSet() or USBDCDFeatureSet() is called. The \e pvFeature value +//! is a pointer to a 32-bit value containing the a logical OR of the following +//! values: +//! +//! - \b USBLIB_FEATURE_POWER_BUS - USB device mode is bus powered(default). +//! - \b USBLIB_FEATURE_POWER_SELF - USB device mode is self powered. +//! - \b USBLIB_FEATURE_REMOTE_WAKE - Enable USB remote wake feature. +// +//***************************************************************************** +#define USBLIB_FEATURE_POWER 0x00000005 + +//***************************************************************************** +// +// The defines used with the USBDCDFeatureSet() or USBHCDFeatureSet() calls +// for \b USBLIB_FEATURE_POWER feature requests. +// +//***************************************************************************** +#define USBLIB_FEATURE_POWER_SELF \ + 0x00000001 +#define USBLIB_FEATURE_POWER_BUS \ + 0x00000000 +#define USBLIB_FEATURE_REMOTE_WAKE \ + 0x00000002 + +//***************************************************************************** +// +// The structure used with the USBDCDFeatureSet() or USBHCDFeatureSet() calls +// to set feature enables and resume timing parameter. +// +//***************************************************************************** +typedef struct +{ + uint32_t ui32HIRD; + uint32_t ui32Features; +} +tLPMFeature; + +//***************************************************************************** +// +//! The structure used by the application to initialize a buffer object that +//! will provide buffered access to either a transmit or receive channel. +// +//***************************************************************************** +typedef struct +{ + // + //! This field sets the mode of the buffer. If true, the buffer + //! operates as a transmit buffer and supports calls to USBBufferWrite + //! by the client. If false, the buffer operates as a receive buffer + //! and supports calls to USBBufferRead. + // + bool bTransmitBuffer; + + // + //! A pointer to the callback function which will be called to notify + //! the application of all asynchronous events related to the operation + //! of the buffer. + // + tUSBCallback pfnCallback; + + // + //! A pointer that the buffer will pass back to the client in the + //! first parameter of all callbacks related to this instance. + // + void *pvCBData; + + // + //! The function which should be called to transmit a packet of data + //! in transmit mode or receive a packet in receive mode. + // + tUSBPacketTransfer pfnTransfer; + + // + //! The function which should be called to determine if the endpoint is + //! ready to accept a new packet for transmission in transmit mode or + //! to determine the size of the buffer required to read a packet in + //! receive mode. + // + tUSBPacketAvailable pfnAvailable; + + // + //! The handle to pass to the low level function pointers + //! provided in the pfnTransfer and pfnAvailable members. For USB device + //! use, this is the psDevice parameter required by the relevant device + //! class driver APIs. For USB host use, this is the pipe identifier + //! returned by USBHCDPipeAlloc. + // + void *pvHandle; + + // + //! A pointer to memory to be used as the ring buffer for this + //! instance. + // + uint8_t *pui8Buffer; + + // + //! The size, in bytes, of the buffer pointed to by pi8Buffer. + // + uint32_t ui32BufferSize; + + // + //! A pointer to USB_BUFFER_WORKSPACE_SIZE bytes of RAM that the buffer + //! object can use for workspace. + // + void *pvWorkspace; +} +tUSBBuffer; + +//***************************************************************************** +// +//! The structure used for encapsulating all the items associated with a +//! ring buffer. +// +//***************************************************************************** +typedef struct +{ + // + //! The ring buffer size. + // + uint32_t ui32Size; + + // + //! The ring buffer write index. + // + volatile uint32_t ui32WriteIndex; + + // + //! The ring buffer read index. + // + volatile uint32_t ui32ReadIndex; + + // + //! The ring buffer. + // + uint8_t *pui8Buf; +} +tUSBRingBufObject; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// USB buffer API function prototypes. +// +//***************************************************************************** +extern const tUSBBuffer *USBBufferInit(const tUSBBuffer *psBuffer); +extern void USBBufferZeroLengthPacketInsert(const tUSBBuffer *psBuffer, + bool bSendZLP); +extern void USBBufferInfoGet(const tUSBBuffer *psBuffer, + tUSBRingBufObject *psRingBuf); +extern void *USBBufferCallbackDataSet(tUSBBuffer *psBuffer, void *pvCBData); +extern uint32_t USBBufferWrite(const tUSBBuffer *psBuffer, + const uint8_t *pui8Data, uint32_t ui32Length); +extern void USBBufferDataWritten(const tUSBBuffer *psBuffer, + uint32_t ui32Length); +extern void USBBufferDataRemoved(const tUSBBuffer *psBuffer, + uint32_t ui32Length); +extern void USBBufferFlush(const tUSBBuffer *psBuffer); +extern uint32_t USBBufferRead(const tUSBBuffer *psBuffer, uint8_t *pui8Data, + uint32_t ui32Length); +extern uint32_t USBBufferDataAvailable(const tUSBBuffer *psBuffer); +extern uint32_t USBBufferSpaceAvailable(const tUSBBuffer *psBuffer); +extern uint32_t USBBufferEventCallback(void *pvCBData, uint32_t ui32Event, + uint32_t ui32MsgValue, void *pvMsgData); +extern bool USBRingBufFull(tUSBRingBufObject *psUSBRingBuf); +extern bool USBRingBufEmpty(tUSBRingBufObject *psUSBRingBuf); +extern void USBRingBufFlush(tUSBRingBufObject *psUSBRingBuf); +extern uint32_t USBRingBufUsed(tUSBRingBufObject *psUSBRingBuf); +extern uint32_t USBRingBufFree(tUSBRingBufObject *psUSBRingBuf); +extern uint32_t USBRingBufContigUsed(tUSBRingBufObject *psUSBRingBuf); +extern uint32_t USBRingBufContigFree(tUSBRingBufObject *psUSBRingBuf); +extern uint32_t USBRingBufSize(tUSBRingBufObject *psUSBRingBuf); +extern uint8_t USBRingBufReadOne(tUSBRingBufObject *psUSBRingBuf); +extern void USBRingBufRead(tUSBRingBufObject *psUSBRingBuf, + uint8_t *pui8Data, uint32_t ui32Length); +extern void USBRingBufWriteOne(tUSBRingBufObject *psUSBRingBuf, + uint8_t ui8Data); +extern void USBRingBufWrite(tUSBRingBufObject *psUSBRingBuf, + const uint8_t *pui8Data, uint32_t ui32Length); +extern void USBRingBufAdvanceWrite(tUSBRingBufObject *psUSBRingBuf, + uint32_t ui32NumBytes); +extern void USBRingBufAdvanceRead(tUSBRingBufObject *psUSBRingBuf, + uint32_t ui32NumBytes); +extern void USBRingBufInit(tUSBRingBufObject *psUSBRingBuf, + uint8_t *pui8Buf, uint32_t ui32Size); + +//***************************************************************************** +// +// Mode selection and dual mode interrupt steering functions. +// +//***************************************************************************** +extern void USBStackModeSet(uint32_t ui32Index, tUSBMode iUSBMode, + tUSBModeCallback pfnCallback); +extern void USBDualModeInit(uint32_t ui32Index); +extern void USBDualModeTerm(uint32_t ui32Index); +extern void USBOTGMain(uint32_t ui32MsTicks); +extern void USBOTGPollRate(uint32_t ui32Index, uint32_t ui32PollRate); +extern void USBOTGModeInit(uint32_t ui32Index, uint32_t ui32PollRate, + void *pHostData, uint32_t ui32HostDataSize); +extern void USBOTGModeTerm(uint32_t ui32Index); +extern void USB0OTGModeIntHandler(void); +extern bool USBOTGFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, + void *pvFeature); +extern void USB0DualModeIntHandler(void); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBLIB_H__ diff --git a/usblib/usblib.uvopt b/usblib/usblib.uvopt new file mode 100644 index 0000000..0f71b15 --- /dev/null +++ b/usblib/usblib.uvopt @@ -0,0 +1,650 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj + *.lib + *.txt; *.h; *.inc + *.plm + *.cpp + + + + 0 + 0 + + + + usblib + 0x4 + ARM-ADS + + 8000000 + + 1 + 1 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\rvmdk\ + + + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + + + 1 + 0 + 1 + + 255 + + + 0 + Data Sheet + DATASHTS\Luminary\TM4C1230C3PM.PDF + + + + SARMCM3.DLL + -MPU + DCM.DLL + -pCM4 + SARMCM3.DLL + -MPU + TCM.DLL + -pCM4 + + + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 1 + 0 + 1 + 0 + 0 + 3 + + + + + + + + + + + BIN\lmidk-agdi.dll + + + + 0 + lmidk-agdi + -O4622 -S3 -FO29 + + + + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + + + Source + 1 + 0 + 0 + + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbbuffer.c + usbbuffer.c + + + 1 + 2 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdaudio.c + usbdaudio.c + + + 1 + 3 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdbulk.c + usbdbulk.c + + + 1 + 4 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdcdc.c + usbdcdc.c + + + 1 + 5 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdcdesc.c + usbdcdesc.c + + + 1 + 6 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdcomp.c + usbdcomp.c + + + 1 + 7 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdconfig.c + usbdconfig.c + + + 1 + 8 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbddfu-rt.c + usbddfu-rt.c + + + 1 + 9 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdenum.c + usbdenum.c + + + 1 + 10 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbdesc.c + usbdesc.c + + + 1 + 11 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdhandler.c + usbdhandler.c + + + 1 + 12 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdhid.c + usbdhid.c + + + 1 + 13 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdhidgamepad.c + usbdhidgamepad.c + + + 1 + 14 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdhidkeyb.c + usbdhidkeyb.c + + + 1 + 15 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdhidmouse.c + usbdhidmouse.c + + + 1 + 16 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbdma.c + usbdma.c + + + 1 + 17 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdmsc.c + usbdmsc.c + + + 1 + 18 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhaudio.c + usbhaudio.c + + + 1 + 19 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhid.c + usbhhid.c + + + 1 + 20 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhidkeyboard.c + usbhhidkeyboard.c + + + 1 + 21 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhidmouse.c + usbhhidmouse.c + + + 1 + 22 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhub.c + usbhhub.c + + + 1 + 23 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhmsc.c + usbhmsc.c + + + 1 + 24 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhostenum.c + usbhostenum.c + + + 1 + 25 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhscsi.c + usbhscsi.c + + + 1 + 26 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbkeyboardmap.c + usbkeyboardmap.c + + + 1 + 27 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbmode.c + usbmode.c + + + 1 + 28 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbringbuf.c + usbringbuf.c + + + 1 + 29 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbtick.c + usbtick.c + + + 1 + 30 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbulpi.c + usbulpi.c + + + + + Documentation + 1 + 0 + 0 + + 2 + 31 + 5 + 0 + 1 + 0 + 0 + 1 + 1 + 0 + .\readme.txt + readme.txt + + 44 + 0 + 1 + + -1 + -1 + + + -1 + -1 + + + 0 + 0 + 729 + 300 + + + + + + + 1 + 0 + + 100 + 0 + + + .\readme.txt + 0 + 1 + 1 + + + + + +
diff --git a/usblib/usblib.uvproj b/usblib/usblib.uvproj new file mode 100644 index 0000000..e71fe93 --- /dev/null +++ b/usblib/usblib.uvproj @@ -0,0 +1,555 @@ + + + + 1.1 + +
### uVision Project, (C) Keil Software
+ + + + usblib + 0x4 + ARM-ADS + + + TM4C1230C3PM + Texas Instruments + IRAM(0x20000000-0x20002FFF) IROM(0-0x7FFF) CLOCK(8000000) CPUTYPE("Cortex-M4") FPU2 + + "STARTUP\Luminary\Startup.s" ("Luminary Startup Code") + UL2CM3(-O207 -S0 -C0 -FO7 -FD20000000 -FC800 -FN1 -FF0LM4F_32 -FS00 -FL08000) + 5919 + LM4Fxxxx.H + + + + + + + + + + SFD\Luminary\TM4C1230C3PM.SFR + 0 + + + + Luminary\ + Luminary\ + + 0 + 0 + 0 + 0 + 1 + + .\rvmdk\ + usblib + 0 + 1 + 0 + 1 + 1 + .\rvmdk\ + 1 + 0 + 0 + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + 0 + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + + + SARMCM3.DLL + -MPU + DCM.DLL + -pCM4 + SARMCM3.DLL + -MPU + TCM.DLL + -pCM4 + + + + 1 + 0 + 0 + 0 + 16 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + + + 0 + 1 + 0 + 1 + 1 + 1 + 0 + 1 + + 0 + 3 + + + + + + + + + + + + + + BIN\lmidk-agdi.dll + + + + + 1 + 0 + 0 + 0 + 1 + 4097 + + BIN\lmidk-agdi.dll + + + + + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "Cortex-M4" + + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 2 + 0 + 0 + 8 + 1 + 0 + 0 + 3 + 3 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x3000 + + + 1 + 0x0 + 0x8000 + + + 0 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x8000 + + + 1 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x3000 + + + 0 + 0x0 + 0x0 + + + + + + 0 + 3 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + + --c99 + rvmdk + + ..; + + + + 1 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + 0 + 1 + 0 + 0x00000000 + 0x20000000 + + + + + + + + + + + + Source + + + usbbuffer.c + 1 + .\usbbuffer.c + + + usbdaudio.c + 1 + .\device\usbdaudio.c + + + usbdbulk.c + 1 + .\device\usbdbulk.c + + + usbdcdc.c + 1 + .\device\usbdcdc.c + + + usbdcdesc.c + 1 + .\device\usbdcdesc.c + + + usbdcomp.c + 1 + .\device\usbdcomp.c + + + usbdconfig.c + 1 + .\device\usbdconfig.c + + + usbddfu-rt.c + 1 + .\device\usbddfu-rt.c + + + usbdenum.c + 1 + .\device\usbdenum.c + + + usbdesc.c + 1 + .\usbdesc.c + + + usbdhandler.c + 1 + .\device\usbdhandler.c + + + usbdhid.c + 1 + .\device\usbdhid.c + + + usbdhidgamepad.c + 1 + .\device\usbdhidgamepad.c + + + usbdhidkeyb.c + 1 + .\device\usbdhidkeyb.c + + + usbdhidmouse.c + 1 + .\device\usbdhidmouse.c + + + usbdma.c + 1 + .\usbdma.c + + + usbdmsc.c + 1 + .\device\usbdmsc.c + + + usbhaudio.c + 1 + .\host\usbhaudio.c + + + usbhhid.c + 1 + .\host\usbhhid.c + + + usbhhidkeyboard.c + 1 + .\host\usbhhidkeyboard.c + + + usbhhidmouse.c + 1 + .\host\usbhhidmouse.c + + + usbhhub.c + 1 + .\host\usbhhub.c + + + usbhmsc.c + 1 + .\host\usbhmsc.c + + + usbhostenum.c + 1 + .\host\usbhostenum.c + + + usbhscsi.c + 1 + .\host\usbhscsi.c + + + usbkeyboardmap.c + 1 + .\usbkeyboardmap.c + + + usbmode.c + 1 + .\usbmode.c + + + usbringbuf.c + 1 + .\usbringbuf.c + + + usbtick.c + 1 + .\usbtick.c + + + usbulpi.c + 1 + .\usbulpi.c + + + + + Documentation + + + readme.txt + 5 + .\readme.txt + + + + + + + +
diff --git a/usblib/usblibpriv.h b/usblib/usblibpriv.h new file mode 100644 index 0000000..51359f7 --- /dev/null +++ b/usblib/usblibpriv.h @@ -0,0 +1,529 @@ +//***************************************************************************** +// +// usblibpriv.h - Private header file used to share internal variables and +// function prototypes between the various modules in the USB +// library. This header MUST NOT be used by application code. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBLIBPRIV_H__ +#define __USBLIBPRIV_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_dma_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Internal interrupt handlers called from the main vectors in device and +// host mode. +// +//***************************************************************************** +extern void USBDeviceIntHandlerInternal(uint32_t ui32Index, + uint32_t ui32Status); +extern void USBHostIntHandlerInternal(uint32_t ui32Index, uint32_t ui32Status); + +//***************************************************************************** +// +// The maximum number of tick handlers that can be registered in a system. +// +//***************************************************************************** +#define MAX_USB_TICK_HANDLERS 6 + +//***************************************************************************** +// +// This value defines the number of SOF ticks that must pass before a call +// is made to InternalUSBStartOfFrameTick. The value 5 ensures that the +// function is called every 5 milliseconds assuming that SOF interrupts are +// enabled and SOF is present. +// +//***************************************************************************** +#define USB_SOF_TICK_DIVIDE 5 + +//***************************************************************************** +// +// Tick handler function pointer type. +// +//***************************************************************************** +typedef void(* tUSBTickHandler)(void *pvInstance, uint32_t ui32TicksmS); + +//***************************************************************************** +// +// Internal functions use to initialize the tick handler and register tick +// callbacks. +// +//***************************************************************************** +extern void InternalUSBTickInit(void); +extern void InternalUSBTickReset(void); +extern int32_t InternalUSBRegisterTickHandler(tUSBTickHandler pfnHandler, + void *pvInstance); +extern void InternalUSBStartOfFrameTick(uint32_t ui32TicksmS); +extern void InternalUSBHCDSendEvent(uint32_t ui32Index, tEventInfo *psEvent, + uint32_t ui32EvFlag); + +//***************************************************************************** +// +// g_ui32CurrentUSBTick holds the elapsed time in milliseconds since the +// tick module was first initialized based on calls to the function +// InternalUSBStartOfFrameTick. The granularity is USB_SOF_TICK_DIVIDE +// milliseconds. +// +//***************************************************************************** +extern uint32_t g_ui32CurrentUSBTick; + +//***************************************************************************** +// +// g_ui32USBSOFCount is a global counter for Start of Frame interrupts. It is +// incremented by the low level device- or host-mode interrupt handlers. +// +//***************************************************************************** +extern uint32_t g_ui32USBSOFCount; + +//***************************************************************************** +// +// InternalUSBGetTime is a macro which will return the system time in +// milliseconds as calculated based on calls to the function +// InternalUSBStartOfFrameTick. The granularity is USB_SOF_TICK_DIVIDE +// milliseconds. +// +// Currently, this merely returns the value of a global variable. +// +//***************************************************************************** +#define InternalUSBGetTime() g_ui32CurrentUSBTick + +//***************************************************************************** +// +// Macros to convert between USB controller base address and an index. These +// are currently trivial but are included to allow for the possibility of +// supporting more than one controller in the future. +// +//***************************************************************************** +#define USBBaseToIndex(BaseAddr)(0) +#define USBIndexToBase(Index) (USB0_BASE) + +// +// Maximum number of channels for Type 0 USB controllers. +// +#define USB_MAX_DMA_CHANNELS_0 6 + +// +// Maximum number of channels for all other USB controllers. +// +#define USB_MAX_DMA_CHANNELS 8 + +//***************************************************************************** +// +// Values returned by the USBLibDMAChannelStatus() function. +// +//***************************************************************************** +#define USBLIBSTATUS_DMA_IDLE 0x00000000 +#define USBLIBSTATUS_DMA_COMPLETE \ + 0x00000001 +#define USBLIBSTATUS_DMA_ERROR 0x00000002 +#define USBLIBSTATUS_DMA_PENDING \ + 0x00000004 + +//***************************************************************************** +// +// DMA endpoint types used with the USBLibDMAChannelAllocate() function. +// +//***************************************************************************** +#define USB_DMA_EP_RX 0x00000080 +#define USB_DMA_EP_TX 0x00000000 +#define USB_DMA_EP_HOST 0x00000040 +#define USB_DMA_EP_DEVICE 0x00000000 +#define USB_DMA_EP_TYPE_CTRL 0x00000000 +#define USB_DMA_EP_TYPE_ISOC 0x00000001 +#define USB_DMA_EP_TYPE_BULK 0x00000002 +#define USB_DMA_EP_TYPE_INT 0x00000003 +#define USB_DMA_EP_TYPE_M 0x00000003 + +//***************************************************************************** +// +// This is the internal instance data for the DMA functions and should not +// be modified outside the usbdma.c file. +// +//***************************************************************************** +struct tUSBDMAInstance +{ + uint32_t ui32Base; + + uint32_t ui32IntNum; + + uint32_t pui32Config[USB_MAX_DMA_CHANNELS]; + + uint32_t pui32MaxPacketSize[USB_MAX_DMA_CHANNELS]; + + uint32_t *ppui32Data[USB_MAX_DMA_CHANNELS]; + + uint32_t pui32Count[USB_MAX_DMA_CHANNELS]; + + uint8_t pui8Endpoint[USB_MAX_DMA_CHANNELS]; + + uint32_t pui32EPDMAMode0[USB_MAX_DMA_CHANNELS]; + + uint32_t pui32EPDMAMode1[USB_MAX_DMA_CHANNELS]; + + uint32_t ui32Pending; + + uint32_t ui32Complete; + + void (* pfnArbSizeSet)(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel, + uint32_t ui32ArbSize); + uint32_t (* pfnChannelAllocate)(tUSBDMAInstance *psUSBDMAInst, + uint8_t ui8Endpoint, + uint32_t ui32MaxPacketSize, + uint32_t ui32Config); + void (* pfnChannelEnable)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel); + void (* pfnChannelDisable)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel); + void (* pfnChannelRelease)(tUSBDMAInstance *psUSBDMAInst, + uint8_t ui8Endpoint); + uint32_t (* pfnChannelStatus)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel); + void (* pfnChannelIntDisable)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel); + void (* pfnChannelIntEnable)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel); + void (* pfnIntHandler)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Status); + uint32_t (* pfnIntStatus)(tUSBDMAInstance *psUSBDMAInst); + void (* pfnIntStatusClear)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Status); + uint32_t (* pfnStatus)(tUSBDMAInstance *psUSBDMAInst); + uint32_t (* pfnTransfer)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel, void *pvBuffer, + uint32_t ui32Size); + void (* pfnUnitSizeSet)(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel, + uint32_t ui32BitSize); +}; + +//***************************************************************************** +// +// These are the USB libraries DMA functions. +// +//***************************************************************************** +extern tUSBDMAInstance * USBLibDMAInit(uint32_t ui32Index); +extern void * USBLibDMAAddrGet(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel); +extern uint32_t USBLibDMASizeGet(tUSBDMAInstance *psUSBDMAInst, + uint32_t ui32Channel); + +//***************************************************************************** +// +//! This function returns the current DMA status for a given DMA channel. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Channel is the DMA channel number used to retrieve the DMA +//! status. +//! +//! This function returns the current status of a DMA transfer on a given +//! DMA channel. The DMA channel is specified by the \e ui32Channel parameter. +//! +//! \return This function returns one of the \b USBLIBSTATUS_DMA_* values. +// +//***************************************************************************** +#define USBLibDMAChannelStatus(psUSBDMAInst, ui32Channel) \ + psUSBDMAInst->pfnChannelStatus(psUSBDMAInst, ui32Channel) + +//***************************************************************************** +// +//! This function is used to return any global status information for USB DMA. +//! +//! \param psUSBDMAInst is a generic instance pointer that can be used to +//! distinguish between different hardware instances. +//! +//! This function performs returns the global status for the USB DMA +//! interface. +//! +//! \return Always returns 0. +// +//***************************************************************************** +#define USBLibDMAStatus(psUSBDMAInst) psUSBDMAInst->pfnStatus(psUSBDMAInst) + +//***************************************************************************** +// +//! This function returns the current DMA interrupt status. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! +//! This function returns the interrupt status for all DMA channels. The value +//! returned is a per channel interrupt mapping with the DMA channels mapped +//! into bits 0-31 by channel number with channel 1 starting at bit 0. +//! +//! \note This function does not return an endpoint interrupt status, but the +//! interrupt status for the DMA interface used with the USB controller. +//! +//! \return This function returns the pending DMA interrupts. +// +//***************************************************************************** +#define USBLibDMAIntStatus(psUSBDMAInst) \ + psUSBDMAInst->pfnIntStatus(psUSBDMAInst) + +//***************************************************************************** +// +//! This function clears the requested DMA interrupt status. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Status contains the interrupts to clear. +//! +//! This function clears the current DMA interrupt status for the +//! controller specified by the \e ui32Instance parameter. The \e ui32Status +//! value has the same format as the value returned from the +//! USBLibDMAIntStatus() function which is a per channel interrupt mapping. +//! The DMA channels are mapped into bits 0-31 by channel number with channel 1 +//!starting at bit 0. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAIntStatusClear(psUSBDMAInst, ui32Status) \ + psUSBDMAInst->pfnIntStatusClear(psUSBDMAInst, ui32Status) + +//***************************************************************************** +// +//! This function enables DMA for a given channel. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Channel is the DMA channel to enable. +//! +//! This function enables DMA on the channel number passed in the +//! \e ui32Channel parameter. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAChannelEnable(psUSBDMAInst, ui32Channel) \ + psUSBDMAInst->pfnChannelEnable(psUSBDMAInst, ui32Channel) + +//***************************************************************************** +// +//! This function disables DMA for a given DMA channel. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Channel is the DMA channel to disable. +//! +//! This function disables DMA on the channel number passed in the +//!\e ui32Channel parameter. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAChannelDisable(psUSBDMAInst, ui32Channel) \ + psUSBDMAInst->pfnChannelDisable(psUSBDMAInst, ui32Channel) + +//***************************************************************************** +// +//! This function is configures a USB transfer on a given DMA channel. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Channel is the DMA channel to use. +//! \param pvBuffer is a pointer to the buffer to use for the transfer. +//! \param ui32Size is the size of the data to be transferred in bytes. +//! +//! This function is called to configure a transfer using the USB +//! controller depending on the parameters. The \e ui32Channel parameter +//! holds the channel number to use for this transfer which must have already +//! been allocated with a call to the USBLibDMAChannelAllocate() function. The +//! transaction is configured to transfer \e ui32Size bytes to/from the buffer +//! held in the \e pvBuffer pointer. +//! +//! \return This function returns the number of bytes scheduled to be +//! transferred. +// +//***************************************************************************** +#define USBLibDMATransfer(psUSBDMAInst, ui32Channel, pvBuffer, ui32Size) \ + psUSBDMAInst->pfnTransfer(psUSBDMAInst, ui32Channel, \ + pvBuffer, ui32Size) + +//***************************************************************************** +// +//! This function is called by the USB interrupt handler. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Status is the DMA interrupt status. +//! +//! This function is called by the USB interrupt handler to allow the DMA +//! interface to handle interrupts outside of the context of the normal USB +//! interrupt handler. The \e ui32Status is the current DMA interrupt status +//! at the time of the USB interrupt. Since some DMA controller interrupts are +//! cleared automatically when read, this value must be retrieved by calling +//! the USBLibDMAIntStatus() function and passed into this function. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAIntHandler(psUSBDMAInst, ui32Status) \ + psUSBDMAInst->pfnIntHandler(psUSBDMAInst, ui32Status) + +//***************************************************************************** +// +//! This function is used to assign a DMA channel to an endpoint. +//! +//! \param psUSBDMAInst is the DMA instance data for a USB controller. +//! \param ui8Endpoint is the endpoint number to assign a DMA channel. +//! \param ui32MaxPacketSize is the maximum packet size for the endpoint +//! assigned that is being assigned to the DMA channel. +//! \param ui32Config are the basic configuration options for the DMA channel. +//! +//! This function assigns a DMA channel to a given endpoint. The +//! \e ui8Endpoint parameter is the zero based endpoint number that is assigned +//! a DMA channel. The \e ui32Config parameter contains any configuration +//! options for the DMA channel. The current options include the following: +//! - \b USB_DMA_EP_TX - this request is for a transmit DMA channel. +//! - \b USB_DMA_EP_RX - this request is for a receive DMA channel. +//! +//! \note The maximum number of available DMA channels to endpoints varies +//! between devices. +//! +//! \return Zero or the DMA channel assigned to the endpoint. +// +//***************************************************************************** +#define USBLibDMAChannelAllocate(psUSBDMAInst, ui8Endpoint, ui32MaxPacketSize,\ + ui32Config) \ + psUSBDMAInst->pfnChannelAllocate(psUSBDMAInst, \ + ui8Endpoint, \ + ui32MaxPacketSize, \ + ui32Config) + +//***************************************************************************** +// +//! This function is used to free a DMA channel that was assigned to an +//! endpoint. +//! +//! \param psUSBDMAInst is the DMA instance data for a USB controller. +//! \param ui8Endpoint is the DMA channel number to free up. +//! +//! This function frees up a DMA channel that was allocated to an endpoint +//! by the USBLibDMAChannelAllocate() function. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAChannelRelease(psUSBDMAInst, ui8Endpoint) \ + psUSBDMAInst->pfnChannelRelease(psUSBDMAInst, ui8Endpoint) + +//***************************************************************************** +// +//! This function is used to set the individual transfer size of a DMA channel. +//! +//! \param psUSBDMAInst is the DMA instance data for a USB controller. +//! \param ui32Channel is the DMA channel number to modify. +//! \param ui32BitSize is the individual transfer size in bits(8, 16 or 32). +//! +//! This function configures the individual transfer size of the DMA channel +//! provided in the \e ui32Channel parameter. The \e ui32Channel must already +//! be allocated to an endpoint by calling the USBLibDMAChannelAllocate() +//! function. The \e ui32BitSize parameter should be on of the following +//! values: 8, 16 or 32. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAUnitSizeSet(psUSBDMAInst, ui32Channel, ui32BitSize) \ + psUSBDMAInst->pfnUnitSizeSet(psUSBDMAInst, ui32Channel, \ + ui32BitSize); + +//***************************************************************************** +// +//! This function is used to set the arbitration size for a DMA channel. +//! +//! \param psUSBDMAInst is the DMA instance data for a USB controller. +//! \param ui32Channel is the DMA channel number to modify. +//! \param ui32ArbSize is the transfer arbitration size in bytes. +//! +//! This function configures the individual transfer size of the DMA channel +//! provided in the \e ui32Channel parameter. The \e ui32Channel must already +//! be allocated to an endpoint by calling the USBLibDMAChannelAllocate() +//! function. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAArbSizeSet(psUSBDMAInst, ui32Channel, ui32ArbSize) \ + psUSBDMAInst->pfnArbSizeSet(psUSBDMAInst, ui32Channel, \ + ui32ArbSize); + +//***************************************************************************** +// +//! This function enables the DMA interrupt for a given channel. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Channel is the DMA channel interrupt to enable. +//! +//! This function enables DMA interrupt on the channel number passed in the +//! \e ui32Channel parameter. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAChannelIntEnable(psUSBDMAInst, ui32Channel) \ + psUSBDMAInst->pfnChannelIntEnable(psUSBDMAInst, ui32Channel) + +//***************************************************************************** +// +//! This function disables DMA interrupt for a given DMA channel. +//! +//! \param psUSBDMAInst is the DMA structure pointer for this instance. +//! \param ui32Channel is the DMA channel interrupt to disable. +//! +//! This function disables the DMA interrupt on the channel number passed in +//! the \e ui32Channel parameter. +//! +//! \return None. +// +//***************************************************************************** +#define USBLibDMAChannelIntDisable(psUSBDMAInst, ui32Channel) \ + psUSBDMAInst->pfnChannelIntDisable(psUSBDMAInst, ui32Channel) + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +#endif // __USBLIBPRIV_H__ diff --git a/usblib/usbmode.c b/usblib/usbmode.c new file mode 100644 index 0000000..3d95c11 --- /dev/null +++ b/usblib/usbmode.c @@ -0,0 +1,1180 @@ +//***************************************************************************** +// +// usbmode.c - Functions related to dual mode USB device/host operation. +// +// 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_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_usb.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup general_usblib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The following label defines interrupts that we will always pass to the host +// interrupt handler even if we are in dual mode and not yet sure of which +// mode we are operating in. +// +//***************************************************************************** +#define USB_HOST_INTS (USB_INTCTRL_VBUS_ERR) + +//***************************************************************************** +// +// Global variable indicating which mode of operation the application has +// requested. +// +//***************************************************************************** +volatile tUSBMode g_iUSBMode = eUSBModeNone; + +//***************************************************************************** +// +// The default and the current polling rate for the USB OTG library. +// +//***************************************************************************** +volatile uint32_t g_ui32PollRate; + +//***************************************************************************** +// +// The current time remaining in milliseconds before checking the cable +// connection. +// +//***************************************************************************** +volatile uint32_t g_ui32WaitTicks = 0; + +//***************************************************************************** +// +// This enum holds the various states that we can be in while performing +// USB mode checking. This involves use of the OTG session request to poll +// the USB ID pin to determine whether a device or a host is connected. +// +//***************************************************************************** +typedef enum +{ + // + // No checking is currently pending. + // + eUSBOTGModeIdle, + + // + // Waiting on ID mode detection. + // + eUSBOTGModeWaitID, + + // + // Waiting for next poll interval. + // + eUSBOTGModeWait, + + // + // Now in B-side wait for connect. + // + eUSBOTGModeBWaitCon, + + // + // Now in A-side device mode. + // + eUSBOTGModeBDevice, + + // + // Now in A-side host mode. + // + eUSBOTGModeAHost, +} +tUSBOTGState; + +volatile tUSBOTGState g_eOTGModeState; + +//***************************************************************************** +// +// Global variable indicating whether we are currently operating in host or +// device mode if the user has requested Dual mode operation. +// +//***************************************************************************** +static volatile tUSBMode g_iDualMode = eUSBModeNone; + +static void USBOTGRemovePower(uint32_t ui32Index); + +//***************************************************************************** +// +// Global variable holding a pointer to the callback function which will be +// called when the USB mode changes between device and host. +// +//***************************************************************************** +static tUSBModeCallback g_pfnUSBModeCallback; + +//***************************************************************************** +// +// This function is used to handle switching between host, device and +// unconfigured modes. +// +// \param iUSBMode is one of eUSBModeHost, eUSBModeDevice, or eUSBModeNone. +// +// Based on the current state held in g_iDualMode variable this function will +// handle the transition of the mode of operation in OTG mode and calling +// the callback function if it is present. +// +// \return None. +// +//***************************************************************************** +static void +USBOTGSetMode(tUSBMode iUSBMode) +{ + if((g_iDualMode != iUSBMode) || (g_iDualMode == eUSBModeNone)) + { + // + // If going from host mode to unconfigured mode then remove power. + // + if((g_iDualMode == eUSBModeHost) && (iUSBMode == eUSBModeNone)) + { + // + // Take the steps to remove power in the of host mode OTG. + // + USBOTGRemovePower(0); + } + + // + // If going from device mode to unconfigured mode then end the current + // session. + // + if((g_iDualMode == eUSBModeDevice) && (iUSBMode == eUSBModeNone)) + { + // + // End the current session. + // + USBOTGSessionRequest(USB0_BASE, false); + } + + // + // Reset the delay whenever returning to eUSBModeNone. + // + if(iUSBMode == eUSBModeNone) + { + g_ui32WaitTicks = g_ui32PollRate; + } + + // + // Do we have a mode change callback installed? + // + if((g_pfnUSBModeCallback) && (g_iDualMode != iUSBMode)) + { + // + // Inform the callback of the new operating mode. + // + g_pfnUSBModeCallback(0, iUSBMode); + } + + // + // Save the new mode. + // + g_iDualMode = iUSBMode; + } +} + +//***************************************************************************** +// +//! Allows dual mode application to switch between USB device and host modes +//! and provides a method to force the controller into the desired mode. +//! +//! \param ui32Index specifies the USB controller whose mode of operation is to +//! be set. This parameter must be set to 0. +//! \param iUSBMode indicates the mode that the application wishes to operate +//! in. Valid values are \b eUSBModeDevice to operate as a USB device and +//! \b eUSBModeHost to operate as a USB host. +//! \param pfnCallback is a pointer to a function which the USB library will +//! call each time the mode is changed to indicate the new operating mode. In +//! cases where \e iUSBMode is set to either \b eUSBModeDevice or +//! \b eUSBModeHost, the callback will be made immediately to allow the +//! application to perform any host or device specific initialization. +//! +//! This function allows a USB application that can operate in host +//! or device mode to indicate to the USB stack the mode that it wishes to +//! use. The caller is responsible for cleaning up the interface and removing +//! itself from the bus prior to making this call and reconfiguring afterwards. +//! The \e pfnCallback function can be a NULL(0) value to indicate that no +//! notification is required. +//! +//! For successful dual mode mode operation, an application must register +//! USB0DualModeIntHandler() as the interrupt handler for the USB0 interrupt. +//! This handler is responsible for steering interrupts to the device or host +//! stack depending upon the chosen mode. Devices which do not require dual +//! mode capability should register either \e USB0DeviceIntHandler() or +//! \e USB0HostIntHandler() instead. Registering \e USB0DualModeIntHandler() +//! for a single mode application will result in an application binary larger +//! than required since library functions for both USB operating modes will be +//! included even though only one mode is required. +//! +//! Single mode applications (those offering exclusively USB device or USB +//! host functionality) are only required to call this function if they need to +//! force the mode of the controller to Host or Device mode. This is usually +//! in the event that the application needs to reused the USBVBUS and/or USBID +//! pins as GPIOs. +//! +//! \return None. +// +//***************************************************************************** +void +USBStackModeSet(uint32_t ui32Index, tUSBMode iUSBMode, + tUSBModeCallback pfnCallback) +{ + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + + // + // Remember the mode so that we can steer the interrupts appropriately. + // + g_iUSBMode = iUSBMode; + + // + // Remember the callback pointer. + // + g_pfnUSBModeCallback = pfnCallback; + + // + // If we are being asked to be either a host or device, we will not be + // trying to auto-detect the mode so make the callback immediately. + // + if((iUSBMode == eUSBModeDevice) || (iUSBMode == eUSBModeHost)) + { + // + // Make sure that a callback was provided. + // + if(g_pfnUSBModeCallback) + { + g_pfnUSBModeCallback(0, iUSBMode); + } + } +} + +//***************************************************************************** +// +//! Steers USB interrupts from controller to the correct handler in the USB +//! stack. +//! +//! This interrupt handler is used in applications which require to operate +//! in both host and device mode. It steers the USB hardware interrupt to the +//! correct handler in the USB stack depending upon the current operating mode +//! of the application, USB device or host. +//! +//! For successful dual mode operation, an application must register +//! USB0DualModeIntHandler() in the CPU vector table as the interrupt handler +//! for the USB0 interrupt. This handler is responsible for steering +//! interrupts to the device or host stack depending upon the chosen mode. +//! +//! \note Devices which do not require dual mode capability should register +//! either USB0DeviceIntHandler() or USB0HostIntHandler() instead. Registering +//! USB0DualModeIntHandler() for a single mode application will result in an +//! application binary larger than required since library functions for both +//! USB operating modes will be included even though only one mode is actually +//! required. +//! +//! \return None. +// +//***************************************************************************** +void +USB0DualModeIntHandler(void) +{ + uint32_t ui32Status; + + // + // Read the USB interrupt status. + // + ui32Status = USBIntStatusControl(USB0_BASE); + + // + // Pass through the subset of interrupts that we always want + // the host stack to see regardless of whether or not we + // are actually in host mode at this point. + // + if(ui32Status & USB_HOST_INTS) + { + // + // Call the host's interrupt handler. + // + USBHostIntHandlerInternal(0, ui32Status & USB_HOST_INTS); + + // + // We have already processed these interrupts so clear them + // from the status. + // + ui32Status &= ~USB_HOST_INTS; + } + + // + // Steer the interrupt to the appropriate handler within the stack + // depending upon our current operating mode. Note that we need to pass + // the ui32Status parameter since the USB interrupt register is + // clear-on-read. + // + switch(g_iUSBMode) + { + case eUSBModeNone: + { + // + // No mode is set yet so we have no idea what to do. Just ignore + // the interrupt. + // + break; + } + + // + // Operating in pure host mode. + // + case eUSBModeHost: + { + // + // Call the host interrupt handler if there is anything still to + // process. + // + USBHostIntHandlerInternal(0, ui32Status); + + break; + } + + // + // Operating in pure device mode. + // + case eUSBModeDevice: + { + // + // Call the device interrupt handler. + // + USBDeviceIntHandlerInternal(0, ui32Status); + + break; + } + + default: + { + break; + } + } +} + +//***************************************************************************** +// +// Close the Doxygen group general_usblib_api. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup dualmode_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Initializes the USB controller for dual mode operation. +//! +//! \param ui32Index specifies the USB controller that is to be initialized for +//! dual mode operation. This parameter must be set to 0. +//! +//! This function initializes the USB controller hardware into a state +//! suitable for dual mode operation. Applications may use this function to +//! ensure that the controller is in a neutral state and able to receive +//! appropriate interrupts before host or device mode is chosen using a call +//! to USBStackModeSet(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDualModeInit(uint32_t ui32Index) +{ + // + // We only support a single USB controller. + // + ASSERT(ui32Index == 0); + + // + // Configure the End point 0. + // + USBHostEndpointConfig(USB0_BASE, USB_EP_0, 64, 0, 0, + (USB_EP_MODE_CTRL | USB_EP_SPEED_FULL | + USB_EP_HOST_OUT)); + + // + // Enable USB Interrupts. + // + MAP_USBIntEnableControl(USB0_BASE, USB_INTCTRL_RESET | + USB_INTCTRL_DISCONNECT | + USB_INTCTRL_SESSION | + USB_INTCTRL_BABBLE | + USB_INTCTRL_CONNECT | + USB_INTCTRL_RESUME | + USB_INTCTRL_SUSPEND | + USB_INTCTRL_VBUS_ERR); + + // + // Enable all endpoint interrupts. + // + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Initialize the USB tick module. + // + InternalUSBTickInit(); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_psDCDInst[0].ui32IntNum); + // + // Turn on session request to enable ID pin checking. + // + USBOTGSessionRequest(USB0_BASE, true); + + // + // Initialize the power configuration. + // + USBHostPwrConfig(USB0_BASE, USBHCDPowerConfigGet(ui32Index)); + + // + // If power enable is automatic then then USBHostPwrEnable() has to be + // called to allow the USB controller to control the power enable pin. + // + if(USBHCDPowerAutomatic(ui32Index)) + { + // + // This will not turn on power but instead will allow the USB + // controller to turn on power when needed. + // + USBHostPwrEnable(USB0_BASE); + } +} + +//***************************************************************************** +// +//! Returns the USB controller to the default mode when in dual mode operation. +//! +//! \param ui32Index specifies the USB controller whose dual mode operation is +//! to be ended. This parameter must be set to 0. +//! +//! Applications using both host and device modes may call this function to +//! disable interrupts in preparation for shutdown or a change of operating +//! mode. +//! +//! \return None. +// +//***************************************************************************** +void +USBDualModeTerm(uint32_t ui32Index) +{ + // + // We only support a single USB controller. + // + ASSERT(ui32Index == 0); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_psDCDInst[0].ui32IntNum); + + MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL); + + MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL); +} + +//***************************************************************************** +// +// Close the Doxygen group dualmode_api. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup usblib_otg +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Returns the USB controller to and inactive state when in OTG mode +//! operation. +//! +//! \param ui32Index specifies the USB controller to end OTG mode operations. +//! +//! Applications using OTG mode may call this function to disable interrupts +//! in preparation for shutdown or a change of operating mode. +//! +//! \return None. +// +//***************************************************************************** +void +USBOTGModeTerm(uint32_t ui32Index) +{ + // + // We only support a single USB controller. + // + ASSERT(ui32Index == 0); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_psDCDInst[0].ui32IntNum); + + // + // Disable all control interrupts. + // + MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL); + + // + // Disable all endpoint interrupts. + // + MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Set the mode to none if it is not already. + // + USBOTGSetMode(eUSBModeNone); +} + +//***************************************************************************** +// +//! Initializes the USB controller for OTG mode operation. +//! +//! \param ui32Index specifies the USB controller that is to be initialized for +//! OTG mode operation. +//! \param ui32PollingRate is the rate in milliseconds to poll the controller +//! for changes in mode. +//! \param pvPool is a pointer to the data to use as a memory pool for this +//! controller. +//! \param ui32PoolSize is the size in bytes of the buffer passed in as +//! \e pvPool. +//! +//! This function initializes the USB controller hardware into a state +//! suitable for OTG mode operation. Applications must use this function to +//! ensure that the controller is in a neutral state and able to receive +//! appropriate interrupts before host or device mode is chosen by OTG +//! negotiation. The \e ui32PollingRate parameter is used to set the rate at +//! which the USB library will poll the controller to determine the mode. This +//! has the most effect on how quickly the USB library will detect changes when +//! going to host mode. The parameters \e pvPool and \e ui32PoolSize are +//! passed on to the USB host library functions to provide memory for the USB +//! library when it is acting as a host. Any device and host initialization +//! should have been called before calling this function to prevent the USB +//! library from attempting to run in device or host mode before the USB +//! library is fully configured. +//! +//! \return None. +// +//***************************************************************************** +void +USBOTGModeInit(uint32_t ui32Index, uint32_t ui32PollingRate, + void *pvPool, uint32_t ui32PoolSize) +{ + // + // We only support a single USB controller. + // + ASSERT(ui32Index == 0); + + // + // This should never be called if not in OTG mode. + // + ASSERT(g_iUSBMode == eUSBModeOTG); + + // + // Force OTG mode in all cases since anything else is invalid, but a DEBUG + // build will still ASSERT above if this value is incorrect. + // + g_iUSBMode = eUSBModeOTG; + + // + // Remember that we have not yet determined whether we are device or + // host. + // + g_iDualMode = eUSBModeNone; + + // + // Set the default polling rate. + // + g_ui32PollRate = ui32PollingRate; + + // + // Enable the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Initialize the host controller stack. + // + USBHCDInit(ui32Index, pvPool, ui32PoolSize); + + // + // Configure the End point 0. + // + USBHostEndpointConfig(USB0_BASE, USB_EP_0, 64, 0, 0, + (USB_EP_MODE_CTRL | USB_EP_SPEED_FULL | + USB_EP_HOST_OUT)); + + // + // Enable control interrupts. + // + MAP_USBIntEnableControl(USB0_BASE, USB_INTCTRL_RESET | + USB_INTCTRL_DISCONNECT | + USB_INTCTRL_SESSION | + USB_INTCTRL_BABBLE | + USB_INTCTRL_CONNECT | + USB_INTCTRL_RESUME | + USB_INTCTRL_SUSPEND | + USB_INTCTRL_VBUS_ERR | + USB_INTCTRL_MODE_DETECT | + USB_INTCTRL_SOF); + + // + // Make sure the mode OTG mode and not forced device or host. + // + USBOTGMode(USB0_BASE); + + // + // Enable all endpoint interrupts. + // + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Initialize the power configuration. + // + USBHCDPowerConfigSet(ui32Index, USBHCDPowerConfigGet(ui32Index)); + + // + // If power enable is automatic then then USBHostPwrEnable() has to be + // called to allow the USB controller to control the power enable pin. + // + if(USBHCDPowerAutomatic(ui32Index)) + { + // + // This will not turn on power but instead will allow the USB + // controller to turn on power when needed. + // + USBHostPwrEnable(USB0_BASE); + } + + // + // Enable the USB interrupt. + // + if(CLASS_IS_TM4C129) + { + OS_INT_ENABLE(INT_USB0_TM4C129); + } + else + { + OS_INT_ENABLE(INT_USB0_TM4C123); + } +} + +//***************************************************************************** +// +// This function handles the steps required to remove power in OTG mode. +// +// \param ui32Index specifies which USB controller should remove power. +// +// This function will perform the steps required to remove power from the USB +// bus as required by the OTG specification. This call will first issue a +// bus suspend followed by clearing the current session and then removing +// power. +// +// \return None. +// +//***************************************************************************** +static void +USBOTGRemovePower(uint32_t ui32Index) +{ + tEventInfo sEvent; + + // + // Do suspend signaling. + // + USBHostSuspend(USB0_BASE); + + // + // End the session in either device or host mode. + // + USBOTGSessionRequest(USB0_BASE, false); + + // + // Check if the controller is automatically applying power or not. + // + if(USBHCDPowerAutomatic(ui32Index) == 0) + { + // + // Call the registered event driver to allow it to disable power. + // + sEvent.ui32Event = USB_EVENT_POWER_DISABLE; + sEvent.ui32Instance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWRDIS); + } +} + +//***************************************************************************** +// +//! This call sets the USB OTG controllers poll rate when checking for the mode +//! of the controller. +//! +//! \param ui32Index specifies which USB controller to set the polling rate. +//! \param ui32PollRate is the rate in milliseconds to poll for changes in the +//! controller mode. +//! +//! This function is called to set the USB OTG libraries polling rate when +//! checking the status of the cable. The \e ui32PollRate value used sets the +//! rate in milliseconds that the USB OTG library will poll the cable to see +//! if the controller should enter host mode. This value has no effect on +//! device detection rate as the controller will detect being connected to a +//! host controller automatically. The \e ui32PollRate can be set to 0 to +//! disable polling. The USB OTG library can still function with the polling +//! rate set to zero, however it will fail to detect host mode properly when no +//! device is present at the end of the USB OTG B side of the cable. +//! +//! \note This function should only be called on devices that support OTG +//! functionality. +//! +//! \return None. +// +//***************************************************************************** +void +USBOTGPollRate(uint32_t ui32Index, uint32_t ui32PollRate) +{ + // + // Save the timeout. + // + g_ui32PollRate = ui32PollRate; +} + +//***************************************************************************** +// +//! Handles OTG mode changes and also steers other USB interrupts from +//! the controller to the correct handler in the USB stack. +//! +//! This interrupt handler is used in applications which require to operate +//! in both host and device mode using OTG. When in host or device mode, it +//! steers the USB hardware interrupt to the correct handler in the USB stack +//! depending upon the current operating mode. It also handles other OTG +//! related interrupt events. +//! +//! For successful OTG mode operation, an application must register +//! USB0OTGModeIntHandler() in the CPU vector table as the interrupt handler +//! for the USB0 interrupt. +//! +//! \note This interrupt handler should only be used on controllers that +//! support OTG functionality. +//! +//! \return None. +// +//***************************************************************************** +void +USB0OTGModeIntHandler(void) +{ + uint32_t ui32Status; + tEventInfo sEvent; + + // + // Read the USB interrupt status. + // + ui32Status = USBIntStatusControl(USB0_BASE); + + // + // Check if this was an mode detect interrupt and under manual power + // control. + // + if((ui32Status & USB_INTCTRL_MODE_DETECT) && + (USBHCDPowerAutomatic(0) == 0)) + { + uint32_t ui32Mode; + + ui32Mode = USBModeGet(USB0_BASE); + + switch(ui32Mode) + { + // + // Device is on the A side of the cable and power needs to be + // applied. + // + case USB_OTG_MODE_ASIDE_NPWR: + case USB_OTG_MODE_ASIDE_SESS: + case USB_OTG_MODE_ASIDE_AVAL: + { + // + // Since power is not automatically enabled, call the + // registered event handler to allow the application to turn + // on power. + // + sEvent.ui32Event = USB_EVENT_POWER_ENABLE; + sEvent.ui32Instance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWREN); + + break; + } + + // + // Device is on the B side of the cable and powered. + // + case USB_OTG_MODE_BSIDE_DEV: + { + // + // Now in device mode on the B side of the cable and will wait + // for a connect before becoming a device. + // + g_eOTGModeState = eUSBOTGModeBWaitCon; + + break; + } + + // + // Any other mode detect indicates eUSBModeNone. + // + default: + { + // + // Just inform the application that the mode was not device + // or host. + // + USBOTGSetMode(eUSBModeNone); + + break; + } + } + } + + // + // If there was a VBUS error then the power should be shut off and the + // system is reset to waiting for detection again. + // + if(ui32Status & USB_INTCTRL_VBUS_ERR) + { + // + // Just inform the application that the mode was not device + // or host. + // + USBOTGSetMode(eUSBModeNone); + + // + // Return to idle mode. + // + g_eOTGModeState = eUSBOTGModeWait; + } + + // + // If there is a disconnect interrupt and the controller was on the B side + // cable as a device then go back to the IDLE state. + // + if((ui32Status & USB_INTCTRL_DISCONNECT) && + (g_eOTGModeState == eUSBOTGModeBDevice)) + { + // + // No longer a device so switch to unconfigured mode. + // + USBOTGSetMode(eUSBModeNone); + + // + // Return to idle mode. + // + g_eOTGModeState = eUSBOTGModeWait; + + return; + } + + // + // Handle receiving a reset. + // + if((ui32Status & USB_INTCTRL_RESET)&& + (g_eOTGModeState != eUSBOTGModeBDevice)) + { + // + // Getting a reset interrupt when not already a b side device indicates + // that a host is resetting the device and the controller should + // move to device mode. + // + g_eOTGModeState = eUSBOTGModeBDevice; + + // + // Save the new mode. + // + USBOTGSetMode(eUSBModeDevice); + } + + // + // If there is a connect interrupt while the library is waiting for + // one then move to full host mode state. + // + if(ui32Status & USB_INTCTRL_CONNECT) + { + // + // Move to A side host state. + // + g_eOTGModeState = eUSBOTGModeAHost; + + // + // Inform the application that controller is in host mode. + // + USBOTGSetMode(eUSBModeHost); + } + + // + // Call the correct device or host interrupt handler based on the current + // mode of operation. + // + switch(g_eOTGModeState) + { + case eUSBOTGModeAHost: + { + // + // Call the host interrupt handler if there is anything still to + // process. + // + USBHostIntHandlerInternal(0, ui32Status); + + break; + } + + // + // Operating in pure device mode. + // + case eUSBOTGModeBDevice: + { + // + // Call the device interrupt handler. + // + USBDeviceIntHandlerInternal(0, ui32Status); + + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB host stack code to indicated that it +// has completed handing the device disconnection. +// +// \param ui32Index specifies the USB controller that has completed disconnect. +// +// This internal library function is used when the hsot controller has +// completed any deferred handling when it has detected a device has been +// disconnected. The functions main purpose is to return the OTG controller to +// a state that allows for resuming normal OTG cable detection and negotiation. +// +// \note This function should not be called outside the library. +// +//***************************************************************************** +void +OTGDeviceDisconnect(uint32_t ui32Index) +{ + // + // This function is only valid when called in host mode. + // + if(g_eOTGModeState == eUSBOTGModeAHost) + { + // + // No longer a host so switch to unconfigured mode. + // + USBOTGSetMode(eUSBModeNone); + + g_eOTGModeState = eUSBOTGModeWait; + } +} + +//***************************************************************************** +// +//! This function is the main routine for the OTG Controller Driver. +//! +//! \param ui32MsTicks is the number of milliseconds that have passed since the +//! last time this function was called. +//! +//! This function is the main routine for the USB controller when using the +//! library in OTG mode. This routine must be called periodically by the main +//! application outside of a callback context. The \e ui32MsTicks value is +//! used for basic timing needed by the USB library when operating in OTG mode. +//! This allows for a simple cooperative system to access the the OTG +//! controller driver interface without the need for an RTOS. All time +//! critical operations are handled in interrupt context but all longer +//! operations are run from the this function to allow them to block and wait +//! for completion without holding off other interrupts. +//! +//! \return None. +// +//***************************************************************************** +void +USBOTGMain(uint32_t ui32MsTicks) +{ + tEventInfo sEvent; + + if(ui32MsTicks > g_ui32WaitTicks) + { + g_ui32WaitTicks = 0; + } + else + { + g_ui32WaitTicks -= ui32MsTicks; + } + + switch(g_eOTGModeState) + { + case eUSBOTGModeIdle: + { + g_eOTGModeState = eUSBOTGModeWaitID; + + // + // Initiate a session request and check the ID pin. + // + USBOTGSessionRequest(USB0_BASE, true); + break; + } + case eUSBOTGModeWait: + case eUSBOTGModeWaitID: + { + // + // If reached the timeout and polling is enabled then look again. + // + if((g_ui32WaitTicks == 0) && (g_ui32PollRate != 0)) + { + // + // Remove the session request. + // + USBOTGSessionRequest(USB0_BASE, false); + + // + // Return to idle mode. + // + USBOTGSetMode(eUSBModeNone); + + // + // Check if the controller is automatically applying power or + // not. + // + if(USBHCDPowerAutomatic(0) == 0) + { + // + // Call the registered event driver to allow it to disable + // power. + // + sEvent.ui32Event = USB_EVENT_POWER_DISABLE; + sEvent.ui32Instance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWRDIS); + } + + // + // Go back to the idle state. + // + g_eOTGModeState = eUSBOTGModeIdle; + } + break; + } + case eUSBOTGModeAHost: + { + // + // Call the host main routine when acting as a host. + // + USBHCDMain(); + break; + } + case eUSBOTGModeBWaitCon: + case eUSBOTGModeBDevice: + default: + { + break; + } + } +} + +//***************************************************************************** +// +//! This function sets one of the \b USBLIB_FEATURE_ requests. +//! +//! \param ui32Index is the index of the USB controller to access. +//! \param ui32Feature is one of the \b USBLIB_FEATURE_ defines. +//! \param pvFeature is a pointer to the data for the \b USBLIB_FEATURE +//! request. +//! +//! This function sends the requested feature request to the USB library. +//! Not all features are supported by all devices so see the documentation +//! for the \b USBLIB_FEATURE_ to determine if the feature is supported. +//! +//! \return Returns \b true if the feature was set and \b false if the feature +//! is not supported or could not be changed to the requested value. +// +//***************************************************************************** +bool +USBOTGFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, void *pvFeature) +{ + bool bRetCode; + + bRetCode = true; + + // + // Pass this on to the host and device and indicate false if + // either fails. + // + if(USBDCDFeatureSet(ui32Index, ui32Feature, pvFeature) == false) + { + bRetCode = false; + } + + if(USBHCDFeatureSet(ui32Index, ui32Feature, pvFeature) == false) + { + bRetCode = false; + } + + return(bRetCode); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbmsc.h b/usblib/usbmsc.h new file mode 100644 index 0000000..115695b --- /dev/null +++ b/usblib/usbmsc.h @@ -0,0 +1,444 @@ +//***************************************************************************** +// +// usbmsc.h - Generic types and defines use by the mass storage class. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBMSC_H__ +#define __USBMSC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The request for the maximum number of logical units on a mass storage +// device. +// +//***************************************************************************** +#define USBREQ_GET_MAX_LUN 0xfe + +//***************************************************************************** +// +// The request for the bulk only reset of a mass storage device. +// +//***************************************************************************** +#define USBREQ_BULK_ONLY_RESET 0xff + +//***************************************************************************** +// +// The signatures defined by USB MSC class specification. +// +//***************************************************************************** +#define CBW_SIGNATURE 0x43425355 +#define CSW_SIGNATURE 0x53425355 + +//***************************************************************************** +// +// Flag for the bmCBWFlags member of tMSCCBW +// +//***************************************************************************** +#define CBWFLAGS_DIR_M 0x80 +#define CBWFLAGS_DIR_IN 0x80 +#define CBWFLAGS_DIR_OUT 0x00 + +//***************************************************************************** +// +// All structures defined in this section of the header require byte packing of +// fields. This is usually accomplished using the PACKED macro but, for IAR +// Embedded Workbench, this requries a pragma. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// The following packed structure is used to access the Command Block Wrapper +// (CBW) data structure that is used when communicating with USB Mass Storage +// Class devices. +// +//***************************************************************************** +typedef struct +{ + // + // Signature that helps identify this data packet as a CBW. The signature + // field shall contain the value 0x43425355 (little endian), indicating a + // CBW. + // + uint32_t dCBWSignature; + + // + // The Command Block Tag sent by the host controller. The device shall + // echo the contents of this field back to the host in the dCSWTag field + // of the associated CSW. The dCSWTag positively associates a CSW with the + // corresponding CBW. + // + uint32_t dCBWTag; + + // + // The number of bytes of data that the host expects to transfer on the + // Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during + // the execution of this command. If this field is zero, the device and + // the host will not transfer data between the CBW and the associated CSW, + // and the device will ignore the value of the Direction bit in + // bmCBWFlags. + // + uint32_t dCBWDataTransferLength; + + // + // The device will ignore these bits if the dCBWDataTransferLength value + // is set to 0. + // + // The bits of this field are defined as follows: + // Bit 7 Direction + // 0 = Data-Out from host to the device, + // 1 = Data-In from the device to the host. + // Bit 6 Obsolete - The host shall set this bit to zero. + // Bits 5..0 Reserved - the host shall set these bits to zero. + // + uint8_t bmCBWFlags; + + // + // The device Logical Unit Number (LUN) to which the command block is being + // sent. For devices that support multiple LUNs, the host shall place into + // this field the LUN to which this command block is addressed. Otherwise, + // the host shall set this field to zero. + // + uint8_t bCBWLUN; + + // + // The valid length of the CBWCB in bytes. This defines the valid length + // of the command block. The only legal values are 1 through 16. All + // other values are reserved. + // + uint8_t bCBWCBLength; + + // + // This array holds the command block to be executed by the device. The + // MSC device will interpret the first bCBWCBLength bytes in this field as + // a command block as defined by the command set identified by + // bInterfaceSubClass. If the command set supported by the device uses + // command blocks of fewer than 16 bytes in length, the significant bytes + // shall be transferred first, beginning with the byte at offset 15. The + // device will ignore the content of the CBWCB field past the byte at + // offset (15 + bCBWCBLength - 1). + // + uint8_t CBWCB[16]; +} +PACKED tMSCCBW; + +//***************************************************************************** +// +// Flags for the bCSWStatus member of tMSCCSW +// +//***************************************************************************** +#define CSWSTATUS_CMD_SUCCESS 0 +#define CSWSTATUS_CMD_FAILED 1 +#define CSWSTATUS_PHASE_ERROR 2 + +//***************************************************************************** +// +// This structure encapsulates the Command Status Word (CSW) structure that is +// sent in response to all CBW commands. +// +//***************************************************************************** +typedef struct +{ + // + // Signature that identifies this data packet as a CSW. The signature + // field must contain the value 53425355h (little endian) to indicate CSW. + // + uint32_t dCSWSignature; + + // + // The device will set this field to the value received in the dCBWTag of + // the associated CBW. + // + uint32_t dCSWTag; + + // + // For OUT transactions the device will fill the dCSWDataResidue field with + // the difference between the amount of data expected as stated in the + // dCBWDataTransferLength, and the actual amount of data processed by the + // device. For IN transactions the device will fill the dCSWDataResidue + // field with the difference between the amount of data expected as stated + // in the dCBWDataTransferLength and the actual amount of relevant data + // sent by the device. The dCSWDataResidue will not exceed the value sent + // in the dCBWDataTransferLength. + // + uint32_t dCSWDataResidue; + + // + // The bCSWStatus field indicates the success or failure of the command. + // The device shall set this byte to zero if the command completed + // successfully. A non-zero value shall indicate a failure during command + // execution. + // + uint8_t bCSWStatus; +} +PACKED tMSCCSW; + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// SCSI Command return codes. +// +//***************************************************************************** +#define SCSI_CMD_STATUS_PASS 0x00 +#define SCSI_CMD_STATUS_FAIL 0x01 + +//***************************************************************************** +// +// SCSI commands. +// +//***************************************************************************** +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_INQUIRY_CMD 0x12 +#define SCSI_MODE_SENSE_6 0x1a +#define SCSI_START_STOP_UNIT 0x1b +#define SCSI_MEDIUM_REMOVAL 0x1e +#define SCSI_READ_CAPACITIES 0x23 +#define SCSI_READ_CAPACITY 0x25 +#define SCSI_READ_10 0x28 +#define SCSI_WRITE_10 0x2a + +//***************************************************************************** +// +// SCSI Test Unit Ready definitions. +// +//***************************************************************************** + +//***************************************************************************** +// +// SCSI Inquiry command definitions. +// +//***************************************************************************** + +//***************************************************************************** +// +// Size of the SCSI inquiry response data. +// +//***************************************************************************** +#define SCSI_INQUIRY_DATA_SZ 36 + +//***************************************************************************** +// +// Offset 0 of the Inquiry Data. +// +//***************************************************************************** +#define SCSI_INQ_PQ_M 0xe0 // Peripheral Qualifier Mask. +#define SCSI_INQ_PQ_CNCT 0x00 // Device connected. +#define SCSI_INQ_PQ_DISC 0x20 // Device disconnected. +#define SCSI_INQ_PDT_M 0x1f // Peripheral Device Type Mask. +#define SCSI_INQ_PDT_SBC 0x00 // Direct Access device. + +//***************************************************************************** +// +// Offset 1 of the Inquiry Data. +// +//***************************************************************************** +#define SCSI_INQ_RMB 0x80 // Device is removable. + +//***************************************************************************** +// +// Macro to check if removeable. +// +//***************************************************************************** +#define SCSIIsRemovable(pData) \ + (((uint8_t *)pData)[1] & SCSI_INQ_RMB) + +//***************************************************************************** +// +// SCSI Read Capacity definitions. +// +//***************************************************************************** + +//***************************************************************************** +// +// Size of the SCSI Read Capacity response data. +// +//***************************************************************************** +#define SCSI_READ_CAPACITY_SZ 0x08 + +//***************************************************************************** +// +// SCSI Mode Sense definitions, these are passed in via the ui32Flags parameter +// of the SCSIModeSense() function call. +// +//***************************************************************************** + +//***************************************************************************** +// +// Disable block descriptors. +// +//***************************************************************************** +#define SCSI_MS_DBD 0x00000800 + +//***************************************************************************** +// +// Page Code values, used in combination with Page Control values. +// +//***************************************************************************** +#define SCSI_MS_PC_VENDOR 0x00000000 +#define SCSI_MS_PC_DISCO 0x00020000 +#define SCSI_MS_PC_CONTROL 0x000a0000 +#define SCSI_MS_PC_LUN 0x00180000 +#define SCSI_MS_PC_PORT 0x00190000 +#define SCSI_MS_PC_POWER 0x001a0000 +#define SCSI_MS_PC_INFORM 0x001c0000 +#define SCSI_MS_PC_ALL 0x003f0000 + +//***************************************************************************** +// +// Page Control values. +// +//***************************************************************************** +#define SCSI_MS_PC_CURRENT 0x00000000 +#define SCSI_MS_PC_CHANGEABLE 0x00400000 +#define SCSI_MS_PC_DEFAULT 0x00800000 +#define SCSI_MS_PC_SAVED 0x00c00000 + +//***************************************************************************** +// +// Request Sense Definitions. +// +//***************************************************************************** + +//***************************************************************************** +// +// Size of the data returned by the Request Sense command. +// +//***************************************************************************** +#define SCSI_REQUEST_SENSE_SZ 18 + +#define SCSI_RS_SKEY 2 // Sense Key offset. +#define SCSI_RS_SKEY_AD_SKEY 12 // Additional Sense Key offset. + +//***************************************************************************** +// +// Offset 0 in the Request Sense response. +// +//***************************************************************************** +#define SCSI_RS_VALID 0x80 // Response is valid. +#define SCSI_RS_CUR_ERRORS 0x70 // Current errors returned. +#define SCSI_RS_DEFER_ERRORS 0x71 // Deferred errors returned. + +//***************************************************************************** +// +// Offset 2 in the Request Sense response. +// +//***************************************************************************** +#define SCSI_RS_KEY_M 0x0f // Sense Key. +#define SCSI_RS_KEY_NO_SENSE 0x00 // No Sense Data. +#define SCSI_RS_KEY_RECOVRD_ERR 0x01 // Recovered Error. +#define SCSI_RS_KEY_NOT_READY 0x02 // Not Ready. +#define SCSI_RS_KEY_MEDIUM_ERR 0x03 // Error in the media. +#define SCSI_RS_KEY_HW_ERR 0x04 // Hardware Error, non recoverable. +#define SCSI_RS_KEY_ILGL_RQST 0x05 // Illegal request. +#define SCSI_RS_KEY_UNIT_ATTN 0x06 // Unit changed or reset. +#define SCSI_RS_KEY_DATA_PROT 0x07 // Write Protect error. +#define SCSI_RS_KEY_BLANK_CHK 0x08 // Write once error, block not clear. +#define SCSI_RS_KEY_ABORT 0x0b // Last command was aborted. +#define SCSI_RS_ILI 0x20 // Incorrect length indicator. +#define SCSI_RS_EOM 0x40 // End of medium condition. +#define SCSI_RS_FILEMARK 0x80 // Command has read a filemark/setmark. +#define SCSI_RS_MED_NOT_PRSNT 0x003a // Medium not present. +#define SCSI_RS_MED_NOTRDY2RDY 0x0028 // Not ready to ready transition. +#define SCSI_RS_PV_INVALID 0x0226 // Parameter Value Invalid. + +//***************************************************************************** +// +// Additional information for SCSI_RS_KEY_NOT_READY +// +//***************************************************************************** +#define SCSI_RS_KEY_NOTPRSNT 0x3A // Media Not Present. + +//***************************************************************************** +// +// Prevent/Allow Medium Removal Definitions. +// +//***************************************************************************** + +//***************************************************************************** +// +// Offset 4 in the Prevent/Allow Medium Removal command. +// +//***************************************************************************** +#define SCSI_PE_MEDRMV_M 0x03 +#define SCSI_PE_MEDRMV_ALLOW 0x00 +#define SCSI_PE_MEDRMV_PREVENT 0x01 + +//***************************************************************************** +// +// Start/Stop Unit Definitions. +// +//***************************************************************************** + +//***************************************************************************** +// +// Offset 1 in the Start/Stop Unit command. +// +//***************************************************************************** +#define SCSI_SS_UNIT_IMMED 0x01 +#define SCSI_SS_UNIT_LUN_M 0xe0 + +//***************************************************************************** +// +// Offset 4 in the Start/Stop Unit command. +// +//***************************************************************************** +#define SCSI_SS_UNIT_START 0x01 +#define SCSI_SS_UNIT_LOEJ 0x02 +#define SCSI_SS_UNIT_PWR_M 0xf0 +#define SCSI_SS_UNIT_PWR_ACTIVE 0x10 +#define SCSI_SS_UNIT_PWR_IDLE 0x20 +#define SCSI_SS_UNIT_PWR_STDBY 0x30 +#define SCSI_SS_UNIT_PWR_DSLEEP 0x50 + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBMSC_H__ diff --git a/usblib/usbringbuf.c b/usblib/usbringbuf.c new file mode 100644 index 0000000..733ef64 --- /dev/null +++ b/usblib/usbringbuf.c @@ -0,0 +1,715 @@ +//***************************************************************************** +// +// usbringbuf.c - USB library ring buffer management utilities. +// +// 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/debug.h" +#include "driverlib/interrupt.h" +#include "usblib/usblib.h" + +//***************************************************************************** +// +//! \addtogroup usblib_buffer_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Define NULL, if not already defined. +// +//***************************************************************************** +#ifndef NULL +#define NULL ((void *)0) +#endif + +//***************************************************************************** +// +// Change the value of a variable atomically. +// +// \param pui32Val points to the index whose value is to be modified. +// \param ui32Delta is the number of bytes to increment the index by. +// \param ui32Size is the size of the buffer the index refers to. +// +// This function is used to increment a read or write buffer index that may be +// written in various different contexts. It ensures that the +// read/modify/write sequence is not interrupted and, hence, guards against +// corruption of the variable. The new value is adjusted for buffer wrap. +// +// \return None. +// +//***************************************************************************** +static void +UpdateIndexAtomic(volatile uint32_t *pui32Val, uint32_t ui32Delta, + uint32_t ui32Size) +{ + bool bIntsOff; + + // + // Turn interrupts off temporarily. + // + bIntsOff = IntMasterDisable(); + + // + // Update the variable value. + // + *pui32Val += ui32Delta; + + // + // Correct for wrap. We use a loop here since we don't want to use a + // modulus operation with interrupts off but we don't want to fail in + // case ui32Delta is greater than ui32Size (which is extremely unlikely + // but...) + // + while(*pui32Val >= ui32Size) + { + *pui32Val -= ui32Size; + } + + // + // Restore the interrupt state + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Determines whether a ring buffer is full or not. +//! +//! \param psUSBRingBuf is the ring buffer object to empty. +//! +//! This function is used to determine whether or not a given ring buffer is +//! full. The structure is specifically to ensure that we do not see +//! warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is full or \b false otherwise. +// +//***************************************************************************** +bool +USBRingBufFull(tUSBRingBufObject *psUSBRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psUSBRingBuf->ui32WriteIndex; + ui32Read = psUSBRingBuf->ui32ReadIndex; + + // + // Return the full status of the buffer. + // + return((((ui32Write + 1) % psUSBRingBuf->ui32Size) == ui32Read) ? true : + false); +} + +//***************************************************************************** +// +//! Determines whether a ring buffer is empty or not. +//! +//! \param psUSBRingBuf is the ring buffer object to empty. +//! +//! This function is used to determine whether or not a given ring buffer is +//! empty. The structure is specifically to ensure that we do not see +//! warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is empty or \b false otherwise. +// +//***************************************************************************** +bool +USBRingBufEmpty(tUSBRingBufObject *psUSBRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psUSBRingBuf->ui32WriteIndex; + ui32Read = psUSBRingBuf->ui32ReadIndex; + + // + // Return the empty status of the buffer. + // + return((ui32Write == ui32Read) ? true : false); +} + +//***************************************************************************** +// +//! Empties the ring buffer. +//! +//! \param psUSBRingBuf is the ring buffer object to empty. +//! +//! Discards all data from the ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufFlush(tUSBRingBufObject *psUSBRingBuf) +{ + bool bIntsOff; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Set the Read/Write pointers to be the same. Do this with interrupts + // disabled to prevent the possibility of corruption of the read index. + // + bIntsOff = IntMasterDisable(); + psUSBRingBuf->ui32ReadIndex = psUSBRingBuf->ui32WriteIndex; + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Returns number of bytes stored in ring buffer. +//! +//! \param psUSBRingBuf is the ring buffer object to check. +//! +//! This function returns the number of bytes stored in the ring buffer. +//! +//! \return Returns the number of bytes stored in the ring buffer. +// +//***************************************************************************** +uint32_t +USBRingBufUsed(tUSBRingBufObject *psUSBRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psUSBRingBuf->ui32WriteIndex; + ui32Read = psUSBRingBuf->ui32ReadIndex; + + // + // Return the number of bytes contained in the ring buffer. + // + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (psUSBRingBuf->ui32Size - (ui32Read - ui32Write))); +} + +//***************************************************************************** +// +//! Returns number of bytes available in a ring buffer. +//! +//! \param psUSBRingBuf is the ring buffer object to check. +//! +//! This function returns the number of bytes available in the ring buffer. +//! +//! \return Returns the number of bytes available in the ring buffer. +// +//***************************************************************************** +uint32_t +USBRingBufFree(tUSBRingBufObject *psUSBRingBuf) +{ + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return((psUSBRingBuf->ui32Size - 1) - USBRingBufUsed(psUSBRingBuf)); +} + +//***************************************************************************** +// +//! Returns number of contiguous bytes of data stored in ring buffer ahead of +//! the current read pointer. +//! +//! \param psUSBRingBuf is the ring buffer object to check. +//! +//! This function returns the number of contiguous bytes of data available in +//! the ring buffer ahead of the current read pointer. This represents the +//! largest block of data which does not straddle the buffer wrap. +//! +//! \return Returns the number of contiguous bytes available. +// +//***************************************************************************** +uint32_t +USBRingBufContigUsed(tUSBRingBufObject *psUSBRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psUSBRingBuf->ui32WriteIndex; + ui32Read = psUSBRingBuf->ui32ReadIndex; + + // + // Return the number of contiguous bytes available. + // + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (psUSBRingBuf->ui32Size - ui32Read)); +} + +//***************************************************************************** +// +//! Returns number of contiguous free bytes available in a ring buffer. +//! +//! \param psUSBRingBuf is the ring buffer object to check. +//! +//! This function returns the number of contiguous free bytes ahead of the +//! current write pointer in the ring buffer. +//! +//! \return Returns the number of contiguous bytes available in the ring +//! buffer. +// +//***************************************************************************** +uint32_t +USBRingBufContigFree(tUSBRingBufObject *psUSBRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psUSBRingBuf->ui32WriteIndex; + ui32Read = psUSBRingBuf->ui32ReadIndex; + + // + // Return the number of contiguous bytes available. + // + if(ui32Read > ui32Write) + { + // + // The read pointer is above the write pointer so the amount of free + // space is the difference between the two indices minus 1 to account + // for the buffer full condition (write index one behind read index). + // + return((ui32Read - ui32Write) - 1); + } + else + { + // + // If the write pointer is above the read pointer, the amount of free + // space is the size of the buffer minus the write index. We need to + // add a special-case adjustment if the read index is 0 since we need + // to leave 1 byte empty to ensure we can tell the difference between + // the buffer being full and empty. + // + return(psUSBRingBuf->ui32Size - ui32Write - ((ui32Read == 0) ? 1 : 0)); + } +} + +//***************************************************************************** +// +//! Returns the size in bytes of a ring buffer. +//! +//! \param psUSBRingBuf is the ring buffer object to check. +//! +//! This function returns the size of the ring buffer. +//! +//! \return Returns the size in bytes of the ring buffer. +// +//***************************************************************************** +uint32_t +USBRingBufSize(tUSBRingBufObject *psUSBRingBuf) +{ + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return(psUSBRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Reads a single byte of data from a ring buffer. +//! +//! \param psUSBRingBuf points to the ring buffer to be written to. +//! +//! This function reads a single byte of data from a ring buffer. +//! +//! \return The byte read from the ring buffer. +// +//***************************************************************************** +uint8_t +USBRingBufReadOne(tUSBRingBufObject *psUSBRingBuf) +{ + uint8_t ui8Temp; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(USBRingBufUsed(psUSBRingBuf) != 0); + + // + // Write the data byte. + // + ui8Temp = psUSBRingBuf->pui8Buf[psUSBRingBuf->ui32ReadIndex]; + + // + // Increment the read index. + // + UpdateIndexAtomic(&psUSBRingBuf->ui32ReadIndex, 1, psUSBRingBuf->ui32Size); + + // + // Return the character read. + // + return(ui8Temp); +} + +//***************************************************************************** +// +//! Reads data from a ring buffer. +//! +//! \param psUSBRingBuf points to the ring buffer to be read from. +//! \param pui8Data points to where the data should be stored. +//! \param ui32Length is the number of bytes to be read. +//! +//! This function reads a sequence of bytes from a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufRead(tUSBRingBufObject *psUSBRingBuf, uint8_t *pui8Data, + uint32_t ui32Length) +{ + uint32_t ui32Temp; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + ASSERT(pui8Data != NULL); + ASSERT(ui32Length != 0); + + // + // Verify that data is available in the buffer. + // + ASSERT(ui32Length <= USBRingBufUsed(psUSBRingBuf)); + + // + // Read the data from the ring buffer. + // + for(ui32Temp = 0; ui32Temp < ui32Length; ui32Temp++) + { + pui8Data[ui32Temp] = USBRingBufReadOne(psUSBRingBuf); + } +} + +//***************************************************************************** +// +//! Removes bytes from the ring buffer by advancing the read index. +//! +//! \param psUSBRingBuf points to the ring buffer from which bytes are to be +//! removed. +//! \param ui32NumBytes is the number of bytes to be removed from the buffer. +//! +//! This function advances the ring buffer read index by a given number of +//! bytes, removing that number of bytes of data from the buffer. If +//! \e ui32NumBytes is larger than the number of bytes currently in the buffer, +//! the buffer is emptied. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufAdvanceRead(tUSBRingBufObject *psUSBRingBuf, uint32_t ui32NumBytes) +{ + uint32_t ui32Count; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Make sure that we are not being asked to remove more data than is + // there to be removed. + // + ui32Count = USBRingBufUsed(psUSBRingBuf); + ui32Count = (ui32Count < ui32NumBytes) ? ui32Count : ui32NumBytes; + + // + // Advance the buffer read index by the required number of bytes. + // + UpdateIndexAtomic(&psUSBRingBuf->ui32ReadIndex, ui32Count, + psUSBRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Adds bytes to the ring buffer by advancing the write index. +//! +//! \param psUSBRingBuf points to the ring buffer to which bytes have been +//! added. +//! \param ui32NumBytes is the number of bytes added to the buffer. +//! +//! This function should be used by clients who wish to add data to the buffer +//! directly rather than via calls to USBRingBufWrite() or +//! USBRingBufWriteOne(). It advances the write index by a given number of +//! bytes. +//! +//! \note It is considered an error if the \e ui32NumBytes parameter is larger +//! than the amount of free space in the buffer and a debug build of this +//! function will fail (ASSERT) if this condition is detected. In a release +//! build, the buffer read pointer will be advanced if too much data is written +//! but this will, of course, result in some of the oldest data in the buffer +//! being discarded and also, depending upon how data is being read from +//! the buffer, may result in a race condition which could corrupt the read +//! pointer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufAdvanceWrite(tUSBRingBufObject *psUSBRingBuf, uint32_t ui32NumBytes) +{ + uint32_t ui32Count; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Make sure we were not asked to add a silly number of bytes. + // + ASSERT(ui32NumBytes <= psUSBRingBuf->ui32Size); + + // + // Determine how much free space we currently think the buffer has. + // + ui32Count = USBRingBufFree(psUSBRingBuf); + + // + // Check that the client has not added more data to the buffer than there + // is space for. In this case, corruption may have occurred since the + // buffer may have been read under interrupt context while the writer was + // busy trashing the area around the read pointer. + // + ASSERT(ui32Count >= ui32NumBytes); + + // + // Update the write pointer. + // + psUSBRingBuf->ui32WriteIndex += ui32NumBytes; + + // + // Check and correct for wrap. + // + if(psUSBRingBuf->ui32WriteIndex >= psUSBRingBuf->ui32Size) + { + psUSBRingBuf->ui32WriteIndex -= psUSBRingBuf->ui32Size; + } + + // + // Did the client add more bytes than the buffer had free space for? This + // should be considered a bug since, unless this function is called in + // the same context as the code which is reading from the buffer, writing + // over the earliest data can cause corrupted data to be read. The + // ASSERT above catches this in debug builds but, in release builds, we + // go ahead and try to fix up the read pointer appropriately. + // + if(ui32Count < ui32NumBytes) + { + // + // Yes - we need to advance the read pointer to ahead of the write + // pointer to discard some of the oldest data. + // + psUSBRingBuf->ui32ReadIndex = psUSBRingBuf->ui32WriteIndex + 1; + + // + // Correct for buffer wrap if necessary. + // + if(psUSBRingBuf->ui32ReadIndex >= psUSBRingBuf->ui32Size) + { + psUSBRingBuf->ui32ReadIndex -= psUSBRingBuf->ui32Size; + } + } + +} + +//***************************************************************************** +// +//! Writes a single byte of data to a ring buffer. +//! +//! \param psUSBRingBuf points to the ring buffer to be written to. +//! \param ui8Data is the byte to be written. +//! +//! This function writes a single byte of data into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufWriteOne(tUSBRingBufObject *psUSBRingBuf, uint8_t ui8Data) +{ + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(USBRingBufFree(psUSBRingBuf) != 0); + + // + // Write the data byte. + // + psUSBRingBuf->pui8Buf[psUSBRingBuf->ui32WriteIndex] = ui8Data; + + // + // Increment the write index. + // + UpdateIndexAtomic(&psUSBRingBuf->ui32WriteIndex, 1, + psUSBRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Writes data to a ring buffer. +//! +//! \param psUSBRingBuf points to the ring buffer to be written to. +//! \param pui8Data points to the data to be written. +//! \param ui32Length is the number of bytes to be written. +//! +//! This function write a sequence of bytes into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufWrite(tUSBRingBufObject *psUSBRingBuf, const uint8_t *pui8Data, + uint32_t ui32Length) +{ + uint32_t ui32Temp; + + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + ASSERT(pui8Data != NULL); + ASSERT(ui32Length != 0); + + // + // Verify that space is available in the buffer. + // + ASSERT(ui32Length <= USBRingBufFree(psUSBRingBuf)); + + // + // Write the data into the ring buffer. + // + for(ui32Temp = 0; ui32Temp < ui32Length; ui32Temp++) + { + USBRingBufWriteOne(psUSBRingBuf, pui8Data[ui32Temp]); + } +} + +//***************************************************************************** +// +//! Initializes a ring buffer object. +//! +//! \param psUSBRingBuf points to the ring buffer to be initialized. +//! \param pui8Buf points to the data buffer to be used for the ring buffer. +//! \param ui32Size is the size of the buffer in bytes. +//! +//! This function initializes a ring buffer object, preparing it to store data. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufInit(tUSBRingBufObject *psUSBRingBuf, uint8_t *pui8Buf, + uint32_t ui32Size) +{ + // + // Check the arguments. + // + ASSERT(psUSBRingBuf != NULL); + ASSERT(pui8Buf != NULL); + ASSERT(ui32Size != 0); + + // + // Initialize the ring buffer object. + // + psUSBRingBuf->ui32Size = ui32Size; + psUSBRingBuf->pui8Buf = pui8Buf; + psUSBRingBuf->ui32WriteIndex = psUSBRingBuf->ui32ReadIndex = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbtick.c b/usblib/usbtick.c new file mode 100644 index 0000000..c2af1c1 --- /dev/null +++ b/usblib/usbtick.c @@ -0,0 +1,219 @@ +//***************************************************************************** +// +// usbtick.c - Functions related to USB stack tick timer handling. +// +// 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/debug.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup general_usblib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These are the internal timer tick handlers used by the USB stack. Handlers +// in g_pfnTickHandlers are called in the context of the USB SOF interrupt +// every USB_SOF_TICK_DIVIDE milliseconds. +// +//***************************************************************************** +tUSBTickHandler g_pfnTickHandlers[MAX_USB_TICK_HANDLERS]; +void *g_pvTickInstance[MAX_USB_TICK_HANDLERS]; + +//***************************************************************************** +// +// Flag to indicate whether or not we have been initialized. +// +//***************************************************************************** +bool g_bUSBTimerInitialized = false; + +//***************************************************************************** +// +// This is the current tick value in ms for the system. This is used for all +// instances of USB controllers and for all timer tick handlers. +// +//***************************************************************************** +uint32_t g_ui32CurrentUSBTick = 0; + +//***************************************************************************** +// +// This is the total number of SOF interrupts received since the system +// booted. The value is incremented by the low level device- or host-interrupt +// handler functions. +// +//***************************************************************************** +uint32_t g_ui32USBSOFCount = 0; + +//***************************************************************************** +// +// This internal function initializes the variables used in processing timer +// ticks. +// +// This function should only be called from within the USB library. It is set +// up to ensure that it can be called multiple times if necessary without +// the previous configuration being erased (to cater for OTG mode switching). +// +// \return None. +// +//***************************************************************************** +void +InternalUSBTickInit(void) +{ + uint32_t ui32Loop; + + if(!g_bUSBTimerInitialized) + { + for(ui32Loop = 0; ui32Loop < MAX_USB_TICK_HANDLERS; ui32Loop++) + { + g_pfnTickHandlers[ui32Loop] = (tUSBTickHandler)0; + g_pvTickInstance[ui32Loop] = 0; + } + + g_bUSBTimerInitialized = true; + } +} + +//***************************************************************************** +// +// This internal function resets the USB tick handler. +// +// This function should only be called from within the USB library. It will +// clear out the tick handler state and should be called to allow the tick +// handlers to be initialized once USBDCDInit() function is called. +// +// \return None. +// +//***************************************************************************** +void +InternalUSBTickReset(void) +{ + // + // Reset the initialized flag so that the next time InternalUSBTickInit() + // is called. + // + g_bUSBTimerInitialized = 0; +} + +//***************************************************************************** +// +// This internal function handles registering OTG, Host, or Device SOF timer +// handler functions. +// +// \param pfHandler specifies the handler to call for the given type of +// handler. +// \param pvInstance is the instance pointer that will be returned to the +// function provided in the \e pfHandler function. +// +// This function should only be called inside the USB library and only as a +// result to a call to reinitialize the stack in a new mode. Currently the +// following 3 types of timer tick handlers can be registered: +// TICK_HANDLER_OTG, TICK_HANDLER_HOST, or TICK_HANDLER_DEVICE. Handlers +// registered via this function are called in the context of the SOF interrupt. +// +// \return A value of zero means that the tick handler was registered and any +// other value indicates an error. +// +//***************************************************************************** +int32_t +InternalUSBRegisterTickHandler(tUSBTickHandler pfHandler, void *pvInstance) +{ + int32_t i32Idx; + + for(i32Idx = 0; i32Idx < MAX_USB_TICK_HANDLERS; i32Idx++) + { + if(g_pfnTickHandlers[i32Idx] == 0) + { + // + // Save the handler. + // + g_pfnTickHandlers[i32Idx] = pfHandler; + + // + // Save the instance data. + // + g_pvTickInstance[i32Idx] = pvInstance; + + break; + } + } + + if(i32Idx == MAX_USB_TICK_HANDLERS) + { + return(-1); + } + return(0); +} + +//***************************************************************************** +// +//! \internal +//! +//! Calls internal handlers in response to a tick based on the start of frame +//! interrupt. +//! +//! \param ui32TicksmS specifies how many milliseconds have passed since the +//! last call to this function. +//! +//! This function is called every 5mS in the context of the Start of Frame +//! (SOF) interrupt. It is used to call any registered internal tick +//! functions. +//! +//! This function should only be called from within the USB library. +//! +//! \return None. +// +//***************************************************************************** +void +InternalUSBStartOfFrameTick(uint32_t ui32TicksmS) +{ + int32_t i32Idx; + + // + // Advance time. + // + g_ui32CurrentUSBTick += ui32TicksmS; + + // + // Call any registered SOF tick handlers. + // + for(i32Idx = 0; i32Idx < MAX_USB_TICK_HANDLERS; i32Idx++) + { + if(g_pfnTickHandlers[i32Idx]) + { + g_pfnTickHandlers[i32Idx](g_pvTickInstance[i32Idx], ui32TicksmS); + } + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbulpi.c b/usblib/usbulpi.c new file mode 100644 index 0000000..dbbf68a --- /dev/null +++ b/usblib/usbulpi.c @@ -0,0 +1,176 @@ +//***************************************************************************** +// +// usbulpi.c - ULPI access functions. +// +// Copyright (c) 2013-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 "usbulpi.h" + +//***************************************************************************** +// +// Hardware ULPI registers. +// +//***************************************************************************** +#define ULPI_FCTL 0x04 +#define ULPI_FCTL_SET 0x05 +#define ULPI_FCTL_CLEAR 0x06 +#define ULPI_ICTL 0x07 +#define ULPI_ICTL_SET 0x08 +#define ULPI_ICTL_CLEAR 0x09 +#define ULPI_OTGCTL 0x0A +#define ULPI_OTGCTL_SET 0x0B +#define ULPI_OTGCTL_CLEAR 0x0C + +//***************************************************************************** +// +// The following are defines for the bit fields in the ULPI_FCTL register. +// +//***************************************************************************** +#define ULPI_FCTL_XCVR_M 0x03 +#define ULPI_FCTL_XCVR_HS 0x00 +#define ULPI_FCTL_XCVR_FS 0x01 +#define ULPI_FCTL_XCVR_LS 0x02 +#define ULPI_FCTL_XCVR_FSLS 0x03 +#define ULPI_FCTL_TERMSEL 0x04 +#define ULPI_FCTL_OPMODE_M 0x18 +#define ULPI_FCTL_OPMODE_NORM 0x00 +#define ULPI_FCTL_OPMODE_NODRV 0x08 +#define ULPI_FCTL_OPMODE_NONRZI 0x10 +#define ULPI_FCTL_OPMODE_DISAUTO \ + 0x18 +#define ULPI_FCTL_OPMODE_RESET 0x20 +#define ULPI_FCTL_OPMODE_SUSPEND \ + 0x40 + +//***************************************************************************** +// +// The following are defines for the bit fields in the ULPI_ICTL register. +// +//***************************************************************************** +#define ULPI_ICTL_SER6PIN 0x01 +#define ULPI_ICTL_SER3PIN 0x02 +#define ULPI_ICTL_AUTORESUME 0x10 +#define ULPI_ICTL_INDINV 0x20 +#define ULPI_ICTL_INDPASSTHRU 0x40 +#define ULPI_ICTL_PROTDIS 0x80 + +//***************************************************************************** +// +// The following are defines for the bit fields in the ULPI_OTGCTL register. +// +//***************************************************************************** +#define ULPI_OTGCTL_ID_EN 0x01 +#define ULPI_OTGCTL_DPPD_EN 0x02 +#define ULPI_OTGCTL_DMPD_EN 0x04 +#define ULPI_OTGCTL_DISCHRG_VBUS \ + 0x08 +#define ULPI_OTGCTL_CHRG_VBUS 0x10 +#define ULPI_OTGCTL_VBUSINT_EN 0x20 +#define ULPI_OTGCTL_VBUSEXT_EN 0x40 +#define ULPI_OTGCTL_VBUSEXT_IND 0x80 + +//***************************************************************************** +// +//! Sets the configuration of an external USB Phy. +//! +//! \param ui32Base specifies the USB module base address. +//! \param ui32Config specifies the configuration options for the external Phy. +//! +//! This function sets the configuration options for an externally connected +//! USB Phy that is connected using the ULPI interface. The \e ui32Config +//! parameter holds all of the configuration options defined by the +//! \b UPLI_CFG_ values. The values are grouped as follows: +//! +//! Connection speed, using one of the following: +//! - \b UPLI_CFG_HS enables high speed operation. +//! - \b UPLI_CFG_FS enables full speed operation. +//! - \b UPLI_CFG_HS enables low speed operation. +//! +//! Any of the following can be included: +//! - \b UPLI_CFG_AUTORESUME enable automatic transmission of resume signaling +//! from the Phy. +//! - \b UPLI_CFG_INVVBUSIND inverts the external VBUS indicator if it is +//! selected. +//! - \b UPLI_CFG_PASSTHRUIND passes the external VBUS indicator through +//! without using the Phy's VBUS comparator. +//! - \b ULPI_CFG_EXTVBUSDRV enables an external VBUS drive source. +//! - \b ULPI_CFG_EXTVBUSIND enables an external signal for VBUS valid. +//! +//! \return None. +// +//***************************************************************************** +void +ULPIConfigSet(uint32_t ui32Base, uint32_t ui32Config) +{ + uint8_t ui8Val; + + ui8Val = USBULPIRegRead(ui32Base, ULPI_FCTL); + ui8Val &= ~(ULPI_FCTL_XCVR_M); + ui8Val = ui8Val | (uint8_t)ui32Config; + + USBULPIRegWrite(ui32Base, ULPI_FCTL, ui8Val); + + ui8Val = USBULPIRegRead(ui32Base, ULPI_ICTL); + ui8Val &= ~(ULPI_ICTL_AUTORESUME | ULPI_ICTL_INDINV | + ULPI_ICTL_INDPASSTHRU); + ui8Val = ui8Val | (uint8_t)((ui32Config >> 8) & 0xff); + + USBULPIRegWrite(ui32Base, ULPI_ICTL, ui8Val); + + ui8Val = USBULPIRegRead(ui32Base, ULPI_OTGCTL); + ui8Val &= ~(ULPI_OTGCTL_VBUSINT_EN | ULPI_OTGCTL_VBUSEXT_EN | + ULPI_OTGCTL_VBUSEXT_IND); + ui8Val = ui8Val | (uint8_t)((ui32Config >> 16) & 0xff); + + USBULPIRegWrite(ui32Base, ULPI_OTGCTL, ui8Val); +} + +//***************************************************************************** +// +//! Enables or disables power to the external USB Phy. +//! +//! \param ui32Base specifies the USB module base address. +//! \param bEnable specifies if the Phy is fully powered or in suspend mode. +//! +//! This function sets the current power configuration for the external ULPI +//! connected Phy. When \e bEnable is \b true the Phy is fully powered and +//! when \b false the USB Phy is in suspend mode. +//! +//! \return None. +// +//***************************************************************************** +void +ULPIPowerTransceiver(uint32_t ui32Base, bool bEnable) +{ + if(bEnable) + { + USBULPIRegWrite(ui32Base, ULPI_FCTL_CLEAR, + ULPI_FCTL_OPMODE_SUSPEND); + } + else + { + USBULPIRegWrite(ui32Base, ULPI_FCTL_SET, + ULPI_FCTL_OPMODE_SUSPEND); + } +} diff --git a/usblib/usbulpi.h b/usblib/usbulpi.h new file mode 100644 index 0000000..9943d24 --- /dev/null +++ b/usblib/usbulpi.h @@ -0,0 +1,40 @@ +//***************************************************************************** +// +// usbulpi.h - Header file for ULPI access functions. +// +// Copyright (c) 2013-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. +// +//***************************************************************************** + +#ifndef __ULPI_H__ +#define __ULPI_H__ + +#define ULPI_CFG_HS 0x00000000 +#define ULPI_CFG_FS 0x00000001 +#define ULPI_CFG_LS 0x00000002 +#define ULPI_CFG_AUTORESUME 0x00001000 +#define ULPI_CFG_INVVBUSIND 0x00002000 +#define ULPI_CFG_PASSTHRUIND 0x00004000 +#define ULPI_CFG_EXTVBUSDRV 0x00400000 +#define ULPI_CFG_EXTVBUSIND 0x00800000 + +extern void ULPIConfigSet(uint32_t ui32Base, uint32_t ui32Config); +extern void ULPIPowerTransceiver(uint32_t ui32Base, bool bEnable); + +#endif diff --git a/utils/cmdline.c b/utils/cmdline.c new file mode 100644 index 0000000..c9f5971 --- /dev/null +++ b/utils/cmdline.c @@ -0,0 +1,193 @@ +//***************************************************************************** +// +// cmdline.c - Functions to help with processing command lines. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup cmdline_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include +#include "utils/cmdline.h" + +//***************************************************************************** +// +// Defines the maximum number of arguments that can be parsed. +// +//***************************************************************************** +#ifndef CMDLINE_MAX_ARGS +#define CMDLINE_MAX_ARGS 8 +#endif + +//***************************************************************************** +// +// An array to hold the pointers to the command line arguments. +// +//***************************************************************************** +static char *g_ppcArgv[CMDLINE_MAX_ARGS + 1]; + +//***************************************************************************** +// +//! Process a command line string into arguments and execute the command. +//! +//! \param pcCmdLine points to a string that contains a command line that was +//! obtained by an application by some means. +//! +//! This function will take the supplied command line string and break it up +//! into individual arguments. The first argument is treated as a command and +//! is searched for in the command table. If the command is found, then the +//! command function is called and all of the command line arguments are passed +//! in the normal argc, argv form. +//! +//! The command table is contained in an array named g_psCmdTable +//! containing tCmdLineEntry structures which must be provided by the +//! application. The array must be terminated with an entry whose \b pcCmd +//! field contains a NULL pointer. +//! +//! \return Returns \b CMDLINE_BAD_CMD if the command is not found, +//! \b CMDLINE_TOO_MANY_ARGS if there are more arguments than can be parsed. +//! Otherwise it returns the code that was returned by the command function. +// +//***************************************************************************** +int +CmdLineProcess(char *pcCmdLine) +{ + char *pcChar; + uint_fast8_t ui8Argc; + bool bFindArg = true; + tCmdLineEntry *psCmdEntry; + + // + // Initialize the argument counter, and point to the beginning of the + // command line string. + // + ui8Argc = 0; + pcChar = pcCmdLine; + + // + // Advance through the command line until a zero character is found. + // + while(*pcChar) + { + // + // If there is a space, then replace it with a zero, and set the flag + // to search for the next argument. + // + if(*pcChar == ' ') + { + *pcChar = 0; + bFindArg = true; + } + + // + // Otherwise it is not a space, so it must be a character that is part + // of an argument. + // + else + { + // + // If bFindArg is set, then that means we are looking for the start + // of the next argument. + // + if(bFindArg) + { + // + // As long as the maximum number of arguments has not been + // reached, then save the pointer to the start of this new arg + // in the argv array, and increment the count of args, argc. + // + if(ui8Argc < CMDLINE_MAX_ARGS) + { + g_ppcArgv[ui8Argc] = pcChar; + ui8Argc++; + bFindArg = false; + } + + // + // The maximum number of arguments has been reached so return + // the error. + // + else + { + return(CMDLINE_TOO_MANY_ARGS); + } + } + } + + // + // Advance to the next character in the command line. + // + pcChar++; + } + + // + // If one or more arguments was found, then process the command. + // + if(ui8Argc) + { + // + // Start at the beginning of the command table, to look for a matching + // command. + // + psCmdEntry = &g_psCmdTable[0]; + + // + // Search through the command table until a null command string is + // found, which marks the end of the table. + // + while(psCmdEntry->pcCmd) + { + // + // If this command entry command string matches argv[0], then call + // the function for this command, passing the command line + // arguments. + // + if(!strcmp(g_ppcArgv[0], psCmdEntry->pcCmd)) + { + return(psCmdEntry->pfnCmd(ui8Argc, g_ppcArgv)); + } + + // + // Not found, so advance to the next entry. + // + psCmdEntry++; + } + } + + // + // Fall through to here means that no matching command was found, so return + // an error. + // + return(CMDLINE_BAD_CMD); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/cmdline.h b/utils/cmdline.h new file mode 100644 index 0000000..42ab9ea --- /dev/null +++ b/utils/cmdline.h @@ -0,0 +1,137 @@ +//***************************************************************************** +// +// cmdline.h - Prototypes for command line processing functions. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +#ifndef __CMDLINE_H__ +#define __CMDLINE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup cmdline_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Defines the value that is returned if the command is not found. +// +//***************************************************************************** +#define CMDLINE_BAD_CMD (-1) + +//***************************************************************************** +// +//! Defines the value that is returned if there are too many arguments. +// +//***************************************************************************** +#define CMDLINE_TOO_MANY_ARGS (-2) + +//***************************************************************************** +// +//! Defines the value that is returned if there are too few arguments. +// +//***************************************************************************** +#define CMDLINE_TOO_FEW_ARGS (-3) + +//***************************************************************************** +// +//! Defines the value that is returned if an argument is invalid. +// +//***************************************************************************** +#define CMDLINE_INVALID_ARG (-4) + +//***************************************************************************** +// +// Command line function callback type. +// +//***************************************************************************** +typedef int (*pfnCmdLine)(int argc, char *argv[]); + +//***************************************************************************** +// +//! Structure for an entry in the command list table. +// +//***************************************************************************** +typedef struct +{ + // + //! A pointer to a string containing the name of the command. + // + const char *pcCmd; + + // + //! A function pointer to the implementation of the command. + // + pfnCmdLine pfnCmd; + + // + //! A pointer to a string of brief help text for the command. + // + const char *pcHelp; +} +tCmdLineEntry; + +//***************************************************************************** +// +//! This is the command table that must be provided by the application. The +//! last element of the array must be a structure whose pcCmd field contains +//! a NULL pointer. +// +//***************************************************************************** +extern tCmdLineEntry g_psCmdTable[]; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern int CmdLineProcess(char *pcCmdLine); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __CMDLINE_H__ diff --git a/utils/cpu_usage.c b/utils/cpu_usage.c new file mode 100644 index 0000000..1adf020 --- /dev/null +++ b/utils/cpu_usage.c @@ -0,0 +1,206 @@ +//***************************************************************************** +// +// cpu_usage.c - Routines to determine the CPU utilization. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/timer.h" +#include "utils/cpu_usage.h" + +//***************************************************************************** +// +//! \addtogroup cpu_usage_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The peripheral identifier for the timer modules that could be used for +// tracking CPU utilization. +// +//***************************************************************************** +static uint32_t g_pui32CPUUsageTimerPeriph[6] = +{ + SYSCTL_PERIPH_TIMER0, SYSCTL_PERIPH_TIMER1, SYSCTL_PERIPH_TIMER2, + SYSCTL_PERIPH_TIMER3, SYSCTL_PERIPH_TIMER4, SYSCTL_PERIPH_TIMER5 +}; + +//***************************************************************************** +// +// The base address of the timer modules that could be used for tracking CPU +// utilization. +// +//***************************************************************************** +static uint32_t g_pui32CPUUsageTimerBase[6] = +{ + TIMER0_BASE, TIMER1_BASE, TIMER2_BASE, TIMER3_BASE, TIMER4_BASE, + TIMER5_BASE +}; + +//***************************************************************************** +// +// The index of the timer module that will be used for tracking CPU +// utilization. +// +//***************************************************************************** +static uint32_t g_ui32CPUUsageTimer; + +//***************************************************************************** +// +// The number of processor clock ticks per timing period. +// +//***************************************************************************** +static uint32_t g_ui32CPUUsageTicks; + +//***************************************************************************** +// +// The value of timer two on the previous timing period. This is used to +// determine the number of clock ticks counted by the timer during the timing +// period. +// +//***************************************************************************** +static uint32_t g_ui32CPUUsagePrevious; + +//***************************************************************************** +// +//! Updates the CPU usage for the new timing period. +//! +//! This function, when called at the end of a timing period, will update the +//! CPU usage. +//! +//! \return Returns the CPU usage percentage as a 16.16 fixed-point value. +// +//***************************************************************************** +uint32_t +CPUUsageTick(void) +{ + uint32_t ui32Value, ui32Usage; + + // + // Get the current value of the timer. + // + ui32Value = + MAP_TimerValueGet(g_pui32CPUUsageTimerBase[g_ui32CPUUsageTimer], + TIMER_A); + + // + // Based on the number of clock ticks accumulated by the timer during the + // previous timing period, compute the CPU usage as a 16.16 fixed-point + // value. + // + ui32Usage = ((((g_ui32CPUUsagePrevious - ui32Value) * 6400) / + g_ui32CPUUsageTicks) * 1024); + + // + // Save the previous value of the timer. + // + g_ui32CPUUsagePrevious = ui32Value; + + // + // Return the new CPU usage value. + // + return(ui32Usage); +} + +//***************************************************************************** +// +//! Initializes the CPU usage measurement module. +//! +//! \param ui32ClockRate is the rate of the clock supplied to the timer module. +//! \param ui32Rate is the number of times per second that CPUUsageTick() is +//! called. +//! \param ui32Timer is the index of the timer module to use. +//! +//! This function prepares the CPU usage measurement module for measuring the +//! CPU usage of the application. +//! +//! \return None. +// +//***************************************************************************** +void +CPUUsageInit(uint32_t ui32ClockRate, uint32_t ui32Rate, uint32_t ui32Timer) +{ + // + // Check the arguments. + // + ASSERT(ui32ClockRate > ui32Rate); + ASSERT(ui32Timer < 6); + + // + // Save the timer index. + // + g_ui32CPUUsageTimer = ui32Timer; + + // + // Determine the number of system clocks per measurement period. + // + g_ui32CPUUsageTicks = ui32ClockRate / ui32Rate; + + // + // Set the previous value of the timer to the initial timer value. + // + g_ui32CPUUsagePrevious = 0xffffffff; + + // + // Enable peripheral clock gating. + // + MAP_SysCtlPeripheralClockGating(true); + + // + // Enable the third timer while the processor is in run mode, but disable + // it in sleep mode. It will therefore count system clocks when the + // processor is running but not when it is sleeping. + // + MAP_SysCtlPeripheralEnable(g_pui32CPUUsageTimerPeriph[ui32Timer]); + MAP_SysCtlPeripheralSleepDisable(g_pui32CPUUsageTimerPeriph[ui32Timer]); + + // + // Configure the third timer for 32-bit periodic operation. + // + MAP_TimerConfigure(g_pui32CPUUsageTimerBase[ui32Timer], + TIMER_CFG_PERIODIC); + + // + // Set the load value for the third timer to the maximum value. + // + MAP_TimerLoadSet(g_pui32CPUUsageTimerBase[ui32Timer], TIMER_A, 0xffffffff); + + // + // Enable the third timer. It will now count the number of system clocks + // during which the processor is executing code. + // + MAP_TimerEnable(g_pui32CPUUsageTimerBase[ui32Timer], TIMER_A); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/cpu_usage.h b/utils/cpu_usage.h new file mode 100644 index 0000000..181ea2c --- /dev/null +++ b/utils/cpu_usage.h @@ -0,0 +1,57 @@ +//***************************************************************************** +// +// cpu_usage.h - Prototypes for the CPU utilization routines. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +#ifndef __CPU_USAGE_H__ +#define __CPU_USAGE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototypes for the CPU utilization routines. +// +//***************************************************************************** +extern uint32_t CPUUsageTick(void); +extern void CPUUsageInit(uint32_t ui32ClockRate, uint32_t ui32Rate, + uint32_t ui32Timer); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __CPU_USAGE_H__ diff --git a/utils/flash_pb.c b/utils/flash_pb.c new file mode 100644 index 0000000..ac85830 --- /dev/null +++ b/utils/flash_pb.c @@ -0,0 +1,493 @@ +//***************************************************************************** +// +// flash_pb.c - Flash parameter block functions. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_flash.h" +#include "inc/hw_types.h" +#include "inc/hw_sysctl.h" +#include "driverlib/debug.h" +#include "driverlib/flash.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "utils/flash_pb.h" + +//***************************************************************************** +// +//! \addtogroup flash_pb_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The address of the beginning of the flash used for storing parameter blocks; +// this must be the start of an erase block in the flash. +// +//***************************************************************************** +static uint8_t *g_pui8FlashPBStart; + +//***************************************************************************** +// +// The address of the end of the flash used for storing parameter blocks; this +// must be the start of an erase block in the flash, or the first location +// after the end of the flash array if the last erase block is used for storing +// parameters. +// +//***************************************************************************** +static uint8_t *g_pui8FlashPBEnd; + +//***************************************************************************** +// +// The size of the parameter block when stored in flash; this must be a power +// of two less than or equal to the flash erase sector size such that a single +// flash sector contains an integral number of parameter blocks. +// +//***************************************************************************** +static uint32_t g_ui32FlashPBSize; + +//***************************************************************************** +// +// The address of the most recent parameter block in flash. +// +//***************************************************************************** +static uint8_t *g_pui8FlashPBCurrent; + +//***************************************************************************** +// +// The erase sector size of the current flash. +// +//***************************************************************************** +#define FLASH_SECTOR_SIZE MAP_SysCtlFlashSectorSizeGet() + +//***************************************************************************** +// +//! Determines if the parameter block at the given address is valid. +//! +//! \param pui8Offset is the address of the parameter block to check. +//! +//! This function will compute the checksum of a parameter block in flash to +//! determine if it is valid. +//! +//! \return Returns one if the parameter block is valid and zero if it is not. +// +//***************************************************************************** +static uint32_t +FlashPBIsValid(uint8_t *pui8Offset) +{ + uint32_t ui32Idx, ui32Sum; + + // + // Check the arguments. + // + ASSERT(pui8Offset != (void *)0); + + // + // Loop through the bytes in the block, computing the checksum. + // + for(ui32Idx = 0, ui32Sum = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + ui32Sum += pui8Offset[ui32Idx]; + } + + // + // The checksum should be zero, so return a failure if it is not. + // + if((ui32Sum & 255) != 0) + { + return(0); + } + + // + // If the sum is equal to the size * 255, then the block is all ones and + // should not be considered valid. + // + if((g_ui32FlashPBSize * 255) == ui32Sum) + { + return(0); + } + + // + // This is a valid parameter block. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the address of the most recent parameter block. +//! +//! This function returns the address of the most recent parameter block that +//! is stored in flash. +//! +//! \return Returns the address of the most recent parameter block, or NULL if +//! there are no valid parameter blocks in flash. +// +//***************************************************************************** +uint8_t * +FlashPBGet(void) +{ + // + // See if there is a valid parameter block. + // + if(g_pui8FlashPBCurrent) + { + // + // Return the address of the most recent parameter block. + // + return(g_pui8FlashPBCurrent); + } + + // + // There are no valid parameter blocks in flash, so return NULL. + // + return(0); +} + +//***************************************************************************** +// +//! Writes a new parameter block to flash. +//! +//! \param pui8Buffer is the address of the parameter block to be written to +//! flash. +//! +//! This function will write a parameter block to flash. Saving the new +//! parameter blocks involves three steps: +//! +//! - Setting the sequence number such that it is one greater than the sequence +//! number of the latest parameter block in flash. +//! - Computing the checksum of the parameter block. +//! - Writing the parameter block into the storage immediately following the +//! latest parameter block in flash; if that storage is at the start of an +//! erase block, that block is erased first. +//! +//! By this process, there is always a valid parameter block in flash. If +//! power is lost while writing a new parameter block, the checksum will not +//! match and the partially written parameter block will be ignored. This is +//! what makes this fault-tolerant. +//! +//! Another benefit of this scheme is that it provides wear leveling on the +//! flash. Since multiple parameter blocks fit into each erase block of flash, +//! and multiple erase blocks are used for parameter block storage, it takes +//! quite a few parameter block saves before flash is re-written. +//! +//! \return None. +// +//***************************************************************************** +void +FlashPBSave(uint8_t *pui8Buffer) +{ + uint8_t *pui8New; + uint32_t ui32Idx, ui32Sum; + + // + // Check the arguments. + // + ASSERT(pui8Buffer != (void *)0); + + // + // See if there is a valid parameter block in flash. + // + if(g_pui8FlashPBCurrent) + { + // + // Set the sequence number to one greater than the most recent + // parameter block. + // + pui8Buffer[0] = g_pui8FlashPBCurrent[0] + 1; + + // + // Try to write the new parameter block immediately after the most + // recent parameter block. + // + pui8New = g_pui8FlashPBCurrent + g_ui32FlashPBSize; + if(pui8New == g_pui8FlashPBEnd) + { + pui8New = g_pui8FlashPBStart; + } + } + else + { + // + // There is not a valid parameter block in flash, so set the sequence + // number of this parameter block to zero. + // + pui8Buffer[0] = 0; + + // + // Try to write the new parameter block at the beginning of the flash + // space for parameter blocks. + // + pui8New = g_pui8FlashPBStart; + } + + // + // Compute the checksum of the parameter block to be written. + // + for(ui32Idx = 0, ui32Sum = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + ui32Sum -= pui8Buffer[ui32Idx]; + } + + // + // Store the checksum into the parameter block. + // + pui8Buffer[1] += ui32Sum; + + // + // Look for a location to store this parameter block. This infinite loop + // will be explicitly broken out of when a valid location is found. + // + while(1) + { + // + // See if this location is at the start of an erase block. + // + if(((uint32_t)pui8New & (FLASH_SECTOR_SIZE - 1)) == 0) + { + // + // Erase this block of the flash. This does not assume that the + // erase succeeded in case this block of the flash has become bad + // through too much use. Given the extremely low frequency that + // the parameter blocks are written, this will likely never fail. + // But, that assumption is not made in order to be safe. + // + MAP_FlashErase((uint32_t)pui8New); + } + + // + // Loop through this portion of flash to see if is all ones (in other + // words, it is an erased portion of flash). + // + for(ui32Idx = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + if(pui8New[ui32Idx] != 0xff) + { + break; + } + } + + // + // If all bytes in this portion of flash are ones, then break out of + // the loop since this is a good location for storing the parameter + // block. + // + if(ui32Idx == g_ui32FlashPBSize) + { + break; + } + + // + // Increment to the next parameter block location. + // + pui8New += g_ui32FlashPBSize; + if(pui8New == g_pui8FlashPBEnd) + { + pui8New = g_pui8FlashPBStart; + } + + // + // If every possible location has been checked and none are valid, then + // it will not be possible to write this parameter block. Simply + // return without writing it. + // + if((g_pui8FlashPBCurrent && (pui8New == g_pui8FlashPBCurrent)) || + (!g_pui8FlashPBCurrent && (pui8New == g_pui8FlashPBStart))) + { + return; + } + } + + // + // Write this parameter block to flash. + // + MAP_FlashProgram((uint32_t *)pui8Buffer, (uint32_t)pui8New, + g_ui32FlashPBSize); + + // + // Compare the parameter block data to the data that should now be in + // flash. Return if any of the data does not compare, leaving the previous + // parameter block in flash as the most recent (since the current parameter + // block failed to properly program). + // + for(ui32Idx = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + if(pui8New[ui32Idx] != pui8Buffer[ui32Idx]) + { + return; + } + } + + // + // The new parameter block becomes the most recent parameter block. + // + g_pui8FlashPBCurrent = pui8New; +} + +//***************************************************************************** +// +//! Initializes the flash parameter block. +//! +//! \param ui32Start is the address of the flash memory to be used for storing +//! flash parameter blocks; this must be the start of an erase block in the +//! flash. +//! \param ui32End is the address of the end of flash memory to be used for +//! storing flash parameter blocks; this must be the start of an erase block in +//! the flash (the first block that is NOT part of the flash memory to be +//! used), or the address of the first word after the flash array if the last +//! block of flash is to be used. +//! \param ui32Size is the size of the parameter block when stored in flash; +//! this must be a power of two less than or equal to the flash erase block +//! size (typically 1024). +//! +//! This function initializes a fault-tolerant, persistent storage mechanism +//! for a parameter block for an application. The last several erase blocks +//! of flash (as specified by \e ui32Start and \e ui32End are used for the +//! storage; more than one erase block is required in order to be +//! fault-tolerant. +//! +//! A parameter block is an array of bytes that contain the persistent +//! parameters for the application. The only special requirement for the +//! parameter block is that the first byte is a sequence number (explained +//! in FlashPBSave()) and the second byte is a checksum used to validate the +//! correctness of the data (the checksum byte is the byte such that the sum of +//! all bytes in the parameter block is zero). +//! +//! The portion of flash for parameter block storage is split into N +//! equal-sized regions, where each region is the size of a parameter block +//! (\e ui32Size). Each region is scanned to find the most recent valid +//! parameter block. The region that has a valid checksum and has the highest +//! sequence number (with special consideration given to wrapping back to zero) +//! is considered to be the current parameter block. +//! +//! In order to make this efficient and effective, three conditions must be +//! met. The first is \e ui32Start and \e ui32End must be specified such that +//! at least two erase blocks of flash are dedicated to parameter block +//! storage. If not, fault tolerance can not be guaranteed since an erase of a +//! single block will leave a window where there are no valid parameter blocks +//! in flash. The second condition is that the size (\e ui32Size) of the +//! parameter block must be an integral divisor of the size of an erase block +//! of flash. If not, a parameter block will end up spanning between two erase +//! blocks of flash, making it more difficult to manage. The final condition +//! is that the size of the flash dedicated to parameter blocks (\e ui32End - +//! \e ui32Start) divided by the parameter block size (\e ui32Size) must be +//! less than or equal to 128. If not, it will not be possible in all cases to +//! determine which parameter block is the most recent (specifically when +//! dealing with the sequence number wrapping back to zero). +//! +//! When the microcontroller is initially programmed, the flash blocks used for +//! parameter block storage are left in an erased state. +//! +//! This function must be called before any other flash parameter block +//! functions are called. +//! +//! \return None. +// +//***************************************************************************** +void +FlashPBInit(uint32_t ui32Start, uint32_t ui32End, uint32_t ui32Size) +{ + uint8_t *pui8Offset, *pui8Current; + uint8_t ui8One, ui8Two; + + // + // Check the arguments. + // + ASSERT((ui32Start % FLASH_SECTOR_SIZE) == 0); + ASSERT((ui32End % FLASH_SECTOR_SIZE) == 0); + ASSERT((FLASH_SECTOR_SIZE % ui32Size) == 0); + + // + // Save the characteristics of the flash memory to be used for storing + // parameter blocks. + // + g_pui8FlashPBStart = (uint8_t *)ui32Start; + g_pui8FlashPBEnd = (uint8_t *)ui32End; + g_ui32FlashPBSize = ui32Size; + + // + // Loop through the portion of flash memory used for storing parameter + // blocks. + // + for(pui8Offset = g_pui8FlashPBStart, pui8Current = 0; + pui8Offset < g_pui8FlashPBEnd; pui8Offset += g_ui32FlashPBSize) + { + // + // See if this is a valid parameter block (in other words, the checksum + // is correct). + // + if(FlashPBIsValid(pui8Offset)) + { + // + // See if a valid parameter block has been previously found. + // + if(pui8Current != 0) + { + // + // Get the sequence numbers for the current and new parameter + // blocks. + // + ui8One = pui8Current[0]; + ui8Two = pui8Offset[0]; + + // + // See if the sequence number for the new parameter block is + // greater than the current block. The comparison isn't + // straightforward since the one byte sequence number will wrap + // after 256 parameter blocks. + // + if(((ui8One > ui8Two) && ((ui8One - ui8Two) < 128)) || + ((ui8Two > ui8One) && ((ui8Two - ui8One) > 128))) + { + // + // The new parameter block is older than the current + // parameter block, so skip the new parameter block and + // keep searching. + // + continue; + } + } + + // + // The new parameter block is more recent than the current one, so + // make it the new current parameter block. + // + pui8Current = pui8Offset; + } + } + + // + // Save the address of the most recent parameter block found. If no valid + // parameter blocks were found, this will be a NULL pointer. + // + g_pui8FlashPBCurrent = pui8Current; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/flash_pb.h b/utils/flash_pb.h new file mode 100644 index 0000000..f68329d --- /dev/null +++ b/utils/flash_pb.h @@ -0,0 +1,58 @@ +//***************************************************************************** +// +// flash_pb.h - Prototypes for the flash parameter block functions. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __FLASH_PB_H__ +#define __FLASH_PB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototype for the flash parameter block functions. +// +//***************************************************************************** +extern uint8_t *FlashPBGet(void); +extern void FlashPBSave(uint8_t *pui8Buffer); +extern void FlashPBInit(uint32_t ui32Start, uint32_t ui32End, + uint32_t ui32Size); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __FLASH_PB_H__ diff --git a/utils/fswrapper.c b/utils/fswrapper.c new file mode 100644 index 0000000..74e80af --- /dev/null +++ b/utils/fswrapper.c @@ -0,0 +1,860 @@ +//***************************************************************************** +// +// fswrapper.c - File System Processing for lwIP Web Server Apps. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "httpserver_raw/fs.h" +#include "httpserver_raw/fsdata.h" +#include "fatfs/src/ff.h" +#include "fatfs/src/diskio.h" +#include "utils/fswrapper.h" +#include "utils/lwiplib.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup fswrapper_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Static file system images for use with this module may be created using +// the makefsfile.exe utility. Both position-independent (built using the -b +// command line option to makefsfile) and position dependent file system images +// may be used. +// +//***************************************************************************** +typedef struct +{ + // + // The index of the file system containing this file. + // + uint32_t ui32MountIndex; + + // + // The FatFs file structure allocated if the target file is in the FAT + // file system. + // + FIL *psFATFile; +} +fs_wrapper_data; + +//***************************************************************************** +// +// A marker used to indicate that a passed filename cannot be mapped to any of +// the configured mount points. +// +//***************************************************************************** +#define BAD_MOUNT_INDEX 0xFFFFFFFF + +//***************************************************************************** +// +// This macro is used to extract pointers from the file descriptors. We +// support files systems linked into the image as well as external, position +// independent file system images and this macro allows us to use the same code +// to extract pointers from file descriptors in each case. +// +//***************************************************************************** +#define FS_POINTER(ptTree, ptValue, bPosInd) \ + ((char *)((bPosInd) ? ((int8_t *)(ptTree) + (uint32_t)(ptValue)) : \ + (int8_t *)(ptValue))) + +//***************************************************************************** +// +// The pointer to the mount point table and the number of entries in the +// table. +// +//***************************************************************************** +static fs_mount_data *g_psMountPoints = NULL; +static uint32_t g_ui32NumMountPoints = 0; +static uint32_t g_ui32DefaultMountIndex = BAD_MOUNT_INDEX; +static bool g_bFatFsEnabled = false; + +//***************************************************************************** +// +// Given a filename, this function determine which of the configured mount +// points it resides under. It returns the index of the mount point in the +// g_psMountPoints array and also a pointer to the first character of the +// filename with the mount point name (directory) stripped from it. +// +//***************************************************************************** +static uint32_t +fs_find_mount_index(const char *pcName, char **ppcFSFilename) +{ + uint32_t ui32Loop; + int iLenDirName; + int iLenMountName; + char *pcSlash; + + // + // First extract the top level directory name which we need to match + // with the mount point name. For this to exist, the pcName string + // must start with a '/' character and must contain at least one more + // '/'. + // + if(pcName[0] == '/') + { + // + // The string starts with a '/'. Does it contain a second one? + // + pcSlash = strchr(pcName + 1, '/'); + + // + // Did we find another forward slash character? + // + if(pcSlash) + { + // + // Yes - the mount point name is between the start of the + // string and the slash we just found. How long is this string? + // + iLenDirName = (int)(pcSlash - (pcName + 1)); + } + else + { + // + // The mount point name is the whole string. + // + iLenDirName = ustrlen(pcName + 1); + pcSlash = (char *)pcName + 1 + iLenDirName; + } + + // + // Now figure out which, if any, of the mount points this matches. + // + for(ui32Loop = 0; ui32Loop < g_ui32NumMountPoints; ui32Loop++) + { + // + // Skip the default mount point if found. + // + if(!g_psMountPoints[ui32Loop].pcNamePrefix) + { + continue; + } + + // + // How long is the name of this mount point? + // + iLenMountName = ustrlen(g_psMountPoints[ui32Loop].pcNamePrefix); + + // + // Does the mount point name match the directory name extracted + // from the passed pcName? + // + if(iLenMountName == iLenDirName) + { + // + // The lengths match but are the strings the same? + // + if(!ustrncmp(g_psMountPoints[ui32Loop].pcNamePrefix, + pcName + 1, iLenDirName)) + { + // + // Yes - we have a match. Set the stripped filename to + // the second '/' and return the mount point index. + // + *ppcFSFilename = pcSlash; + return(ui32Loop); + } + } + } + } + + // + // If we drop out of the loop, we didn't find a specific mount point for + // this file so just return the filename passed and the default mount + // point. + // + *ppcFSFilename = (char *)pcName; + + return(g_ui32DefaultMountIndex); +} + +//***************************************************************************** +// +//! Initializes the file system wrapper. +//! +//! \param psMountPoints points to an array of fs_mount_data structures. Each +//! element in the array maps a top level directory name to a particular +//! file system image or to the FAT file system and a logical drive number. +//! \param ui32NumMountPoints provides the number of populated elements in the +//! \e psMountPoints array. +//! +//! This function should be called to initialize the file system wrapper and +//! provide it with the information required to access the files in multiple +//! file system images via a single filename space. +//! +//! Each entry in \e psMountPoints describes a top level directory in the +//! unified namespace and indicates to fswrapper where the files for that +//! directory can be found. Each entry can describe either a file system +//! image in system memory or a logical disk handled via the FatFs file system +//! driver. +//! +//! For example, consider the following 3 entry mount point table: +//! +//! \verbatim +//! { +//! { "internal", &g_pui8FSImage, 0, NULL, NULL }, +//! { "sdcard", NULL, 0, SDCardEnable, SDCardDisable }, +//! { NULL, &g_pui8FSDefault, 0, NULL, NULL} +//! } +//! \endverbatim +//! +//! Requests to open file ``/internal/index.html'' will be handled by +//! attempting to open ``/index.html'' in the internal file system pointed to +//! by \e g_pui8FSImage. Similarly, opening ``/sdcard/images/logo.gif'' will +//! result in a call to the FAT f_open function requesting +//! ``0:/images/logo.gif''. If a request to open ``index.htm'' is received, +//! this is handled by attempting to open ``index.htm'' in the default internal +//! file system image, \e g_pui8FSDefault. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +fs_init(fs_mount_data *psMountPoints, uint32_t ui32NumMountPoints) +{ + uint32_t ui32Loop; + + // + // Check for non-zero parameters in debug builds. + // + ASSERT(psMountPoints); + ASSERT(ui32NumMountPoints); + + // + // Remember the mount point information we have been given. + // + if(psMountPoints && ui32NumMountPoints) + { + // + // Remember the information passed. + // + g_psMountPoints = psMountPoints; + g_ui32NumMountPoints = ui32NumMountPoints; + + // + // Check to determine if any of the mount points refer to FAT file + // system drivers. We also hijack this loop to determine what the + // default mount point (if any) is. + // + g_bFatFsEnabled = false; + for(ui32Loop = 0; ui32Loop < g_ui32NumMountPoints; ui32Loop++) + { + // + // If the pui8FSImage field of a mount point structure is NULL, + // this implies that we are using the FAT file system for that + // node. + // + if(!g_psMountPoints[ui32Loop].pui8FSImage) + { + g_bFatFsEnabled = true; + } + + // + // Does this entry describe the default mount point? + // + if(g_psMountPoints[ui32Loop].pcNamePrefix == NULL) + { + g_ui32DefaultMountIndex = ui32Loop; + } + } + + return(true); + } + else + { + // + // Return an error due to being passed a bad parameter. + // + return(false); + } +} + +//***************************************************************************** +// +//! Provides a periodic tick for the file system. +//! +//! \param ui32TickMS is the number of milliseconds which have elapsed since +//! the last time this function was called. +//! +//! Applications making use of the file system wrapper with underlying FatFs +//! drives must call this function at least once every 10 milliseconds to +//! provide a time reference for use by the file system. It is typically +//! called in the context of the application's SysTick interrupt handler or +//! from the handler of some other timer interrupt. +//! +//! If only binary file system images are in use, this function need not be +//! called. +//! +//! \return None +// +// +//***************************************************************************** +void +fs_tick(uint32_t ui32TickMS) +{ + static uint32_t ui32TickCounter = 0; + + // + // Check if the file system has been enabled yet. + // + if(!g_bFatFsEnabled) + { + return; + } + + // + // Increment the tick counter. + // + ui32TickCounter += ui32TickMS; + + // + // Check to see if the FAT FS tick needs to run. + // + if(ui32TickCounter >= 10) + { + ui32TickCounter = 0; + disk_timerproc(); + } +} + +//***************************************************************************** +// +//! Opens a file. +//! +//! \param pcName points to a NULL terminated string containing the path and +//! file name to open. +//! +//! This function opens a file and returns a handle allowing it to be read. +//! +//! \return Returns a valid file handle on success or NULL on failure. +// +//***************************************************************************** +struct fs_file * +fs_open(const char *pcName) +{ + const struct fsdata_file *psTree; + const struct fsdata_file *psEnd = NULL; + struct fs_file *psFile = NULL; + fs_wrapper_data *psWrapper; + FRESULT fresult = FR_OK; + bool bPosInd = false; + char *pcFSFilename; + char *pcFilename; + uint32_t ui32Length; + + // + // Allocate memory for the file system structure. + // + psFile = mem_malloc(sizeof(struct fs_file)); + if(NULL == psFile) + { + return(NULL); + } + + // + // Allocate memory for our internal control structure. + // + psFile->pextension = mem_malloc(sizeof(fs_wrapper_data)); + psWrapper = (fs_wrapper_data *)psFile->pextension; + + if(NULL == psWrapper) + { + return(NULL); + } + + // + // Find which mount point we need to use to satisfy this file open request. + // + psWrapper->ui32MountIndex = fs_find_mount_index(pcName, &pcFSFilename); + if(psWrapper->ui32MountIndex == BAD_MOUNT_INDEX) + { + // + // We can't map the mount index so return an error. + // + mem_free(psWrapper); + mem_free(psFile); + return(NULL); + } + + // + // Enable access to the physical medium if we have been provided with + // a callback for this. + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnEnable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnEnable(psWrapper->ui32MountIndex); + } + + // + // Are we opening a file on an internal file system image? + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pui8FSImage) + { + // + // Initialize the file system tree pointer to the root of the linked + // list for this mount point's file system image. + // + psTree = ((const struct fsdata_file *) + g_psMountPoints[psWrapper->ui32MountIndex].pui8FSImage); + + // + // Which type of file system are we dealing with? + // + if(psTree->next == FILE_SYSTEM_MARKER) + { + // + // If we found the marker, this is a position independent file + // system image. Remember this and fix up the pointer to the + // first descriptor by skipping over the 4 byte marker and the + // 4 byte image size entry. We also keep track of where the file + // system image ends since this allows us to do a bit more error + // checking later. + // + bPosInd = true; + ui32Length = *(uint32_t *)((uint8_t *)psTree + 4); + psTree = (struct fsdata_file *)((int8_t *)psTree + 8); + psEnd = (struct fsdata_file *)((int8_t *)psTree + ui32Length); + } + + // + // Begin processing the linked list, looking for the requested file + // name. + // + while(NULL != psTree) + { + // + // Compare the requested file "name" to the file name in the + // current node. + // + if(ustrncmp(pcFSFilename, + FS_POINTER(psTree, psTree->name, bPosInd), + psTree->len) == 0) + { + // + // Fill in the data pointer and length values from the + // linked list node. + // + psFile->data = FS_POINTER(psTree, psTree->data, bPosInd); + psFile->len = psTree->len; + + // + // For now, we setup the read index to the end of the file, + // indicating that all data has been read. This indicates that + // all the data is currently available in a contiguous block + // of memory (which is always the case with an internal file + // system image). + // + psFile->index = psTree->len; + + // + // We are not using a FAT file system file and don't need to + // remap the filename so set these pointers to NULL. + // + psWrapper->psFATFile = NULL; + + // + // Exit the loop and return the file system pointer. + // + break; + } + + // + // If we get here, we did not find the file at this node of the + // linked list. Get the next element in the list. We can't just + // assign psTree from psTree->next since this will give us the + // wrong pointer for a position independent image (where the values + // in the structure are offsets from the start of the file + // descriptor, not absolute pointers) but we do know that a 0 in + // the "next" field does indicate that this is the last file so we + // can use that info to force the loop to exit at the end. + // + if(psTree->next == 0) + { + psTree = NULL; + } + else + { + psTree = (struct fsdata_file *)FS_POINTER(psTree, psTree->next, + bPosInd); + + // + // If this is a position independent file system image, we can + // also check that the new node is within the image. If it + // isn't, the image is corrupted to stop the search. + // + if(bPosInd && (psTree >= psEnd)) + { + psTree = NULL; + } + } + } + + // + // If we didn't find the file, ptTee will be NULL. Make sure we + // return a NULL pointer if this happens. + // + if(NULL == psTree) + { + mem_free(psFile->pextension); + mem_free(psFile); + psFile = NULL; + } + } + else + { + // + // This file is on the FAT file system. + // + + // + // Allocate memory for the Fat File system handle. + // + psWrapper->psFATFile = mem_malloc(sizeof(FIL)); + if(NULL == psWrapper->psFATFile) + { + mem_free(psFile->pextension); + mem_free(psFile); + psFile = NULL; + } + else + { + // + // Reformat the filename to start with the FAT logical drive + // number. + // + ui32Length = ustrlen(pcFSFilename) + 16; + pcFilename = mem_malloc(ui32Length); + if(!pcFilename) + { + // + // Can't allocate temporary storage for the reformatted + // filename! + // + mem_free(psWrapper->psFATFile); + mem_free(psFile->pextension); + mem_free(psFile); + psFile = NULL; + } + else + { + usnprintf(pcFilename, ui32Length, "%d:%s", + g_psMountPoints[psWrapper->ui32MountIndex]. + ui32DriveNum, pcFSFilename); + // + // Attempt to open the file on the Fat File System. + // + fresult = f_open(psWrapper->psFATFile, pcFilename, FA_READ); + + // + // Free the filename storage + // + mem_free(pcFilename); + + // + // Did we open the file correctly? + // + if(FR_OK == fresult) + { + // + // Yes - fill in the file structure to indicate that a + // FAT file is in use. + // + psFile->data = NULL; + psFile->len = 0; + psFile->index = 0; + } + else + { + // + // If we get here, we failed to find the file on the FAT + // file system so free up the FAT handle/object. + // + mem_free(psWrapper->psFATFile); + mem_free(psWrapper); + mem_free(psFile); + psFile = NULL; + } + } + } + } + + // + // Disable access to the physical medium if we have been provided with + // a callback for this. + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnDisable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnDisable(psWrapper->ui32MountIndex); + } + + return(psFile); +} + +//***************************************************************************** +// +//! Closes a file. +//! +//! \param phFile is the handle of the file that is to be closed. This will +//! have been returned by an earlier call to fs_open(). +//! +//! This function closes the file identified by \e phFile and frees all +//! resources associated with the file handle. +//! +//! \return None. +// +//***************************************************************************** +void +fs_close(struct fs_file *phFile) +{ + fs_wrapper_data *psWrapper; + + psWrapper = (fs_wrapper_data *)phFile->pextension; + + // + // If a Fat file was opened, free its object. + // + if(psWrapper->psFATFile) + { + // + // Close the file. + // + f_close(psWrapper->psFATFile); + + // + // Free the file object. + // + mem_free(psWrapper->psFATFile); + } + + // + // Free our file wrapper control structure. + // + mem_free(phFile->pextension); + + // + // Free the main file system object. + // + mem_free(phFile); +} + +//***************************************************************************** +// +//! Reads data from an open file. +//! +//! \param phFile is the handle of the file which is to be read. This will +//! have been returned by a previous call to fs_open(). +//! \param pcBuffer points to the first byte of the buffer into which the +//! data read from the file will be copied. This buffer must be large enough +//! to hold \e iCount bytes. +//! \param iCount is the maximum number of bytes of data that are to be read +//! from the file. +//! +//! This function reads the next block of data from the given file into a +//! buffer and returns the number of bytes read or -1 if the end of the file +//! has been reached. +//! +//! \return Returns the number of bytes read from the file or -1 if the end of +//! the file has been reached and no more data is available. +// +//***************************************************************************** +int +fs_read(struct fs_file *phFile, char *pcBuffer, int iCount) +{ + int iAvailable, iRetcode; + fs_wrapper_data *psWrapper; + + psWrapper = (fs_wrapper_data *)phFile->pextension; + + // + // Call the application's enable function for this physical medium (if + // an enable function has been provided). + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnEnable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnEnable(psWrapper->ui32MountIndex); + } + + // + // Check to see if a Fat File was opened and process it. + // + if(psWrapper->psFATFile) + { + uint32_t ui32BytesRead; + FRESULT fresult; + + // + // Read the data. + // + fresult = f_read(psWrapper->psFATFile, pcBuffer, iCount, + (UINT*)&ui32BytesRead); + if((fresult != FR_OK) || (ui32BytesRead == 0)) + { + iRetcode = -1; + } + else + { + iRetcode = (int)ui32BytesRead; + } + } + else + { + // + // We are reading a file from a file system image. Check to see if + // more data is available. + // + if(phFile->len == phFile->index) + { + // + // There is no remaining data. Return a -1 for EOF indication. + // + return(-1); + } + + // + // Determine how much data we can copy. The minimum of the 'iCount' + // parameter or the available data in the file system buffer. + // + iAvailable = phFile->len - phFile->index; + if(iAvailable > iCount) + { + iAvailable = iCount; + } + + // + // Copy the data. + // + memcpy(pcBuffer, phFile->data + phFile->index, iAvailable); + phFile->index += iAvailable; + + // + // Return the count of data that we copied. + // + iRetcode = iAvailable; + } + + // + // Call the application's disable function now that we have finished + // accessing the file. + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnDisable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnDisable(psWrapper->ui32MountIndex); + } + + // + // Return the number of bytes read. + // + return(iRetcode); +} + +//***************************************************************************** +// +//! Maps a path string containing mount point names to a path suitable for +//! use in calls to the FatFs APIs. +//! +//! \param pcPath points to a string containing a path in the namespace +//! defined by the mount information passed to fs_init(). +//! \param pcMapped points to a buffer into which the mapped path string will +//! be written. +//! \param iLen is the size, in bytes, of the buffer pointed to by pcMapped. +//! +//! This function may be used by applications which want to make use of FatFs +//! functions which are not directly mapped by the fswrapper layer. A path +//! in the namespace defined by the mount points passed to function fs_init() +//! is translated to an equivalent path in the FatFs namespace and this may +//! then be used in a direct call to functions such as f_opendir() or +//! f_getfree(). +//! +//! \return Returns \b true on success or \b false if fs_init() has not +//! been called, if the path provided maps to an internal file system image +//! rather than a FatFs logical drive or if the buffer pointed to by +//! \e pcMapped is too small to fit the output string. +// +//***************************************************************************** +bool +fs_map_path(const char *pcPath, char *pcMapped, int iLen) +{ + char *pcFSFilename; + uint32_t ui32MountIndex; + int iCount; + + // + // If no mount points have been defined, return an error. + // + if(!g_psMountPoints) + { + return(false); + } + + // + // Find which mount point we need to use to satisfy this file open request. + // + ui32MountIndex = fs_find_mount_index(pcPath, &pcFSFilename); + + // + // If we got a bad mount index or the index returned represents a mount + // point that is not in the FAT file system, return an error. + // + if((ui32MountIndex == BAD_MOUNT_INDEX) || + (g_psMountPoints[ui32MountIndex].pui8FSImage)) + { + // + // We can't map the mount index so return an error. + // + return(false); + } + + // + // Now we can generate the FatFs namespace path string. + // + iCount = usnprintf(pcMapped, iLen, "%d:%s", + g_psMountPoints[ui32MountIndex].ui32DriveNum, + pcFSFilename); + + // + // Tell the user how we got on. The count returned by usnprintf is the + // number of characters that should have been written, excluding the + // terminating NULL so we use this to check for overflow of the output + // buffer. + // + return((iLen >= (iCount + 1)) ? true : false); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/fswrapper.h b/utils/fswrapper.h new file mode 100644 index 0000000..889cdd4 --- /dev/null +++ b/utils/fswrapper.h @@ -0,0 +1,142 @@ +//***************************************************************************** +// +// fswrapper.h - Public type definitons and function prototypes for the simple +// file system wrapper module. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __FSWRAPPER_H__ +#define __FSWRAPPER_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup fswrapper_api +//! @{ +// +//***************************************************************************** + +typedef struct +{ + // + //! This string provides a pseudo-directory name that will be used to + //! identify this mount point in future calls to fs_open. If this string + //! is NULL, this indicates that this is the default file system which will + //! be used when any simple filename, not including a leading directory + //! name is passed or when the leading directory name is not found in the + //! list of mount points passed to fs_init. + // + const char *pcNamePrefix; + + // + //! A pointer to the start of the file system image that is to be used to + //! satisfy requests for files whose name begins "/name" where + //! "name" is the string provided in the pcNamePrefix field. This pointer + //! may point to either a position-dependent or position-independent file + //! system image generated by the makefsfile executable or makefsdata + //! Perl script. If NULL, it is assumed that the FAT file system is to be + //! used and that the drive number provided in ui32DriveNum should be + //! substituted for "name" in the supplied filename before attempting to + //! open the FAT file. + // + uint8_t *pui8FSImage; + + // + //! If this mount point describes a logical drive in the FAT file system, + //! this field indicates the drive number that is to be accessed. This + //! number will be substituted for the string provided in the pcNamePrefix + //! field in the filename passed to fs_open before that filename is passed + //! down to the FAT file system. For example, if pcNamePrefix is "sdcard" + //! and ui32DriveNum is 0, a call to fs_open passing + //! "/sdcard/images/logo.gif" will be passed to the FATfs f_open call as + //! "/0/images/logo.gif". This field is ignored if pui8FSImage is not NULL + //! (indicating that this mount point refers to a file system image rather + //! than the FAT file system). + //! + uint32_t ui32DriveNum; + + // + //! This function pointer is called whenever a file is to be opened, read + //! or (for read/write file systems) written on this file system. If any + //! special setup is required to allow access to the physical medium (for + //! example, setting the SSI mode or clock frequency), the application may + //! use this callback to perform that initialization. If this field is + //! NULL, no callback will be made. + // + void (*pfnEnable)(uint32_t ui32FSIndex); + + // + //! This callback is made after access to the physical medium has been + //! completed. An application may assume that no further access to the + //! medium will be made until a call to pfnEnable is made. If this field + //! is NULL, no callback will be made. + // + void (*pfnDisable)(uint32_t ui32FSIndex); +} +fs_mount_data; + +//***************************************************************************** +// +// This marker, "FIMG", is placed at the beginning of a position-independent +// file system image to differentiate it from a position-dependent image. +// +//***************************************************************************** +#define FILE_SYSTEM_MARKER ((const struct fsdata_file *)0x474D4946) + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public function prototypes +// +//***************************************************************************** +extern bool fs_init(fs_mount_data *psMountPoints, uint32_t ui32NumMountPoints); +extern void fs_tick(uint32_t ui32TickMS); +extern struct fs_file *fs_open(const char *name); +extern void fs_close(struct fs_file *file); +extern int fs_read(struct fs_file *file, char *buffer, int count); +extern bool fs_map_path(const char *pcPath, char *pcMapped, int iLen); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __FSWRAPPER_H__ diff --git a/utils/isqrt.c b/utils/isqrt.c new file mode 100644 index 0000000..aa908f6 --- /dev/null +++ b/utils/isqrt.c @@ -0,0 +1,118 @@ +//***************************************************************************** +// +// isqrt.c - Integer square root. +// +// Copyright (c) 2005-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 Utility Library. +// +//***************************************************************************** + +#include +#include "utils/isqrt.h" + +//***************************************************************************** +// +//! \addtogroup isqrt_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Compute the integer square root of an integer. +//! +//! \param ui32Value is the value whose square root is desired. +//! +//! This function will compute the integer square root of the given input +//! value. Since the value returned is also an integer, it is actually better +//! defined as the largest integer whose square is less than or equal to the +//! input value. +//! +//! \return Returns the square root of the input value. +// +//***************************************************************************** +uint32_t +isqrt(uint32_t ui32Value) +{ + uint32_t ui32Rem, ui32Root, ui32Idx; + + // + // Initialize the remainder and root to zero. + // + ui32Rem = 0; + ui32Root = 0; + + // + // Loop over the sixteen bits in the root. + // + for(ui32Idx = 0; ui32Idx < 16; ui32Idx++) + { + // + // Shift the root up by a bit to make room for the new bit that is + // about to be computed. + // + ui32Root <<= 1; + + // + // Get two more bits from the input into the remainder. + // + ui32Rem = ((ui32Rem << 2) + (ui32Value >> 30)); + ui32Value <<= 2; + + // + // Make the test root be 2n + 1. + // + ui32Root++; + + // + // See if the root is greater than the remainder. + // + if(ui32Root <= ui32Rem) + { + // + // Subtract the test root from the remainder. + // + ui32Rem -= ui32Root; + + // + // Increment the root, setting the second LSB. + // + ui32Root++; + } + else + { + // + // The root is greater than the remainder, so the new bit of the + // root is actually zero. + // + ui32Root--; + } + } + + // + // Return the computed root. + // + return(ui32Root >> 1); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/isqrt.h b/utils/isqrt.h new file mode 100644 index 0000000..2d0de39 --- /dev/null +++ b/utils/isqrt.h @@ -0,0 +1,55 @@ +//***************************************************************************** +// +// isqrt.h - Prototype for the integer square root function. +// +// Copyright (c) 2006-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 Utility Library. +// +//***************************************************************************** + +#ifndef __ISQRT_H__ +#define __ISQRT_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The prototype for the integer square root function. +// +//***************************************************************************** +extern uint32_t isqrt(uint32_t ui32Value); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/utils/locator.c b/utils/locator.c new file mode 100644 index 0000000..fb240b2 --- /dev/null +++ b/utils/locator.c @@ -0,0 +1,342 @@ +//***************************************************************************** +// +// locator.c - A device locator server using UDP in lwIP. +// +// Copyright (c) 2009-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 Utility Library. +// +//***************************************************************************** + +#include +#include "utils/locator.h" +#include "utils/lwiplib.h" + +//***************************************************************************** +// +//! \addtogroup locator_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are used to describe the device locator protocol. +// +//***************************************************************************** +#define TAG_CMD 0xff +#define TAG_STATUS 0xfe +#define CMD_DISCOVER_TARGET 0x02 + +//***************************************************************************** +// +// An array that contains the device locator response data. The format of the +// data is as follows: +// +// Byte Description +// -------- ------------------------ +// 0 TAG_STATUS +// 1 packet length +// 2 CMD_DISCOVER_TARGET +// 3 board type +// 4 board ID +// 5..8 client IP address +// 9..14 MAC address +// 15..18 firmware version +// 19..82 application title +// 83 checksum +// +//***************************************************************************** +static uint8_t g_pui8LocatorData[84]; + +//***************************************************************************** +// +// This function is called by the lwIP TCP/IP stack when it receives a UDP +// packet from the discovery port. It produces the response packet, which is +// sent back to the querying client. +// +//***************************************************************************** +static void +LocatorReceive(void *arg, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + uint8_t *pui8Data; + uint32_t ui32Idx; + + // + // Validate the contents of the datagram. + // + pui8Data = p->payload; + if((p->len != 4) || (pui8Data[0] != TAG_CMD) || (pui8Data[1] != 4) || + (pui8Data[2] != CMD_DISCOVER_TARGET) || + (pui8Data[3] != ((0 - TAG_CMD - 4 - CMD_DISCOVER_TARGET) & 0xff))) + { + pbuf_free(p); + return; + } + + // + // The incoming pbuf is no longer needed, so free it. + // + pbuf_free(p); + + // + // Allocate a new pbuf for sending the response. + // + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(g_pui8LocatorData), PBUF_RAM); + if(p == NULL) + { + return; + } + + // + // Calculate and fill in the checksum on the response packet. + // + for(ui32Idx = 0, g_pui8LocatorData[sizeof(g_pui8LocatorData) - 1] = 0; + ui32Idx < (sizeof(g_pui8LocatorData) - 1); ui32Idx++) + { + g_pui8LocatorData[sizeof(g_pui8LocatorData) - 1] -= + g_pui8LocatorData[ui32Idx]; + } + + // + // Copy the response packet data into the pbuf. + // + pui8Data = p->payload; + for(ui32Idx = 0; ui32Idx < sizeof(g_pui8LocatorData); ui32Idx++) + { + pui8Data[ui32Idx] = g_pui8LocatorData[ui32Idx]; + } + + // + // Send the response. + // + udp_sendto(pcb, p, addr, port); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +//! Initializes the locator service. +//! +//! This function prepares the locator service to handle device discovery +//! requests. A UDP server is created and the locator response data is +//! initialized to all empty. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorInit(void) +{ + uint32_t ui32Idx; + void *pcb; + + // + // Clear out the response data. + // + for(ui32Idx = 0; ui32Idx < 84; ui32Idx++) + { + g_pui8LocatorData[ui32Idx] = 0; + } + + // + // Fill in the header for the response data. + // + g_pui8LocatorData[0] = TAG_STATUS; + g_pui8LocatorData[1] = sizeof(g_pui8LocatorData); + g_pui8LocatorData[2] = CMD_DISCOVER_TARGET; + + // + // Fill in the MAC address for the response data. + // + g_pui8LocatorData[9] = 0; + g_pui8LocatorData[10] = 0; + g_pui8LocatorData[11] = 0; + g_pui8LocatorData[12] = 0; + g_pui8LocatorData[13] = 0; + g_pui8LocatorData[14] = 0; + + // + // Create a new UDP port for listening to device locator requests. + // + pcb = udp_new(); + udp_recv(pcb, LocatorReceive, NULL); + udp_bind(pcb, IP_ADDR_ANY, 23); +} + +//***************************************************************************** +// +//! Sets the board type in the locator response packet. +//! +//! \param ui32Type is the type of the board. +//! +//! This function sets the board type field in the locator response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorBoardTypeSet(uint32_t ui32Type) +{ + // + // Save the board type in the response data. + // + g_pui8LocatorData[3] = ui32Type & 0xff; +} + +//***************************************************************************** +// +//! Sets the board ID in the locator response packet. +//! +//! \param ui32ID is the ID of the board. +//! +//! This function sets the board ID field in the locator response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorBoardIDSet(uint32_t ui32ID) +{ + // + // Save the board ID in the response data. + // + g_pui8LocatorData[4] = ui32ID & 0xff; +} + +//***************************************************************************** +// +//! Sets the client IP address in the locator response packet. +//! +//! \param ui32IP is the IP address of the currently connected client. +//! +//! This function sets the IP address of the currently connected client in the +//! locator response packet. The IP should be set to 0.0.0.0 if there is no +//! client connected. It should never be set for devices that do not have a +//! strict one-to-one mapping of client to server (for example, a web server). +//! +//! \return None. +// +//***************************************************************************** +void +LocatorClientIPSet(uint32_t ui32IP) +{ + // + // Save the client IP address in the response data. + // + g_pui8LocatorData[5] = ui32IP & 0xff; + g_pui8LocatorData[6] = (ui32IP >> 8) & 0xff; + g_pui8LocatorData[7] = (ui32IP >> 16) & 0xff; + g_pui8LocatorData[8] = (ui32IP >> 24) & 0xff; +} + +//***************************************************************************** +// +//! Sets the MAC address in the locator response packet. +//! +//! \param pui8MACArray is the MAC address of the network interface. +//! +//! This function sets the MAC address of the network interface in the locator +//! response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorMACAddrSet(uint8_t *pui8MACArray) +{ + // + // Save the MAC address. + // + g_pui8LocatorData[9] = pui8MACArray[0]; + g_pui8LocatorData[10] = pui8MACArray[1]; + g_pui8LocatorData[11] = pui8MACArray[2]; + g_pui8LocatorData[12] = pui8MACArray[3]; + g_pui8LocatorData[13] = pui8MACArray[4]; + g_pui8LocatorData[14] = pui8MACArray[5]; +} + +//***************************************************************************** +// +//! Sets the firmware version in the locator response packet. +//! +//! \param ui32Version is the version number of the device firmware. +//! +//! This function sets the version number of the device firmware in the locator +//! response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorVersionSet(uint32_t ui32Version) +{ + // + // Save the firmware version number in the response data. + // + g_pui8LocatorData[15] = ui32Version & 0xff; + g_pui8LocatorData[16] = (ui32Version >> 8) & 0xff; + g_pui8LocatorData[17] = (ui32Version >> 16) & 0xff; + g_pui8LocatorData[18] = (ui32Version >> 24) & 0xff; +} + +//***************************************************************************** +// +//! Sets the application title in the locator response packet. +//! +//! \param pcAppTitle is a pointer to the application title string. +//! +//! This function sets the application title in the locator response packet. +//! The string is truncated at 64 characters if it is longer (without a +//! terminating 0), and is zero-filled to 64 characters if it is shorter. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorAppTitleSet(const char *pcAppTitle) +{ + uint32_t ui32Count; + + // + // Copy the application title string into the response data. + // + for(ui32Count = 0; (ui32Count < 64) && *pcAppTitle; ui32Count++) + { + g_pui8LocatorData[ui32Count + 19] = *pcAppTitle++; + } + + // + // Zero-fill the remainder of the space in the response data (if any). + // + for(; ui32Count < 64; ui32Count++) + { + g_pui8LocatorData[ui32Count + 19] = 0; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/locator.h b/utils/locator.h new file mode 100644 index 0000000..0ef7979 --- /dev/null +++ b/utils/locator.h @@ -0,0 +1,61 @@ +//***************************************************************************** +// +// locator.h - Prototypes for the device locator server. +// +// Copyright (c) 2009-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 Utility Library. +// +//***************************************************************************** + +#ifndef __LOCATOR_H__ +#define __LOCATOR_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern void LocatorInit(void); +extern void LocatorBoardTypeSet(uint32_t ui32Type); +extern void LocatorBoardIDSet(uint32_t ui32ID); +extern void LocatorClientIPSet(uint32_t ui32IP); +extern void LocatorMACAddrSet(uint8_t *pui8MACArray); +extern void LocatorVersionSet(uint32_t ui32Version); +extern void LocatorAppTitleSet(const char *pcAppTitle); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __LOCATOR_H__ diff --git a/utils/lwiplib.c b/utils/lwiplib.c new file mode 100644 index 0000000..89539bd --- /dev/null +++ b/utils/lwiplib.c @@ -0,0 +1,1399 @@ +//***************************************************************************** +// +// lwiplib.c - lwIP TCP/IP Library Abstraction Layer. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +// Ensure that the lwIP compile time options are included first. +// +//***************************************************************************** +#include +#include +#include "utils/lwiplib.h" + +//***************************************************************************** +// +// Ensure that ICMP checksum offloading is enabled; otherwise the TM4C129 +// driver will not operate correctly. +// +//***************************************************************************** +#ifndef LWIP_OFFLOAD_ICMP_CHKSUM +#define LWIP_OFFLOAD_ICMP_CHKSUM 1 +#endif + +//***************************************************************************** +// +// Include lwIP high-level API code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/api/api_lib.c" +#include "third_party/lwip-1.4.1/src/api/api_msg.c" +#include "third_party/lwip-1.4.1/src/api/err.c" +#include "third_party/lwip-1.4.1/src/api/netbuf.c" +#include "third_party/lwip-1.4.1/src/api/netdb.c" +#include "third_party/lwip-1.4.1/src/api/netifapi.c" +#include "third_party/lwip-1.4.1/src/api/sockets.c" +#include "third_party/lwip-1.4.1/src/api/tcpip.c" + +//***************************************************************************** +// +// Include the core lwIP TCP/IP stack code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/core/def.c" +#include "third_party/lwip-1.4.1/src/core/dhcp.c" +#include "third_party/lwip-1.4.1/src/core/dns.c" +#include "third_party/lwip-1.4.1/src/core/init.c" +#include "third_party/lwip-1.4.1/src/core/mem.c" +#include "third_party/lwip-1.4.1/src/core/memp.c" +#include "third_party/lwip-1.4.1/src/core/netif.c" +#include "third_party/lwip-1.4.1/src/core/pbuf.c" +#include "third_party/lwip-1.4.1/src/core/raw.c" +#include "third_party/lwip-1.4.1/src/core/stats.c" +#include "third_party/lwip-1.4.1/src/core/sys.c" +#include "third_party/lwip-1.4.1/src/core/tcp.c" +#include "third_party/lwip-1.4.1/src/core/tcp_in.c" +#include "third_party/lwip-1.4.1/src/core/tcp_out.c" +#include "third_party/lwip-1.4.1/src/core/timers.c" +#include "third_party/lwip-1.4.1/src/core/udp.c" + +//***************************************************************************** +// +// Include the IPV4 code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/core/ipv4/autoip.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/icmp.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/igmp.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/inet.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/inet_chksum.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/ip.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/ip_addr.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/ip_frag.c" + +//***************************************************************************** +// +// Include the IPV6 code. +// Note: Code is experimental and not ready for use. +// References are included for completeness. +// +//***************************************************************************** +#if 0 +#include "third_party/lwip-1.4.1/src/core/ipv6/icmp6.c" +#include "third_party/lwip-1.4.1/src/core/ipv6/inet6.c" +#include "third_party/lwip-1.4.1/src/core/ipv6/ip6.c" +#include "third_party/lwip-1.4.1/src/core/ipv6/ip6_addr.c" +#endif + +//***************************************************************************** +// +// Include the lwIP SNMP code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/core/snmp/asn1_dec.c" +#include "third_party/lwip-1.4.1/src/core/snmp/asn1_enc.c" +#include "third_party/lwip-1.4.1/src/core/snmp/mib2.c" +#include "third_party/lwip-1.4.1/src/core/snmp/mib_structs.c" +#include "third_party/lwip-1.4.1/src/core/snmp/msg_in.c" +#include "third_party/lwip-1.4.1/src/core/snmp/msg_out.c" + +//***************************************************************************** +// +// Include the network interface code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/netif/etharp.c" + +//***************************************************************************** +// +// Include the network interface PPP code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/netif/ppp/auth.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/chap.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/chpms.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/fsm.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/ipcp.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/lcp.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/magic.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/md5.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/pap.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/ppp.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/ppp_oe.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/randm.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/vj.c" + +//***************************************************************************** +// +// Include Tiva-specific lwIP interface/porting layer code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/ports/tiva-tm4c129/perf.c" +#include "third_party/lwip-1.4.1/ports/tiva-tm4c129/sys_arch.c" +#include "third_party/lwip-1.4.1/ports/tiva-tm4c129/netif/tiva-tm4c129.c" + +//***************************************************************************** +// +//! \addtogroup lwiplib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The lwIP Library abstration layer provides for a host callback function to +// be called periodically in the lwIP context. This is the timer interval, in +// ms, for this periodic callback. If the timer interval is defined to 0 (the +// default value), then no periodic host callback is performed. +// +//***************************************************************************** +#ifndef HOST_TMR_INTERVAL +#define HOST_TMR_INTERVAL 0 +#else +extern void lwIPHostTimerHandler(void); +#endif + +//***************************************************************************** +// +// The link detect polling interval. +// +//***************************************************************************** +#define LINK_TMR_INTERVAL 10 + +//***************************************************************************** +// +// Set the PHY configuration to the default (internal) option if necessary. +// +//***************************************************************************** +#ifndef EMAC_PHY_CONFIG +#define EMAC_PHY_CONFIG (EMAC_PHY_TYPE_INTERNAL | \ + EMAC_PHY_INT_MDIX_EN | \ + EMAC_PHY_AN_100B_T_FULL_DUPLEX) +#endif + +//***************************************************************************** +// +// Driverlib headers needed for this library module. +// +//***************************************************************************** +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_nvic.h" +#include "driverlib/debug.h" +#include "driverlib/emac.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#if !NO_SYS +#if RTOS_FREERTOS +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" +#endif +#if ((RTOS_FREERTOS) < 1) + #error No RTOS is defined. Please define an RTOS. +#endif +#if ((RTOS_FREERTOS) > 1) + #error More than one RTOS defined. Please define only one RTOS at a time. +#endif +#endif + +//***************************************************************************** +// +// The lwIP network interface structure for the Tiva Ethernet MAC. +// +//***************************************************************************** +static struct netif g_sNetIF; + +//***************************************************************************** +// +// The application's interrupt handler for hardware timer events from the MAC. +// +//***************************************************************************** +tHardwareTimerHandler g_pfnTimerHandler; + +//***************************************************************************** +// +// The local time for the lwIP Library Abstraction layer, used to support the +// Host and lwIP periodic callback functions. +// +//***************************************************************************** +#if NO_SYS +uint32_t g_ui32LocalTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the TCP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS +static uint32_t g_ui32TCPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the HOST timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && HOST_TMR_INTERVAL +static uint32_t g_ui32HostTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the ARP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_ARP +static uint32_t g_ui32ARPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the AutoIP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_AUTOIP +static uint32_t g_ui32AutoIPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the DHCP Coarse timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_DHCP +static uint32_t g_ui32DHCPCoarseTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the DHCP Fine timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_DHCP +static uint32_t g_ui32DHCPFineTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the IP Reassembly timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && IP_REASSEMBLY +static uint32_t g_ui32IPReassemblyTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the IGMP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_IGMP +static uint32_t g_ui32IGMPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the DNS timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_DNS +static uint32_t g_ui32DNSTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the link detect timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && (LWIP_AUTOIP || LWIP_DHCP) +static uint32_t g_ui32LinkTimer = 0; +#endif + +//***************************************************************************** +// +// The default IP address acquisition mode. +// +//***************************************************************************** +static uint32_t g_ui32IPMode = IPADDR_USE_STATIC; + +//***************************************************************************** +// +// The most recently detected link state. +// +//***************************************************************************** +#if LWIP_AUTOIP || LWIP_DHCP +static bool g_bLinkActive = false; +#endif + +//***************************************************************************** +// +// The IP address to be used. This is used during the initialization of the +// stack and when the interface configuration is changed. +// +//***************************************************************************** +static uint32_t g_ui32IPAddr; + +//***************************************************************************** +// +// The netmask to be used. This is used during the initialization of the stack +// and when the interface configuration is changed. +// +//***************************************************************************** +static uint32_t g_ui32NetMask; + +//***************************************************************************** +// +// The gateway address to be used. This is used during the initialization of +// the stack and when the interface configuration is changed. +// +//***************************************************************************** +static uint32_t g_ui32GWAddr; + +//***************************************************************************** +// +// The stack size for the interrupt task. +// +//***************************************************************************** +#if !NO_SYS +#define STACKSIZE_LWIPINTTASK 128 +#endif + +//***************************************************************************** +// +// The handle for the "queue" (semaphore) used to signal the interrupt task +// from the interrupt handler. +// +//***************************************************************************** +#if !NO_SYS +static xQueueHandle g_pInterrupt; +#endif + +//***************************************************************************** +// +// This task handles reading packets from the Ethernet controller and supplying +// them to the TCP/IP thread. +// +//***************************************************************************** +#if !NO_SYS +static void +lwIPInterruptTask(void *pvArg) +{ + // + // Loop forever. + // + while(1) + { + // + // Wait until the semaphore has been signaled. + // + while(xQueueReceive(g_pInterrupt, &pvArg, portMAX_DELAY) != pdPASS) + { + } + + // + // Processes any packets waiting to be sent or received. + // + tivaif_interrupt(&g_sNetIF, (uint32_t)pvArg); + + // + // Re-enable the Ethernet interrupts. + // + MAP_EMACIntEnable(EMAC0_BASE, (EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT | + EMAC_INT_TX_STOPPED | + EMAC_INT_RX_NO_BUFFER | + EMAC_INT_RX_STOPPED | EMAC_INT_PHY)); + } +} +#endif + +//***************************************************************************** +// +// This function performs a periodic check of the link status and responds +// appropriately if it has changed. +// +//***************************************************************************** +#if LWIP_AUTOIP || LWIP_DHCP +static void +lwIPLinkDetect(void) +{ + bool bHaveLink; + struct ip_addr ip_addr; + struct ip_addr net_mask; + struct ip_addr gw_addr; + + // + // See if there is an active link. + // + bHaveLink = MAP_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) & EPHY_BMSR_LINKSTAT; + + // + // Return without doing anything else if the link state hasn't changed. + // + if(bHaveLink == g_bLinkActive) + { + return; + } + + // + // Save the new link state. + // + g_bLinkActive = bHaveLink; + + // + // Clear any address information from the network interface. + // + ip_addr.addr = 0; + net_mask.addr = 0; + gw_addr.addr = 0; + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + + // + // See if there is a link now. + // + if(bHaveLink) + { + // + // Start DHCP, if enabled. + // +#if LWIP_DHCP + if(g_ui32IPMode == IPADDR_USE_DHCP) + { + dhcp_start(&g_sNetIF); + } +#endif + + // + // Start AutoIP, if enabled and DHCP is not. + // +#if LWIP_AUTOIP + if(g_ui32IPMode == IPADDR_USE_AUTOIP) + { + autoip_start(&g_sNetIF); + } +#endif + } + else + { + // + // Stop DHCP, if enabled. + // +#if LWIP_DHCP + if(g_ui32IPMode == IPADDR_USE_DHCP) + { + dhcp_stop(&g_sNetIF); + } +#endif + + // + // Stop AutoIP, if enabled and DHCP is not. + // +#if LWIP_AUTOIP + if(g_ui32IPMode == IPADDR_USE_AUTOIP) + { + autoip_stop(&g_sNetIF); + } +#endif + } +} +#endif + +//***************************************************************************** +// +// This function services all of the lwIP periodic timers, including TCP and +// Host timers. This should be called from the lwIP context, which may be +// the Ethernet interrupt (in the case of a non-RTOS system) or the lwIP +// thread, in the event that an RTOS is used. +// +//***************************************************************************** +#if NO_SYS +static void +lwIPServiceTimers(void) +{ + // + // Service the host timer. + // +#if HOST_TMR_INTERVAL + if((g_ui32LocalTimer - g_ui32HostTimer) >= HOST_TMR_INTERVAL) + { + g_ui32HostTimer = g_ui32LocalTimer; + lwIPHostTimerHandler(); + } +#endif + + // + // Service the ARP timer. + // +#if LWIP_ARP + if((g_ui32LocalTimer - g_ui32ARPTimer) >= ARP_TMR_INTERVAL) + { + g_ui32ARPTimer = g_ui32LocalTimer; + etharp_tmr(); + } +#endif + + // + // Service the TCP timer. + // +#if LWIP_TCP + if((g_ui32LocalTimer - g_ui32TCPTimer) >= TCP_TMR_INTERVAL) + { + g_ui32TCPTimer = g_ui32LocalTimer; + tcp_tmr(); + } +#endif + + // + // Service the AutoIP timer. + // +#if LWIP_AUTOIP + if((g_ui32LocalTimer - g_ui32AutoIPTimer) >= AUTOIP_TMR_INTERVAL) + { + g_ui32AutoIPTimer = g_ui32LocalTimer; + autoip_tmr(); + } +#endif + + // + // Service the DCHP Coarse Timer. + // +#if LWIP_DHCP + if((g_ui32LocalTimer - g_ui32DHCPCoarseTimer) >= DHCP_COARSE_TIMER_MSECS) + { + g_ui32DHCPCoarseTimer = g_ui32LocalTimer; + dhcp_coarse_tmr(); + } +#endif + + // + // Service the DCHP Fine Timer. + // +#if LWIP_DHCP + if((g_ui32LocalTimer - g_ui32DHCPFineTimer) >= DHCP_FINE_TIMER_MSECS) + { + g_ui32DHCPFineTimer = g_ui32LocalTimer; + dhcp_fine_tmr(); + } +#endif + + // + // Service the IP Reassembly Timer + // +#if IP_REASSEMBLY + if((g_ui32LocalTimer - g_ui32IPReassemblyTimer) >= IP_TMR_INTERVAL) + { + g_ui32IPReassemblyTimer = g_ui32LocalTimer; + ip_reass_tmr(); + } +#endif + + // + // Service the IGMP Timer + // +#if LWIP_IGMP + if((g_ui32LocalTimer - g_ui32IGMPTimer) >= IGMP_TMR_INTERVAL) + { + g_ui32IGMPTimer = g_ui32LocalTimer; + igmp_tmr(); + } +#endif + + // + // Service the DNS Timer + // +#if LWIP_DNS + if((g_ui32LocalTimer - g_ui32DNSTimer) >= DNS_TMR_INTERVAL) + { + g_ui32DNSTimer = g_ui32LocalTimer; + dns_tmr(); + } +#endif + + // + // Service the link timer. + // +#if LWIP_AUTOIP || LWIP_DHCP + if((g_ui32LocalTimer - g_ui32LinkTimer) >= LINK_TMR_INTERVAL) + { + g_ui32LinkTimer = g_ui32LocalTimer; + lwIPLinkDetect(); + } +#endif +} +#endif + +//***************************************************************************** +// +// Handles the timeout for the host callback function timer when using a RTOS. +// +//***************************************************************************** +#if !NO_SYS && HOST_TMR_INTERVAL +static void +lwIPPrivateHostTimer(void *pvArg) +{ + // + // Call the application-supplied host timer callback function. + // + lwIPHostTimerHandler(); + + // + // Re-schedule the host timer callback function timeout. + // + sys_timeout(HOST_TMR_INTERVAL, lwIPPrivateHostTimer, NULL); +} +#endif + +//***************************************************************************** +// +// Handles the timeout for the link detect timer when using a RTOS. +// +//***************************************************************************** +#if !NO_SYS && (LWIP_AUTOIP || LWIP_DHCP) +static void +lwIPPrivateLinkTimer(void *pvArg) +{ + // + // Perform the link detection. + // + lwIPLinkDetect(); + + // + // Re-schedule the link detect timer timeout. + // + sys_timeout(LINK_TMR_INTERVAL, lwIPPrivateLinkTimer, NULL); +} +#endif + +//***************************************************************************** +// +// Completes the initialization of lwIP. This is directly called when not +// using a RTOS and provided as a callback to the TCP/IP thread when using a +// RTOS. +// +//***************************************************************************** +static void +lwIPPrivateInit(void *pvArg) +{ + struct ip_addr ip_addr; + struct ip_addr net_mask; + struct ip_addr gw_addr; + + // + // If not using a RTOS, initialize the lwIP stack. + // +#if NO_SYS + lwip_init(); +#endif + + // + // If using a RTOS, create a queue (to be used as a semaphore) to signal + // the Ethernet interrupt task from the Ethernet interrupt handler. + // +#if !NO_SYS +#if RTOS_FREERTOS + g_pInterrupt = xQueueCreate(1, sizeof(void *)); +#endif +#endif + + // + // If using a RTOS, create the Ethernet interrupt task. + // +#if !NO_SYS +#if RTOS_FREERTOS + xTaskCreate(lwIPInterruptTask, (signed portCHAR *)"eth_int", + STACKSIZE_LWIPINTTASK, 0, tskIDLE_PRIORITY + 1, + 0); +#endif +#endif + + // + // Setup the network address values. + // + if(g_ui32IPMode == IPADDR_USE_STATIC) + { + ip_addr.addr = htonl(g_ui32IPAddr); + net_mask.addr = htonl(g_ui32NetMask); + gw_addr.addr = htonl(g_ui32GWAddr); + } + else + { + ip_addr.addr = 0; + net_mask.addr = 0; + gw_addr.addr = 0; + } + + // + // Create, configure and add the Ethernet controller interface with + // default settings. ip_input should be used to send packets directly to + // the stack when not using a RTOS and tcpip_input should be used to send + // packets to the TCP/IP thread's queue when using a RTOS. + // +#if NO_SYS + netif_add(&g_sNetIF, &ip_addr, &net_mask, &gw_addr, NULL, tivaif_init, + ip_input); +#else + netif_add(&g_sNetIF, &ip_addr, &net_mask, &gw_addr, NULL, tivaif_init, + tcpip_input); +#endif + netif_set_default(&g_sNetIF); + + // + // Bring the interface up. + // + netif_set_up(&g_sNetIF); + + // + // Setup a timeout for the host timer callback function if using a RTOS. + // +#if !NO_SYS && HOST_TMR_INTERVAL + sys_timeout(HOST_TMR_INTERVAL, lwIPPrivateHostTimer, NULL); +#endif + + // + // Setup a timeout for the link detect callback function if using a RTOS. + // +#if !NO_SYS && (LWIP_AUTOIP || LWIP_DHCP) + sys_timeout(LINK_TMR_INTERVAL, lwIPPrivateLinkTimer, NULL); +#endif +} + +//***************************************************************************** +// +//! Initializes the lwIP TCP/IP stack. +//! +//! \param ui32SysClkHz is the current system clock rate in Hz. +//! \param pui8MAC is a pointer to a six byte array containing the MAC +//! address to be used for the interface. +//! \param ui32IPAddr is the IP address to be used (static). +//! \param ui32NetMask is the network mask to be used (static). +//! \param ui32GWAddr is the Gateway address to be used (static). +//! \param ui32IPMode is the IP Address Mode. \b IPADDR_USE_STATIC will force +//! static IP addressing to be used, \b IPADDR_USE_DHCP will force DHCP with +//! fallback to Link Local (Auto IP), while \b IPADDR_USE_AUTOIP will force +//! Link Local only. +//! +//! This function performs initialization of the lwIP TCP/IP stack for the +//! Ethernet MAC, including DHCP and/or AutoIP, as configured. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPInit(uint32_t ui32SysClkHz, const uint8_t *pui8MAC, uint32_t ui32IPAddr, + uint32_t ui32NetMask, uint32_t ui32GWAddr, uint32_t ui32IPMode) +{ + // + // Check the parameters. + // +#if LWIP_DHCP && LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#elif LWIP_DHCP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP)); +#elif LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#else + ASSERT(ui32IPMode == IPADDR_USE_STATIC); +#endif + + // + // Enable the ethernet peripheral. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0); + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0); + + // + // Enable the internal PHY if it's present and we're being + // asked to use it. + // + if((EMAC_PHY_CONFIG & EMAC_PHY_TYPE_MASK) == EMAC_PHY_TYPE_INTERNAL) + { + // + // We've been asked to configure for use with the internal + // PHY. Is it present? + // + if(MAP_SysCtlPeripheralPresent(SYSCTL_PERIPH_EPHY0)) + { + // + // Yes - enable and reset it. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0); + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0); + } + else + { + // + // Internal PHY is not present on this part so hang here. + // + while(1) + { + } + } + } + + // + // Wait for the MAC to come out of reset. + // + while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0)) + { + } + + // + // Configure for use with whichever PHY the user requires. + // + MAP_EMACPHYConfigSet(EMAC0_BASE, EMAC_PHY_CONFIG); + + // + // Initialize the MAC and set the DMA mode. + // + MAP_EMACInit(EMAC0_BASE, ui32SysClkHz, + EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, + 4, 4, 0); + + // + // Set MAC configuration options. + // + MAP_EMACConfigSet(EMAC0_BASE, (EMAC_CONFIG_FULL_DUPLEX | + EMAC_CONFIG_CHECKSUM_OFFLOAD | + EMAC_CONFIG_7BYTE_PREAMBLE | + EMAC_CONFIG_IF_GAP_96BITS | + EMAC_CONFIG_USE_MACADDR0 | + EMAC_CONFIG_SA_FROM_DESCRIPTOR | + EMAC_CONFIG_BO_LIMIT_1024), + (EMAC_MODE_RX_STORE_FORWARD | + EMAC_MODE_TX_STORE_FORWARD | + EMAC_MODE_TX_THRESHOLD_64_BYTES | + EMAC_MODE_RX_THRESHOLD_64_BYTES), 0); + + // + // Program the hardware with its MAC address (for filtering). + // + MAP_EMACAddrSet(EMAC0_BASE, 0, (uint8_t *)pui8MAC); + + // + // Save the network configuration for later use by the private + // initialization. + // + g_ui32IPMode = ui32IPMode; + g_ui32IPAddr = ui32IPAddr; + g_ui32NetMask = ui32NetMask; + g_ui32GWAddr = ui32GWAddr; + + // + // Initialize lwIP. The remainder of initialization is done immediately if + // not using a RTOS and it is deferred to the TCP/IP thread's context if + // using a RTOS. + // +#if NO_SYS + lwIPPrivateInit(0); +#else + tcpip_init(lwIPPrivateInit, 0); +#endif +} + +//***************************************************************************** +// +//! Registers an interrupt callback function to handle the IEEE-1588 timer. +//! +//! \param pfnTimerFunc points to a function which is called whenever the +//! Ethernet MAC reports an interrupt relating to the IEEE-1588 hardware timer. +//! +//! This function allows an application to register a handler for all +//! interrupts generated by the IEEE-1588 hardware timer in the Ethernet MAC. +//! To allow minimal latency timer handling, the callback function provided +//! will be called in interrupt context, regardless of whether or not lwIP is +//! configured to operate with an RTOS. In an RTOS environment, the callback +//! function is responsible for ensuring that all processing it performs is +//! compatible with the low level interrupt context it is called in. +//! +//! The callback function takes two parameters. The first is the base address +//! of the MAC reporting the timer interrupt and the second is the timer +//! interrupt status as reported by EMACTimestampIntStatus(). Note that +//! EMACTimestampIntStatus() causes the timer interrupt sources to be cleared +//! so the application should not call EMACTimestampIntStatus() within the +//! handler. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPTimerCallbackRegister(tHardwareTimerHandler pfnTimerFunc) +{ + // + // Remember the callback function address passed. + // + g_pfnTimerHandler = pfnTimerFunc; +} + +//***************************************************************************** +// +//! Handles periodic timer events for the lwIP TCP/IP stack. +//! +//! \param ui32TimeMS is the incremental time for this periodic interrupt. +//! +//! This function will update the local timer by the value in \e ui32TimeMS. +//! If the system is configured for use without an RTOS, an Ethernet interrupt +//! will be triggered to allow the lwIP periodic timers to be serviced in the +//! Ethernet interrupt. +//! +//! \return None. +// +//***************************************************************************** +#if NO_SYS +void +lwIPTimer(uint32_t ui32TimeMS) +{ + // + // Increment the lwIP Ethernet timer. + // + g_ui32LocalTimer += ui32TimeMS; + + // + // Generate an Ethernet interrupt. This will perform the actual work + // of checking the lwIP timers and taking the appropriate actions. This is + // needed since lwIP is not re-entrant, and this allows all lwIP calls to + // be placed inside the Ethernet interrupt handler ensuring that all calls + // into lwIP are coming from the same context, preventing any reentrancy + // issues. Putting all the lwIP calls in the Ethernet interrupt handler + // avoids the use of mutexes to avoid re-entering lwIP. + // + HWREG(NVIC_SW_TRIG) |= INT_EMAC0 - 16; +} +#endif + +//***************************************************************************** +// +//! Handles Ethernet interrupts for the lwIP TCP/IP stack. +//! +//! This function handles Ethernet interrupts for the lwIP TCP/IP stack. At +//! the lowest level, all receive packets are placed into a packet queue for +//! processing at a higher level. Also, the transmit packet queue is checked +//! and packets are drained and transmitted through the Ethernet MAC as needed. +//! If the system is configured without an RTOS, additional processing is +//! performed at the interrupt level. The packet queues are processed by the +//! lwIP TCP/IP code, and lwIP periodic timers are serviced (as needed). +//! +//! \return None. +// +//***************************************************************************** +void +lwIPEthernetIntHandler(void) +{ + uint32_t ui32Status; + uint32_t ui32TimerStatus; +#if !NO_SYS + portBASE_TYPE xWake; +#endif + + // + // Read and Clear the interrupt. + // + ui32Status = MAP_EMACIntStatus(EMAC0_BASE, true); + + // + // If the interrupt really came from the Ethernet and not our + // timer, clear it. + // + if(ui32Status) + { + MAP_EMACIntClear(EMAC0_BASE, ui32Status); + } + + // + // Check to see whether a hardware timer interrupt has been reported. + // + if(ui32Status & EMAC_INT_TIMESTAMP) + { + // + // Yes - read and clear the timestamp interrupt status. + // + ui32TimerStatus = EMACTimestampIntStatus(EMAC0_BASE); + + // + // If a timer interrupt handler has been registered, call it. + // + if(g_pfnTimerHandler) + { + g_pfnTimerHandler(EMAC0_BASE, ui32TimerStatus); + } + } + + // + // The handling of the interrupt is different based on the use of a RTOS. + // +#if NO_SYS + // + // No RTOS is being used. If a transmit/receive interrupt was active, + // run the low-level interrupt handler. + // + if(ui32Status) + { + tivaif_interrupt(&g_sNetIF, ui32Status); + } + + // + // Service the lwIP timers. + // + lwIPServiceTimers(); +#else + // + // A RTOS is being used. Signal the Ethernet interrupt task. + // + xQueueSendFromISR(g_pInterrupt, (void *)&ui32Status, &xWake); + + // + // Disable the Ethernet interrupts. Since the interrupts have not been + // handled, they are not asserted. Once they are handled by the Ethernet + // interrupt task, it will re-enable the interrupts. + // + MAP_EMACIntDisable(EMAC0_BASE, (EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT | + EMAC_INT_TX_STOPPED | + EMAC_INT_RX_NO_BUFFER | + EMAC_INT_RX_STOPPED | EMAC_INT_PHY)); + + // + // Potentially task switch as a result of the above queue write. + // +#if RTOS_FREERTOS + if(xWake == pdTRUE) + { + portYIELD_FROM_ISR(true); + } +#endif +#endif +} + +//***************************************************************************** +// +//! Returns the IP address for this interface. +//! +//! This function will read and return the currently assigned IP address for +//! the Stellaris Ethernet interface. +//! +//! \return Returns the assigned IP address for this interface. +// +//***************************************************************************** +uint32_t +lwIPLocalIPAddrGet(void) +{ +#if LWIP_AUTOIP || LWIP_DHCP + if(g_bLinkActive) + { + return((uint32_t)g_sNetIF.ip_addr.addr); + } + else + { + return(0xffffffff); + } +#else + return((uint32_t)g_sNetIF.ip_addr.addr); +#endif +} + +//***************************************************************************** +// +//! Returns the network mask for this interface. +//! +//! This function will read and return the currently assigned network mask for +//! the Stellaris Ethernet interface. +//! +//! \return the assigned network mask for this interface. +// +//***************************************************************************** +uint32_t +lwIPLocalNetMaskGet(void) +{ + return((uint32_t)g_sNetIF.netmask.addr); +} + +//***************************************************************************** +// +//! Returns the gateway address for this interface. +//! +//! This function will read and return the currently assigned gateway address +//! for the Stellaris Ethernet interface. +//! +//! \return the assigned gateway address for this interface. +// +//***************************************************************************** +uint32_t +lwIPLocalGWAddrGet(void) +{ + return((uint32_t)g_sNetIF.gw.addr); +} + +//***************************************************************************** +// +//! Returns the local MAC/HW address for this interface. +//! +//! \param pui8MAC is a pointer to an array of bytes used to store the MAC +//! address. +//! +//! This function will read the currently assigned MAC address into the array +//! passed in \e pui8MAC. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPLocalMACGet(uint8_t *pui8MAC) +{ + MAP_EMACAddrGet(EMAC0_BASE, 0, pui8MAC); +} + +//***************************************************************************** +// +// Completes the network configuration change. This is directly called when +// not using a RTOS and provided as a callback to the TCP/IP thread when using +// a RTOS. +// +//***************************************************************************** +static void +lwIPPrivateNetworkConfigChange(void *pvArg) +{ + uint32_t ui32IPMode; + struct ip_addr ip_addr; + struct ip_addr net_mask; + struct ip_addr gw_addr; + + // + // Get the new address mode. + // + ui32IPMode = (uint32_t)pvArg; + + // + // Setup the network address values. + // + if(ui32IPMode == IPADDR_USE_STATIC) + { + ip_addr.addr = htonl(g_ui32IPAddr); + net_mask.addr = htonl(g_ui32NetMask); + gw_addr.addr = htonl(g_ui32GWAddr); + } +#if LWIP_DHCP || LWIP_AUTOIP + else + { + ip_addr.addr = 0; + net_mask.addr = 0; + gw_addr.addr = 0; + } +#endif + + // + // Switch on the current IP Address Aquisition mode. + // + switch(g_ui32IPMode) + { + // + // Static IP + // + case IPADDR_USE_STATIC: + { + // + // Set the new address parameters. This will change the address + // configuration in lwIP, and if necessary, will reset any links + // that are active. This is valid for all three modes. + // + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + + // + // If we are going to DHCP mode, then start the DHCP server now. + // +#if LWIP_DHCP + if((ui32IPMode == IPADDR_USE_DHCP) && g_bLinkActive) + { + dhcp_start(&g_sNetIF); + } +#endif + + // + // If we are going to AutoIP mode, then start the AutoIP process + // now. + // +#if LWIP_AUTOIP + if((ui32IPMode == IPADDR_USE_AUTOIP) && g_bLinkActive) + { + autoip_start(&g_sNetIF); + } +#endif + + // + // And we're done. + // + break; + } + + // + // DHCP (with AutoIP fallback). + // +#if LWIP_DHCP + case IPADDR_USE_DHCP: + { + // + // If we are going to static IP addressing, then disable DHCP and + // force the new static IP address. + // + if(ui32IPMode == IPADDR_USE_STATIC) + { + dhcp_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + } + + // + // If we are going to AUTO IP addressing, then disable DHCP, set + // the default addresses, and start AutoIP. + // +#if LWIP_AUTOIP + else if(ui32IPMode == IPADDR_USE_AUTOIP) + { + dhcp_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + if(g_bLinkActive) + { + autoip_start(&g_sNetIF); + } + } +#endif + break; + } +#endif + + // + // AUTOIP + // +#if LWIP_AUTOIP + case IPADDR_USE_AUTOIP: + { + // + // If we are going to static IP addressing, then disable AutoIP and + // force the new static IP address. + // + if(ui32IPMode == IPADDR_USE_STATIC) + { + autoip_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + } + + // + // If we are going to DHCP addressing, then disable AutoIP, set the + // default addresses, and start dhcp. + // +#if LWIP_DHCP + else if(ui32IPMode == IPADDR_USE_DHCP) + { + autoip_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + if(g_bLinkActive) + { + dhcp_start(&g_sNetIF); + } + } +#endif + break; + } +#endif + } + + // + // Bring the interface up. + // + netif_set_up(&g_sNetIF); + + // + // Save the new mode. + // + g_ui32IPMode = ui32IPMode; +} + +//***************************************************************************** +// +//! Change the configuration of the lwIP network interface. +//! +//! \param ui32IPAddr is the new IP address to be used (static). +//! \param ui32NetMask is the new network mask to be used (static). +//! \param ui32GWAddr is the new Gateway address to be used (static). +//! \param ui32IPMode is the IP Address Mode. \b IPADDR_USE_STATIC 0 will +//! force static IP addressing to be used, \b IPADDR_USE_DHCP will force DHCP +//! with fallback to Link Local (Auto IP), while \b IPADDR_USE_AUTOIP will +//! force Link Local only. +//! +//! This function will evaluate the new configuration data. If necessary, the +//! interface will be brought down, reconfigured, and then brought back up +//! with the new configuration. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPNetworkConfigChange(uint32_t ui32IPAddr, uint32_t ui32NetMask, + uint32_t ui32GWAddr, uint32_t ui32IPMode) +{ + // + // Check the parameters. + // +#if LWIP_DHCP && LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#elif LWIP_DHCP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP)); +#elif LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#else + ASSERT(ui32IPMode == IPADDR_USE_STATIC); +#endif + + // + // Save the network configuration for later use by the private network + // configuration change. + // + g_ui32IPAddr = ui32IPAddr; + g_ui32NetMask = ui32NetMask; + g_ui32GWAddr = ui32GWAddr; + + // + // Complete the network configuration change. The remainder is done + // immediately if not using a RTOS and it is deferred to the TCP/IP + // thread's context if using a RTOS. + // +#if NO_SYS + lwIPPrivateNetworkConfigChange((void *)ui32IPMode); +#else + tcpip_callback(lwIPPrivateNetworkConfigChange, (void *)ui32IPMode); +#endif +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/lwiplib.h b/utils/lwiplib.h new file mode 100644 index 0000000..3a3f622 --- /dev/null +++ b/utils/lwiplib.h @@ -0,0 +1,121 @@ +//***************************************************************************** +// +// lwiplib.h - Prototypes for the lwIP library wrapper API. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __LWIPLIB_H__ +#define __LWIPLIB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// lwIP Options +// +//***************************************************************************** +#include "lwip/opt.h" + +//***************************************************************************** +// +// Ensure that AUTOIP COOP option is configured correctly. +// +//***************************************************************************** +#undef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP ((LWIP_DHCP) && (LWIP_AUTOIP)) + +//***************************************************************************** +// +// lwIP API Header Files +// +//***************************************************************************** +#include +#include "lwip/api.h" +#include "lwip/netifapi.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include "lwip/sockets.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/tcp_impl.h" +#include "lwip/timers.h" + +//***************************************************************************** +// +// IP Address Acquisition Modes +// +//***************************************************************************** +#define IPADDR_USE_STATIC 0 +#define IPADDR_USE_DHCP 1 +#define IPADDR_USE_AUTOIP 2 + +//***************************************************************************** +// +// Hardware timer interrupt callback function type (available only when running +// on TM4C parts). This function is called in interrupt context whenever the +// Ethernet MAC reports an interrupt from the IEEE-1588 timestamping +// timer. The first parameter is the base address of the MAC and the second +// is the interrupt status as reported via EthMACTimestampIntStatus. +// +//***************************************************************************** +typedef void (* tHardwareTimerHandler)(uint32_t ui32Base, + uint32_t ui32IntStatus); + +//***************************************************************************** +// +// lwIP Abstraction Layer API +// +//***************************************************************************** +extern void lwIPInit(uint32_t ui32SysClkHz, const uint8_t *pui8Mac, + uint32_t ui32IPAddr, uint32_t ui32NetMask, + uint32_t ui32GWAddr, uint32_t ui32IPMode); +extern void lwIPTimerCallbackRegister(tHardwareTimerHandler pfnTimerFunc); +extern void lwIPTimer(uint32_t ui32TimeMS); +extern void lwIPEthernetIntHandler(void); +extern uint32_t lwIPLocalIPAddrGet(void); +extern uint32_t lwIPLocalNetMaskGet(void); +extern uint32_t lwIPLocalGWAddrGet(void); +extern void lwIPLocalMACGet(uint8_t *pui8Mac); +extern void lwIPNetworkConfigChange(uint32_t ui32IPAddr, uint32_t ui32NetMask, + uint32_t ui32GWAddr, uint32_t ui32IPMode); +extern uint32_t lwIPAcceptUDPPort(uint16_t ui16Port); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __LWIPLIB_H__ diff --git a/utils/ptpdlib.c b/utils/ptpdlib.c new file mode 100644 index 0000000..ec340d2 --- /dev/null +++ b/utils/ptpdlib.c @@ -0,0 +1,56 @@ +//***************************************************************************** +// +// ptpdlib.c - ptpd Library Abstraction Layer. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +// Include the necessary system header files. +// +//***************************************************************************** +#include + +//***************************************************************************** +// +// Include the library source code header files next. +// +//***************************************************************************** +#include "utils/ptpdlib.h" + +//***************************************************************************** +// +// Include ptpd library code. +// +//***************************************************************************** +#include "ptpd-1.1.0/src/arith.c" +#include "ptpd-1.1.0/src/bmc.c" +#include "ptpd-1.1.0/src/protocol.c" + +//***************************************************************************** +// +// Include ptpd porting layer code. +// +//***************************************************************************** +#include "ptpd-1.1.0/src/dep-tiva/ptpd_timer.c" +#include "ptpd-1.1.0/src/dep-tiva/ptpd_servo.c" +#include "ptpd-1.1.0/src/dep-tiva/ptpd_msg.c" +#include "ptpd-1.1.0/src/dep-tiva/ptpd_net.c" diff --git a/utils/ptpdlib.h b/utils/ptpdlib.h new file mode 100644 index 0000000..5692877 --- /dev/null +++ b/utils/ptpdlib.h @@ -0,0 +1,55 @@ +//***************************************************************************** +// +// ptpdlib.h - Prototypes for the ptpd library wrapper API. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __PTPDLIB_H__ +#define __PTPDLIB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// ptpd API Header Files +// +//***************************************************************************** +#include "ptpd-1.1.0/src/ptpd.h" + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __PTPDLIB_H__ diff --git a/utils/random.c b/utils/random.c new file mode 100644 index 0000000..0588c44 --- /dev/null +++ b/utils/random.c @@ -0,0 +1,169 @@ +//***************************************************************************** +// +// random.c - Random number generator utilizing MD4 hash function of +// environmental noise captured as the seed and a linear congruence +// generator for the random numbers. +// +// Copyright (c) 2011-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 Utility Library. +// +//***************************************************************************** + +#include +#include "ustdlib.h" +#include "random.h" + +//***************************************************************************** +// +//! \addtogroup random_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The pool of entropy that has been collected. +// +//***************************************************************************** +static uint32_t g_pui32RandomEntropy[16]; + +//***************************************************************************** +// +// The index of the next byte to be added to the entropy pool. +// +//***************************************************************************** +static uint32_t g_ui32RandomIndex = 0; + +//***************************************************************************** +// +//! Add entropy to the pool. +//! +//! \param ui32Entropy is an 8-bit value that is added to the entropy pool +//! +//! This function allows the user application code to add entropy (random data) +//! to the pool at any time. +//! +//! \return None +// +//***************************************************************************** +void +RandomAddEntropy(uint32_t ui32Entropy) +{ + // + // Add this byte to the entropy pool. + // + ((uint8_t *)g_pui32RandomEntropy)[g_ui32RandomIndex] = ui32Entropy & 0xff; + + // + // Increment to the next byte of the entropy pool. + // + g_ui32RandomIndex = (g_ui32RandomIndex + 1) & 63; +} + +//***************************************************************************** +// +//! Set the random number generator seed. +//! +//! Seed the random number generator by running a MD4 hash on the entropy pool. +//! Note that the entropy pool may change from beneath us, but for the purposes +//! of generating random numbers that is not a concern. Also, the MD4 hash was +//! broken long ago, but since it is being used to generate random numbers +//! instead of providing security this is not a concern. +//! +//! \return New seed value. +// +//***************************************************************************** +uint32_t +RandomSeed(void) +{ + uint32_t ui32A, ui32B, ui32C, ui32D, ui32Temp, ui32Idx; + + // + // Initialize the digest. + // + ui32A = 0x67452301; + ui32B = 0xefcdab89; + ui32C = 0x98badcfe; + ui32D = 0x10325476; + + // + // Perform the first round of operations. + // +#define F(a, b, c, d, k, s) \ + { \ + ui32Temp = a + (d ^ (b & (c ^ d))) + g_pui32RandomEntropy[k]; \ + a = (ui32Temp << s) | (ui32Temp >> (32 - s)); \ + } + for(ui32Idx = 0; ui32Idx < 16; ui32Idx += 4) + { + F(ui32A, ui32B, ui32C, ui32D, ui32Idx + 0, 3); + F(ui32D, ui32A, ui32B, ui32C, ui32Idx + 1, 7); + F(ui32C, ui32D, ui32A, ui32B, ui32Idx + 2, 11); + F(ui32B, ui32C, ui32D, ui32A, ui32Idx + 3, 19); + } + + // + // Perform the second round of operations. + // +#define G(a, b, c, d, k, s) \ + { \ + ui32Temp = (a + ((b & c) | (b & d) | (c & d)) + \ + g_pui32RandomEntropy[k] + 0x5a827999); \ + a = (ui32Temp << s) | (ui32Temp >> (32 - s)); \ + } + for(ui32Idx = 0; ui32Idx < 4; ui32Idx++) + { + G(ui32A, ui32B, ui32C, ui32D, ui32Idx + 0, 3); + G(ui32D, ui32A, ui32B, ui32C, ui32Idx + 4, 5); + G(ui32C, ui32D, ui32A, ui32B, ui32Idx + 8, 9); + G(ui32B, ui32C, ui32D, ui32A, ui32Idx + 12, 13); + } + + // + // Perform the third round of operations. + // +#define H(a, b, c, d, k, s) \ + { \ + ui32Temp = a + (b ^ c ^ d) + g_pui32RandomEntropy[k] + 0x6ed9eba1; \ + a = (ui32Temp << s) | (ui32Temp >> (32 - s)); \ + } + for(ui32Idx = 0; ui32Idx < 4; ui32Idx += 2) + { + H(ui32A, ui32B, ui32C, ui32D, ui32Idx + 0, 3); + H(ui32D, ui32A, ui32B, ui32C, ui32Idx + 8, 9); + H(ui32C, ui32D, ui32A, ui32B, ui32Idx + 4, 11); + H(ui32B, ui32C, ui32D, ui32A, ui32Idx + 12, 15); + + if(ui32Idx == 2) + { + ui32Idx -= 3; + } + } + + // + // Use the first word of the resulting digest as the random number seed. + // + return(ui32A + 0x67452301); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/random.h b/utils/random.h new file mode 100644 index 0000000..a2c719e --- /dev/null +++ b/utils/random.h @@ -0,0 +1,56 @@ +//***************************************************************************** +// +// random.h - Header for random number generation functions. +// +// Copyright (c) 2011-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 Utility Library. +// +//***************************************************************************** + +#ifndef __RANDOM_H__ +#define __RANDOM_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototypes for the random number generator functions. +// +//***************************************************************************** +extern void RandomAddEntropy(uint32_t ui32Entropy); +extern uint32_t RandomSeed(void); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __RANDOM_H__ diff --git a/utils/ringbuf.c b/utils/ringbuf.c new file mode 100644 index 0000000..f489239 --- /dev/null +++ b/utils/ringbuf.c @@ -0,0 +1,712 @@ +//***************************************************************************** +// +// ringbuf.c - Ring buffer management utilities. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "utils/ringbuf.h" + +//***************************************************************************** +// +//! \addtogroup ringbuf_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Define NULL, if not already defined. +// +//***************************************************************************** +#ifndef NULL +#define NULL ((void *)0) +#endif + +//***************************************************************************** +// +// Change the value of a variable atomically. +// +// \param pui32Val points to the index whose value is to be modified. +// \param ui32Delta is the number of bytes to increment the index by. +// \param ui32Size is the size of the buffer the index refers to. +// +// This function is used to increment a read or write buffer index that may be +// written in various different contexts. It ensures that the +// read/modify/write sequence is not interrupted and, hence, guards against +// corruption of the variable. The new value is adjusted for buffer wrap. +// +// \return None. +// +//***************************************************************************** +static void +UpdateIndexAtomic(volatile uint32_t *pui32Val, uint32_t ui32Delta, + uint32_t ui32Size) +{ + bool bIntsOff; + + // + // Turn interrupts off temporarily. + // + bIntsOff = IntMasterDisable(); + + // + // Update the variable value. + // + *pui32Val += ui32Delta; + + // + // Correct for wrap. We use a loop here since we don't want to use a + // modulus operation with interrupts off but we don't want to fail in + // case ui32Delta is greater than ui32Size (which is extremely unlikely + // but...) + // + while(*pui32Val >= ui32Size) + { + *pui32Val -= ui32Size; + } + + // + // Restore the interrupt state + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is full or not. +//! +//! \param psRingBuf is the ring buffer object to empty. +//! +//! This function is used to determine whether or not a given ring buffer is +//! full. The structure is specifically to ensure that we do not see +//! warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is full or \b false otherwise. +// +//***************************************************************************** +bool +RingBufFull(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the full status of the buffer. + // + return((((ui32Write + 1) % psRingBuf->ui32Size) == ui32Read) ? true : + false); +} + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is empty or not. +//! +//! \param psRingBuf is the ring buffer object to empty. +//! +//! This function is used to determine whether or not a given ring buffer is +//! empty. The structure is specifically to ensure that we do not see +//! warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is empty or \b false otherwise. +// +//***************************************************************************** +bool +RingBufEmpty(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the empty status of the buffer. + // + return((ui32Write == ui32Read) ? true : false); +} + +//***************************************************************************** +// +//! Empties the ring buffer. +//! +//! \param psRingBuf is the ring buffer object to empty. +//! +//! Discards all data from the ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufFlush(tRingBufObject *psRingBuf) +{ + bool bIntsOff; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Set the Read/Write pointers to be the same. Do this with interrupts + // disabled to prevent the possibility of corruption of the read index. + // + bIntsOff = IntMasterDisable(); + psRingBuf->ui32ReadIndex = psRingBuf->ui32WriteIndex; + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Returns number of bytes stored in ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of bytes stored in the ring buffer. +//! +//! \return Returns the number of bytes stored in the ring buffer. +// +//***************************************************************************** +uint32_t +RingBufUsed(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the number of bytes contained in the ring buffer. + // + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (psRingBuf->ui32Size - (ui32Read - ui32Write))); +} + +//***************************************************************************** +// +//! Returns number of bytes available in a ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of bytes available in the ring buffer. +//! +//! \return Returns the number of bytes available in the ring buffer. +// +//***************************************************************************** +uint32_t +RingBufFree(tRingBufObject *psRingBuf) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return((psRingBuf->ui32Size - 1) - RingBufUsed(psRingBuf)); +} + +//***************************************************************************** +// +//! Returns number of contiguous bytes of data stored in ring buffer ahead of +//! the current read pointer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of contiguous bytes of data available in +//! the ring buffer ahead of the current read pointer. This represents the +//! largest block of data which does not straddle the buffer wrap. +//! +//! \return Returns the number of contiguous bytes available. +// +//***************************************************************************** +uint32_t +RingBufContigUsed(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the number of contiguous bytes available. + // + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (psRingBuf->ui32Size - ui32Read)); +} + +//***************************************************************************** +// +//! Returns number of contiguous free bytes available in a ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of contiguous free bytes ahead of the +//! current write pointer in the ring buffer. +//! +//! \return Returns the number of contiguous bytes available in the ring +//! buffer. +// +//***************************************************************************** +uint32_t +RingBufContigFree(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the number of contiguous bytes available. + // + if(ui32Read > ui32Write) + { + // + // The read pointer is above the write pointer so the amount of free + // space is the difference between the two indices minus 1 to account + // for the buffer full condition (write index one behind read index). + // + return((ui32Read - ui32Write) - 1); + } + else + { + // + // If the write pointer is above the read pointer, the amount of free + // space is the size of the buffer minus the write index. We need to + // add a special-case adjustment if the read index is 0 since we need + // to leave 1 byte empty to ensure we can tell the difference between + // the buffer being full and empty. + // + return(psRingBuf->ui32Size - ui32Write - ((ui32Read == 0) ? 1 : 0)); + } +} + +//***************************************************************************** +// +//! Return size in bytes of a ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the size of the ring buffer. +//! +//! \return Returns the size in bytes of the ring buffer. +// +//***************************************************************************** +uint32_t +RingBufSize(tRingBufObject *psRingBuf) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return(psRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Reads a single byte of data from a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be written to. +//! +//! This function reads a single byte of data from a ring buffer. +//! +//! \return The byte read from the ring buffer. +// +//***************************************************************************** +uint8_t +RingBufReadOne(tRingBufObject *psRingBuf) +{ + uint8_t ui8Temp; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(RingBufUsed(psRingBuf) != 0); + + // + // Write the data byte. + // + ui8Temp = psRingBuf->pui8Buf[psRingBuf->ui32ReadIndex]; + + // + // Increment the read index. + // + UpdateIndexAtomic(&psRingBuf->ui32ReadIndex, 1, psRingBuf->ui32Size); + + // + // Return the character read. + // + return(ui8Temp); +} + +//***************************************************************************** +// +//! Reads data from a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be read from. +//! \param pui8Data points to where the data should be stored. +//! \param ui32Length is the number of bytes to be read. +//! +//! This function reads a sequence of bytes from a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufRead(tRingBufObject *psRingBuf, uint8_t *pui8Data, uint32_t ui32Length) +{ + uint32_t ui32Temp; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + ASSERT(pui8Data != NULL); + ASSERT(ui32Length != 0); + + // + // Verify that data is available in the buffer. + // + ASSERT(ui32Length <= RingBufUsed(psRingBuf)); + + // + // Read the data from the ring buffer. + // + for(ui32Temp = 0; ui32Temp < ui32Length; ui32Temp++) + { + pui8Data[ui32Temp] = RingBufReadOne(psRingBuf); + } +} + +//***************************************************************************** +// +//! Remove bytes from the ring buffer by advancing the read index. +//! +//! \param psRingBuf points to the ring buffer from which bytes are to be +//! removed. +//! \param ui32NumBytes is the number of bytes to be removed from the buffer. +//! +//! This function advances the ring buffer read index by a given number of +//! bytes, removing that number of bytes of data from the buffer. If +//! \e ui32NumBytes is larger than the number of bytes currently in the buffer, +//! the buffer is emptied. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufAdvanceRead(tRingBufObject *psRingBuf, uint32_t ui32NumBytes) +{ + uint32_t ui32Count; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Make sure that we are not being asked to remove more data than is + // there to be removed. + // + ui32Count = RingBufUsed(psRingBuf); + ui32Count = (ui32Count < ui32NumBytes) ? ui32Count : ui32NumBytes; + + // + // Advance the buffer read index by the required number of bytes. + // + UpdateIndexAtomic(&psRingBuf->ui32ReadIndex, ui32Count, + psRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Add bytes to the ring buffer by advancing the write index. +//! +//! \param psRingBuf points to the ring buffer to which bytes have been added. +//! \param ui32NumBytes is the number of bytes added to the buffer. +//! +//! This function should be used by clients who wish to add data to the buffer +//! directly rather than via calls to RingBufWrite() or RingBufWriteOne(). It +//! advances the write index by a given number of bytes. If the +//! \e ui32NumBytes parameter is larger than the amount of free space in the +//! buffer, the read pointer will be advanced to cater for the addition. Note +//! that this will result in some of the oldest data in the buffer being +//! discarded. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufAdvanceWrite(tRingBufObject *psRingBuf, + uint32_t ui32NumBytes) +{ + uint32_t ui32Count; + bool bIntsOff; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Make sure we were not asked to add a silly number of bytes. + // + ASSERT(ui32NumBytes <= psRingBuf->ui32Size); + + // + // Determine how much free space we currently think the buffer has. + // + ui32Count = RingBufFree(psRingBuf); + + // + // Advance the buffer write index by the required number of bytes and + // check that we have not run past the read index. Note that we must do + // this within a critical section (interrupts disabled) to prevent + // race conditions that could corrupt one or other of the indices. + // + bIntsOff = IntMasterDisable(); + + // + // Update the write pointer. + // + psRingBuf->ui32WriteIndex += ui32NumBytes; + + // + // Check and correct for wrap. + // + if(psRingBuf->ui32WriteIndex >= psRingBuf->ui32Size) + { + psRingBuf->ui32WriteIndex -= psRingBuf->ui32Size; + } + + // + // Did the client add more bytes than the buffer had free space for? + // + if(ui32Count < ui32NumBytes) + { + // + // Yes - we need to advance the read pointer to ahead of the write + // pointer to discard some of the oldest data. + // + psRingBuf->ui32ReadIndex = psRingBuf->ui32WriteIndex + 1; + + // + // Correct for buffer wrap if necessary. + // + if(psRingBuf->ui32ReadIndex >= psRingBuf->ui32Size) + { + psRingBuf->ui32ReadIndex -= psRingBuf->ui32Size; + } + } + + // + // Restore interrupts if we turned them off earlier. + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Writes a single byte of data to a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be written to. +//! \param ui8Data is the byte to be written. +//! +//! This function writes a single byte of data into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufWriteOne(tRingBufObject *psRingBuf, uint8_t ui8Data) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(RingBufFree(psRingBuf) != 0); + + // + // Write the data byte. + // + psRingBuf->pui8Buf[psRingBuf->ui32WriteIndex] = ui8Data; + + // + // Increment the write index. + // + UpdateIndexAtomic(&psRingBuf->ui32WriteIndex, 1, psRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Writes data to a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be written to. +//! \param pui8Data points to the data to be written. +//! \param ui32Length is the number of bytes to be written. +//! +//! This function write a sequence of bytes into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufWrite(tRingBufObject *psRingBuf, uint8_t *pui8Data, + uint32_t ui32Length) +{ + uint32_t ui32Temp; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + ASSERT(pui8Data != NULL); + ASSERT(ui32Length != 0); + + // + // Verify that space is available in the buffer. + // + ASSERT(ui32Length <= RingBufFree(psRingBuf)); + + // + // Write the data into the ring buffer. + // + for(ui32Temp = 0; ui32Temp < ui32Length; ui32Temp++) + { + RingBufWriteOne(psRingBuf, pui8Data[ui32Temp]); + } +} + +//***************************************************************************** +// +//! Initialize a ring buffer object. +//! +//! \param psRingBuf points to the ring buffer to be initialized. +//! \param pui8Buf points to the data buffer to be used for the ring buffer. +//! \param ui32Size is the size of the buffer in bytes. +//! +//! This function initializes a ring buffer object, preparing it to store data. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufInit(tRingBufObject *psRingBuf, uint8_t *pui8Buf, + uint32_t ui32Size) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + ASSERT(pui8Buf != NULL); + ASSERT(ui32Size != 0); + + // + // Initialize the ring buffer object. + // + psRingBuf->ui32Size = ui32Size; + psRingBuf->pui8Buf = pui8Buf; + psRingBuf->ui32WriteIndex = psRingBuf->ui32ReadIndex = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/ringbuf.h b/utils/ringbuf.h new file mode 100644 index 0000000..9b1ff56 --- /dev/null +++ b/utils/ringbuf.h @@ -0,0 +1,105 @@ +//***************************************************************************** +// +// ringbuf.h - Defines and Macros for the ring buffer utilities. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __RINGBUF_H__ +#define __RINGBUF_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure used for encapsulating all the items associated with a +// ring buffer. +// +//***************************************************************************** +typedef struct +{ + // + // The ring buffer size. + // + uint32_t ui32Size; + + // + // The ring buffer write index. + // + volatile uint32_t ui32WriteIndex; + + // + // The ring buffer read index. + // + volatile uint32_t ui32ReadIndex; + + // + // The ring buffer. + // + uint8_t *pui8Buf; + +} +tRingBufObject; + +//***************************************************************************** +// +// API Function prototypes +// +//***************************************************************************** +extern bool RingBufFull(tRingBufObject *psRingBuf); +extern bool RingBufEmpty(tRingBufObject *psRingBuf); +extern void RingBufFlush(tRingBufObject *psRingBuf); +extern uint32_t RingBufUsed(tRingBufObject *psRingBuf); +extern uint32_t RingBufFree(tRingBufObject *psRingBuf); +extern uint32_t RingBufContigUsed(tRingBufObject *psRingBuf); +extern uint32_t RingBufContigFree(tRingBufObject *psRingBuf); +extern uint32_t RingBufSize(tRingBufObject *psRingBuf); +extern uint8_t RingBufReadOne(tRingBufObject *psRingBuf); +extern void RingBufRead(tRingBufObject *psRingBuf, uint8_t *pui8Data, + uint32_t ui32Length); +extern void RingBufWriteOne(tRingBufObject *psRingBuf, uint8_t ui8Data); +extern void RingBufWrite(tRingBufObject *psRingBuf, uint8_t *pui8Data, + uint32_t ui32Length); +extern void RingBufAdvanceWrite(tRingBufObject *psRingBuf, + uint32_t ui32NumBytes); +extern void RingBufAdvanceRead(tRingBufObject *psRingBuf, + uint32_t ui32NumBytes); +extern void RingBufInit(tRingBufObject *psRingBuf, uint8_t *pui8Buf, + uint32_t ui32Size); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __RINGBUF_H__ diff --git a/utils/scheduler.c b/utils/scheduler.c new file mode 100644 index 0000000..bb66c4e --- /dev/null +++ b/utils/scheduler.c @@ -0,0 +1,310 @@ +//***************************************************************************** +// +// scheduler.c - A simple task scheduler +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "inc/hw_ints.h" +#include "driverlib/systick.h" +#include "driverlib/sysctl.h" +#include "driverlib/interrupt.h" +#include "driverlib/debug.h" +#include "utils/scheduler.h" + +//***************************************************************************** +// +//! \addtogroup scheduler_api +//! @{ +// +//***************************************************************************** + +static volatile uint32_t g_ui32SchedulerTickCount; + +//***************************************************************************** +// +//! Handles the SysTick interrupt on behalf of the scheduler module. +//! +//! Applications using the scheduler module must ensure that this function is +//! hooked to the SysTick interrupt vector. +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerSysTickIntHandler(void) +{ + g_ui32SchedulerTickCount++; +} + +//***************************************************************************** +// +//! Initializes the task scheduler. +//! +//! \param ui32TicksPerSecond sets the basic frequency of the SysTick interrupt +//! used by the scheduler to determine when to run the various task functions. +//! +//! This function must be called during application startup to configure the +//! SysTick timer. This is used by the scheduler module to determine when each +//! of the functions provided in the g_psSchedulerTable array is called. +//! +//! The caller is responsible for ensuring that SchedulerSysTickIntHandler() +//! has previously been installed in the SYSTICK vector in the vector table +//! and must also ensure that interrupts are enabled at the CPU level. +//! +//! Note that this call does not start the scheduler calling the configured +//! functions. All function calls are made in the context of later calls to +//! SchedulerRun(). This call merely configures the SysTick interrupt that is +//! used by the scheduler to determine what the current system time is. +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerInit(uint32_t ui32TicksPerSecond) +{ + ASSERT(ui32TicksPerSecond); + + // + // Configure SysTick for a periodic interrupt. + // + SysTickPeriodSet(SysCtlClockGet() / ui32TicksPerSecond); + SysTickEnable(); + SysTickIntEnable(); +} + +//***************************************************************************** +// +//! Instructs the scheduler to update its task table and make calls to +//! functions needing called. +//! +//! This function must be called periodically by the client to allow the +//! scheduler to make calls to any configured task functions if it is their +//! time to be called. The call must be made at least as frequently as the +//! most frequent task configured in the g_psSchedulerTable array. +//! +//! Although the scheduler makes use of the SysTick interrupt, all calls to +//! functions configured in \e g_psSchedulerTable are made in the context of +//! SchedulerRun(). +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerRun(void) +{ + uint32_t ui32Loop; + tSchedulerTask *pi16Task; + + // + // Loop through each task in the task table. + // + for(ui32Loop = 0; ui32Loop < g_ui32SchedulerNumTasks; ui32Loop++) + { + // + // Get a pointer to the task information. + // + pi16Task = &g_psSchedulerTable[ui32Loop]; + + // + // Is this task active and, if so, is it time to call it's function? + // + if(pi16Task->bActive && + (SchedulerElapsedTicksGet(pi16Task->ui32LastCall) >= + pi16Task->ui32FrequencyTicks)) + { + // + // Remember the timestamp at which we make the function call. + // + pi16Task->ui32LastCall = g_ui32SchedulerTickCount; + + // + // Call the task function, passing the provided parameter. + // + pi16Task->pfnFunction(pi16Task->pvParam); + } + } +} + +//***************************************************************************** +// +//! Enables a task and allows the scheduler to call it periodically. +//! +//! \param ui32Index is the index of the task which is to be enabled in the +//! global \e g_psSchedulerTable array. +//! \param bRunNow is \b true if the task is to be run on the next call to +//! SchedulerRun() or \b false if one whole period is to elapse before the task +//! is run. +//! +//! This function marks one of the configured tasks as enabled and causes +//! SchedulerRun() to call that task periodically. The caller may choose to +//! have the enabled task run for the first time on the next call to +//! SchedulerRun() or to wait one full task period before making the first +//! call. +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerTaskEnable(uint32_t ui32Index, bool bRunNow) +{ + // + // Is the task index passed valid? + // + if(ui32Index < g_ui32SchedulerNumTasks) + { + // + // Yes - mark the task as active. + // + g_psSchedulerTable[ui32Index].bActive = true; + + // + // Set the last call time to ensure that the function is called either + // next time the scheduler is run or after the desired number of ticks + // depending upon the value of the bRunNow parameter. + // + if(bRunNow) + { + // + // Cause the task to run on the next call to SchedulerRun(). + // + g_psSchedulerTable[ui32Index].ui32LastCall = + (g_ui32SchedulerTickCount - + g_psSchedulerTable[ui32Index].ui32FrequencyTicks); + } + else + { + // + // Cause the task to run after one full time period. + // + g_psSchedulerTable[ui32Index].ui32LastCall = + g_ui32SchedulerTickCount; + } + } +} + +//***************************************************************************** +// +//! Disables a task and prevents the scheduler from calling it. +//! +//! \param ui32Index is the index of the task which is to be disabled in the +//! global \e g_psSchedulerTable array. +//! +//! This function marks one of the configured tasks as inactive and prevents +//! SchedulerRun() from calling it. The task may be reenabled by calling +//! SchedulerTaskEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerTaskDisable(uint32_t ui32Index) +{ + // + // Is the task index passed valid? + // + if(ui32Index < g_ui32SchedulerNumTasks) + { + // + // Yes - mark the task as inactive. + // + g_psSchedulerTable[ui32Index].bActive = false; + } +} + +//***************************************************************************** +// +//! Returns the current system time in ticks since power on. +//! +//! This function may be called by a client to retrieve the current system +//! time. The value returned is a count of ticks elapsed since the system +//! last booted. +//! +//! \return Tick count since last boot. +// +//***************************************************************************** +uint32_t +SchedulerTickCountGet(void) +{ + return(g_ui32SchedulerTickCount); +} + +//***************************************************************************** +// +//! Returns the number of ticks elapsed since the provided tick count. +//! +//! \param ui32TickCount is the tick count from which to determine the elapsed +//! time. +//! +//! This function may be called by a client to determine how much time has +//! passed since a particular tick count provided in the \e ui32TickCount +//! parameter. This function takes into account wrapping of the global tick +//! counter and assumes that the provided tick count always represents a time +//! in the past. The returned value will, of course, be wrong if the tick +//! counter has wrapped more than once since the passed \e ui32TickCount. As a +//! result, please do not use this function if you are dealing with timeouts +//! of 497 days or longer (assuming you use a 10mS tick period). +//! +//! \return The number of ticks elapsed since the provided tick count. +// +//***************************************************************************** +uint32_t +SchedulerElapsedTicksGet(uint32_t ui32TickCount) +{ + // + // Determine the calculation based upon whether the global tick count has + // wrapped since the passed ui32TickCount. + // + return(SchedulerElapsedTicksCalc(ui32TickCount, g_ui32SchedulerTickCount)); +} + +//***************************************************************************** +// +//! Returns the number of ticks elapsed between two times. +//! +//! \param ui32TickStart is the system tick count for the start of the period. +//! \param ui32TickEnd is the system tick count for the end of the period. +//! +//! This function may be called by a client to determine the number of ticks +//! which have elapsed between provided starting and ending tick counts. The +//! function takes into account wrapping cases where the end tick count is +//! lower than the starting count assuming that the ending tick count always +//! represents a later time than the starting count. +//! +//! \return The number of ticks elapsed between the provided start and end +//! counts. +// +//***************************************************************************** +uint32_t +SchedulerElapsedTicksCalc(uint32_t ui32TickStart, uint32_t ui32TickEnd) +{ + return((ui32TickEnd > ui32TickStart) ? (ui32TickEnd - ui32TickStart) : + ((0xFFFFFFFF - ui32TickStart) + ui32TickEnd + 1)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/scheduler.h b/utils/scheduler.h new file mode 100644 index 0000000..79ae1e2 --- /dev/null +++ b/utils/scheduler.h @@ -0,0 +1,140 @@ +//***************************************************************************** +// +// scheduler.h - Public header for the simple timed function scheduler module. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** +#ifndef __SCHEDULER_H__ +#define __SCHEDULER_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup scheduler_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototype of a function that the scheduler can call periodically. +// +//***************************************************************************** +typedef void (*tSchedulerFunction)(void *pvParam); + +//***************************************************************************** +// +//! The structure defining a function which the scheduler will call +//! periodically. +// +//***************************************************************************** +typedef struct +{ + // + //! A pointer to the function which is to be called periodically by the + //! scheduler. + // + void (*pfnFunction)(void *); + + // + //! The parameter which is to be passed to this function when it is called. + // + void *pvParam; + + // + //! The frequency the function is to be called expressed in terms of system + //! ticks. If this value is 0, the function will be called on every call + //! to SchedulerRun. + // + uint32_t ui32FrequencyTicks; + + // + //! Tick count when this function was last called. This field is updated + //! by the scheduler. + // + uint32_t ui32LastCall; + + // + //! A flag indicating whether or not this task is active. If true, the + //! function will be called periodically. If false, the function is + //! disabled and will not be called. + // + bool bActive; +} +tSchedulerTask; + +//***************************************************************************** +// +//! This global table must be populated by the client and contains information +//! on each function that the scheduler is to call. +// +//***************************************************************************** +extern tSchedulerTask g_psSchedulerTable[]; + +//***************************************************************************** +// +//! This global variable must be exported by the client. It must contain the +//! number of entries in the g_psSchedulerTable array. +// +//***************************************************************************** +extern uint32_t g_ui32SchedulerNumTasks; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public function prototypes +// +//***************************************************************************** +extern void SchedulerSysTickIntHandler(void); +extern void SchedulerInit(uint32_t ui32TicksPerSecond); +extern void SchedulerRun(void); +extern void SchedulerTaskEnable(uint32_t ui32Index, bool bRunNow); +extern void SchedulerTaskDisable(uint32_t ui32Index); +extern uint32_t SchedulerTickCountGet(void); +extern uint32_t SchedulerElapsedTicksGet(uint32_t ui32TickCount); +extern uint32_t SchedulerElapsedTicksCalc(uint32_t ui32TickStart, + uint32_t ui32TickEnd); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __ SCHEDULER_H_ diff --git a/utils/sine.c b/utils/sine.c new file mode 100644 index 0000000..52cd222 --- /dev/null +++ b/utils/sine.c @@ -0,0 +1,126 @@ +//***************************************************************************** +// +// sine.c - Fixed point sine trigonometric function. +// +// Copyright (c) 2006-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 Utility Library. +// +//***************************************************************************** + +#include +#include "utils/sine.h" + +//***************************************************************************** +// +//! \addtogroup sine_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// A table of the value of the sine function for the first ninety degrees with +// 129 entries (that is, [0] = 0 degrees, [128] = 90 degrees). Each entry is +// in 0.16 fixed point notation. +// +//***************************************************************************** +static const uint16_t g_pui16FixedSineTable[] = +{ + 0x0000, 0x0324, 0x0648, 0x096C, 0x0C8F, 0x0FB2, 0x12D5, 0x15F6, 0x1917, + 0x1C37, 0x1F56, 0x2273, 0x2590, 0x28AA, 0x2BC4, 0x2EDB, 0x31F1, 0x3505, + 0x3817, 0x3B26, 0x3E33, 0x413E, 0x4447, 0x474D, 0x4A50, 0x4D50, 0x504D, + 0x5347, 0x563E, 0x5931, 0x5C22, 0x5F0E, 0x61F7, 0x64DC, 0x67BD, 0x6A9B, + 0x6D74, 0x7049, 0x7319, 0x75E5, 0x78AD, 0x7B70, 0x7E2E, 0x80E7, 0x839C, + 0x864B, 0x88F5, 0x8B9A, 0x8E39, 0x90D3, 0x9368, 0x95F6, 0x987F, 0x9B02, + 0x9D7F, 0x9FF6, 0xA267, 0xA4D2, 0xA736, 0xA994, 0xABEB, 0xAE3B, 0xB085, + 0xB2C8, 0xB504, 0xB73A, 0xB968, 0xBB8F, 0xBDAE, 0xBFC7, 0xC1D8, 0xC3E2, + 0xC5E4, 0xC7DE, 0xC9D1, 0xCBBB, 0xCD9F, 0xCF7A, 0xD14D, 0xD318, 0xD4DB, + 0xD695, 0xD848, 0xD9F2, 0xDB94, 0xDD2D, 0xDEBE, 0xE046, 0xE1C5, 0xE33C, + 0xE4AA, 0xE60F, 0xE76B, 0xE8BF, 0xEA09, 0xEB4B, 0xEC83, 0xEDB2, 0xEED8, + 0xEFF5, 0xF109, 0xF213, 0xF314, 0xF40B, 0xF4FA, 0xF5DE, 0xF6BA, 0xF78B, + 0xF853, 0xF912, 0xF9C7, 0xFA73, 0xFB14, 0xFBAC, 0xFC3B, 0xFCBF, 0xFD3A, + 0xFDAB, 0xFE13, 0xFE70, 0xFEC4, 0xFF0E, 0xFF4E, 0xFF84, 0xFFB1, 0xFFD3, + 0xFFEC, 0xFFFB, 0xFFFF +}; + +//***************************************************************************** +// +//! Computes an approximation of the sine of the input angle. +//! +//! \param ui32Angle is an angle expressed as a 0.32 fixed-point value that is +//! the percentage of the way around a circle. +//! +//! This function computes the sine for the given input angle. The angle is +//! specified in 0.32 fixed point format, and is therefore always between 0 and +//! 360 degrees, inclusive of 0 and exclusive of 360. +//! +//! \return Returns the sine of the angle, in 16.16 fixed point format. +// +//***************************************************************************** +int32_t +sine(uint32_t ui32Angle) +{ + uint32_t ui32Idx; + + // + // Add 0.5 to the angle. Since only the upper 9 bits are used to compute + // the sine value, adding one to the tenth bit is 0.5 from the point of + // view of the sine table. + // + ui32Angle += 0x00400000; + + // + // Get the index into the sine table from bits 30:23. + // + ui32Idx = (ui32Angle >> 23) & 255; + + // + // If bit 30 is set, the angle is between 90 and 180 or 270 and 360. In + // these cases, the sine value is decreasing from one instead of increasing + // from zero. The indexing into the table needs to be reversed. + // + if(ui32Angle & 0x40000000) + { + ui32Idx = 256 - ui32Idx; + } + + // + // Get the value of the sine. + // + ui32Idx = g_pui16FixedSineTable[ui32Idx]; + + // + // If bit 31 is set, the angle is between 180 and 360. In this case, the + // sine value is negative; otherwise it is positive. + // + if(ui32Angle & 0x80000000) + { + return(0 - ui32Idx); + } + else + { + return(ui32Idx); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/sine.h b/utils/sine.h new file mode 100644 index 0000000..c3f8c51 --- /dev/null +++ b/utils/sine.h @@ -0,0 +1,85 @@ +//***************************************************************************** +// +// sine.h - Prototypes for the fixed point sine trigonometric function. +// +// Copyright (c) 2006-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 Utility Library. +// +//***************************************************************************** + +#ifndef __SINE_H__ +#define __SINE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup sine_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Computes an approximation of the cosine of the input angle. +//! +//! \param ui32Angle is an angle expressed as a 0.32 fixed-point value that is +//! the percentage of the way around a circle. +//! +//! This function computes the cosine for the given input angle. The angle is +//! specified in 0.32 fixed point format, and is therefore always between 0 and +//! 360 degrees, inclusive of 0 and exclusive of 360. +//! +//! \return Returns the cosine of the angle, in 16.16 fixed point format. +// +//***************************************************************************** +#define cosine(ui32Angle) sine((ui32Angle) + 0x40000000) + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototype for the fixed point sine function. +// +//***************************************************************************** +extern int32_t sine(uint32_t ui32Angle); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SINE_H__ diff --git a/utils/smbus.c b/utils/smbus.c new file mode 100644 index 0000000..8ed9c3a --- /dev/null +++ b/utils/smbus.c @@ -0,0 +1,5173 @@ +//***************************************************************************** +// +// smbus.c - SMBus protocol layer API. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_i2c.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/i2c.h" +#include "driverlib/sw_crc.h" +#include "driverlib/sysctl.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/udma.h" +#include "utils/smbus.h" + +//***************************************************************************** +// +//! \addtogroup smbus_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states for the master and slave interrupt handler state machines. +// +//***************************************************************************** +#define SMBUS_STATE_IDLE 0 +#define SMBUS_STATE_SLAVE_POST_COMMAND 1 +#define SMBUS_STATE_WRITE_BLOCK_SIZE 2 +#define SMBUS_STATE_WRITE_NEXT 3 +#define SMBUS_STATE_WRITE_FINAL 4 +#define SMBUS_STATE_WRITE_DONE 5 +#define SMBUS_STATE_READ_ONE 6 +#define SMBUS_STATE_READ_FIRST 7 +#define SMBUS_STATE_READ_BLOCK_SIZE 8 +#define SMBUS_STATE_READ_NEXT 9 +#define SMBUS_STATE_READ_FINAL 10 +#define SMBUS_STATE_READ_WAIT 11 +#define SMBUS_STATE_READ_PEC 12 +#define SMBUS_STATE_READ_DONE 13 +#define SMBUS_STATE_READ_ERROR_STOP 14 + +//***************************************************************************** +// +// Status flags for various instance-specific tasks. +// +//***************************************************************************** +#define FLAG_PEC 0 +#define FLAG_PROCESS_CALL 1 +#define FLAG_BLOCK_TRANSFER 2 +#define FLAG_TRANSFER_IN_PROGRESS 3 +#define FLAG_RAW_I2C 4 +#define FLAG_ADDRESS_RESOLVED 5 +#define FLAG_ADDRESS_VALID 6 +#define FLAG_ARP 7 +//***************************************************************************** +// +//! Enables Packet Error Checking (PEC). +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables the transmission and checking of a PEC byte in SMBus +//! transactions. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusPECEnable(tSMBus *psSMBus) +{ + // + // Set the PEC flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 1; +} + +//***************************************************************************** +// +//! Disables Packet Error Checking (PEC). +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function disables the transmission and checking of a PEC byte in SMBus +//! transactions. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusPECDisable(tSMBus *psSMBus) +{ + // + // Clear the PEC flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; +} + +//***************************************************************************** +// +//! Sets the ARP flag in the configuration structure. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sets the Address Resolution Protocol (ARP) flag in the +//! configuration structure. This flag can be used to track the state of a +//! device during the ARP process. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPEnable(tSMBus *psSMBus) +{ + // + // Set the ARP flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ARP) = 1; +} + +//***************************************************************************** +// +//! Clears the ARP flag in the configuration structure. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function clears the Address Resolution Protocol (ARP) flag in the +//! configuration structure. This flag can be used to track the state of a +//! device during the ARP process. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPDisable(tSMBus *psSMBus) +{ + // + // Clear the ARP flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ARP) = 0; +} + +//***************************************************************************** +// +//! Returns the number of bytes in the receive buffer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function returns the number of bytes in the active receive buffer. +//! It can be used to determine how many bytes have been received in the slave +//! receive or master block read configurations. +//! +//! \return Number of bytes in the buffer. +// +//***************************************************************************** +uint8_t +SMBusRxPacketSizeGet(tSMBus *psSMBus) +{ + // + // Return the number of bytes received. + // + return(psSMBus->ui8RxIndex); +} + +//***************************************************************************** +// +//! Returns the state of an SMBus transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function returns the status of an SMBus transaction. It can be used +//! to determine whether a transfer is ongoing or complete. +//! +//! \return Returns \b SMBUS_TRANSFER_IN_PROGRESS if transfer is ongoing, or +//! \b SMBUS_TRANSFER_COMPLETE if transfer has completed. +// +//***************************************************************************** +tSMBusStatus +SMBusStatusGet(tSMBus *psSMBus) +{ + // + // Check to see if there is an ongoing transfer. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS)) + { + // + // If the flag is set, return in progress status. + // + return(SMBUS_TRANSFER_IN_PROGRESS); + } + + // + // If the transfer complete flag is cleared, transfer is done. + // + else + { + // + // If the flag isn't set, return complete status. + // + return(SMBUS_TRANSFER_COMPLETE); + } +} + +//***************************************************************************** +// +//! Encodes a UDID structure and address into SMBus-transferable byte order. +//! +//! \param pUDID specifies the structure to encode. +//! \param ui8Address specifies the address to send with the UDID (byte 17). +//! \param pui8Data specifies the location of the destination data buffer. +//! +//! This function takes a tSMBusUDID structure and re-orders the bytes so that +//! it can be transferred on the bus. The destination data buffer must contain +//! at least 17 bytes. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPUDIDPacketEncode(tSMBusUDID *pUDID, uint8_t ui8Address, + uint8_t *pui8Data) +{ + // + // Place data from the UDID structure and address into the data buffer + // using the correct MSB->LSB + address order. + // + pui8Data[0] = pUDID->ui8DeviceCapabilities; + pui8Data[1] = pUDID->ui8Version; + pui8Data[2] = (uint8_t)((pUDID->ui16VendorID & 0xff00) >> 8); + pui8Data[3] = (uint8_t)(pUDID->ui16VendorID & 0x00ff); + pui8Data[4] = (uint8_t)((pUDID->ui16DeviceID & 0xff00) >> 8); + pui8Data[5] = (uint8_t)(pUDID->ui16DeviceID & 0x00ff); + pui8Data[6] = (uint8_t)((pUDID->ui16Interface & 0xff00) >> 8); + pui8Data[7] = (uint8_t)(pUDID->ui16Interface & 0x00ff); + pui8Data[8] = (uint8_t)((pUDID->ui16SubSystemVendorID & 0xff00) >> 8); + pui8Data[9] = (uint8_t)(pUDID->ui16SubSystemVendorID & 0x00ff); + pui8Data[10] = (uint8_t)((pUDID->ui16SubSystemDeviceID & 0xff00) >> 8); + pui8Data[11] = (uint8_t)(pUDID->ui16SubSystemDeviceID & 0x00ff); + pui8Data[12] = (uint8_t)((pUDID->ui32VendorSpecificID & 0xff000000) >> + 24); + pui8Data[13] = (uint8_t)((pUDID->ui32VendorSpecificID & 0x00ff0000) >> + 16); + pui8Data[14] = (uint8_t)((pUDID->ui32VendorSpecificID & 0x0000ff00) >> + 8); + pui8Data[15] = (uint8_t)(pUDID->ui32VendorSpecificID & 0x000000ff); + pui8Data[16] = ui8Address; +} + +//***************************************************************************** +// +//! Decodes an SMBus packet into a UDID structure and address. +//! +//! \param pUDID specifies the structure that is updated with new data. +//! \param pui8Address specifies the location of the variable that holds the +//! the address sent with the UDID (byte 17). +//! \param pui8Data specifies the location of the source data. +//! +//! This function takes a data buffer and decodes it into a tSMBusUDID +//! structure and an address variable. It is assumed that there are 17 bytes +//! in the data buffer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPUDIDPacketDecode(tSMBusUDID *pUDID, uint8_t *pui8Address, + uint8_t *pui8Data) +{ + // + // Populate the UDID structure with data from the input data buffer. + // + pUDID->ui8DeviceCapabilities = pui8Data[0]; + pUDID->ui8Version = pui8Data[1]; + pUDID->ui16VendorID = (uint16_t)((pui8Data[2] << 8) | pui8Data[3]); + pUDID->ui16DeviceID = (uint16_t)((pui8Data[4] << 8) | pui8Data[5]); + pUDID->ui16Interface = (uint16_t)((pui8Data[6] << 8) | pui8Data[7]); + pUDID->ui16SubSystemVendorID = (uint16_t)((pui8Data[8] << 8) | + pui8Data[9]); + pUDID->ui16SubSystemDeviceID = (uint16_t)((pui8Data[10] << 8) | + pui8Data[11]); + pUDID->ui32VendorSpecificID = (uint32_t)((pui8Data[12] << 24) | + (pui8Data[13] << 16) | + (pui8Data[14] << 8) | + pui8Data[15]); + + // + // Populate the address. + // + *pui8Address = pui8Data[16]; +} + +//***************************************************************************** +// +//! Initiates a master Quick Command transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param bData is the value of the single data bit sent to the slave. +//! +//! Quick Command is an SMBus protocol that sends a single data bit using the +//! I2C R/S bit. This function issues a single I2C transfer with the slave +//! address and data bit. +//! +//! This protocol does not support PEC. The PEC flag is explicitly cleared +//! within this function, so if PEC is enabled prior to calling it, it must +//! be re-enabled afterwards. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterQuickCommand(tSMBus *psSMBus, uint8_t ui8TargetAddress, + bool bData) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8TxSize = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // This protocol does NOT support PEC, so the flag must be cleared. If + // PEC is needed again after this transaction, it should be explicitly + // enabled again. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, bData); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_QUICK_COMMAND); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Host Notify transfer to the SMBus Host. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8OwnSlaveAddress specifies the peripheral's own slave address. +//! \param pui8Data is a pointer to the two byte data payload. +//! +//! The Host Notify protocol is used by SMBus slaves to alert the bus Host +//! about an event. Most slave devices that operate in this environment only +//! become a bus master when this packet type is used. Host Notify always +//! sends two data bytes to the host along with the peripheral's own slave +//! address so that the Host knows which peripheral requested the Host's +//! attention. +//! +//! This protocol does not support PEC. The PEC flag is explicitly cleared +//! within this function, so if PEC is enabled prior to calling it, it must +//! be re-enabled afterwards. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterHostNotify(tSMBus *psSMBus, uint8_t ui8OwnSlaveAddress, + uint8_t *pui8Data) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = SMBUS_ADR_HOST; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = 2; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // This protocol does NOT support PEC, so the flag must be cleared. If + // PEC is needed again after this transaction, it should be explicitly + // enabled again. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, ui8OwnSlaveAddress); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Send Byte transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Data is the data byte to send to the slave. +//! +//! The Send Byte protocol is a basic SMBus protocol that sends a single data +//! byte to the slave. Unlike most of the other SMBus protocols, Send Byte +//! does not send a ``command'' byte before the data payload and is intended +//! for basic communication. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteSend(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Data) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Data; + psSMBus->pui8TxBuffer = &ui8Data; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the data byte on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, ui8Data); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the data to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->pui8TxBuffer[0], + 1); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + } + else + { + // + // Update the state machine. Since it's the only byte being sent, + // the state machine's next state is idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_SINGLE_SEND); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Receive Byte transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the location to store the received data +//! byte. +//! +//! The Receive Byte protocol is a basic SMBus protocol that receives a single +//! data byte from the slave. Unlike most of the other SMBus protocols, +//! Receive Byte does not send a ``command'' byte before the data payload and +//! is intended for basic communication. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteReceive(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8RxSize = 1; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // set the R/S bit to '1' for the CRC calculation. + // + ui8TempData = ((psSMBus->ui8TargetSlaveAddress << 1) & 0xfe) | 1; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the read operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_START); + } + else + { + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the read operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Write Byte or Write Word transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data payload. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function supports both the Write Byte and Write Word protocols. The +//! amount of data to send is user defined, but limited to 1 or 2 bytes. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 2, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteWordWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data, + uint8_t ui8Size) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 2 bytes are requested, indicate error. + // + if(ui8Size > 2) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = ui8Size; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + else + { + // + // If only one byte to send, move to the final state. + // + if(ui8Size == 1) + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + } + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Read Byte or Read Word transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8Data is a pointer to the receive data buffer. +//! \param ui8Size is the number of bytes to receive from the slave. +//! +//! This function supports both the Read Byte and Read Word protocols. The +//! amount of data to receive is user defined, but limited to 1 or 2 bytes. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 2, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteWordRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data, + uint8_t ui8Size) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 2 bytes are requested, indicate error. + // + if(ui8Size > 2) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = ui8Size; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // set the R/S bit to '1' for the CRC calculation. + // + ui8TempData = psSMBus->ui8TargetSlaveAddress << 1; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + } + else + { + // + // Update the state machine. + // + if(psSMBus->ui8RxSize == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + } + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_ONE; + } + } + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Block Write transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function supports the Block Write protocol. The amount of data sent +//! to the slave is user defined, but limited to 32 bytes per the SMBus spec. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 32, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterBlockWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data, + uint8_t ui8Size) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 32 bytes are requested, indicate error. + // + if(ui8Size > 32) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = ui8Size; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; + + // + // Clear the process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the size to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8TxSize, 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + } + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Write the first byte of the data. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_BLOCK_SIZE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Block Read transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8Data is a pointer to the receive data buffer. +//! +//! This function supports the Block Read protocol. The amount of data read +//! is defined by the slave device, but should never exceed 32 bytes per the +//! SMBus spec. The receive size is the first data byte returned by the slave, +//! so this function assumes a size of 3 until the actual number is sent by +//! the slave. In the application interrupt handler, SMBusRxPacketSizeGet() +//! can be used to obtain the amount of data sent by the slave. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterBlockRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the block transfer flag.. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; + + // + // Clear the process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Initially set the RX size to 3 to make the state machine work. + // The slave will respond with the actual size of the transfer in the + // first byte and that data will replace this initial value. + // + psSMBus->ui8RxSize = 3; + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // set the R/S bit to '1' for the CRC calculation. + // + ui8TempData = psSMBus->ui8TargetSlaveAddress << 1; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + } + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Process Call transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8TxData is a pointer to the transmit data buffer. +//! \param pui8RxData is a pointer to the receive data buffer. +//! +//! This function supports the Process Call protocol. The amount of data sent +//! to and received from the slave is fixed to 2 bytes per direction (2 sent, +//! 2 received). +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterProcessCall(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8TxData, + uint8_t *pui8RxData) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8TxData; + psSMBus->pui8RxBuffer = pui8RxData; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8TxSize = 2; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = 2; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the process call flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; + + // + // Clear the block transfer and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + } + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Block Process Call transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8TxData is a pointer to the transmit data buffer. +//! \param ui8TxSize is the number of bytes to send to the slave. +//! \param pui8RxData is a pointer to the receive data buffer. +//! +//! This function supports the Block Write/Block Read Process Call protocol. +//! The amount of data sent to the slave is user defined but limited to 32 data +//! bytes. The amount of data read is defined by the slave device, but should +//! never exceed 32 bytes per the SMBus spec. The receive size is the first +//! data byte returned by the slave, so the actual size is populated in +//! SMBusMasterISRProcess(). In the application interrupt handler, +//! SMBusRxPacketSizeGet() can be used to obtain the amount of data sent by +//! the slave. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8TxSize is greater than 32, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterBlockProcessCall(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8TxData, + uint8_t ui8TxSize, uint8_t *pui8RxData) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 32 bytes are requested, indicate error. + // + if(ui8TxSize > 32) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8TxData; + psSMBus->pui8RxBuffer = pui8RxData; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8TxSize = ui8TxSize; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = 3; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the process call and block transfer flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; + + // + // Clear the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the size to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8TxSize, 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + } + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_BLOCK_SIZE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a ``raw'' I2C write transfer to a slave device. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function sends a user-defined number of bytes to an I2C slave without +//! using an SMBus protocol. The data size is only limited to the size of the +//! ui8Size variable, which is an unsigned character (8 bits, value of 255). +//! +//! Because this function uses ``raw'' I2C, PEC is not supported. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterI2CWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data, uint8_t ui8Size) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = ui8Size; + psSMBus->ui8TxIndex = 1; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + + // + // PEC is not supported by raw I2C transfers, so force it to be disabled. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Clear the block transfer and process call flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + + // + // Set the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the first byte on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->pui8TxBuffer[0]); + + // + // Choose what to do based on the transmit size. + // + if(ui8Size == 1) + { + // + // Update the state machine. Since it's the only byte being sent, + // the state machine's next state is idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_SINGLE_SEND); + } + else if(ui8Size == 2) + { + // + // If there are only 2 bytes to send just jump to the final write + // state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a ``raw'' I2C read transfer to a slave device. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the receive data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function receives a user-defined number of bytes from an I2C slave +//! without using an SMBus protocol. The data size is only limited to the size +//! of the ui8Size variable, which is an unsigned character (8 bits, value of +//! 255). +//! +//! Because this function uses ``raw'' I2C, PEC is not supported. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterI2CRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data, uint8_t ui8Size) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = ui8Size; + + // + // PEC is not supported by raw I2C transfers, so force it to be disabled. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Clear the block transfer and process call flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + + // + // Set the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Choose what to do based on the receive size. + // + if(ui8Size == 1) + { + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + } + else if(ui8Size == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + + if(ui8Size == 1) + { + // + // Start the single receive. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + } + else + { + // + // Start the burst receive. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_START); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a ``raw'' I2C write-read transfer to a slave device. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8TxData is a pointer to the transmit data buffer. +//! \param ui8TxSize is the number of bytes to send to the slave. +//! \param pui8RxData is a pointer to the receive data buffer. +//! \param ui8RxSize is the number of bytes to receive from the slave. +//! +//! This function initiates a write-read transfer to an I2C slave without using +//! an SMBus protocol. The user-defined number of bytes is written to the +//! slave first, followed by the reception of the user-defined number of bytes. +//! The transmit and receive data sizes are only limited to the size of the +//! ui8TxSize and ui8RxSize variables, which are unsigned characters (8 bits, +//! value of 255). +//! +//! Because this function uses ``raw'' I2C, PEC is not supported. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterI2CWriteRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8TxData, uint8_t ui8TxSize, + uint8_t *pui8RxData, uint8_t ui8RxSize) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->pui8TxBuffer = pui8TxData; + psSMBus->pui8RxBuffer = pui8RxData; + psSMBus->ui8TxIndex = 1; + psSMBus->ui8TxSize = ui8TxSize; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = ui8RxSize; + + // + // PEC is not supported by raw I2C transfers, so force it to be disabled. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Set the process call flag. Even though this is technically not an SMBus + // process call, this flag is used in the interrupt state machine for + // the bus turn around. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; + + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + + // + // Set the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Write the first byte of the data. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->pui8TxBuffer[0]); + + // + // Choose what to do based on the transmit size. + // + if(ui8TxSize == 1) + { + // + // Move to the read first state for the turn around. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + } + else if(ui8TxSize == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``general'' Get UDID packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the receive data buffer. +//! +//! This function sends a ``general'' Get UDID packet, used during Address +//! Resolution Protocol (ARP). Since SMBus requires that data bytes be +//! transmitted in a certain order, the raw data in the pui8Data needs to be +//! treated as such. To put the data in a known order, use +//! SMBusARPUDIDPacketDecode(). +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPGetUDIDGen(tSMBus *psSMBus, uint8_t *pui8Data) +{ + // + // Use the block read protocol to receive the UDID. + // + return(SMBusMasterBlockRead(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_ARP_GET_UDID, pui8Data)); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``directed'' Get UDID packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the receive data buffer. +//! +//! This function sends a ``directed'' Get UDID packet, used during Address +//! Resolution Protocol (ARP). A directed packet differs from a general packet +//! in that it targets a specific slave device. Since SMBus requires that data +//! bytes be transmitted in a certain order, the raw data in the pui8Data needs +//! to be treated as such. To put the data in a known order, use +//! SMBusARPUDIDPacketDecode(). +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPGetUDIDDir(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data) +{ + // + // Use the block read protocol to receive the UDID. + // + return(SMBusMasterBlockRead(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + (ui8TargetAddress << 1 | 1), pui8Data)); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``general'' Reset Device packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends a ``general'' Reset Device packet, used during Address +//! Resolution Protocol (ARP). This packet is used by an ARP Master to force +//! all non-PSA (Persistent Slave Address), ARP-capable devices to return to +//! their initial state. This packet also tells the devices to clear their +//! Address Resolved (AR) and Address Valid (AV) flags. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPResetDeviceGen(tSMBus *psSMBus) +{ + // + // Use the Send Byte protocol to send the packet. + // + return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_ARP_RESET_DEVICE)); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``directed'' Reset Device packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends a ``directed'' Reset Device packet, used during Address +//! Resolution Protocol (ARP). This packet is used by an ARP Master to force +//! a specific non-PSA (Persistent Slave Address), ARP-capable device to return +//! to its initial state. This packet also tells the device to clear its +//! Address Resolved (AR) and Address Valid (AV) flags. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPResetDeviceDir(tSMBus *psSMBus, uint8_t ui8TargetAddress) +{ + // + // Use the Send Byte protocol to send the packet. + // + return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + (ui8TargetAddress << 1))); +} + +//***************************************************************************** +// +//! Sends an ARP Assign Address packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the transmit data buffer. This buffer +//! should be correctly formatted using SMBusARPUDIDPacketEncode() and +//! should contain the UDID data and the address for the slave. +//! +//! This function sends an Assign Address packet, used during Address +//! Resolution Protocol (ARP). Because SMBus requires data bytes be sent out +//! MSB first, the UDID and target address should be formatted correctly by the +//! application or using SMBusARPUDIDPacketEncode() and placed into a data +//! buffer pointed to by pui8Data. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPAssignAddress(tSMBus *psSMBus, uint8_t *pui8Data) +{ + // + // Use the Block Write protocol to send the packet. + // + return(SMBusMasterBlockWrite(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_ARP_ASSIGN_ADDRESS, pui8Data, 17)); +} + +//***************************************************************************** +// +//! Sends a Notify ARP Master packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the transmit data buffer. The data payload +//! should be 0x0000 for this packet. +//! +//! This function sends a Notify ARP Master packet, used during Address +//! Resolution Protocol (ARP). This packet is used by a slave to indicate +//! to the ARP Master that it needs attention. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPNotifyMaster(tSMBus *psSMBus, uint8_t *pui8Data) +{ + // + // Use the Host Notify protocol to send the packet. + // + return(SMBusMasterHostNotify(psSMBus, (SMBUS_ADR_DEFAULT_DEVICE << 1), + pui8Data)); +} + +//***************************************************************************** +// +//! Sends a Prepare to ARP packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends a Prepare to ARP packet, used during Address Resolution +//! Protocol (ARP). This packet is used by an ARP Master to alert devices on +//! the bus that ARP is about to begin. All ARP-capable devices must +//! acknowledge all bytes in this packet and clear their Address Resolved (AR) +//! flag. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPPrepareToARP(tSMBus *psSMBus) +{ + // + // Use the Send Byte protocol to send the packet. + // + return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_PREPARE_TO_ARP)); +} + +//***************************************************************************** +// +//! Master ISR processing function for the SMBus application. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function must be called in the application interrupt service routine +//! (ISR) to process SMBus master interrupts. +//! +//! \return Returns \b SMBUS_TIMEOUT if a bus timeout is detected, +//! \b SMBUS_ARB_LOST if I2C bus arbitration lost is detected, +//! \b SMBUS_ADDR_ACK_ERROR if the address phase of a transfer results in a +//! NACK, \b SMBUS_DATA_ACK_ERROR if the data phase of a transfer results in a +//! NACK, \b SMBUS_DATA_SIZE_ERROR if a receive buffer overrun is detected or +//! if a transmit operation tries to write more data than is allowed, +//! \b SMBUS_MASTER_ERROR if an unknown error occurs, \b SMBUS_PEC_ERROR if the +//! received PEC byte does not match the locally calculated value, or +//! \b SMBUS_OK if processing finished successfully. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterIntProcess(tSMBus *psSMBus) +{ + uint32_t ui32IntStatus; + uint32_t ui32ErrorStatus; + uint8_t ui8TempData; + + // + // Determine which interrupt made us get here. + // + ui32IntStatus = MAP_I2CMasterIntStatusEx(psSMBus->ui32I2CBase, true); + + // + // Check for the timeout interrupt. Since the peripheral will + // automatically issue a stop, just clear the interrupt and return. + // + if(ui32IntStatus & I2C_MASTER_INT_TIMEOUT) + { + // + // Clear all pending interrupts and wait for the bus to become + // free so we can issue a STOP. + // + MAP_I2CMasterIntClearEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_TIMEOUT | + I2C_MASTER_INT_DATA); + + // + // Clear the transfer in progress flag. New transactions will + // be aborted until the bus is free. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return to caller. + // + return(SMBUS_TIMEOUT); + } + else + { + // + // Clear the data interrupt. + // + MAP_I2CMasterIntClearEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_DATA); + } + + // + // Read the master interrupt status bits. + // + ui32ErrorStatus = HWREG(psSMBus->ui32I2CBase + I2C_O_MCS); + + // + // Check for arbitration lost. + // + if(ui32ErrorStatus & I2C_MCS_ARBLST) + { + // + // Put the state machine back in the idle state. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return to caller. + // + return(SMBUS_ARB_LOST); + } + + // + // Check for an error. + // + if(ui32ErrorStatus & I2C_MCS_ERROR) + { + // + // Put the state machine back in the idle state. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Check to see if the bus is free. There are two interrupts when a + // NACK happens, and the bus should only be free during the second + // interrupt. During the first interrupt (when the bus is busy), + // generate the necessary STOP condition. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + // + // Issue a STOP. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); + } + else + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + } + + // + // Check for ACK errors. + // + if(ui32ErrorStatus & I2C_MCS_ADRACK) + { + // + // Return to caller. + // + return(SMBUS_ADDR_ACK_ERROR); + } + else if(ui32ErrorStatus & I2C_MCS_DATACK) + { + // + // Return to caller. + // + return(SMBUS_DATA_ACK_ERROR); + } + else + { + // + // Return to caller. Should never get here. + // + return(SMBUS_MASTER_ERROR); + } + } + + // + // If no error conditions, determine what to do based on the state. + // + switch(psSMBus->ui8MasterState) + { + // + // The idle state. This state should only be reached after the last + // byte of a master transmit. + // + case SMBUS_STATE_IDLE: + { + // + // If the peripheral is not busy clear the transfer in progress + // flag. This means that the peripheral has given up the bus, + // most likely due to the end of a transmit operation. + // + if(!MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + } + + // + // This state is done. + // + break; + } + + // + // When using a block write, the transfer size must be sent before the + // data payload. + // + case SMBUS_STATE_WRITE_BLOCK_SIZE: + { + // + // Write the block write size to the data register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8TxSize); + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // The next data byte is from the data payload. + // + if((psSMBus->ui8TxSize == 1) && + !(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + else + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + + // + // This state is done. + // + break; + } + // + // The state for the middle of a burst write. + // + case SMBUS_STATE_WRITE_NEXT: + { + // + // Write the next byte to the data register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]); + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // Determine the next state based on the values of the PEC and + // process call flags. + // + + // + // If PEC is active and process call is not active. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // If a process call, there is no PEC byte on the transmit. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL)) + { + // + // Check to see if the TX index is equal to size minus 1. + // + if(psSMBus->ui8TxIndex == (psSMBus->ui8TxSize - 1)) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + } + else + { + // + // If the TX index is the same as the size, we're done. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + } + } + + // + // If PEC is not used, regardless of whether this is a process + // call. + // + else + { + // + // Check to see if the TX index is equal to the size minus 1. + // + if(psSMBus->ui8TxIndex == (psSMBus->ui8TxSize - 1)) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + } + + // + // This state is done. + // + break; + } + + // + // The state for the final write of a burst sequence. + // + case SMBUS_STATE_WRITE_FINAL: + { + // + // Determine what data to write to the data register based + // on the values of the PEC and process call flags. + // + // + // If PEC is active, write the PEC byte to the data register. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // If a process call is active, send data, not CRC. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL)) + { + // + // Write the final byte from TX buffer to the data + // register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->pui8TxBuffer[psSMBus-> + ui8TxIndex++]); + } + else + { + // + // Write the calculated CRC (PEC) byte to the data + // register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->ui8CalculatedCRC); + } + } + else + { + // + // Write the final byte from TX buffer to the data register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->pui8TxBuffer[psSMBus-> + ui8TxIndex++]); + } + + // + // If a process call is active, send out the repeated start to + // begin the RX portion. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL)) + { + // + // Move to the read first "turnaround" state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_CONT); + } + else + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_FINISH); + + // + // Since we end the transaction after the last byte is sent, + // the next state is idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + } + + // + // This state is done. + // + break; + } + + // + // The state for a single byte read. + // + case SMBUS_STATE_READ_ONE: + { + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Perform a single byte read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // The next state is the wait for final read state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + + // + // This state is done. + // + break; + } + + // + // The state for the start of a burst read. + // + case SMBUS_STATE_READ_FIRST: + { + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Handle the case where PEC is used. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the target address and R/S bit to the running CRC + // calculation. + // + ui8TempData = + ((psSMBus->ui8TargetSlaveAddress << 1) & 0xfe) | 1; + + // + // Update the calculated CRC value in the configuration + // structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, &ui8TempData, 1); + + // + // Set the next state in the state machine. + // + if(psSMBus->ui8RxSize > 1) + { + // + // If this is a block transfer, the next state is to read + // back the number of bytes that the slave will be sending. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_BLOCK_SIZE; + } + + // + // For every other case... + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + } + + // + // If 1 byte remains, move to the final read state. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + else + { + // + // Set the next state in the state machine. + // + if(psSMBus->ui8RxSize > 2) + { + // + // If this is a block transfer, the next state is to read + // back the number of bytes that the slave will be sending. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_BLOCK_SIZE; + } + + // + // For every other case... + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + } + + // + // If 2 bytes remain, move to the final read state. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + + // + // Start the burst receive. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_START); + + // + // This state is done. + // + break; + } + + // + // The state for the size of a block read. + // + case SMBUS_STATE_READ_BLOCK_SIZE: + { + // + // Update the RX size with the data byte. + // + psSMBus->ui8RxSize = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If more than 32 bytes are going to be sent, error. + // + if((psSMBus->ui8RxSize > 32) || (psSMBus->ui8RxSize == 0)) + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP; + + // + // If too many or too few bytes, error. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // Break from this case. + // + break; + } + + // + // If PEC is enabled, add the size byte to the calculation and + // add one to the size variable to account for the extra PEC byte. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Calculate the new CRC and update configuration structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8RxSize, 1); + } + + // + // Update the state machine. + // + switch(psSMBus->ui8RxSize) + { + // + // 1 byte remaining. + // + case 1: + { + // + // If only one byte remains and PEC, go to the second + // to last byte state. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + + // + // If only one byte remains and no PEC, end the burst + // transfer. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + } + + // + // This switch is done. + // + break; + } + + // + // 2 bytes remaining. + // + case 2: + { + // + // If two bytes and PEC remain, move to read next + // state. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + + // + // If two bytes remain, move to the final read state. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + + // + // This switch is done. + // + break; + } + + // + // For every other situation (in other words, remaining bytes + // is greater than 2). + // + default: + { + // + // If more than 2 bytes to read, move to the next byte + // state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + + // + // This switch is done. + // + break; + } + } + + // + // Determine how to step the I2C state machine. + // + if((psSMBus->ui8RxSize == 1) && + !HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // If exactly 1 byte remains, read the byte and send a STOP. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_FINISH); + } + else + { + // + // Otherwise, continue the burst read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + } + + // + // This state is done. + // + break; + } + + // + // The state for the middle of a burst read. + // + case SMBUS_STATE_READ_NEXT: + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize) + { + // + // Dummy read of data register. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If too many or too few bytes, error. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP; + + // + // Break from this case. + // + break; + } + + // + // Read the received character. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] = + MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // Continue the burst read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + + // + // If PEC is enabled, add the received byte to the calculation. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Calculate the new CRC and update configuration structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex], + 1); + + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + + // + // If there is 1 byte remaining, make next state be the + // end of burst read state. + // + if((psSMBus->ui8RxSize - psSMBus->ui8RxIndex) == 1) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + else + { + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + + // + // If there are two bytes remaining, make next state be the + // end of burst read state. + // + if((psSMBus->ui8RxSize - psSMBus->ui8RxIndex) == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + + // + // This state is done. + // + break; + } + + // + // The state for the end of a burst read. + // + case SMBUS_STATE_READ_FINAL: + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize) + { + // + // Dummy read of data register. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If too many or too few bytes, error. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP; + + // + // Break from this case. + // + break; + } + + // + // Read the received character. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] = + MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // The next state is the wait for final read state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + + // + // Finish the burst read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // If PEC is enabled, add the received byte to the calculation. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Calculate the new CRC and update configuration structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex], + 1); + } + + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + + // + // This state is done. + // + break; + } + + // + // This state is for the final read of a single or burst read. + // + case SMBUS_STATE_READ_WAIT: + { + // + // Read the received byte. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If PEC is enabled, check the value that just came in to see + // if it matches. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex > psSMBus->ui8RxSize) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, + FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Store the received CRC byte. + // + psSMBus->ui8ReceivedCRC = ui8TempData; + + // + // If the CRC doesn't match, send a NACK and indicate the + // failure to the application. + // + if(psSMBus->ui8ReceivedCRC != psSMBus->ui8CalculatedCRC) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, + FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_PEC_ERROR); + } + } + else + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, + FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Read the received byte. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] = ui8TempData; + + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + } + + // + // The state machine is now idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // This state is done. + // + break; + } + + // + // This state is for a transaction that needed to end due to a + // size error. + // + case SMBUS_STATE_READ_ERROR_STOP: + { + // + // Dummy read the received byte. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // The state machine is now idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_DATA_SIZE_ERROR); + } + } + + // + // Return to caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Enables the appropriate master interrupts for stack processing. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables the I2C interrupts used by the SMBus master. Both +//! the peripheral-level and NVIC-level interrupts are enabled. +//! SMBusMasterInit() must be called before this function because this function +//! relies on the I2C base address being defined. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusMasterIntEnable(tSMBus *psSMBus) +{ + // + // Enable the master interrupts. + // + MAP_I2CMasterIntEnableEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_DATA | + I2C_MASTER_INT_TIMEOUT); + + // + // Enable the interrupt in the NVIC. + // + switch(psSMBus->ui32I2CBase) + { + case I2C0_BASE: + { + MAP_IntEnable(INT_I2C0); + break; + } + + case I2C1_BASE: + { + MAP_IntEnable(INT_I2C1); + break; + } + + case I2C2_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C2_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C2_TM4C129); + } + break; + } + + case I2C3_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C3_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C3_TM4C129); + } + break; + } + + case I2C4_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C4_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C4_TM4C129); + } + break; + } + + case I2C5_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C5_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C5_TM4C129); + } + break; + } + + case I2C6_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C6_TM4C129); + } + break; + } + + case I2C7_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C7_TM4C129); + } + break; + } + + case I2C8_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C8_TM4C129); + } + break; + } + + case I2C9_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C9_TM4C129); + } + break; + } + } +} + +//***************************************************************************** +// +//! Initializes an I2C master peripheral for SMBus functionality. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui32I2CBase specifies the base address of the I2C master peripheral. +//! \param ui32SMBusClock specifies the system clock speed of the MCU. +//! +//! This function initializes an I2C peripheral for SMBus master use. The +//! instance-specific configuration structure is initialized to a set of known +//! values and the I2C peripheral is configured for 100kHz use, which is +//! required by the SMBus specification. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusMasterInit(tSMBus *psSMBus, uint32_t ui32I2CBase, + uint32_t ui32SMBusClock) +{ + // + // Initialize the configuration structure. + // + psSMBus->pUDID = 0; + psSMBus->ui32I2CBase = ui32I2CBase; + psSMBus->ui16Flags = 0; + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + psSMBus->ui8OwnSlaveAddress = 0; + psSMBus->ui8TargetSlaveAddress = 0; + psSMBus->ui8CurrentCommand = 0; + psSMBus->ui8CalculatedCRC = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + + // + // Enable and initialize the I2C master module Using the system clock. + // The I2C transfer rate will always be 100kHz since fast mode is not + // supported by SMBus. + // + MAP_I2CMasterInitExpClk(psSMBus->ui32I2CBase, ui32SMBusClock, false); + + // + // Configure bus timeout to 25ms. 12-bit value for 25ms is 0x9C4 (2500 + // clocks), so round upper 8 bits to 0x9C. Each clock is 10us since + // 100kHz I2C is required for SMBus. + // + MAP_I2CMasterTimeoutSet(psSMBus->ui32I2CBase, 0x9C); +} + +//***************************************************************************** +// +//! Slave ISR processing function for the SMBus application. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function must be called in the application interrupt service routine +//! (ISR) to process SMBus slave interrupts. +//! +//! If manual acknowledge is enabled using SMBusSlaveManualACKEnable(), this +//! function processes the data byte, but does not send the ACK/NACK value. In +//! this case, the user application is responsible for sending the acknowledge +//! bit based on the return code of this function. +//! +//! When receiving a Quick Command from the master, the slave has some set-up +//! requirements. When the master sends the R/S (data) bit as '0', nothing +//! additional needs to be done in the slave and SMBusSlaveIntProcess() returns +//! \b SMBUS_SLAVE_QCMD_0. However, when the master sends the R/S (data) bit +//! as '1', the slave must write the data register with data containing a '1' +//! in bit 7. This means that when receiving a Quick Command, the slave must +//! set up the TX buffer to either have 1 data byte with bit 7 set to '1' or +//! set up the TX buffer to be zero length. In the case where 1 data byte is +//! put in the TX buffer, SMBusSlaveIntProcess() returns \b SMBUS_OK the first +//! time its called and \b SMBUS_SLAVE_QCMD_0 the second. In the case where +//! the TX buffer has no data, SMBusSlaveIntProcess() will return +//! \b SMBUS_SLAVE_ERROR the first time its called, and \b SMBUS_SLAVE_QCMD_1 +//! the second time. +//! +//! \return Returns \b SMBUS_SLAVE_FIRST_BYTE if the first byte (typically the +//! SMBus command) has been received; \b SMBUS_SLAVE_NOT_READY if the slave's +//! transmit buffer is not yet initialized when the master requests data from +//! the slave; \b SMBUS_DATA_SIZE_ERROR if during a master block write, the +//! size sent by the master is greater than the amount of available space in +//! the receive buffer; \b SMBUS_SLAVE_ERROR if a buffer overrun is detected +//! during a slave receive operation or if data is sent and was not expected; +//! \b SMBUS_SLAVE_QCMD_0 if a Quick Command was received with data '0'; +//! \b SMBUS_SLAVE_QCMD_1 if a Quick Command was received with data '1'; +//! \b SMBUS_TRANSFER_COMPLETE if a STOP is detected on the bus, marking the +//! end of a transfer; \b SMBUS_PEC_ERROR if the received PEC byte does not +//! match the locally calculated value; or \b SMBUS_OK if processing finished +//! successfully. +// +//***************************************************************************** +tSMBusStatus +SMBusSlaveIntProcess(tSMBus *psSMBus) +{ + uint32_t ui32InterruptStatus; + uint32_t ui32SlaveStatus = 0; + uint8_t ui8CRCTemp; + uint8_t ui8DataTemp; + + // + // Determine which interrupt was asserted. + // + ui32InterruptStatus = I2CSlaveIntStatusEx(psSMBus->ui32I2CBase, true); + + // + // Check the status register. + // + ui32SlaveStatus = I2CSlaveStatus(psSMBus->ui32I2CBase); + + // + // Check for the START interrupt. + // + if(ui32InterruptStatus & I2C_SLAVE_INT_START) + { + // + // Clear the interrupt. + // + I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_START); + + + // + // This interrupt is not supported outside of using the FIFO. + // + return(SMBUS_OK); + } + + // + // Check for the STOP interrupt. + // + if(ui32InterruptStatus & I2C_SLAVE_INT_STOP) + { + // + // Make sure the transfer in progress flag is cleared. In the case + // of Quick Command, it should never be set, so this is safe. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Clear the interrupt. + // + I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_STOP); + + // + // Check to see if a Quick Command was sent. + // + if(ui32SlaveStatus & 0x10) + { + // + // Make sure the TX/RX index is 0. If not, we should not be here. + // Other data should not have been sent or received during a Quick + // Command. + // + if((psSMBus->ui8RxIndex != 0) || (psSMBus->ui8TxIndex != 0)) + { + // + // Return an error. + // + return(SMBUS_SLAVE_ERROR); + } + + // + // Tell caller a Quick Command has occurred and the data value. + // + if(ui32SlaveStatus & 0x20) + { + return(SMBUS_SLAVE_QCMD_1); + } + else + { + return(SMBUS_SLAVE_QCMD_0); + } + } + + // + // Move to the idle state. + // + psSMBus->ui8SlaveState = SMBUS_STATE_IDLE; + + // + // Return end of transfer. + // + return(SMBUS_TRANSFER_COMPLETE); + } + + // + // Check for the DATA interrupt. + // + if(ui32InterruptStatus & I2C_SLAVE_INT_DATA) + { + // + // Clear the I2C interrupt. + // + I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_DATA); + + // + // Make sure that at least one of the relevant status bits is set. + // + if(!(ui32SlaveStatus & 0x07)) + { + // + // No status bits were set - this is bad. Should never get here. + // + return(SMBUS_SLAVE_ERROR); + } + + // + // Every time this interrupt occurs, a transfer is in progress. Make + // sure the flag is set appropriately. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Handle the request type. + // + switch((ui32SlaveStatus & 0x07)) + { + // + // The first byte after the slave's own address has been received. + // This is almost always the command byte in SMBus. The only + // exception is when the Send Byte protocol is used by the master. + // + case I2C_SLAVE_ACT_RREQ_FBR: + { + // + // Check which slave address was called out. Set the active + // address to the matched address. + // + if(I2CSlaveStatus(psSMBus->ui32I2CBase) & I2C_SCSR_OAR2SEL) + { + psSMBus->ui8OwnSlaveAddress = + HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR2) & 0x7f; + } + else + { + psSMBus->ui8OwnSlaveAddress = + HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR); + } + + // + // If raw I2C, data goes into buffer. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C)) + { + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] = + I2CSlaveDataGet(psSMBus->ui32I2CBase); + } + + // + // Read the first byte into the ui8CurrentCommand member. + // + else + { + psSMBus->ui8CurrentCommand = + I2CSlaveDataGet(psSMBus->ui32I2CBase); + } + + // + // If PEC is enabled, add the address to the CRC calculation. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the address to the CRC calculation. In this case + // R/S will always be 0. Also, this is the start of the + // CRC calculation, so the initial value is 0. + // + ui8CRCTemp = psSMBus->ui8OwnSlaveAddress << 1; + + // + // Calculate new CRC. + // + psSMBus->ui8CalculatedCRC = Crc8CCITT(0, &ui8CRCTemp, 1); + + // + // Add the data byte (ui8CurrentCommand) to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_SLAVE_POST_COMMAND; + + // + // Actions for this case are complete. + // + return(SMBUS_SLAVE_FIRST_BYTE); + } + + // + // A data byte other than the first data byte has been received. + // + case I2C_SLAVE_ACT_RREQ: + { + // + // Determine what to do based on the current state. + // + switch(psSMBus->ui8SlaveState) + { + // + // Receive first post-command byte. + // + case SMBUS_STATE_SLAVE_POST_COMMAND: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // Check if this is a block transfer. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + // + // Make sure there is enough space in the buffer. + // If not, NACK. If there is, overwrite the + // current size with the size sent by the master. + // + if(ui8DataTemp > psSMBus->ui8RxSize) + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + // + // Indicate a size error. + // + return(SMBUS_DATA_SIZE_ERROR); + } + else + { + // + // Update the size. + // + psSMBus->ui8RxSize = ui8DataTemp; + + // + // Check to see if PEC is enabled. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT; + } + + // + // This state is done. + // + break; + } + + // + // If there is no data to receive and no PEC, nothing + // to do. Software should never get here. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + // + // Report an error. + // + return(SMBUS_SLAVE_ERROR); + } + else + { + // + // Put the data in the buffer. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] = + ui8DataTemp; + + // + // If this is the last data byte. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_PEC; + } + else + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_DONE; + } + } + + // + // All other cases. + // + else + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT; + } + } + + // + // Actions for this case are complete. + // + break; + } + + // + // Read the next byte into the buffer. + // + case SMBUS_STATE_READ_NEXT: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // If there is no data to receive and no PEC, nothing + // to do. Software should never get here. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + // + // Report an error. + // + return(SMBUS_SLAVE_ERROR); + } + else + { + // + // Put the data in the buffer. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] = + ui8DataTemp; + + // + // If this is the last data byte. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Update the state machine. + // + if(HWREGBITB(&psSMBus->ui16Flags, + FLAG_PROCESS_CALL)) + { + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_DONE; + } + else + { + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_PEC; + } + } + else + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_DONE; + } + } + + // + // All other cases. + // + else + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT; + } + } + + break; + } + + // + // Read the PEC byte and compare it. + // + case SMBUS_STATE_READ_PEC: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // Compare PEC. + // + if(psSMBus->ui8CalculatedCRC != ui8DataTemp) + { + // + // Indicate PEC error. + // + return(SMBUS_PEC_ERROR); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + break; + } + + // + // No more data to receive. If we get here, read data + // into a dummy variable and NACK. + // + case SMBUS_STATE_READ_DONE: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // Report an error. + // + return(SMBUS_SLAVE_ERROR); + } + } + + // + // Actions for this case are complete. + // + break; + } + + // + // The master has requested that the slave transmit data back to + // master. + // + case I2C_SLAVE_ACT_TREQ: + { + // + // Initialize temporary variable that stores transmit byte to + // 0xff. If data is not set by another condition, the 0xff + // carries through. This happens if ui8TxIndex is equal to or + // greater than ui8TxSize. + // + ui8DataTemp = 0xff; + + // + // Determine what to do based on the current state. + // + switch(psSMBus->ui8SlaveState) + { + // + // The state machine is currently idle, or if the last + // state was SMBUS_STATE_SLAVE_POST_COMMAND or + // SMBUS_READ_DONE, this is the first byte transmitted. In + // the case of slave post command, this means that the + // command was received followed by a repeated start (with + // R/S = 1). In the case of read next, this means that a + // raw I2C master transmit changed direction with a + // repeated start and is now a master receive. In the case + // of read done, this means that a previous master transmit + // was finished (non-command followed by a repeated start). + // + case SMBUS_STATE_IDLE: + case SMBUS_STATE_SLAVE_POST_COMMAND: + case SMBUS_STATE_READ_NEXT: + case SMBUS_STATE_READ_DONE: + { + // + // Check which slave address was called out. Set the + // active address to the matched address. + // + if(I2CSlaveStatus(psSMBus->ui32I2CBase) & + I2C_SCSR_OAR2SEL) + { + psSMBus->ui8OwnSlaveAddress = + (HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR2) & + 0x7f); + } + else + { + psSMBus->ui8OwnSlaveAddress = + HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR); + } + + // + // Check to see if the TX buffer is populated. If not, + // return not ready without writing to the data + // register. + // + if(psSMBus->ui8TxSize == 0) + { + return(SMBUS_SLAVE_NOT_READY); + } + + // + // Is this a block transfer? + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + // + // The first byte to send is the size. + // + ui8DataTemp = psSMBus->ui8TxSize; + } + else + { + // + // Is there data to send? + // + if(psSMBus->ui8TxIndex < psSMBus->ui8TxSize) + { + // + // Set the transmit data to the next item in + // the buffer. + // + ui8DataTemp = + psSMBus->pui8TxBuffer[psSMBus-> + ui8TxIndex++]; + } + else + { + // + // Send 0xff per spec. + // + ui8DataTemp = 0xff; + } + } + + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Start calculating the CRC with the address. + // + ui8CRCTemp = + (psSMBus->ui8OwnSlaveAddress << 1) | 1; + + // + // Add the address and R/S bit to the CRC. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8CRCTemp, 1); + + // + // Add the data byte to the CRC calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Move to the next state. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + // + // Final byte is the CRC byte. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // All other cases, move to the next byte + // state. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_NEXT; + } + } + else + { + // + // Move to the next state. + // + switch(psSMBus->ui8TxSize - psSMBus->ui8TxIndex) + { + // + // If all of the data has been sent, move to + // the done state. + // + case 0: + { + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_DONE; + + break; + } + + // + // If 1 left, move to the final byte state. + // + case 1: + { + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + + break; + } + + // + // All other cases, move to the next byte + // state. + // + default: + { + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_NEXT; + + break; + } + } + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // This state is done. + // + break; + } + + // + // The first byte has already been sent, handle the rest. + // + case SMBUS_STATE_WRITE_NEXT: + { + // + // Set the transmit data to the next item in the + // buffer. + // + ui8DataTemp = + psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]; + + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the byte to the CRC calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Check if it's time to move to the next state. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + // + // Final byte is the CRC byte. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + } + } + else + { + // + // Move to the next state. + // + if((psSMBus->ui8TxSize - psSMBus->ui8TxIndex) == 1) + { + // + // If only 1 byte remains, move to the final + // state. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + } + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // This state is done. + // + break; + } + + // + // Write the final byte, whether PEC or data. + // + case SMBUS_STATE_WRITE_FINAL: + { + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Send the CRC byte. + // + ui8DataTemp = psSMBus->ui8CalculatedCRC; + } + else + { + // + // Send the last data byte. + // + ui8DataTemp = + psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]; + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // Move to the write done state. + // + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_DONE; + + // + // This state is done. + // + break; + } + + // + // All data has been sent, send 0xff. + // + case SMBUS_STATE_WRITE_DONE: + { + // + // Send 0xff because there is no more data to send. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, 0xff); + + // + // This state is done. + // + break; + } + } + + // + // Actions for this case are complete. + // + break; + } + } + + // + // Return OK status. + // + return(SMBUS_OK); + } + + // + // Return OK. Should never get here. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Sends data outside of the interrupt processing function. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends data outside the interrupt processing function, and +//! should only be used when SMBusSlaveIntProcess() returns +//! \b SMBUS_SLAVE_NOT_READY. At this point, the application should set up the +//! transfer and call this function (it assumes that the transmit buffer has +//! already been populated when called). When called, this function updates +//! the slave state machine as if SMBusSlaveIntProcess() were called. +//! +//! \return Returns \b SMBUS_SLAVE_NOT_READY if the slave's transmit buffer is +//! not yet initialized (ui8TxSize is 0), or \b SMBUS_OK if processing finished +//! successfully. +// +//***************************************************************************** +tSMBusStatus +SMBusSlaveDataSend(tSMBus *psSMBus) +{ + uint8_t ui8CRCTemp; + uint8_t ui8DataTemp; + + // + // Check to see if the TX buffer is populated. If not, + // return not ready without writing to the data register. + // + if(psSMBus->ui8TxSize == 0) + { + return(SMBUS_SLAVE_NOT_READY); + } + + // + // Is this a block transfer? + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + // + // The first byte to send is the size. + // + ui8DataTemp = psSMBus->ui8TxSize; + } + else + { + // + // Is there data to send? + // + if(psSMBus->ui8TxIndex < psSMBus->ui8TxSize) + { + // + // Set the transmit data to the next item in + // the buffer. + // + ui8DataTemp = psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]; + } + else + { + // + // Send 0xff per spec. Should not get here. + // + ui8DataTemp = 0xff; + } + } + + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Start calculating the CRC with the address. + // + ui8CRCTemp = (psSMBus->ui8OwnSlaveAddress << 1) | 1; + + // + // Add the address and R/S bit to the CRC. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8CRCTemp, 1); + + // + // Add the data byte to the CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Move to the next state. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + // + // Final byte is the CRC byte. + // + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // All other cases, move to the next byte state. + // + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_NEXT; + } + } + else + { + // + // Move to the next state. + // + switch(psSMBus->ui8TxSize - psSMBus->ui8TxIndex) + { + // + // If all of the data has been sent, move to the + // done state. + // + case 0: + { + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_DONE; + + break; + } + + // + // If 1 left, move to the final byte state. + // + case 1: + { + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_FINAL; + + break; + } + + // + // All other cases, move to the next byte state. + // + default: + { + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_NEXT; + + break; + } + } + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // Return to caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Set the address and size of the slave transmit buffer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes in the buffer. +//! +//! This function sets the address and size of the slave transmit buffer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveTxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size) +{ + // + // Set the trasmit buffer. + // + psSMBus->pui8TxBuffer = pui8Data; + + // + // Set the size. + // + psSMBus->ui8TxSize = ui8Size; +} + +//***************************************************************************** +// +//! Set the address and size of the slave receive buffer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the receive data buffer. +//! \param ui8Size is the number of bytes in the buffer. +//! +//! This function sets the address and size of the slave receive buffer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveRxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size) +{ + // + // Set the receive buffer. + // + psSMBus->pui8RxBuffer = pui8Data; + + // + // Set the size. + // + psSMBus->ui8RxSize = ui8Size; +} + +//***************************************************************************** +// +//! Get the current command byte. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Returns the current value of the ui8CurrentCommand variable in the SMBus +//! configuration structure. This can be used to help the user application +//! set up the SMBus slave transmit and receive buffers. +//! +//! \return None. +// +//***************************************************************************** +uint8_t +SMBusSlaveCommandGet(tSMBus *psSMBus) +{ + // + // Return the current command. + // + return(psSMBus->ui8CurrentCommand); +} + +//***************************************************************************** +// +//! Sets the process call flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Sets the process call flag in the configuration structure so that the SMBus +//! slave can respond correctly to a Process Call request. This flag must be +//! set prior to the data portion of the packet. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveProcessCallEnable(tSMBus *psSMBus) +{ + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; +} + +//***************************************************************************** +// +//! Clears the process call flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Clears the process call flag in the configuration structure. The user +//! application can either call this function to clear the flag, or use +//! SMBusSlaveTransferInit() to clear out all transfer-specific flags. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveProcessCallDisable(tSMBus *psSMBus) +{ + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; +} + +//***************************************************************************** +// +//! Sets the block transfer flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Sets the block transfer flag in the configuration structure so that the +//! SMBus slave can respond correctly to a Block Write or Block Read request. +//! This flag must be set prior to the data portion of the packet. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveBlockTransferEnable(tSMBus *psSMBus) +{ + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; +} + +//***************************************************************************** +// +//! Clears the block transfer flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Clears the block transfer flag in the configuration structure. The user +//! application can either call this function to clear the flag, or use +//! SMBusSlaveTransferInit() to clear out all transfer-specific flags. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveBlockTransferDisable(tSMBus *psSMBus) +{ + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; +} + +//***************************************************************************** +// +//! Sets the ``raw'' I2C flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Sets the raw I2C flag in the configuration structure so that the +//! SMBus slave can respond correctly to raw I2C (non-SMBus protocol) requests. +//! This flag must be set prior to the transfer, and is a global setting. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveI2CEnable(tSMBus *psSMBus) +{ + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; +} + +//***************************************************************************** +// +//! Clears the ``raw'' I2C flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Clears the raw I2C flag in the configuration structure. This flag is a +//! global setting similar to the PEC flag and cannot be cleared using +//! SMBusSlaveTransferInit(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveI2CDisable(tSMBus *psSMBus) +{ + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; +} + +//***************************************************************************** +// +//! Sets the value of the AR (Address Resolved) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param bValue is the value to set the flag. +//! +//! This function allows the application to set the value of the AR flag. All +//! SMBus slaves must support the AR and AV flags. On POR, the AR flag is +//! cleared. It is also cleared when a slave receives the ARP Reset Device +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveARPFlagARSet(tSMBus *psSMBus, bool bValue) +{ + // + // Set the block address resolved flag to the desired value. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_RESOLVED) = bValue; +} + +//***************************************************************************** +// +//! Returns the current value of the AR (Address Resolved) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This returns the value of the AR (Address Resolved) flag. +//! +//! \return Returns \b true if set, \b false if cleared. +// +//***************************************************************************** +bool +SMBusSlaveARPFlagARGet(tSMBus *psSMBus) +{ + // + // Get the value of the block address resolved flag. + // + return(HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_RESOLVED)); +} + +//***************************************************************************** +// +//! Sets the value of the AV (Address Valid) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param bValue is the value to set the flag. +//! +//! This function allows the application to set the value of the AV flag. All +//! SMBus slaves must support the AR and AV flags. On POR, the AV flag is +//! cleared. It is also cleared when a slave receives the ARP Reset Device +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveARPFlagAVSet(tSMBus *psSMBus, bool bValue) +{ + // + // Set the block address valid flag to the desired value. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_VALID) = bValue; +} + +//***************************************************************************** +// +//! Returns the current value of the AV (Address Valid) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This returns the value of the AV (Address Valid) flag. +//! +//! \return Returns \b true if set, or \b false if cleared. +// +//***************************************************************************** +bool +SMBusSlaveARPFlagAVGet(tSMBus *psSMBus) +{ + // + // Get the value of the block address valid flag. + // + return(HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_VALID)); +} + +//***************************************************************************** +// +//! Sets up the SMBus slave for a new transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function is used to re-initialize the configuration structure for a +//! new transfer. Once a transfer is complete and the data has been processed, +//! unused flags, states, the data buffers and buffer indexes should be reset +//! to a known state before a new transfer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveTransferInit(tSMBus *psSMBus) +{ + // + // Clear the block transfer, process call and transfer in progress flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Set the configuration structure to a known, zeroed state. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + psSMBus->ui8SlaveState = SMBUS_STATE_IDLE; + psSMBus->ui8CurrentCommand = 0; + psSMBus->ui8CalculatedCRC = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; +} + +//***************************************************************************** +// +//! Sets the value of the ACK bit when using manual acknowledgement. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param bACK specifies whether to ACK (\b true) or NACK (\b false). +//! +//! This function sets the value of the ACK bit. In order for the ACK bit to +//! take effect, manual acknowledgement must be enabled on the slave using +//! SMBusSlaveManualACKEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveACKSend(tSMBus *psSMBus, bool bACK) +{ + // + // Send ACK or NACK based on the value of bACK. + // + if(bACK) + { + I2CSlaveACKValueSet(psSMBus->ui32I2CBase, true); + } + else + { + I2CSlaveACKValueSet(psSMBus->ui32I2CBase, false); + } +} + +//***************************************************************************** +// +//! Enables manual acknowledgement for the SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables manual acknowledge capability in the slave. If the +//! application requires that the slave NACK on a bad command or a bad PEC +//! calculation, manual acknowledgement allows this to happen. +//! +//! In the case of responding to a bad command with a NACK, the application +//! should use SMBusSlaveACKSend() to ACK/NACK the command. The slave ISR +//! should check for the SMBUS_SLAVE_FIRST_BYTE return code from +//! SMBusSlaveISRProcess() and ACK/NACK accordingly. All other cases should be +//! handled in the application based on the return code of +//! SMBusSlaveISRProcess(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveManualACKEnable(tSMBus *psSMBus) +{ + // + // Enable manual acknowledge. + // + I2CSlaveACKOverride(psSMBus->ui32I2CBase, true); +} + +//***************************************************************************** +// +//! Disables manual acknowledgement for the SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function disables manual acknowledge capability in the slave. When +//! manual acknowledgement is disabled, the slave automatically ACKs every +//! byte sent by the master. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveManualACKDisable(tSMBus *psSMBus) +{ + // + // Disable manual acknowledge. + // + I2CSlaveACKOverride(psSMBus->ui32I2CBase, false); +} + +//***************************************************************************** +// +//! Returns the manual acknowledgement status of the SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function returns the state of the I2C ACKOEN bit in the I2CSACKCTL +//! register. This feature is disabled out of reset and must be enabled +//! using SMBusSlaveManualACKEnable(). +//! +//! \return Returns \b true if manual acknowledge is enabled, or \b false if +//! manual acknowledge is disabled. +// +//***************************************************************************** +bool +SMBusSlaveManualACKStatusGet(tSMBus *psSMBus) +{ + // + // Return the value of the bit. + // + return(HWREG(psSMBus->ui32I2CBase + I2C_O_SACKCTL) & 0x1); +} + +//***************************************************************************** +// +//! Determine whether primary or secondary slave address has been requested by +//! the master. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Tells the caller whether the I2C slave address requested by the master or +//! SMBus Host is the primary or secondary I2C slave address of the peripheral. +//! The primary is defined as the address programmed into I2CSOAR, and the +//! secondary as the address programmed into I2CSOAR2. +//! +//! \return Returns \b SMBUS_SLAVE_ADDR_PRIMARY if the primary address is +//! called out or \b SMBUS_SLAVE_ADDR_SECONDARY if the secondary address is +//! called out. +// +//***************************************************************************** +tSMBusStatus +SMBusSlaveIntAddressGet(tSMBus *psSMBus) +{ + // + // Determine whether the primary or secondary address was called out. + // + if(I2CSlaveStatus(psSMBus->ui32I2CBase) & I2C_SCSR_OAR2SEL) + { + return(SMBUS_SLAVE_ADDR_SECONDARY); + } + else + { + return(SMBUS_SLAVE_ADDR_PRIMARY); + } +} + +//***************************************************************************** +// +//! Enables the appropriate slave interrupts for stack processing. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables the I2C interrupts used by the SMBus slave. Both +//! the peripheral-level and NVIC-level interrupts are enabled. +//! SMBusSlaveInit() must be called before this function because this function +//! relies on the I2C base address being defined. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveIntEnable(tSMBus *psSMBus) +{ + // + // Enable the slave interrupts. + // + I2CSlaveIntEnableEx(psSMBus->ui32I2CBase, + I2C_SLAVE_INT_DATA | I2C_SLAVE_INT_STOP); + + // + // Enable the interrupt in the NVIC. + // + switch(psSMBus->ui32I2CBase) + { + case I2C0_BASE: + { + MAP_IntEnable(INT_I2C0); + break; + } + + case I2C1_BASE: + { + MAP_IntEnable(INT_I2C1); + break; + } + + case I2C2_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C2_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C2_TM4C129); + } + break; + } + + case I2C3_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C3_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C3_TM4C129); + } + break; + } + + case I2C4_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C4_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C4_TM4C129); + } + break; + } + + case I2C5_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C5_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C5_TM4C129); + } + break; + } + + case I2C6_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C6_TM4C129); + } + break; + } + + case I2C7_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C7_TM4C129); + } + break; + } + + case I2C8_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C8_TM4C129); + } + break; + } + + case I2C9_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C9_TM4C129); + } + break; + } + } +} + +//***************************************************************************** +// +//! Sets the slave address for an SMBus slave peripheral. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8AddressNum specifies which address (primary or secondary) +//! \param ui8SlaveAddress is the address of the slave. +//! +//! This function sets the slave address. Both the primary and secondary +//! addresses can be set using this function. To set the primary address +//! (stored in I2CSOAR), ui8AddressNum should be '0'. To set the secondary +//! address (stored in I2CSOAR2), ui8AddressNum should be '1'. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveAddressSet(tSMBus *psSMBus, uint8_t ui8AddressNum, + uint8_t ui8SlaveAddress) +{ + // + // Write the slave address. + // + I2CSlaveAddressSet(psSMBus->ui32I2CBase, ui8AddressNum, ui8SlaveAddress); +} + +//***************************************************************************** +// +//! Sets a slave's UDID structure. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pUDID is a pointer to the UDID configuration for the slave. This +//! is only needed if the slave is on a bus that uses ARP. +//! +//! This function sets the UDID for a slave instance. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveUDIDSet(tSMBus *psSMBus, tSMBusUDID *pUDID) +{ + psSMBus->pUDID = pUDID; +} + +//***************************************************************************** +// +//! Initializes an I2C slave peripheral for SMBus functionality. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui32I2CBase specifies the base address of the I2C slave peripheral. +//! +//! This function initializes an I2C peripheral for SMBus slave use. The +//! instance-specific configuration structure is initialized to a set of known +//! values and the I2C peripheral is configured based on the input arguments. +//! +//! The default configuration of the SMBus slave uses automatic +//! acknowledgement. If manual acknowledgement is required, call +//! SMBusSlaveManualACKEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveInit(tSMBus *psSMBus, uint32_t ui32I2CBase) +{ + // + // Initialize the configuration structure. + // + psSMBus->pUDID = 0; + psSMBus->ui32I2CBase = ui32I2CBase; + psSMBus->ui16Flags = 0; + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + psSMBus->ui8SlaveState = SMBUS_STATE_IDLE; + psSMBus->ui8OwnSlaveAddress = 0; + psSMBus->ui8TargetSlaveAddress = 0; + psSMBus->ui8CurrentCommand = 0; + psSMBus->ui8CalculatedCRC = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + + // + // Enable the I2C slave module. The slave is always enabled because the + // SMBus spec requires that all devices respond whne their slave address + // is put on the bus. + // + I2CSlaveEnable(psSMBus->ui32I2CBase); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/smbus.h b/utils/smbus.h new file mode 100644 index 0000000..c483025 --- /dev/null +++ b/utils/smbus.h @@ -0,0 +1,463 @@ +//***************************************************************************** +// +// smbus.h - Prototypes for the SMBus driver. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +#ifndef __SMBUS_H__ +#define __SMBUS_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup smbus_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure holds the SMBus Unique Device ID (UDID). For detailed +//! information, please refer to the SMBus Specification. +// +//***************************************************************************** +typedef struct +{ + // + //! Device capabilities field. This 8-bit field reports generic SMBus + //! capabilities such as address type for ARP. + // + uint8_t ui8DeviceCapabilities; + + // + //! Version Revision field. This 8-bit field reports UDID revision + //! information as well as some vendor-specific things such as silicon + //! revision. + // + uint8_t ui8Version; + + // + //! Vendor ID. This 16-bit field contains the manufacturer's ID as + //! assigned by the SBS Implementers' Forum of the PCI SIG. + // + uint16_t ui16VendorID; + + // + //! Device ID. This 16-bit field contains the device ID assigned by the + //! device manufacturer. + // + uint16_t ui16DeviceID; + + // + //! Interface. This 16-bit field identifies the protocol layer interfaces + //! supported over the SMBus connection. + // + uint16_t ui16Interface; + + // + //! Subsystem Vendor ID. This 16-bit field holds additional information + //! that may be derived from the vendor ID or other information. + // + uint16_t ui16SubSystemVendorID; + + // + //! Subsystem Device ID. This 16-bit field holds additional information + //! that may be derived from the device ID or other information. + // + uint16_t ui16SubSystemDeviceID; + + // + //! Vendor-specific ID. This 32-bit field contains a unique number that + //! can be assigned per device by the manufacturer. + // + uint32_t ui32VendorSpecificID; +} +tSMBusUDID; + +//***************************************************************************** +// +//! This structure contains the state of a single instance of an SMBus module. +//! Master and slave instances require unique configuration structures. +// +//***************************************************************************** +typedef struct +{ + // + //! The SMBus Unique Device ID (UDID) for this SMBus instance. If + //! operating as a host, master-only, or on a bus that does not use Address + //! Resolution Protocol (ARP), this is not required. This member can be + //! set via a direct structure access or using the SMBusSlaveInit + //! function. For detailed information about the UDID, refer to the SMBus + //! spec. + // + tSMBusUDID *pUDID; + + // + //! The base address of the I2C master peripheral. This member can be set + //! via a direct structure access or using the SMBusMasterInit or + //! SMBusSlaveInit functions. + // + uint32_t ui32I2CBase; + + // + //! The address of the data buffer used for transmit operations. For + //! master operations, this member is set by the SMBusMasterxxxx functions + //! that pass a buffer pointer (for example, SMBusMasterBlockWrite). For + //! slave operations, this member can be set via direct structure access or + //! using the SMBusSlaveTxBufferSet function. + // + uint8_t *pui8TxBuffer; + + // + //! The address of the data buffer used for receive operations. For master + //! operations, this member is set by the SMBusMasterxxxx functions that + //! pass a buffer pointer (for example, SMBusMasterBlockRead). For slave + //! operations, this member can be set via direct structure access or using + //! the SMBusSlaveRxBufferSet function. + // + uint8_t *pui8RxBuffer; + + // + //! The amount of data to transmit from pui8TxBuffer. For master + //! operations this member is set by the SMBusMasterxxxx functions either + //! via an input argument (example SMBusMasterByteWordWrite) or explicitly + //! (example SMBusMasterSendByte). In master mode, this member should not + //! be accessed or modified by the application. For slave operations, this + //! member can be set via direct structure access of using the + //! SMBusSlaveTxBufferSet function. + // + uint8_t ui8TxSize; + + // + //! The current index in the transmit buffer. This member should not be + //! accessed or modified by the application. + // + uint8_t ui8TxIndex; + + // + //! The amount of data to receive into pui8RxBuffer. For master + //! operations, this member is set by the SMBusMasterxxxx functions either + //! via an input argument (example SMBusMasterByteWordRead), explicitly + //! (example SMBusMasterReceiveByte), or by the slave (example + //! SMBusMasterBlockRead). In master mode, this member should not be + //! accessed or modified by the application. For slave operations, this + //! member can be set via direct structure access of using the + //! SMBusSlaveRxBufferSet function. + // + uint8_t ui8RxSize; + + // + //! The current index in the receive buffer. This member should not be + //! accessed or modified by the application. + // + uint8_t ui8RxIndex; + + // + //! The active slave address of the I2C peripheral on the device. + //! When using dual address in slave mode, the active address is store + //! here. In master mode, this member is not used. This member is updated + //! as requests come in from the master. + // + uint8_t ui8OwnSlaveAddress; + + // + //! The address of the targeted slave device. In master mode, this member + //! is set by the ui8TargetSlaveAddress argument in the SMBusMasterxxxx + //! transfer functions. In slave mode, it is not used. This member should + //! not be modified by the application. + // + uint8_t ui8TargetSlaveAddress; + + // + //! The last used command. In master mode, this member is set by the + //! ui8Command argument in the SMBusMasterxxxx transfer functions. In + //! slave mode, the first received byte will always be considered the + //! command. This member should not be modified by the application. + // + uint8_t ui8CurrentCommand; + + // + //! The running CRC calculation used for transfers that require Packet + //! Error Checking (PEC). This member is updated by the SMBus software and + //! should not be modified by the application. + // + uint8_t ui8CalculatedCRC; + + // + //! The received CRC calculation used for transfers that require Packet + //! Error Checking (PEC). This member is updated by the SMBus software and + //! should not be modified by the application. + // + uint8_t ui8ReceivedCRC; + + // + //! The current state of the SMBusMasterISRProcess state machine. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8MasterState; + + // + //! The current state of the SMBusSlaveISRProcess state machine. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8SlaveState; + + // + //! Flags used for various items in the SMBus state machines for different + //! transaction types and status. + //! + //! FLAG_PEC can be modified via the SMBusPECEnable or SMBusPECDisable + //! functions or via direct structure access. + //! + //! FLAG_BLOCK_TRANSFER can be set via the SMBusSlaveBlockTransferEnable + //! function and is cleared automatically by the SMBusSlaveTransferInit + //! function or manually using the SMBusSlaveBlockTransferDisable function. + //! + //! FLAG_RAW_I2C can be modified via the SMBusSlaveI2CEnable or + //! SMBusSlaveI2CDisable functions or via direct structure access. + //! + //! FLAG_TRANSFER_IN_PROGRESS should not be modified by the application, + //! but can be read via the SMBusStatusGet function. + //! + //! FLAG_PROCESS_CALL can be set via the SMBusSlaveProcessCallEnable + //! function and is cleared automatically by the SMBusSlaveTransferInit + //! function or manually using the SMBusSlaveProcessCallDisable function. + //! + //! FLAG_ADDRESS_RESOLVED is only used by an SMBus Slave that supports ARP. + //! This flag can be modified via the SMBusSlaveARPFlagARSet function and + //! read via SMBusSlaveARPFlagARGet. It can also be modified by direct + //! structure access. + //! + //! FLAG_ADDRESS_VALID is only used by an SMBus Slave that supports ARP. + //! This flag can be modified via the SMBusSlaveARPFlagAVSet function and + //! read via SMBusSlaveARPFlagAVGet. It can also be modified by direct + //! structure access. + //! + //! FLAG_ARP is used to indicate that ARP is currently active. This flag + //! is not used by the SMBus stack and can (optionally) be used by the + //! application to keep track of the ARP session. + // + uint16_t ui16Flags; +} +tSMBus; + +//***************************************************************************** +// +// ! Return codes. +// +//***************************************************************************** +typedef enum +{ + SMBUS_OK = 0, // General "OK" return code + SMBUS_TIMEOUT, // Master detected bus timeout from slave + SMBUS_PERIPHERAL_BUSY, // The I2C peripheral is currently in use + SMBUS_BUS_BUSY, // The I2C bus is currently in use + SMBUS_ARB_LOST, // Bus arbitration was lost (master mode) + SMBUS_ADDR_ACK_ERROR, // In master mode, the address was NAK'd + SMBUS_DATA_ACK_ERROR, // Data transfer was NAK'd by receiver + SMBUS_PEC_ERROR, // PEC mismatch occurred + SMBUS_DATA_SIZE_ERROR, // Data size error has occurred + SMBUS_MASTER_ERROR, // Error occurred in the master ISR + SMBUS_SLAVE_ERROR, // Error occurred in the slave ISR + SMBUS_SLAVE_QCMD_0, // Slave transaction is Quick Command with + // data value 0. + SMBUS_SLAVE_QCMD_1, // Slave transaction is Quick Command with + // data value 1. + SMBUS_SLAVE_FIRST_BYTE, // The first byte has been received + SMBUS_SLAVE_ADDR_PRIMARY, // Primary address was detected + SMBUS_SLAVE_ADDR_SECONDARY, // Secondary address was detected + SMBUS_TRANSFER_IN_PROGRESS, // A transfer is currently in progress + SMBUS_TRANSFER_COMPLETE, // The last active transfer is complete + SMBUS_SLAVE_NOT_READY, // A slave transmit has been requested, but is + // not ready (TX buffer not set). + SMBUS_FIFO_ERROR, // A master receive operation did not receive + // enough data from the slave. +} +tSMBusStatus; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// ARP Commands +// +//***************************************************************************** +#define SMBUS_CMD_PREPARE_TO_ARP 0x01 +#define SMBUS_CMD_ARP_RESET_DEVICE 0x02 +#define SMBUS_CMD_ARP_GET_UDID 0x03 +#define SMBUS_CMD_ARP_ASSIGN_ADDRESS 0x04 + +//***************************************************************************** +// +// Fixed addresses defined by the SMBus specification. +// +//***************************************************************************** +#define SMBUS_ADR_HOST 0x08 +#define SMBUS_ADR_SMART_BATTERY_CHARGER 0x09 +#define SMBUS_ADR_SMART_BATTERY_SELECTOR 0x0A +#define SMBUS_ADR_SMART_BATTERY 0x0B +#define SMBUS_ADR_DEFAULT_DEVICE 0x61 + +//***************************************************************************** +// +// API Function prototypes +// +//***************************************************************************** +extern void SMBusPECEnable(tSMBus *psSMBus); +extern void SMBusPECDisable(tSMBus *psSMBus); +extern void SMBusARPEnable(tSMBus *psSMBus); +extern void SMBusARPDisable(tSMBus *psSMBus); +extern tSMBusStatus SMBusStatusGet(tSMBus *psSMBus); +extern void SMBusARPUDIDPacketEncode(tSMBusUDID *pUDID, + uint8_t ui8Address, + uint8_t *pui8Data); +extern void SMBusARPUDIDPacketDecode(tSMBusUDID *pUDID, + uint8_t *pui8Address, + uint8_t *pui8Data); +extern uint8_t SMBusRxPacketSizeGet(tSMBus *psSMBus); +extern void SMBusUDIDDataGet(tSMBus *psSMBus, tSMBusUDID *pUDID); +extern tSMBusStatus SMBusMasterQuickCommand(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + bool bData); +extern tSMBusStatus SMBusMasterByteSend(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Data); +extern tSMBusStatus SMBusMasterByteReceive(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterByteWordWrite(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterBlockWrite(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterByteWordRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterBlockRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterProcessCall(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8TxData, + uint8_t *pui8RxData); +extern tSMBusStatus SMBusMasterBlockProcessCall(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8TxData, + uint8_t ui8TxSize, + uint8_t *pui8RxData); +extern tSMBusStatus SMBusMasterHostNotify(tSMBus *psSMBus, + uint8_t ui8OwnSlaveAddress, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterI2CWrite(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterI2CRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterI2CWriteRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8TxData, + uint8_t ui8TxSize, + uint8_t *pui8RxData, + uint8_t ui8RxSize); +extern tSMBusStatus SMBusMasterARPGetUDIDGen(tSMBus *psSMBus, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPGetUDIDDir(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPResetDeviceGen(tSMBus *psSMBus); +extern tSMBusStatus SMBusMasterARPResetDeviceDir(tSMBus *psSMBus, + uint8_t ui8TargetAddress); +extern tSMBusStatus SMBusMasterARPAssignAddress(tSMBus *psSMBus, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPNotifyMaster(tSMBus *psSMBus, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPPrepareToARP(tSMBus *psSMBus); +extern tSMBusStatus SMBusMasterIntProcess(tSMBus *psSMBus); +extern void SMBusMasterIntEnable(tSMBus *psSMBus); +extern void SMBusMasterInit(tSMBus *psSMBus, uint32_t ui32I2CBase, + uint32_t ui32SMBusClock); +extern void SMBusSlaveTxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size); +extern void SMBusSlaveRxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size); +extern uint8_t SMBusSlaveCommandGet(tSMBus *psSMBus); +extern void SMBusSlaveProcessCallEnable(tSMBus *psSMBus); +extern void SMBusSlaveProcessCallDisable(tSMBus *psSMBus); +extern void SMBusSlaveBlockTransferEnable(tSMBus *psSMBus); +extern void SMBusSlaveBlockTransferDisable(tSMBus *psSMBus); +extern void SMBusSlaveI2CEnable(tSMBus *psSMBus); +extern void SMBusSlaveI2CDisable(tSMBus *psSMBus); +extern void SMBusSlaveARPFlagARSet(tSMBus *psSMBus, bool bValue); +extern bool SMBusSlaveARPFlagARGet(tSMBus *psSMBus); +extern void SMBusSlaveARPFlagAVSet(tSMBus *psSMBus, bool bValue); +extern bool SMBusSlaveARPFlagAVGet(tSMBus *psSMBus); +extern void SMBusSlaveTransferInit(tSMBus *psSMBus); +extern tSMBusStatus SMBusSlaveIntProcess(tSMBus *psSMBus); +extern tSMBusStatus SMBusSlaveDataSend(tSMBus *psSMBus); +extern void SMBusSlaveACKSend(tSMBus *psSMBus, bool bACK); +extern void SMBusSlaveManualACKEnable(tSMBus *psSMBus); +extern void SMBusSlaveManualACKDisable(tSMBus *psSMBus); +extern bool SMBusSlaveManualACKStatusGet(tSMBus *psSMBus); +extern tSMBusStatus SMBusSlaveIntAddressGet(tSMBus *psSMBus); +extern void SMBusSlaveIntEnable(tSMBus *psSMBus); +extern void SMBusSlaveUDIDSet(tSMBus *psSMBus, tSMBusUDID *pUDID); +extern void SMBusSlaveAddressSet(tSMBus *psSMBus, uint8_t ui8AddressNum, + uint8_t ui8SlaveAddress); +extern void SMBusSlaveInit(tSMBus *psSMBus, uint32_t ui32I2CBase); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SMBUS_H__ diff --git a/utils/softi2c.c b/utils/softi2c.c new file mode 100644 index 0000000..ab3e5a6 --- /dev/null +++ b/utils/softi2c.c @@ -0,0 +1,1321 @@ +//***************************************************************************** +// +// softi2c.c - Driver for the SoftI2C. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softi2c_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "utils/softi2c.h" + +//***************************************************************************** +// +// The states in the SoftI2C state machine. The code depends upon the fact +// that the value of STATE_X1 is exactly one greater than STATE_X0 (for any +// value of X and for any following digit)...however there is no dependence on +// the values of STATE_Xn and STATE_Yn. +// +//***************************************************************************** +#define SOFTI2C_STATE_IDLE 0 +#define SOFTI2C_STATE_START0 1 +#define SOFTI2C_STATE_START1 2 +#define SOFTI2C_STATE_START2 3 +#define SOFTI2C_STATE_START3 4 +#define SOFTI2C_STATE_START4 5 +#define SOFTI2C_STATE_START5 6 +#define SOFTI2C_STATE_START6 7 +#define SOFTI2C_STATE_START7 8 +#define SOFTI2C_STATE_ADDR0 9 +#define SOFTI2C_STATE_ADDR1 10 +#define SOFTI2C_STATE_ADDR2 11 +#define SOFTI2C_STATE_ADDR3 12 +#define SOFTI2C_STATE_SEND0 13 +#define SOFTI2C_STATE_SEND1 14 +#define SOFTI2C_STATE_SEND2 15 +#define SOFTI2C_STATE_SEND3 16 +#define SOFTI2C_STATE_RECV0 17 +#define SOFTI2C_STATE_RECV1 18 +#define SOFTI2C_STATE_RECV2 19 +#define SOFTI2C_STATE_RECV3 20 +#define SOFTI2C_STATE_STOP0 21 +#define SOFTI2C_STATE_STOP1 22 +#define SOFTI2C_STATE_STOP2 23 +#define SOFTI2C_STATE_STOP3 24 +#define SOFTI2C_STATE_STOP4 25 + +//***************************************************************************** +// +// The flags in the SoftI2C ui8Flags structure member. The first four flags, +// RUN, START, STOP, and ACK, must match with the definitions of the +// SOFTI2C_CMD_* commands in softi2c.h. +// +//***************************************************************************** +#define SOFTI2C_FLAG_RUN 0 +#define SOFTI2C_FLAG_START 1 +#define SOFTI2C_FLAG_STOP 2 +#define SOFTI2C_FLAG_ACK 3 +#define SOFTI2C_FLAG_ADDR_ACK 5 +#define SOFTI2C_FLAG_DATA_ACK 6 +#define SOFTI2C_FLAG_RECEIVE 7 + +//***************************************************************************** +// +//! Performs the periodic update of the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function performs the periodic, time-based updates to the SoftI2C +//! module. The transmission and reception of data over the SoftI2C link is +//! performed by the state machine in this function. +//! +//! This function must be called at four times the desired SoftI2C clock rate. +//! For example, to run the SoftI2C clock at 10 KHz, this function must be +//! called at a 40 KHz rate. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CTimerTick(tSoftI2C *psI2C) +{ + // + // Determine the current state of the state machine. + // + switch(psI2C->ui8State) + { + // + // The state machine is idle. + // + case SOFTI2C_STATE_IDLE: + { + // + // See if the START flag is set, indicating that a start condition + // should be generated. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_START) == 1) + { + // + // Based on the current state of the SCL and SDA pins, pick the + // appropriate place within the state machine to begin the + // start/repeated-start signalling. + // + if(HWREG(psI2C->ui32SCLGPIO) != 0) + { + psI2C->ui8State = SOFTI2C_STATE_START4; + } + else if(HWREG(psI2C->ui32SDAGPIO) == 0) + { + psI2C->ui8State = SOFTI2C_STATE_START0; + } + else + { + psI2C->ui8State = SOFTI2C_STATE_START2; + } + } + + // + // Otherwise, see if the RUN flag is set, indicating that a data + // byte should be transferred. + // + else if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Start the transfer from the first bit. + // + psI2C->ui8CurrentBit = 0; + + // + // See if a byte should be sent or received. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // A byte should be sent. + // + psI2C->ui8State = SOFTI2C_STATE_SEND0; + } + else + { + // + // A byte should be received. Clear out the receive data + // buffer in preparation for receiving the new byte. + // + psI2C->ui8Data = 0; + psI2C->ui8State = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a stop + // condition should be generated. + // + else if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Generate a stop condition. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // See if the SoftI2C state machine has left the idle state. + // + if(psI2C->ui8State != SOFTI2C_STATE_IDLE) + { + // + // The address and data ACK error flags should be cleared; they + // will be set if appropriate while the current command is + // being executed. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ADDR_ACK) = 0; + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_DATA_ACK) = 0; + } + + // + // This state has been handled. + // + break; + } + + // + // The beginning of the start condition sequence when SDA and SCL are + // low. SDA must be driven high prior to driving SCL high so that a + // repeated-start is generated, instead of a stop then start. + // + case SOFTI2C_STATE_START0: + { + // + // Set SDA high. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_START1; + + // + // This state has been handled. + // + break; + } + + // + // Each of these states exists only to provide some timing delay in + // order to conform with the signalling requirements of I2C. This + // depends upon STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START1: + case SOFTI2C_STATE_START3: + case SOFTI2C_STATE_START5: + case SOFTI2C_STATE_STOP1: + case SOFTI2C_STATE_STOP3: + { + // + // Advance to the next state. + // + psI2C->ui8State++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, SCL must be driven high. This depends upon + // STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START2: + case SOFTI2C_STATE_ADDR1: + case SOFTI2C_STATE_SEND1: + case SOFTI2C_STATE_RECV1: + case SOFTI2C_STATE_STOP2: + { + // + // Set SCL high. + // + HWREG(psI2C->ui32SCLGPIO) = 255; + + // + // Advance to the next state. + // + psI2C->ui8State++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, SDA must be driven low. This depends upon + // STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START4: + case SOFTI2C_STATE_STOP0: + { + // + // Set SDA low. + // + HWREG(psI2C->ui32SDAGPIO) = 0; + + // + // Advance to the next state. + // + psI2C->ui8State++; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. + // + case SOFTI2C_STATE_START6: + { + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_START7; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the start condition has been generated. + // + case SOFTI2C_STATE_START7: + { + // + // Start with the first bit of the address. + // + psI2C->ui8CurrentBit = 0; + + // + // Advance to the address output state. + // + psI2C->ui8State = SOFTI2C_STATE_ADDR0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the slave address must be sent. + // + case SOFTI2C_STATE_ADDR0: + { + // + // See if this is one of the first seven bits of the address phase. + // + if(psI2C->ui8CurrentBit < 7) + { + // + // Write the next bit of the slave address to SDA. + // + HWREG(psI2C->ui32SDAGPIO) = + ((psI2C->ui8SlaveAddr & + (1 << (6 - psI2C->ui8CurrentBit))) ? 255 : 0); + } + + // + // Otherwise, see if this is the eight bit of the address phase + // (which is the read/not write bit). + // + else if(psI2C->ui8CurrentBit == 7) + { + // + // Write the read/not write bit to SDA. + // + HWREG(psI2C->ui32SDAGPIO) = + (HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) ? + 255 : 0); + } + + // + // Otherwise, this is the ninth bit of the address phase (in other + // words, the ACK bit). + // + else + { + // + // Change the SDA GPIO into an input so that the ACK or NAK + // provided by the slave can be read. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_ADDR1; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, wait until SCL has gone high (it has been + // released by the SoftI2C master, but may be held low by the slave). + // This depends upon STATE_Xn and STATE_X(n+1) being consecutively + // numbered. + // + case SOFTI2C_STATE_ADDR2: + case SOFTI2C_STATE_SEND2: + case SOFTI2C_STATE_RECV2: + { + // + // See if SCL has gone high. + // + if(HWREG(psI2C->ui32SCLGPIO) != 0) + { + // + // Advance to the next state now that SCL has gone high. + // + psI2C->ui8State++; + } + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. If on the ninth bit of the + // address transfer, the ACK/NAK status is read from the slave. + // + case SOFTI2C_STATE_ADDR3: + { + // + // See if this is the ninth bit of the address phase (in other + // words, the ACK bit). + // + if(psI2C->ui8CurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(psI2C->ui32SDAGPIO) != 0) + { + // + // Since the SDA line is high, the address byte has not + // been ACKed by any slave. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ADDR_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // The start phase (start or repeated-start, plus the address + // byte) have completed, so clear the START flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_START) = 0; + + // + // See if the RUN flag is set, indicating that a data byte + // should be transferred as well. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Reset the current bit to zero for the start of the data + // phase. + // + psI2C->ui8CurrentBit = 0; + + // + // See if the data byte is being sent or received. + // + if(HWREGBITB(&(psI2C->ui8Flags), + SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // The data byte is being sent, so advance to the data + // send state. + // + psI2C->ui8State = SOFTI2C_STATE_SEND0; + } + else + { + // + // The data byte is being received, so clear the data + // buffer and advance to the data receive state. + // + psI2C->ui8Data = 0; + psI2C->ui8State = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a + // stop condition should be generated. + // + else if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the address should be transferred. + // + else + { + // + // Increment the bit count. + // + psI2C->ui8CurrentBit++; + + // + // Advance to the address tranfer state. + // + psI2C->ui8State = SOFTI2C_STATE_ADDR0; + } + + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the data byte must be sent. + // + case SOFTI2C_STATE_SEND0: + { + // + // See if this is one of the first eight bits of the data phase. + // + if(psI2C->ui8CurrentBit < 8) + { + // + // Write the next bit of the data byte to SDA. + // + HWREG(psI2C->ui32SDAGPIO) = + ((psI2C->ui8Data & + (1 << (7 - psI2C->ui8CurrentBit))) ? 255 : 0); + } + + // + // Otherwise, this is the ninth bit of the data phase (in other + // words, the ACK bit). + // + else + { + // + // Change the SDA GPIO into an input so that the ACK or NAK + // provided by the slave can be read. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_SEND1; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. If on the ninth bit of the + // data transfer, the ACK/NAK status is read from the slave. + // + case SOFTI2C_STATE_SEND3: + { + // + // See if this is the ninth bit of the data phase (in other words, + // the ACK bit). + // + if(psI2C->ui8CurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(psI2C->ui32SDAGPIO) != 0) + { + // + // Since the SDA line is high, the data byte has not been + // ACKed by the slave. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_DATA_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Increment the bit count. + // + psI2C->ui8CurrentBit++; + + // + // Advance to the data transmit state. + // + psI2C->ui8State = SOFTI2C_STATE_SEND0; + } + + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the data byte must be received. + // + case SOFTI2C_STATE_RECV0: + { + // + // See if this is the first bit of the data phase. + // + if(psI2C->ui8CurrentBit == 0) + { + // + // Change the SDA GPIO into an input so that the data provided + // by the slave can be read. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Otherwise, see if this is the ninth bit of the data phase (in + // other words, the ACK bit). + // + else if(psI2C->ui8CurrentBit == 8) + { + // + // Change the SDA GPIO into an output so that the ACK bit can + // be driven to the slave. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // See if this byte should be ACKed or NAKed. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ACK) == 1) + { + // + // Drive SDA low to ACK the data byte. + // + HWREG(psI2C->ui32SDAGPIO) = 0; + } + else + { + // + // Allow SDA to get pulled high to NAK the data byte. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + } + } + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_RECV1; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. For the first eight bits of + // the data transfer, the data bits are read from the slave. + // + case SOFTI2C_STATE_RECV3: + { + // + // See if this is the ninth bit of the data phase (in other words, + // the ACK bit). + // + if(psI2C->ui8CurrentBit == 8) + { + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Read the next bit of data from the SDA line. + // + psI2C->ui8Data |= (HWREG(psI2C->ui32SDAGPIO) ? + (1 << (7 - psI2C->ui8CurrentBit)) : 0); + + // + // Increment the bit count. + // + psI2C->ui8CurrentBit++; + + // + // Advance to the data receive state. + // + psI2C->ui8State = SOFTI2C_STATE_RECV0; + } + + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SDA must be driven high to create the stop condition. + // + case SOFTI2C_STATE_STOP4: + { + // + // Set SDA high to create the stop condition. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + + // + // The stop condition has been generated, so clear the STOP flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) = 0; + + // + // Since the requested operations have completed, set the SoftI2C + // ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the I2C peripheral. + // + while(((psI2C->ui8IntStatus & psI2C->ui8IntMask) != 0) && + (psI2C->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psI2C->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Initializes the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function initializes operation of the SoftI2C module. After +//! successful initialization of the SoftI2C module, the software I2C bus is in +//! the idle state. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CInit(tSoftI2C *psI2C) +{ + // + // Configure the SCL pin. + // + MAP_GPIODirModeSet(psI2C->ui32SCLGPIO & 0xfffff000, + (psI2C->ui32SCLGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(psI2C->ui32SCLGPIO & 0xfffff000, + (psI2C->ui32SCLGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SCL pin high. + // + HWREG(psI2C->ui32SCLGPIO) = 255; + + // + // Configure the SDA pin. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SDA pin high. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + + // + // The ``interrupt'' is not asserted at the start. + // + psI2C->ui8IntStatus = 0; + + // + // There are no flags at the start. + // + psI2C->ui8Flags = 0; + + // + // Start the SoftI2C state machine in the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param pfnCallback is a pointer to the callback function. +//! +//! This function sets the address of the callback function that is called when +//! there is an ``interrupt'' produced by the SoftI2C module. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CCallbackSet(tSoftI2C *psI2C, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + psI2C->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SCL signal. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftI2C SCL signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSCLGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the SCL signal. + // + psI2C->ui32SCLGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SDA signal. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftI2C SDA signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSDAGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the SDA signal. + // + psI2C->ui32SDAGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Enables the SoftI2C ``interrupt''. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! Enables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntEnable(tSoftI2C *psI2C) +{ + // + // Enable the master interrupt. + // + psI2C->ui8IntMask = 1; +} + +//***************************************************************************** +// +//! Disables the SoftI2C ``interrupt''. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! Disables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntDisable(tSoftI2C *psI2C) +{ + // + // Disable the master interrupt. + // + psI2C->ui8IntMask = 0; +} + +//***************************************************************************** +// +//! Gets the current SoftI2C ``interrupt'' status. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param bMasked is \b false if the raw ``interrupt'' status is requested and +//! \b true if the masked ``interrupt'' status is requested. +//! +//! This returns the ``interrupt'' status for the SoftI2C module. Either the +//! raw ``interrupt'' status or the status of ``interrupts'' that are allowed +//! to reflect to the processor can be returned. +//! +//! \return The current interrupt status, returned as \b true if active +//! or \b false if not active. +// +//***************************************************************************** +bool +SoftI2CIntStatus(tSoftI2C *psI2C, bool bMasked) +{ + // + // Return either the interrupt status or the raw interrupt status as + // requested. + // + if(bMasked) + { + return((psI2C->ui8IntStatus & psI2C->ui8IntMask) ? true : false); + } + else + { + return(psI2C->ui8IntStatus ? true : false); + } +} + +//***************************************************************************** +// +//! Clears the SoftI2C ``interrupt''. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! The SoftI2C ``interrupt'' source is cleared, so that it no longer asserts. +//! This function must be called in the ``interrupt'' handler to keep it from +//! being called again immediately on exit. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntClear(tSoftI2C *psI2C) +{ + // + // Clear the SoftI2C interrupt source. + // + psI2C->ui8IntStatus = 0; +} + +//***************************************************************************** +// +//! Sets the address that the SoftI2C module places on the bus. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui8SlaveAddr 7-bit slave address +//! \param bReceive flag indicating the type of communication with the slave. +//! +//! This function sets the address that the SoftI2C module places on the bus +//! when initiating a transaction. When the \e bReceive parameter is set to +//! \b true, the address indicates that the SoftI2C moudle is initiating a read +//! from the slave; otherwise the address indicates that the SoftI2C module is +//! initiating a write to the slave. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSlaveAddrSet(tSoftI2C *psI2C, uint8_t ui8SlaveAddr, + bool bReceive) +{ + // + // Check the arguments. + // + ASSERT(!(ui8SlaveAddr & 0x80)); + + // + // Set the address of the slave with which the master will communicate. + // + psI2C->ui8SlaveAddr = ui8SlaveAddr; + + // + // Set a flag to indicate if this is a transmit or receive. + // + if(bReceive) + { + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) = 1; + } + else + { + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) = 0; + } +} + +//***************************************************************************** +// +//! Indicates whether or not the SoftI2C module is busy. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function returns an indication of whether or not the SoftI2C module is +//! busy transmitting or receiving data. +//! +//! \return Returns \b true if the SoftI2C module is busy; otherwise, returns +//! \b false. +// +//***************************************************************************** +bool +SoftI2CBusy(tSoftI2C *psI2C) +{ + // + // Return the busy status. + // + if(psI2C->ui8State != SOFTI2C_STATE_IDLE) + { + return(true); + } + else + { + return(false); + } +} + +//***************************************************************************** +// +//! Controls the state of the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui32Cmd command to be issued to the SoftI2C module. +//! +//! This function is used to control the state of the SoftI2C module send and +//! receive operations. The \e ui8Cmd parameter can be one of the following +//! values: +//! +//! - \b SOFTI2C_CMD_SINGLE_SEND +//! - \b SOFTI2C_CMD_SINGLE_RECEIVE +//! - \b SOFTI2C_CMD_BURST_SEND_START +//! - \b SOFTI2C_CMD_BURST_SEND_CONT +//! - \b SOFTI2C_CMD_BURST_SEND_FINISH +//! - \b SOFTI2C_CMD_BURST_SEND_ERROR_STOP +//! - \b SOFTI2C_CMD_BURST_RECEIVE_START +//! - \b SOFTI2C_CMD_BURST_RECEIVE_CONT +//! - \b SOFTI2C_CMD_BURST_RECEIVE_FINISH +//! - \b SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CControl(tSoftI2C *psI2C, uint32_t ui32Cmd) +{ + // + // Check the arguments. + // + ASSERT((ui32Cmd == SOFTI2C_CMD_SINGLE_SEND) || + (ui32Cmd == SOFTI2C_CMD_SINGLE_RECEIVE) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_START) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_CONT) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_FINISH) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_ERROR_STOP) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_START) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_CONT) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_FINISH) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP)); + + // + // Send the command. + // + psI2C->ui8Flags = (psI2C->ui8Flags & 0xf0) | ui32Cmd; +} + +//***************************************************************************** +// +//! Gets the error status of the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function is used to obtain the error status of the SoftI2C module send +//! and receive operations. +//! +//! \return Returns the error status, as one of \b SOFTI2C_ERR_NONE, +//! \b SOFTI2C_ERR_ADDR_ACK, or \b SOFTI2C_ERR_DATA_ACK. +// +//***************************************************************************** +uint32_t +SoftI2CErr(tSoftI2C *psI2C) +{ + // + // If the SoftI2C is busy, there is no error to report. + // + if(psI2C->ui8State != SOFTI2C_STATE_IDLE) + { + return(SOFTI2C_ERR_NONE); + } + + // + // Return any errors that may have occurred. + // + return((HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ADDR_ACK) ? + SOFTI2C_ERR_ADDR_ACK : 0) | + (HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_DATA_ACK) ? + SOFTI2C_ERR_DATA_ACK : 0)); +} + +//***************************************************************************** +// +//! Transmits a byte from the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui8Data data to be transmitted from the SoftI2C module. +//! +//! This function places the supplied data into SoftI2C module in preparation +//! for being transmitted via an appropriate call to SoftI2CControl(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CDataPut(tSoftI2C *psI2C, uint8_t ui8Data) +{ + // + // Write the byte. + // + psI2C->ui8Data = ui8Data; +} + +//***************************************************************************** +// +//! Receives a byte that has been sent to the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function reads a byte of data from the SoftI2C module that was +//! received as a result of an appropriate call to SoftI2CControl(). +//! +//! \return Returns the byte received by the SoftI2C module, cast as an +//! uint32_t. +// +//***************************************************************************** +uint32_t +SoftI2CDataGet(tSoftI2C *psI2C) +{ + // + // Read a byte. + // + return(psI2C->ui8Data); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softi2c.h b/utils/softi2c.h new file mode 100644 index 0000000..0ac876a --- /dev/null +++ b/utils/softi2c.h @@ -0,0 +1,195 @@ +//***************************************************************************** +// +// softi2c.h - Defines and macros for the SoftI2C. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +#ifndef __SOFTI2C_H__ +#define __SOFTI2C_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup softi2c_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure contains the state of a single instance of a SoftI2C module. +// +//***************************************************************************** +typedef struct +{ + // + //! The address of the callback function that is called to simulate the + //! interrupts that would be produced by a hardware I2C implementation. + //! This address can be set via a direct structure access or using the + //! SoftI2CCallbackSet function. + // + void (*pfnIntCallback)(void); + + // + //! The address of the GPIO pin to be used for the SCL signal. This member + //! can be set via a direct structure access or using the SoftI2CSCLGPIOSet + //! function. + // + uint32_t ui32SCLGPIO; + + // + //! The address of the GPIO pin to be used for the SDA signal. This member + //! can be set via a direct structure access or using the SoftI2CSDAGPIOSet + //! function. + /// + uint32_t ui32SDAGPIO; + + // + //! The flags that control the operation of the SoftI2C module. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8Flags; + + // + //! The slave address that is currently being accessed. This member should + //! not be accessed or modified by the application. + // + uint8_t ui8SlaveAddr; + + // + //! The data that is currently being transmitted or received. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8Data; + + // + //! The current state of the SoftI2C state machine. This member should not + //! be accessed or modified by the application. + // + uint8_t ui8State; + + // + //! The number of bits that have been transmitted and received in the + //! current frame. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8CurrentBit; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8IntMask; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8IntStatus; +} +tSoftI2C; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// SoftI2C commands. +// +//***************************************************************************** +#define SOFTI2C_CMD_SINGLE_SEND 0x00000007 +#define SOFTI2C_CMD_SINGLE_RECEIVE \ + 0x00000007 +#define SOFTI2C_CMD_BURST_SEND_START \ + 0x00000003 +#define SOFTI2C_CMD_BURST_SEND_CONT \ + 0x00000001 +#define SOFTI2C_CMD_BURST_SEND_FINISH \ + 0x00000005 +#define SOFTI2C_CMD_BURST_SEND_ERROR_STOP \ + 0x00000004 +#define SOFTI2C_CMD_BURST_RECEIVE_START \ + 0x0000000b +#define SOFTI2C_CMD_BURST_RECEIVE_CONT \ + 0x00000009 +#define SOFTI2C_CMD_BURST_RECEIVE_FINISH \ + 0x00000005 +#define SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP \ + 0x00000004 + +//***************************************************************************** +// +// SoftI2C error status. +// +//***************************************************************************** +#define SOFTI2C_ERR_NONE 0x00000000 +#define SOFTI2C_ERR_ADDR_ACK 0x00000004 +#define SOFTI2C_ERR_DATA_ACK 0x00000008 + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern bool SoftI2CBusy(tSoftI2C *psI2C); +extern void SoftI2CCallbackSet(tSoftI2C *psI2C, void (*pfnCallback)(void)); +extern void SoftI2CControl(tSoftI2C *psI2C, uint32_t ui32Cmd); +extern uint32_t SoftI2CDataGet(tSoftI2C *psI2C); +extern void SoftI2CDataPut(tSoftI2C *psI2C, uint8_t ui8Data); +extern uint32_t SoftI2CErr(tSoftI2C *psI2C); +extern void SoftI2CInit(tSoftI2C *psI2C); +extern void SoftI2CIntClear(tSoftI2C *psI2C); +extern void SoftI2CIntDisable(tSoftI2C *psI2C); +extern void SoftI2CIntEnable(tSoftI2C *psI2C); +extern bool SoftI2CIntStatus(tSoftI2C *psI2C, bool bMasked); +extern void SoftI2CSCLGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftI2CSDAGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftI2CSlaveAddrSet(tSoftI2C *psI2C, uint8_t ui8SlaveAddr, + bool bReceive); +extern void SoftI2CTimerTick(tSoftI2C *psI2C); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SOFTI2C_H__ diff --git a/utils/softssi.c b/utils/softssi.c new file mode 100644 index 0000000..d2a155c --- /dev/null +++ b/utils/softssi.c @@ -0,0 +1,1297 @@ +//***************************************************************************** +// +// softssi.c - Driver for the SoftSSI. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softssi_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "utils/softssi.h" + +//***************************************************************************** +// +// The states in the SoftSSI state machine. +// +//***************************************************************************** +#define SOFTSSI_STATE_IDLE 0 +#define SOFTSSI_STATE_START 1 +#define SOFTSSI_STATE_IN 2 +#define SOFTSSI_STATE_OUT 3 +#define SOFTSSI_STATE_STOP1 4 +#define SOFTSSI_STATE_STOP2 5 + +//***************************************************************************** +// +// The flags in the SoftSSI ui8Flags structure member. +// +//***************************************************************************** +#define SOFTSSI_FLAG_ENABLE 0x80 +#define SOFTSSI_FLAG_SPH 0x02 +#define SOFTSSI_FLAG_SPO 0x01 + +//***************************************************************************** +// +//! Sets the configuration of a SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui8Protocol specifes the data transfer protocol. +//! \param ui8Bits specifies the number of bits transferred per frame. +//! +//! This function configures the data format of a SoftSSI module. The +//! \e ui8Protocol parameter can be one of the following values: +//! \b SOFTSSI_FRF_MOTO_MODE_0, \b SOFTSSI_FRF_MOTO_MODE_1, +//! \b SOFTSSI_FRF_MOTO_MODE_2, or \b SOFTSSI_FRF_MOTO_MODE_3. These frame +//! formats imply the following polarity and phase configurations: +//! +//!
+//! Polarity Phase         Mode
+//!   0       0   SOFTSSI_FRF_MOTO_MODE_0
+//!   0       1   SOFTSSI_FRF_MOTO_MODE_1
+//!   1       0   SOFTSSI_FRF_MOTO_MODE_2
+//!   1       1   SOFTSSI_FRF_MOTO_MODE_3
+//! 
+//! +//! The \e ui8Bits parameter defines the width of the data transfers, and can +//! be a value between 4 and 16, inclusive. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIConfigSet(tSoftSSI *psSSI, uint8_t ui8Protocol, + uint8_t ui8Bits) +{ + // + // See if a GPIO pin has been set for Fss. + // + if(psSSI->ui32FssGPIO != 0) + { + // + // Configure the Fss pin. + // + MAP_GPIOPinTypeGPIOOutput(psSSI->ui32FssGPIO & 0xfffff000, + (psSSI->ui32FssGPIO & 0x00000fff) >> 2); + + // + // Set the Fss pin high. + // + HWREG(psSSI->ui32FssGPIO) = 255; + } + + // + // Configure the Clk pin. + // + MAP_GPIOPinTypeGPIOOutput(psSSI->ui32ClkGPIO & 0xfffff000, + (psSSI->ui32ClkGPIO & 0x00000fff) >> 2); + + // + // Set the Clk pin high or low based on the configured clock polarity. + // + if((ui8Protocol & SOFTSSI_FLAG_SPO) == 0) + { + HWREG(psSSI->ui32ClkGPIO) = 0; + } + else + { + HWREG(psSSI->ui32ClkGPIO) = 255; + } + + // + // Configure the Tx pin and set it low. + // + MAP_GPIOPinTypeGPIOOutput(psSSI->ui32TxGPIO & 0xfffff000, + (psSSI->ui32TxGPIO & 0x00000fff) >> 2); + HWREG(psSSI->ui32TxGPIO) = 0; + + // + // See if a GPIO pin has been set for Rx. + // + if(psSSI->ui32RxGPIO != 0) + { + // + // Configure the Rx pin. + // + MAP_GPIOPinTypeGPIOInput(psSSI->ui32RxGPIO & 0xfffff000, + (psSSI->ui32RxGPIO & 0x00000fff) >> 2); + } + + // + // Make sure that the transmit and receive FIFOs are empty. + // + psSSI->ui16TxBufferRead = 0; + psSSI->ui16TxBufferWrite = 0; + psSSI->ui16RxBufferRead = 0; + psSSI->ui16RxBufferWrite = 0; + + // + // Save the frame protocol. + // + psSSI->ui8Flags = ui8Protocol; + + // + // Save the number of data bits. + // + psSSI->ui8Bits = ui8Bits; + + // + // Since the FIFOs are empty, the transmit FIFO "interrupt" is asserted. + // + psSSI->ui8IntStatus = SOFTSSI_TXFF; + + // + // Reset the idle counter. + // + psSSI->ui8IdleCount = 0; + + // + // Disable the SoftSSI module. + // + psSSI->ui8Flags &= ~(SOFTSSI_FLAG_ENABLE); + + // + // Start the SoftSSI state machine in the idle state. + // + psSSI->ui8State = SOFTSSI_STATE_IDLE; +} + +//***************************************************************************** +// +//! Handles the assertion/deassertion of the transmit FIFO ``interrupt''. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function is used to determine when to assert or deassert the transmit +//! FIFO ``interrupt''. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftSSITxInt(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the number of words left in the transmit FIFO. + // + if(psSSI->ui16TxBufferRead > psSSI->ui16TxBufferWrite) + { + ui16Temp = (psSSI->ui16TxBufferLen + psSSI->ui16TxBufferWrite - + psSSI->ui16TxBufferRead); + } + else + { + ui16Temp = psSSI->ui16TxBufferWrite - psSSI->ui16TxBufferRead; + } + + // + // If the transmit FIFO is now half full or less, generate a transmit FIFO + // "interrupt". Otherwise, clear the transmit FIFO "interrupt". + // + if(ui16Temp <= (psSSI->ui16TxBufferLen / 2)) + { + psSSI->ui8IntStatus |= SOFTSSI_TXFF; + } + else + { + psSSI->ui8IntStatus &= ~(SOFTSSI_TXFF); + } +} + +//***************************************************************************** +// +//! Handles the assertion/deassertion of the receive FIFO ``interrupt''. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function is used to determine when to assert or deassert the receive +//! FIFO ``interrupt''. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftSSIRxInt(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the number of words in the receive FIFO. + // + if(psSSI->ui16RxBufferRead > psSSI->ui16RxBufferWrite) + { + ui16Temp = (psSSI->ui16RxBufferLen + psSSI->ui16RxBufferWrite - + psSSI->ui16RxBufferRead); + } + else + { + ui16Temp = psSSI->ui16RxBufferWrite - psSSI->ui16RxBufferRead; + } + + // + // If the receive FIFO is now half full or more, generate a receive FIFO + // "interrupt". Otherwise, clear the receive FIFO "interrupt". + // + if(ui16Temp >= (psSSI->ui16RxBufferLen / 2)) + { + psSSI->ui8IntStatus |= SOFTSSI_RXFF; + } + else + { + psSSI->ui8IntStatus &= ~(SOFTSSI_RXFF); + } +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function performs the periodic, time-based updates to the SoftSSI +//! module. The transmission and reception of data over the SoftSSI link is +//! performed by the state machine in this function. +//! +//! This function must be called at twice the desired SoftSSI clock rate. For +//! example, to run the SoftSSI clock at 10 KHz, this function must be called +//! at a 20 KHz rate. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSITimerTick(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the current state of the state machine. + // + switch(psSSI->ui8State) + { + // + // The state machine is idle. + // + case SOFTSSI_STATE_IDLE: + { + // + // See if the SoftSSI module is enabled and there is data in the + // transmit FIFO. + // + if(((psSSI->ui8Flags & SOFTSSI_FLAG_ENABLE) != 0) && + (psSSI->ui16TxBufferRead != psSSI->ui16TxBufferWrite)) + { + // + // Assert the Fss signal if it is configured. + // + if(psSSI->ui32FssGPIO != 0) + { + HWREG(psSSI->ui32FssGPIO) = 0; + } + + // + // Move to the start state. + // + psSSI->ui8State = SOFTSSI_STATE_START; + } + + // + // Otherwise, see if there is data in the receive FIFO. + // + else if((psSSI->ui16RxBufferRead != psSSI->ui16RxBufferWrite) && + (psSSI->ui8IdleCount != 64)) + { + // + // Increment the idle counter. + // + psSSI->ui8IdleCount++; + + // + // See if the idle counter has become large enough to trigger + // a timeout "interrupt". + // + if(psSSI->ui8IdleCount == 64) + { + // + // Trigger the receive timeout "interrupt". + // + psSSI->ui8IntStatus |= SOFTSSI_RXTO; + } + } + + // + // This state has been handled. + // + break; + } + + // + // The start machine is in the transfer start state. + // + case SOFTSSI_STATE_START: + { + // + // Get the next word to transfer from the transmit FIFO. + // + psSSI->ui16TxData = + (psSSI->pui16TxBuffer[psSSI->ui16TxBufferRead] << + (16 - psSSI->ui8Bits)); + + // + // Initialize the receive buffer to zero. + // + psSSI->ui16RxData = 0; + + // + // Initialize the count of bits tranferred. + // + psSSI->ui8CurrentBit = 0; + + // + // Write the first bit of the transmit word to the Tx pin. + // + HWREG(psSSI->ui32TxGPIO) = + (psSSI->ui16TxData & 0x8000) ? 255 : 0; + + // + // Shift to the next bit of the transmit word. + // + psSSI->ui16TxData <<= 1; + + // + // If in SPI mode 1 or 3, then the Clk signal needs to be toggled. + // + if((psSSI->ui8Flags & SOFTSSI_FLAG_SPH) != 0) + { + HWREG(psSSI->ui32ClkGPIO) ^= 255; + } + + // + // Move to the data input state. + // + psSSI->ui8State = SOFTSSI_STATE_IN; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the data input state. + // + case SOFTSSI_STATE_IN: + { + // + // Read the next bit from the Rx signal if it is configured. + // + if(psSSI->ui32RxGPIO != 0) + { + psSSI->ui16RxData = ((psSSI->ui16RxData << 1) | + (HWREG(psSSI->ui32RxGPIO) ? 1 : 0)); + } + + // + // Toggle the Clk signal. + // + HWREG(psSSI->ui32ClkGPIO) ^= 255; + + // + // Increment the number of bits transferred. + // + psSSI->ui8CurrentBit++; + + // + // See if the entire word has been transferred. + // + if(psSSI->ui8CurrentBit != psSSI->ui8Bits) + { + // + // There are more bits to transfer, so move to the data output + // state. + // + psSSI->ui8State = SOFTSSI_STATE_OUT; + } + else + { + // + // Increment the transmit read pointer, removing the word that + // was just transferred from the transmit FIFO. + // + psSSI->ui16TxBufferRead++; + if(psSSI->ui16TxBufferRead == psSSI->ui16TxBufferLen) + { + psSSI->ui16TxBufferRead = 0; + } + + // + // See if a transmit FIFO "interrupt" needs to be asserted. + // + SoftSSITxInt(psSSI); + + // + // Determine the new value for the receive FIFO write pointer. + // + ui16Temp = psSSI->ui16RxBufferWrite + 1; + if(ui16Temp >= psSSI->ui16RxBufferLen) + { + ui16Temp = 0; + } + + // + // See if there is space in the receive FIFO for the word that + // was just received. + // + if(ui16Temp == psSSI->ui16RxBufferRead) + { + // + // The receive FIFO is full, so generate a receive FIFO + // overrun "interrupt". + // + psSSI->ui8IntStatus |= SOFTSSI_RXOR; + } + else + { + // + // Store the new word into the receive FIFO. + // + psSSI->pui16RxBuffer[psSSI->ui16RxBufferWrite] = + psSSI->ui16RxData; + + // + // Save the new receive FIFO write pointer. + // + psSSI->ui16RxBufferWrite = ui16Temp; + + // + // See if a receive FIFO "interrupt" needs to be asserted. + // + SoftSSIRxInt(psSSI); + } + + // + // See if the next word should be transmitted immediately. + // This will occur when there is data in the transmit FIFO, the + // SoftSSI module is enabled, and the SoftSSI module is in SPI + // mode 1 or 3. + // + if(((psSSI->ui8Flags & SOFTSSI_FLAG_ENABLE) != 0) && + ((psSSI->ui8Flags & SOFTSSI_FLAG_SPH) != 0) && + (psSSI->ui16TxBufferRead != psSSI->ui16TxBufferWrite)) + { + // + // Get the next word to transfer from the transmit FIFO. + // + psSSI->ui16TxData = + (psSSI->pui16TxBuffer[psSSI->ui16TxBufferRead] << + (16 - psSSI->ui8Bits)); + + // + // Initialize the receive buffer to zero. + // + psSSI->ui16RxData = 0; + + // + // Initialize the count of bits tranferred. + // + psSSI->ui8CurrentBit = 0; + + // + // Move to the data output state. + // + psSSI->ui8State = SOFTSSI_STATE_OUT; + } + else + { + // + // The next word should not be transmitted immediately, so + // move to the first step of the stop state. + // + psSSI->ui8State = SOFTSSI_STATE_STOP1; + } + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the data output state. + // + case SOFTSSI_STATE_OUT: + { + // + // Write the next bit of the transmit word to the Tx pin. + // + HWREG(psSSI->ui32TxGPIO) = (psSSI->ui16TxData & 0x8000) ? 255 : 0; + + // + // Toggle the Clk signal. + // + HWREG(psSSI->ui32ClkGPIO) ^= 255; + + // + // Shift to the next bit of the transmit word. + // + psSSI->ui16TxData <<= 1; + + // + // Move to the data input state. + // + psSSI->ui8State = SOFTSSI_STATE_IN; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the first step of the stop state. + // + case SOFTSSI_STATE_STOP1: + { + // + // Set the Tx pin low. + // + HWREG(psSSI->ui32TxGPIO) = 0; + + // + // If in SPI mode 1 or 3, then the Clk signal needs to be toggled. + // + if((psSSI->ui8Flags & SOFTSSI_FLAG_SPH) == 0) + { + HWREG(psSSI->ui32ClkGPIO) ^= 255; + } + + // + // Move to the second step of the stop state. + // + psSSI->ui8State = SOFTSSI_STATE_STOP2; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the second step of the stop state. + // + case SOFTSSI_STATE_STOP2: + { + // + // Deassert the Fss signal if it is configured. + // + if(psSSI->ui32FssGPIO != 0) + { + HWREG(psSSI->ui32FssGPIO) = 255; + } + + // + // Move to the idle state. + // + psSSI->ui8State = SOFTSSI_STATE_IDLE; + + // + // Reset the idle counter. + // + psSSI->ui8IdleCount = 0; + + // + // See if the end of transfer "interrupt" should be generated. + // + if(psSSI->ui16TxBufferRead == psSSI->ui16TxBufferWrite) + { + psSSI->ui8IntStatus |= SOFTSSI_TXEOT; + } + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the SSI peripheral. + // + while(((psSSI->ui8IntStatus & psSSI->ui8IntMask) != 0) && + (psSSI->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psSSI->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Enables the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function enables operation of the SoftSSI module. The SoftSSI module +//! must be configured before it is enabled. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIEnable(tSoftSSI *psSSI) +{ + // + // Enable the SoftSSI module. + // + psSSI->ui8Flags |= SOFTSSI_FLAG_ENABLE; +} + +//***************************************************************************** +// +//! Disables the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function disables operation of the SoftSSI module. If a data transfer +//! is in progress, it is finished before the module is fully disabled. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDisable(tSoftSSI *psSSI) +{ + // + // Disable the SoftSSI module. + // + psSSI->ui8Flags &= ~(SOFTSSI_FLAG_ENABLE); +} + +//***************************************************************************** +// +//! Enables individual SoftSSI ``interrupt'' sources. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! enabled. +//! +//! Enables the indicated SoftSSI ``interrupt'' sources. Only the sources that +//! are enabled can be reflected to the callback function; disabled sources do +//! not result in a callback. The \e ui32IntFlags parameter can be any of the +//! \b SOFTSSI_TXEOT, \b SOFTSSI_TXFF, \b SOFTSSI_RXFF, \b SOFTSSI_RXTO, or +//! \b SOFTSSI_RXOR values. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIIntEnable(tSoftSSI *psSSI, uint32_t ui32IntFlags) +{ + // + // Enable the specified "interrupts". + // + psSSI->ui8IntMask |= ui32IntFlags; +} + +//***************************************************************************** +// +//! Disables individual SoftSSI ``interrupt'' sources. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! disabled. +//! +//! Disables the indicated SoftSSI ``interrupt'' sources. The \e ui32IntFlags +//! parameter can be any of the \b SOFTSSI_TXEOT, \b SOFTSSI_TXFF, +//! \b SOFTSSI_RXFF, \b SOFTSSI_RXTO, or \b SOFTSSI_RXOR values. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIIntDisable(tSoftSSI *psSSI, uint32_t ui32IntFlags) +{ + // + // Disable the specified "interrupts". + // + psSSI->ui8IntMask &= ~(ui32IntFlags); +} + +//***************************************************************************** +// +//! Gets the current ``interrupt'' status. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param bMasked is \b false if the raw ``interrupt'' status is required or +//! \b true if the masked ``interrupt'' status is required. +//! +//! This function returns the ``interrupt'' status for the SoftSSI module. +//! Either the raw ``interrupt'' status or the status of ``interrupts'' that +//! are allowed to reflect to the callback can be returned. +//! +//! \return The current ``interrupt'' status, enumerated as a bit field of +//! \b SOFTSSI_TXEOT, \b SOFTSSI_TXFF, \b SOFTSSI_RXFF, \b SOFTSSI_RXTO, and +//! \b SOFTSSI_RXOR. +// +//***************************************************************************** +uint32_t +SoftSSIIntStatus(tSoftSSI *psSSI, bool bMasked) +{ + // + // Return either the "interrupt" status or the raw "interrupt" status as + // requested. + // + if(bMasked) + { + return(psSSI->ui8IntStatus & psSSI->ui8IntMask); + } + else + { + return(psSSI->ui8IntStatus); + } +} + +//***************************************************************************** +// +//! Clears SoftSSI ``interrupt'' sources. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! cleared. +//! +//! The specified SoftSSI ``interrupt'' sources are cleared so that they no +//! longer assert. This function must be called in the ``interrupt'' handler +//! to keep the ``interrupt'' from being recognized again immediately upon +//! exit. The \e ui32IntFlags parameter is the logical OR of any of the +//! \b SOFTSSI_TXEOT, \b SOFTSSI_RXTO, and \b SOFTSSI_RXOR values. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIIntClear(tSoftSSI *psSSI, uint32_t ui32IntFlags) +{ + // + // Clear the requested "interrupt" sources. + // + psSSI->ui8IntStatus &= ~(ui32IntFlags) | SOFTSSI_TXFF | SOFTSSI_RXFF; +} + +//***************************************************************************** +// +//! Determines if there is any data in the receive FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function determines if there is any data available to be read from the +//! receive FIFO. +//! +//! \return Returns \b true if there is data in the receive FIFO or \b false +//! if there is no data in the receive FIFO. +// +//***************************************************************************** +bool +SoftSSIDataAvail(tSoftSSI *psSSI) +{ + // + // Return the availability of data. + // + return((psSSI->ui16RxBufferRead == psSSI->ui16RxBufferWrite) ? false : + true); +} + +//***************************************************************************** +// +//! Determines if there is any space in the transmit FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function determines if there is space available in the transmit FIFO. +//! +//! \return Returns \b true if there is space available in the transmit FIFO or +//! \b false if there is no space available in the transmit FIFO. +// +//***************************************************************************** +bool +SoftSSISpaceAvail(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psSSI->ui16TxBufferWrite + 1; + if(ui16Temp == psSSI->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // Return the availability of space. + // + return((psSSI->ui16TxBufferRead == ui16Temp) ? false : true); +} + +//***************************************************************************** +// +//! Puts a data element into the SoftSSI transmit FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Data is the data to be transmitted over the SoftSSI interface. +//! +//! This function places the supplied data into the transmit FIFO of the +//! specified SoftSSI module. +//! +//! \note The upper 32 - N bits of the \e ui32Data are discarded, where N is +//! the data width as configured by SoftSSIConfigSet(). For example, if the +//! interface is configured for 8-bit data width, the upper 24 bits of +//! \e ui32Data are discarded. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDataPut(tSoftSSI *psSSI, uint32_t ui32Data) +{ + uint16_t ui16Temp; + + // + // Wait until there is space. + // + ui16Temp = psSSI->ui16TxBufferWrite + 1; + if(ui16Temp == psSSI->ui16TxBufferLen) + { + ui16Temp = 0; + } + while(ui16Temp == *(volatile uint16_t *)(&(psSSI->ui16TxBufferRead))) + { + } + + // + // Write the data to the SoftSSI. + // + psSSI->pui16TxBuffer[psSSI->ui16TxBufferWrite] = ui32Data; + psSSI->ui16TxBufferWrite = ui16Temp; + + // + // See if a transmit FIFO "interrupt" needs to be cleared. + // + SoftSSITxInt(psSSI); +} + +//***************************************************************************** +// +//! Puts a data element into the SoftSSI transmit FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Data is the data to be transmitted over the SoftSSI interface. +//! +//! This function places the supplied data into the transmit FIFO of the +//! specified SoftSSI module. If there is no space in the FIFO, then this +//! function returns a zero. +//! +//! \note The upper 32 - N bits of the \e ui32Data are discarded, where N is +//! the data width as configured by SoftSSIConfigSet(). For example, if the +//! interface is configured for 8-bit data width, the upper 24 bits of +//! \e ui32Data are discarded. +//! +//! \return Returns the number of elements written to the SSI transmit FIFO. +// +//***************************************************************************** +int32_t +SoftSSIDataPutNonBlocking(tSoftSSI *psSSI, uint32_t ui32Data) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psSSI->ui16TxBufferWrite + 1; + if(ui16Temp == psSSI->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // Check for space to write. + // + if(ui16Temp != psSSI->ui16TxBufferRead) + { + psSSI->pui16TxBuffer[psSSI->ui16TxBufferWrite] = ui32Data; + psSSI->ui16TxBufferWrite = ui16Temp; + SoftSSITxInt(psSSI); + return(1); + } + else + { + return(0); + } +} + +//***************************************************************************** +// +//! Gets a data element from the SoftSSI receive FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui32Data is a pointer to a storage location for data that was +//! received over the SoftSSI interface. +//! +//! This function gets received data from the receive FIFO of the specified +//! SoftSSI module and places that data into the location specified by the +//! \e pui32Data parameter. +//! +//! \note Only the lower N bits of the value written to \e pui32Data contain +//! valid data, where N is the data width as configured by SoftSSIConfigSet(). +//! For example, if the interface is configured for 8-bit data width, only the +//! lower 8 bits of the value written to \e pui32Data contain valid data. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDataGet(tSoftSSI *psSSI, uint32_t *pui32Data) +{ + // + // Wait until there is data to be read. + // + while(psSSI->ui16RxBufferRead == + *(volatile uint16_t *)(&(psSSI->ui16RxBufferWrite))) + { + } + + // + // Read data from SoftSSI. + // + *pui32Data = psSSI->pui16RxBuffer[psSSI->ui16RxBufferRead]; + psSSI->ui16RxBufferRead++; + if(psSSI->ui16RxBufferRead == psSSI->ui16RxBufferLen) + { + psSSI->ui16RxBufferRead = 0; + } + + // + // See if a receive FIFO "interrupt" needs to be cleared. + // + SoftSSIRxInt(psSSI); +} + +//***************************************************************************** +// +//! Gets a data element from the SoftSSI receive FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui32Data is a pointer to a storage location for data that was +//! received over the SoftSSI interface. +//! +//! This function gets received data from the receive FIFO of the specified +//! SoftSSI module and places that data into the location specified by the +//! \e ui32Data parameter. If there is no data in the FIFO, then this function +//! returns a zero. +//! +//! \note Only the lower N bits of the value written to \e pui32Data contain +//! valid data, where N is the data width as configured by SoftSSIConfigSet(). +//! For example, if the interface is configured for 8-bit data width, only the +//! lower 8 bits of the value written to \e pui32Data contain valid data. +//! +//! \return Returns the number of elements read from the SoftSSI receive FIFO. +// +//***************************************************************************** +int32_t +SoftSSIDataGetNonBlocking(tSoftSSI *psSSI, uint32_t *pui32Data) +{ + // + // Check for data to read. + // + if(psSSI->ui16RxBufferRead != psSSI->ui16RxBufferWrite) + { + *pui32Data = psSSI->pui16RxBuffer[psSSI->ui16RxBufferRead]; + psSSI->ui16RxBufferRead++; + if(psSSI->ui16RxBufferRead == psSSI->ui16RxBufferLen) + { + psSSI->ui16RxBufferRead = 0; + } + SoftSSIRxInt(psSSI); + return(1); + } + else + { + return(0); + } +} + +//***************************************************************************** +// +//! Determines whether the SoftSSI transmitter is busy or not. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! Allows the caller to determine whether all transmitted bytes have cleared +//! the transmitter. If \b false is returned, then the transmit FIFO is empty +//! and all bits of the last transmitted word have left the shift register. +//! +//! \return Returns \b true if the SoftSSI is transmitting or \b false if all +//! transmissions are complete. +// +//***************************************************************************** +bool +SoftSSIBusy(tSoftSSI *psSSI) +{ + // + // Determine if the SSI is busy. + // + return(((psSSI->ui8State == SOFTSSI_STATE_IDLE) && + (((psSSI->ui8Flags & SOFTSSI_FLAG_ENABLE) == 0) || + (psSSI->ui16TxBufferRead == psSSI->ui16TxBufferWrite))) ? false : + true); +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pfnCallback is a pointer to the callback function. +//! +//! This function sets the address of the callback function that is called when +//! there is an ``interrupt'' produced by the SoftSSI module. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSICallbackSet(tSoftSSI *psSSI, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + psSSI->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Fss signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Fss signal. +//! If there is not a GPIO pin allocated for Fss, the SoftSSI module does not +//! assert/deassert the Fss signal, leaving it to the application either to do +//! manually or to not do at all if the slave device has Fss tied to ground. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIFssGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Fss signal. + // + if(ui32Base == 0) + { + psSSI->ui32FssGPIO = 0; + } + else + { + psSSI->ui32FssGPIO = ui32Base + (ui8Pin << 2); + } +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Clk signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Clk signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIClkGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Clk signal. + // + psSSI->ui32ClkGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Tx signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Tx signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSITxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Tx signal. + // + psSSI->ui32TxGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Rx signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Rx signal. If +//! there is not a GPIO pin allocated for Rx, the SoftSSI module does not read +//! data from the slave device. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIRxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Rx signal. + // + if(ui32Base == 0) + { + psSSI->ui32RxGPIO = 0; + } + else + { + psSSI->ui32RxGPIO = ui32Base + (ui8Pin << 2); + } +} + +//***************************************************************************** +// +//! Sets the transmit FIFO buffer for a SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui16TxBuffer is the address of the transmit FIFO buffer. +//! \param ui16Len is the size, in 16-bit half-words, of the transmit FIFO +//! buffer. +//! +//! This function sets the address and size of the transmit FIFO buffer and +//! also resets the read and write pointers, marking the transmit FIFO as +//! empty. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSITxBufferSet(tSoftSSI *psSSI, uint16_t *pui16TxBuffer, + uint16_t ui16Len) +{ + // + // Save the transmit FIFO buffer address and length. + // + psSSI->pui16TxBuffer = pui16TxBuffer; + psSSI->ui16TxBufferLen = ui16Len; + + // + // Reset the transmit FIFO read and write pointers. + // + psSSI->ui16TxBufferRead = 0; + psSSI->ui16TxBufferWrite = 0; +} + +//***************************************************************************** +// +//! Sets the receive FIFO buffer for a SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui16RxBuffer is the address of the receive FIFO buffer. +//! \param ui16Len is the size, in 16-bit half-words, of the receive FIFO +//! buffer. +//! +//! This function sets the address and size of the receive FIFO buffer and also +//! resets the read and write pointers, marking the receive FIFO as empty. +//! When the buffer pointer and length are configured as zero, all data +//! received from the slave device is discarded. This capability is useful +//! when there is no GPIO pin allocated for the Rx signal. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIRxBufferSet(tSoftSSI *psSSI, uint16_t *pui16RxBuffer, + uint16_t ui16Len) +{ + // + // Save the receive FIFO buffer address and length. + // + psSSI->pui16RxBuffer = pui16RxBuffer; + psSSI->ui16RxBufferLen = ui16Len; + + // + // Reset the receive FIFO read and write pointers. + // + psSSI->ui16RxBufferRead = 0; + psSSI->ui16RxBufferWrite = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softssi.h b/utils/softssi.h new file mode 100644 index 0000000..72ee068 --- /dev/null +++ b/utils/softssi.h @@ -0,0 +1,280 @@ +//***************************************************************************** +// +// softssi.h - Defines and macros for the SoftSSI. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +#ifndef __SOFTSSI_H__ +#define __SOFTSSI_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup softssi_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure contains the state of a single instance of a SoftSSI module. +// +//***************************************************************************** +typedef struct +{ + // + //! The address of the callback function that is called to simulate the + //! interrupts that would be produced by a hardware SSI implementation. + //! This address can be set via a direct structure access or using the + //! SoftSSICallbackSet function. + // + void (*pfnIntCallback)(void); + + // + //! The address of the GPIO pin to be used for the Fss signal. If this + //! member is zero, the Fss signal is not generated. This member can be + //! set via a direct structure access or using the SoftSSIFssGPIOSet + //! function. + /// + uint32_t ui32FssGPIO; + + // + //! The address of the GPIO pin to be used for the Clk signal. This member + //! can be set via a direct structure access or using the SoftSSIClkGPIOSet + //! function. + // + uint32_t ui32ClkGPIO; + + // + //! The address of the GPIO pin to be used for the Tx signal. This member + //! can be set via a direct structure access or using the SoftSSITxGPIOSet + //! function. + // + uint32_t ui32TxGPIO; + + // + //! The address of the GPIO pin to be used for the Rx signal. If this + //! member is zero, the Rx signal is not read. This member can be set via + //! a direct structure access or using the SoftSSIRxGPIOSet function. + // + uint32_t ui32RxGPIO; + + // + //! The address of the data buffer used for the transmit FIFO. This member + //! can be set via a direct structure access or using the + //! SoftSSITxBufferSet function. + // + uint16_t *pui16TxBuffer; + + // + //! The address of the data buffer used for the receive FIFO. This member + //! can be set via a direct structure access or using the + //! SoftSSIRxBufferSet function. + // + uint16_t *pui16RxBuffer; + + // + //! The length of the transmit FIFO. This member can be set via a direct + //! structure access or using the SoftSSITxBufferSet function. + // + uint16_t ui16TxBufferLen; + + // + //! The index into the transmit FIFO of the next word to be transmitted. + //! This member should be initialized to zero, but should not be accessed + //! or modified by the application. + // + uint16_t ui16TxBufferRead; + + // + //! The index into the transmit FIFO of the next location to store data + //! into the FIFO. This member should be initialized to zero, but should + //! not be accessed or modified by the application. + // + uint16_t ui16TxBufferWrite; + + // + //! The length of the receive FIFO. This member can be set via a direct + //! structure access or using the SoftSSIRxBufferSet function. + // + uint16_t ui16RxBufferLen; + + // + //! The index into the receive FIFO of the next word to be read from the + //! FIFO. This member should be initialized to zero, but should not be + //! accessed or modified by the application. + // + uint16_t ui16RxBufferRead; + + // + //! The index into the receive FIFO of the location to store the next word + //! received. This member should be initialized to zero, but should not be + //! accessed or modified by the application. + // + uint16_t ui16RxBufferWrite; + + // + //! The word that is currently being transmitted. This member should not + //! be accessed or modified by the application. + // + uint16_t ui16TxData; + + // + //! The word that is currently being received. This member should not be + //! accessed or modified by the application. + // + uint16_t ui16RxData; + + // + //! The flags that control the operation of the SoftSSI module. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8Flags; + + // + //! The number of data bits in each SoftSSI frame, which also specifies the + //! width of each data item in the transmit and receive FIFOs. This member + //! can be set via a direct structure access or using the SoftSSIConfigSet + //! function. + // + uint8_t ui8Bits; + + // + //! The current state of the SoftSSI state machine. This member should not + //! be accessed or modified by the application. + // + uint8_t ui8State; + + // + //! The number of bits that have been transmitted and received in the + //! current frame. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8CurrentBit; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8IntMask; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8IntStatus; + + // + //! The number of tick counts that the SoftSSI module has been idle with + //! data stored in the receive FIFO, which is used to generate the receive + //! timeout interrupt. This member should not be accessed or modified by + //! the application. + // + uint8_t ui8IdleCount; +} +tSoftSSI; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Values that can be passed to SoftSSIIntEnable, SoftSSIIntDisable, and +// SoftSSIIntClear as the ui32IntFlags parameter, and returned by +// SoftSSIIntStatus. +// +//***************************************************************************** +#define SOFTSSI_TXEOT 0x00000010 // TX end of transmit +#define SOFTSSI_TXFF 0x00000008 // TX FIFO half full or less +#define SOFTSSI_RXFF 0x00000004 // RX FIFO half full or more +#define SOFTSSI_RXTO 0x00000002 // RX timeout +#define SOFTSSI_RXOR 0x00000001 // RX overrun + +//***************************************************************************** +// +// Values that can be passed to SoftSSIConfigSet. +// +//***************************************************************************** +#define SOFTSSI_FRF_MOTO_MODE_0 0x00000000 // Moto fmt, polarity 0, phase 0 +#define SOFTSSI_FRF_MOTO_MODE_1 0x00000002 // Moto fmt, polarity 0, phase 1 +#define SOFTSSI_FRF_MOTO_MODE_2 0x00000001 // Moto fmt, polarity 1, phase 0 +#define SOFTSSI_FRF_MOTO_MODE_3 0x00000003 // Moto fmt, polarity 1, phase 1 + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern bool SoftSSIBusy(tSoftSSI *psSSI); +extern void SoftSSICallbackSet(tSoftSSI *psSSI, void (*pfnCallback)(void)); +extern void SoftSSIClkGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftSSIConfigSet(tSoftSSI *psSSI, uint8_t ui8Protocol, + uint8_t ui8Bits); +extern bool SoftSSIDataAvail(tSoftSSI *psSSI); +extern void SoftSSIDataGet(tSoftSSI *psSSI, uint32_t *pui32Data); +extern int32_t SoftSSIDataGetNonBlocking(tSoftSSI *psSSI, uint32_t *pui32Data); +extern void SoftSSIDataPut(tSoftSSI *psSSI, uint32_t ui32Data); +extern int32_t SoftSSIDataPutNonBlocking(tSoftSSI *psSSI, uint32_t ui32Data); +extern void SoftSSIDisable(tSoftSSI *psSSI); +extern void SoftSSIEnable(tSoftSSI *psSSI); +extern void SoftSSIFssGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftSSIIntClear(tSoftSSI *psSSI, uint32_t ui32IntFlags); +extern void SoftSSIIntDisable(tSoftSSI *psSSI, uint32_t ui32IntFlags); +extern void SoftSSIIntEnable(tSoftSSI *psSSI, uint32_t ui32IntFlags); +extern uint32_t SoftSSIIntStatus(tSoftSSI *psSSI, bool bMasked); +extern void SoftSSIRxBufferSet(tSoftSSI *psSSI, uint16_t *pui16RxBuffer, + uint16_t ui16Len); +extern void SoftSSIRxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); +extern bool SoftSSISpaceAvail(tSoftSSI *psSSI); +extern void SoftSSITimerTick(tSoftSSI *psSSI); +extern void SoftSSITxBufferSet(tSoftSSI *psSSI, uint16_t *pui16TxBuffer, + uint16_t ui16Len); +extern void SoftSSITxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SOFTSSI_H__ diff --git a/utils/softuart.c b/utils/softuart.c new file mode 100644 index 0000000..d4aa3b3 --- /dev/null +++ b/utils/softuart.c @@ -0,0 +1,2591 @@ +//***************************************************************************** +// +// softuart.c - Driver for the SoftUART. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softuart_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "inc/hw_uart.h" +#include "driverlib/debug.h" +#include "driverlib/gpio.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/uart.h" +#include "utils/softuart.h" + +//***************************************************************************** +// +// The states in the SoftUART transmit state machine. The code depends upon +// the fact that the value of TXSTATE_DATA_n is n + 1, and that TXSTATE_DATA_0 +// is 1. +// +//***************************************************************************** +#define SOFTUART_TXSTATE_IDLE 0 +#define SOFTUART_TXSTATE_DATA_0 1 +#define SOFTUART_TXSTATE_DATA_1 2 +#define SOFTUART_TXSTATE_DATA_2 3 +#define SOFTUART_TXSTATE_DATA_3 4 +#define SOFTUART_TXSTATE_DATA_4 5 +#define SOFTUART_TXSTATE_DATA_5 6 +#define SOFTUART_TXSTATE_DATA_6 7 +#define SOFTUART_TXSTATE_DATA_7 8 +#define SOFTUART_TXSTATE_START 9 +#define SOFTUART_TXSTATE_PARITY 10 +#define SOFTUART_TXSTATE_STOP_0 11 +#define SOFTUART_TXSTATE_STOP_1 12 +#define SOFTUART_TXSTATE_BREAK 13 + +//***************************************************************************** +// +// The states of the SoftUART receive state machine. The code depends upon the +// the fact that the value of RXSTATE_DATA_n is n, and that RXSTATE_DATA_0 is +// 0. +// +//***************************************************************************** +#define SOFTUART_RXSTATE_DATA_0 0 +#define SOFTUART_RXSTATE_DATA_1 1 +#define SOFTUART_RXSTATE_DATA_2 2 +#define SOFTUART_RXSTATE_DATA_3 3 +#define SOFTUART_RXSTATE_DATA_4 4 +#define SOFTUART_RXSTATE_DATA_5 5 +#define SOFTUART_RXSTATE_DATA_6 6 +#define SOFTUART_RXSTATE_DATA_7 7 +#define SOFTUART_RXSTATE_IDLE 8 +#define SOFTUART_RXSTATE_PARITY 9 +#define SOFTUART_RXSTATE_STOP_0 10 +#define SOFTUART_RXSTATE_STOP_1 11 +#define SOFTUART_RXSTATE_BREAK 12 +#define SOFTUART_RXSTATE_DELAY 13 + +//***************************************************************************** +// +// The flags in the SoftUART ui8Flags structure member. +// +//***************************************************************************** +#define SOFTUART_FLAG_ENABLE 0x01 +#define SOFTUART_FLAG_TXBREAK 0x02 + +//***************************************************************************** +// +// The flags in the SoftUART ui8RxFlags structure member. +// +//***************************************************************************** +#define SOFTUART_RXFLAG_OE 0x08 +#define SOFTUART_RXFLAG_BE 0x04 +#define SOFTUART_RXFLAG_PE 0x02 +#define SOFTUART_RXFLAG_FE 0x01 + +//***************************************************************************** +// +// Additional internal configuration stored in the SoftUART ui16Config +// structure member. +// +//***************************************************************************** +#define SOFTUART_CONFIG_BASE_M 0x00ff +#define SOFTUART_CONFIG_EXT_M 0xff00 +#define SOFTUART_CONFIG_TXLVL_M 0x0700 +#define SOFTUART_CONFIG_TXLVL_1 0x0000 +#define SOFTUART_CONFIG_TXLVL_2 0x0100 +#define SOFTUART_CONFIG_TXLVL_4 0x0200 +#define SOFTUART_CONFIG_TXLVL_6 0x0300 +#define SOFTUART_CONFIG_TXLVL_7 0x0400 +#define SOFTUART_CONFIG_RXLVL_M 0x3800 +#define SOFTUART_CONFIG_RXLVL_1 0x0000 +#define SOFTUART_CONFIG_RXLVL_2 0x0800 +#define SOFTUART_CONFIG_RXLVL_4 0x1000 +#define SOFTUART_CONFIG_RXLVL_6 0x1800 +#define SOFTUART_CONFIG_RXLVL_7 0x2000 + +//***************************************************************************** +// +// The odd parity of each possible data byte. The odd parity of N can be found +// by looking at bit N % 32 of word N / 32. +// +//***************************************************************************** +static uint32_t g_pui32ParityOdd[] = +{ + 0x69969669, 0x96696996, 0x96696996, 0x69969669, + 0x96696996, 0x69969669, 0x69969669, 0x96696996 +}; + +//***************************************************************************** +// +//! Initializes the SoftUART module. +//! +//! \param psUART specifies the soft UART data structure. +//! +//! This function initializes the data structure for the SoftUART module, +//! putting it into the default configuration. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTInit(tSoftUART *psUART) +{ + // + // Clear the SoftUART data structure. + // + memset(psUART, 0, sizeof(tSoftUART)); + + // + // Set the default transmit and receive buffer interrupt level. + // + psUART->ui16Config = SOFTUART_CONFIG_TXLVL_4 | SOFTUART_CONFIG_RXLVL_4; +} + +//***************************************************************************** +// +//! Sets the configuration of a SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Config is the data format for the port (number of data bits, +//! number of stop bits, and parity). +//! +//! This function configures the SoftUART for operation in the specified data +//! format, as specified in the \e ui32Config parameter. +//! +//! The \e ui32Config parameter is the logical OR of three values: the number +//! of data bits, the number of stop bits, and the parity. +//! \b SOFTUART_CONFIG_WLEN_8, \b SOFTUART_CONFIG_WLEN_7, +//! \b SOFTUART_CONFIG_WLEN_6, and \b SOFTUART_CONFIG_WLEN_5 select from eight +//! to five data bits per byte (respectively). \b SOFTUART_CONFIG_STOP_ONE and +//! \b SOFTUART_CONFIG_STOP_TWO select one or two stop bits (respectively). +//! \b SOFTUART_CONFIG_PAR_NONE, \b SOFTUART_CONFIG_PAR_EVEN, +//! \b SOFTUART_CONFIG_PAR_ODD, \b SOFTUART_CONFIG_PAR_ONE, and +//! \b SOFTUART_CONFIG_PAR_ZERO select the parity mode (no parity bit, even +//! parity bit, odd parity bit, parity bit always one, and parity bit always +//! zero, respectively). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTConfigSet(tSoftUART *psUART, uint32_t ui32Config) +{ + // + // See if a GPIO pin has been set for Tx. + // + if(psUART->ui32TxGPIO != 0) + { + // + // Configure the Tx pin. + // + MAP_GPIOPinTypeGPIOOutput(psUART->ui32TxGPIO & 0xfffff000, + (psUART->ui32TxGPIO & 0x00000fff) >> 2); + + // + // Set the Tx pin high. + // + HWREG(psUART->ui32TxGPIO) = 255; + } + + // + // See if a GPIO pin has been set for Rx. + // + if(psUART->ui32RxGPIOPort != 0) + { + // + // Configure the Rx pin. + // + MAP_GPIOPinTypeGPIOInput(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Set the Rx pin to generate an interrupt on the next falling edge. + // + MAP_GPIOIntTypeSet(psUART->ui32RxGPIOPort, psUART->ui8RxPin, + GPIO_FALLING_EDGE); + + // + // Enable the Rx pin interrupt. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntEnable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + } + + // + // Make sure that the transmit and receive buffers are empty. + // + psUART->ui16TxBufferRead = 0; + psUART->ui16TxBufferWrite = 0; + psUART->ui16RxBufferRead = 0; + psUART->ui16RxBufferWrite = 0; + + // + // Save the data format. + // + psUART->ui16Config = ((psUART->ui16Config & SOFTUART_CONFIG_EXT_M) | + (ui32Config & SOFTUART_CONFIG_BASE_M)); + + // + // Enable the SoftUART module. + // + psUART->ui8Flags |= SOFTUART_FLAG_ENABLE; + + // + // The next value to be written to the Tx pin is one since the SoftUART is + // idle. + // + psUART->ui8TxNext = 255; + + // + // Start the SoftUART state machines in the idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + psUART->ui8RxState = SOFTUART_RXSTATE_IDLE; +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftUART transmitter. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function performs the periodic, time-based updates to the SoftUART +//! transmitter. The transmission of data from the SoftUART is performed by +//! the state machine in this function. +//! +//! This function must be called at the desired SoftUART baud rate. For +//! example, to run the SoftUART at 115,200 baud, this function must be called +//! at a 115,200 Hz rate. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTTxTimerTick(tSoftUART *psUART) +{ + uint32_t ui32Temp; + + // + // Write the next value to the Tx data line. This value was computed on + // the previous timer tick, which helps to reduce the jitter on the Tx + // edges (which is important since a UART connection does not contain a + // clock signal). + // + HWREG(psUART->ui32TxGPIO) = psUART->ui8TxNext; + + // + // Determine the current state of the state machine. + // + switch(psUART->ui8TxState) + { + // + // The state machine is idle. + // + case SOFTUART_TXSTATE_IDLE: + { + // + // See if the SoftUART module is enabled. + // + if(!(psUART->ui8Flags & SOFTUART_FLAG_ENABLE)) + { + // + // The SoftUART module is not enabled, so do nothing and stay + // in the idle state. + // + break; + } + + // + // See if the break signal should be asserted. + // + else if(psUART->ui8Flags & SOFTUART_FLAG_TXBREAK) + { + // + // The data line should be driven low while in the break state. + // + psUART->ui8TxNext = 0; + + // + // Move to the break state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_BREAK; + } + + // + // Otherwise, see if there is data in the transmit buffer. + // + else if(psUART->ui16TxBufferRead != psUART->ui16TxBufferWrite) + { + // + // The data line should be driven low to indicate a start bit. + // + psUART->ui8TxNext = 0; + + // + // Move to the start bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_START; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the start bit state. + // + case SOFTUART_TXSTATE_START: + { + // + // Get the next byte to be transmitted. + // + psUART->ui8TxData = psUART->pui8TxBuffer[psUART->ui16TxBufferRead]; + + // + // The next value to be written to the data line is the LSB of the + // next data byte. + // + psUART->ui8TxNext = (psUART->ui8TxData & 1) ? 255 : 0; + + // + // Move to the data bit 0 state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_DATA_0; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte must be output. + // This depends upon TXSTATE_DATA_n and TXSTATE_DATA_(n+1) being + // consecutively numbered. + // + case SOFTUART_TXSTATE_DATA_0: + case SOFTUART_TXSTATE_DATA_1: + case SOFTUART_TXSTATE_DATA_2: + case SOFTUART_TXSTATE_DATA_3: + { + // + // The next value to be written to the data line is the next bit of + // the data byte. + // + psUART->ui8TxNext = + (psUART->ui8TxData & (1 << psUART->ui8TxState)) ? 255 : 0; + + // + // Advance to the next state. + // + psUART->ui8TxState++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte must be output. + // Additionally, based on the configuration of the SoftUART, this bit + // might be the last data bit of the data byte. This depends upon + // TXSTATE_DATA_n and TXSTATE_DATA_(n+1) being consecutively numbered. + // + case SOFTUART_TXSTATE_DATA_4: + case SOFTUART_TXSTATE_DATA_5: + case SOFTUART_TXSTATE_DATA_6: + case SOFTUART_TXSTATE_DATA_7: + { + // + // See if the bit that was just transferred is the last bit of the + // data byte (based on the configuration of the SoftUART). + // + if(((psUART->ui16Config & SOFTUART_CONFIG_WLEN_MASK) >> + SOFTUART_CONFIG_WLEN_S) == + (psUART->ui8TxState - SOFTUART_TXSTATE_DATA_4)) + { + // + // See if parity is enabled. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) != + SOFTUART_CONFIG_PAR_NONE) + { + // + // See if the parity is set to one. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ONE) + { + // + // The next value to be written to the data line is + // one. + // + psUART->ui8TxNext = 255; + } + + // + // Otherwise, see if the parity is set to zero. + // + else if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ZERO) + { + // + // The next value to be written to the data line is + // zero. + // + psUART->ui8TxNext = 0; + } + + // + // Otherwise, there is either even or odd parity. + // + else + { + // + // Find the odd parity for the data byte. + // + psUART->ui8TxNext = + ((g_pui32ParityOdd[psUART->ui8TxData >> 5] & + (1 << (psUART->ui8TxData & 31))) ? 255 : 0); + + // + // If the parity is set to even, then invert the + // parity just computed (making it even parity). + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_EVEN) + { + psUART->ui8TxNext ^= 255; + } + } + + // + // Advance to the parity state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_PARITY; + } + + // + // Parity is not enabled. + // + else + { + // + // The next value to write to the data line is the stop + // bit. + // + psUART->ui8TxNext = 255; + + // + // See if there are one or two stop bits. + // + if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_1; + } + } + } + + // + // Otherwise, there are more data bits to transfer. + // + else + { + // + // The next value to be written to the data line is the next + // bit of the data byte. + // + psUART->ui8TxNext = + (psUART->ui8TxData & (1 << psUART->ui8TxState)) ? 255 : 0; + + // + // Advance to the next state. + // + psUART->ui8TxState++; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the parity bit state. + // + case SOFTUART_TXSTATE_PARITY: + { + // + // The next value to write to the data line is the stop bit. + // + psUART->ui8TxNext = 255; + + // + // See if there are one or two stop bits. + // + if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_1; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the two stop bits state. + // + case SOFTUART_TXSTATE_STOP_0: + { + // + // Advance to the one stop bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_1; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the one stop bit state. + // + case SOFTUART_TXSTATE_STOP_1: + { + // + // The data byte has been completely transferred, so advance the + // read pointer. + // + psUART->ui16TxBufferRead++; + if(psUART->ui16TxBufferRead == psUART->ui16TxBufferLen) + { + psUART->ui16TxBufferRead = 0; + } + + // + // Determine the number of characters in the transmit buffer. + // + if(psUART->ui16TxBufferRead > psUART->ui16TxBufferWrite) + { + ui32Temp = (psUART->ui16TxBufferLen - + (psUART->ui16TxBufferRead - + psUART->ui16TxBufferWrite)); + } + else + { + ui32Temp = (psUART->ui16TxBufferWrite - + psUART->ui16TxBufferRead); + } + + // + // If the transmit buffer fullness just crossed the programmed + // level, generate a transmit "interrupt". + // + if(ui32Temp == psUART->ui16TxBufferLevel) + { + psUART->ui16IntStatus |= SOFTUART_INT_TX; + } + + // + // See if the SoftUART module is enabled. + // + if(!(psUART->ui8Flags & SOFTUART_FLAG_ENABLE)) + { + // + // The SoftUART module is not enabled, so do advance to the + // idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + } + + // + // See if the break signal should be asserted. + // + else if(psUART->ui8Flags & SOFTUART_FLAG_TXBREAK) + { + // + // The data line should be driven low while in the break state. + // + psUART->ui8TxNext = 0; + + // + // Move to the break state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_BREAK; + } + + // + // Otherwise, see if there is data in the transmit buffer. + // + else if(psUART->ui16TxBufferRead != psUART->ui16TxBufferWrite) + { + // + // The data line should be driven low to indicate a start bit. + // + psUART->ui8TxNext = 0; + + // + // Move to the start bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_START; + } + + // + // Otherwise, there is nothing to do. + // + else + { + // + // Assert the end of transmission "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_EOT; + + // + // Advance to the idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the break state. + // + case SOFTUART_TXSTATE_BREAK: + { + // + // See if the break should be deasserted. + // + if(!(psUART->ui8Flags & SOFTUART_FLAG_ENABLE) || + !(psUART->ui8Flags & SOFTUART_FLAG_TXBREAK)) + { + // + // The data line should be driven high to indicate it is idle. + // + psUART->ui8TxNext = 255; + + // + // Advance to the idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + } + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the UART peripheral. + // + while(((psUART->ui16IntStatus & psUART->ui16IntMask) != 0) && + (psUART->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psUART->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Handles the assertion of the receive ``interrupt''. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function is used to determine when to assert the receive ``interrupt'' +//! as a result of writing data into the receive buffer (when characters are +//! received from the Rx pin). +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTRxWriteInt(tSoftUART *psUART) +{ + uint32_t ui32Temp; + + // + // Determine the number of characters in the receive buffer. + // + if(psUART->ui16RxBufferWrite > psUART->ui16RxBufferRead) + { + ui32Temp = psUART->ui16RxBufferWrite - psUART->ui16RxBufferRead; + } + else + { + ui32Temp = (psUART->ui16RxBufferLen + psUART->ui16RxBufferWrite - + psUART->ui16RxBufferRead); + } + + // + // If the receive buffer fullness just crossed the programmed level, + // generate a receive "interrupt". + // + if(ui32Temp == psUART->ui16RxBufferLevel) + { + psUART->ui16IntStatus |= SOFTUART_INT_RX; + } +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftUART receiver. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param bEdgeInt should be \b true if this function is being called because +//! of a GPIO edge interrupt and \b false if it is being called because of a +//! timer interrupt. +//! +//! This function performs the periodic, time-based updates to the SoftUART +//! receiver. The reception of data to the SoftUART is performed by the state +//! machine in this function. +//! +//! This function must be called by the GPIO interrupt handler, and then +//! periodically at the desired SoftUART baud rate. For example, to run the +//! SoftUART at 115,200 baud, this function must be called at a 115,200 Hz +//! rate. +//! +//! \return Returns \b SOFTUART_RXTIMER_NOP if the receive timer should +//! continue to operate or \b SOFTUART_RXTIMER_END if it should be stopped. +// +//***************************************************************************** +uint32_t +SoftUARTRxTick(tSoftUART *psUART, bool bEdgeInt) +{ + uint32_t ui32PinState, ui32Temp, ui32Ret; + + // + // Read the current state of the Rx data line. + // + ui32PinState = MAP_GPIOPinRead(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // The default return code inidicates that the receive timer does not need + // to be stopped. + // + ui32Ret = SOFTUART_RXTIMER_NOP; + + // + // See if this is an edge interrupt while delaying for the receive timeout + // interrupt. + // + if(bEdgeInt && (psUART->ui8RxState == SOFTUART_RXSTATE_DELAY)) + { + // + // The receive timeout has been cancelled since the next character has + // started, so go to the idle state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_IDLE; + } + + // + // Determine the current state of the state machine. + // + switch(psUART->ui8RxState) + { + // + // The state machine is idle. + // + case SOFTUART_RXSTATE_IDLE: + { + // + // The falling edge of the start bit was just sampled, so disable + // the GPIO edge interrupt since the remainder of the character + // will be read using a timer tick. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntDisable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Clear the receive data buffer. + // + psUART->ui8RxData = 0; + + // + // Clear all reception errors other than overrun (which is cleared + // only when the first character after the overrun is written into + // the receive buffer), and set the break error (which is cleared + // if any non-zero bits are read during this character). + // + psUART->ui8RxFlags = ((psUART->ui8RxFlags & SOFTUART_RXFLAG_OE) | + SOFTUART_RXFLAG_BE); + + // + // Advance to the first data bit state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_DATA_0; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte is read. This + // depends upon RXSTATE_DATA_n and RXSTATE_DATA_(n+1) being + // consecutively numbered. + // + case SOFTUART_RXSTATE_DATA_0: + case SOFTUART_RXSTATE_DATA_1: + case SOFTUART_RXSTATE_DATA_2: + case SOFTUART_RXSTATE_DATA_3: + { + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Set this bit of the received character. + // + psUART->ui8RxData |= 1 << psUART->ui8RxState; + + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Advance to the next state. + // + psUART->ui8RxState++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte is read. + // Additionally, based on the configuration of the SoftUART, this bit + // might be the last bit of the data byte. This depends upon + // RXSTATE_DATA_n and RXSTATE_DATA_(n+1) being consecutively numbered. + // + case SOFTUART_RXSTATE_DATA_4: + case SOFTUART_RXSTATE_DATA_5: + case SOFTUART_RXSTATE_DATA_6: + case SOFTUART_RXSTATE_DATA_7: + { + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Set this bit of the received character. + // + psUART->ui8RxData |= 1 << psUART->ui8RxState; + + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if the bit that was just transferred is the last bit of the + // data byte (based on the configuration of the SoftUART). + // + if(((psUART->ui16Config & SOFTUART_CONFIG_WLEN_MASK) >> + SOFTUART_CONFIG_WLEN_S) == + (psUART->ui8RxState - SOFTUART_RXSTATE_DATA_4)) + { + // + // See if parity is enabled. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) != + SOFTUART_CONFIG_PAR_NONE) + { + // + // Advance to the parity state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_PARITY; + } + + // + // Otherwise, see if there are one or two stop bits. + // + else if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_0; + } + + // + // Otherwise, advance to the one stop bit state. + // + else + { + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_1; + } + } + + // + // Otherwise, there are more bits to receive. + // + else + { + // + // Advance to the next state. + // + psUART->ui8RxState++; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the parity bit state. + // + case SOFTUART_RXSTATE_PARITY: + { + // + // See if the parity is set to one. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ONE) + { + // + // Set the expected parity to one. + // + ui32Temp = psUART->ui8RxPin; + } + + // + // Otherwise, see if the parity is set to zero. + // + else if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ZERO) + { + // + // Set the expected parity to zero. + // + ui32Temp = 0; + } + + // + // Otherwise, there is either even or odd parity. + // + else + { + // + // Find the odd parity for the data byte. + // + ui32Temp = ((g_pui32ParityOdd[psUART->ui8RxData >> 5] & + (1 << (psUART->ui8RxData & 31))) ? + psUART->ui8RxPin : 0); + + // + // If the parity is set to even, then invert the parity just + // computed (making it even parity). + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_EVEN) + { + ui32Temp ^= psUART->ui8RxPin; + } + } + + // + // See if the pin state matches the expected parity. + // + if(ui32PinState != ui32Temp) + { + // + // The parity does not match, so set the parity error flag. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_PE; + } + + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if there are one or two stop bits. + // + if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_1; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the two stop bits state. + // + case SOFTUART_RXSTATE_STOP_0: + { + // + // See if the Rx pin is low. + // + if(ui32PinState == 0) + { + // + // Since the Rx pin is low, there is a framing error. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_FE; + } + else + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Advance to the one stop bit state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_1; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the one stop bit state. + // + case SOFTUART_RXSTATE_STOP_1: + { + // + // See if the Rx pin is low. + // + if(ui32PinState == 0) + { + // + // Since the Rx pin is low, there is a framing error. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_FE; + } + else + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if the break error is still asserted (meaning that every bit + // received was zero). + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_BE) + { + // + // Since every bit was zero, advance to the break state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_BREAK; + + // + // This state has been handled. + // + break; + } + + // + // Compute the value of the write pointer advanced by one. + // + ui32Temp = psUART->ui16RxBufferWrite + 1; + if(ui32Temp == psUART->ui16RxBufferLen) + { + ui32Temp = 0; + } + + // + // See if there is space in the receive buffer. + // + if(ui32Temp == psUART->ui16RxBufferRead) + { + // + // Set the overrun error flag. This will remain set until a + // new character can be placed into the receive buffer, which + // will then be given this status. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_OE; + + // + // Set the receive overrun "interrupt" and status if it is not + // already set. + // + if(!(psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN)) + { + psUART->ui8RxStatus |= SOFTUART_RXERROR_OVERRUN; + psUART->ui16IntStatus |= SOFTUART_INT_OE; + } + } + + // + // Otherwise, there is space in the receive buffer. + // + else + { + // + // Write this data byte, along with the receive flags, into the + // receive buffer. + // + psUART->pui16RxBuffer[psUART->ui16RxBufferWrite] = + psUART->ui8RxData | (psUART->ui8RxFlags << 8); + + // + // Advance the write pointer. + // + psUART->ui16RxBufferWrite = ui32Temp; + + // + // Clear the receive flags, most importantly the overrun flag + // since it was just written into the receive buffer. + // + psUART->ui8RxFlags = 0; + + // + // Assert the receive "interrupt" if appropriate. + // + SoftUARTRxWriteInt(psUART); + } + + // + // See if this character had a parity error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_PE) + { + // + // Assert the parity error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_PE; + } + + // + // See if this character had a framing error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_FE) + { + // + // Assert the framing error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_FE; + } + + // + // Enable the falling edge interrupt on the Rx pin so that the next + // start bit can be detected. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntEnable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Advance to the receive timeout delay state. + // + psUART->ui8RxData = 0; + psUART->ui8RxState = SOFTUART_RXSTATE_DELAY; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the break state. + // + case SOFTUART_RXSTATE_BREAK: + { + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Compute the value of the write pointer advanced by one. + // + ui32Temp = psUART->ui16RxBufferWrite + 1; + if(ui32Temp == psUART->ui16RxBufferLen) + { + ui32Temp = 0; + } + + // + // See if there is space in the receive buffer. + // + if(ui32Temp == psUART->ui16RxBufferRead) + { + // + // Set the overrun error flag. This will remain set until a + // new character can be placed into the receive buffer, which + // will then be given this status. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_OE; + + // + // Set the receive overrun "interrupt" and status if it is not + // already set. + // + if(!(psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN)) + { + psUART->ui8RxStatus |= SOFTUART_RXERROR_OVERRUN; + psUART->ui16IntStatus |= SOFTUART_INT_OE; + } + } + + // + // Otherwise, there is space in the receive buffer. + // + else + { + // + // Write this data byte, along with the receive flags, into the + // receive buffer. + // + psUART->pui16RxBuffer[psUART->ui16RxBufferWrite] = + psUART->ui8RxData | (psUART->ui8RxFlags << 8); + + // + // Advance the write pointer. + // + psUART->ui16RxBufferWrite = ui32Temp; + + // + // Clear the receive flags, most importantly the overrun flag + // since it was just written into the receive buffer. + // + psUART->ui8RxFlags = 0; + + // + // Assert the receive "interrupt" if appropriate. + // + SoftUARTRxWriteInt(psUART); + } + + // + // See if this was a break error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_BE) + { + // + // Assert the break error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_BE; + } + + // + // See if this character had a parity error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_PE) + { + // + // Assert the parity error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_PE; + } + + // + // Assert the framing error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_FE; + + // + // Enable the falling edge interrupt on the Rx pin so that the next + // start bit can be detected. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntEnable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Advance to the receive timeout delay state. + // + psUART->ui8RxData = 0; + psUART->ui8RxState = SOFTUART_RXSTATE_DELAY; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the receive timeout delay state. + // + case SOFTUART_RXSTATE_DELAY: + { + // + // See if the receive timeout has expired. + // + if(psUART->ui8RxData++ == 32) + { + // + // Assert the receive timeout "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_RT; + + // + // Tell the caller that the receive timer can be disabled. + // + ui32Ret = SOFTUART_RXTIMER_END; + } + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the UART peripheral. + // + while(((psUART->ui16IntStatus & psUART->ui16IntMask) != 0) && + (psUART->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psUART->pfnIntCallback(); + } + + // + // Return to the caller. + // + return(ui32Ret); +} + +//***************************************************************************** +// +//! Sets the type of parity. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Parity specifies the type of parity to use. +//! +//! Sets the type of parity to use for transmitting and expect when receiving. +//! The \e ui32Parity parameter must be one of \b SOFTUART_CONFIG_PAR_NONE, +//! \b SOFTUART_CONFIG_PAR_EVEN, \b SOFTUART_CONFIG_PAR_ODD, +//! \b SOFTUART_CONFIG_PAR_ONE, or \b SOFTUART_CONFIG_PAR_ZERO. The last two +//! allow direct control of the parity bit; it is always either one or zero +//! based on the mode. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTParityModeSet(tSoftUART *psUART, uint32_t ui32Parity) +{ + // + // Check the arguments. + // + ASSERT((ui32Parity == SOFTUART_CONFIG_PAR_NONE) || + (ui32Parity == SOFTUART_CONFIG_PAR_EVEN) || + (ui32Parity == SOFTUART_CONFIG_PAR_ODD) || + (ui32Parity == SOFTUART_CONFIG_PAR_ONE) || + (ui32Parity == SOFTUART_CONFIG_PAR_ZERO)); + + // + // Set the parity mode. + // + psUART->ui16Config = + (psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) | ui32Parity; +} + +//***************************************************************************** +// +//! Gets the type of parity currently being used. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function gets the type of parity used for transmitting data and +//! expected when receiving data. +//! +//! \return Returns the current parity settings, specified as one of +//! \b SOFTUART_CONFIG_PAR_NONE, \b SOFTUART_CONFIG_PAR_EVEN, +//! \b SOFTUART_CONFIG_PAR_ODD, \b SOFTUART_CONFIG_PAR_ONE, or +//! \b SOFTUART_CONFIG_PAR_ZERO. +// +//***************************************************************************** +uint32_t +SoftUARTParityModeGet(tSoftUART *psUART) +{ + // + // Return the current parity setting. + // + return(psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK); +} + +//***************************************************************************** +// +//! Sets the transmit ``interrupt'' buffer level. +//! +//! \param psUART specifies the soft UART data structure. +//! +//! This function computes the transmit buffer level at which the transmit +//! ``interrupt'' is generated. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTTxLevelSet(tSoftUART *psUART) +{ + // + // Determine the transmit buffer "interrupt" fullness setting. + // + switch(psUART->ui16Config & SOFTUART_CONFIG_TXLVL_M) + { + // + // The transmit "interrupt" should be generated when the buffer is 1/8 + // full. + // + case SOFTUART_CONFIG_TXLVL_1: + { + // + // Set the transmit buffer level to 1/8 of the buffer length. + // + psUART->ui16TxBufferLevel = psUART->ui16TxBufferLen / 8; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 1/4 + // (2/8) full. + // + case SOFTUART_CONFIG_TXLVL_2: + { + // + // Set the transmit buffer level to 1/4 of the buffer length. + // + psUART->ui16TxBufferLevel = psUART->ui16TxBufferLen / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 1/2 + // (4/8) full. + // + case SOFTUART_CONFIG_TXLVL_4: + { + // + // Set the transmit buffer level to 1/2 of the buffer length. + // + psUART->ui16TxBufferLevel = psUART->ui16TxBufferLen / 2; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 3/4 + // (6/8) full. + // + case SOFTUART_CONFIG_TXLVL_6: + { + // + // Set the transmit buffer level to 3/4 of the buffer length. + // + psUART->ui16TxBufferLevel = (psUART->ui16TxBufferLen * 3) / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 7/8 + // full. + // + case SOFTUART_CONFIG_TXLVL_7: + { + // + // Set the transmit buffer level to 7/8 of the buffer length. + // + psUART->ui16TxBufferLevel = (psUART->ui16TxBufferLen * 7) / 8; + + // + // This setting has been handled. + // + break; + } + } +} + +//***************************************************************************** +// +//! Sets the receive ``interrupt'' buffer level. +//! +//! \param psUART specifies the soft UART data structure. +//! +//! This function computes the receive buffer level at which the receive +//! ``interrupt'' is generated. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTRxLevelSet(tSoftUART *psUART) +{ + // + // Determine the receive buffer "interrupt" fullness setting. + // + switch(psUART->ui16Config & SOFTUART_CONFIG_RXLVL_M) + { + // + // The receive "interrupt" should be generated when the buffer is 1/8 + // full. + // + case SOFTUART_CONFIG_RXLVL_1: + { + // + // Set the receive buffer level to 1/8 of the buffer length. + // + psUART->ui16RxBufferLevel = psUART->ui16RxBufferLen / 8; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 1/4 + // (2/8) full. + // + case SOFTUART_CONFIG_RXLVL_2: + { + // + // Set the receive buffer level to 1/4 of the buffer length. + // + psUART->ui16RxBufferLevel = psUART->ui16RxBufferLen / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 1/2 + // (4/8) full. + // + case SOFTUART_CONFIG_RXLVL_4: + { + // + // Set the receive buffer level to 1/2 of the buffer length. + // + psUART->ui16RxBufferLevel = psUART->ui16RxBufferLen / 2; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 3/4 + // (6/8) full. + // + case SOFTUART_CONFIG_RXLVL_6: + { + // + // Set the receive buffer level to 3/4 of the buffer length. + // + psUART->ui16RxBufferLevel = (psUART->ui16RxBufferLen * 3) / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 7/8 + // full. + // + case SOFTUART_CONFIG_RXLVL_7: + { + // + // Set the receive buffer level to 7/8 of the buffer length. + // + psUART->ui16RxBufferLevel = (psUART->ui16RxBufferLen * 7) / 8; + + // + // This setting has been handled. + // + break; + } + } +} + +//***************************************************************************** +// +//! Sets the buffer level at which ``interrupts'' are generated. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32TxLevel is the transmit buffer ``interrupt'' level, specified as +//! one of \b UART_FIFO_TX1_8, \b UART_FIFO_TX2_8, \b UART_FIFO_TX4_8, +//! \b UART_FIFO_TX6_8, or \b UART_FIFO_TX7_8. +//! \param ui32RxLevel is the receive buffer ``interrupt'' level, specified as +//! one of \b UART_FIFO_RX1_8, \b UART_FIFO_RX2_8, \b UART_FIFO_RX4_8, +//! \b UART_FIFO_RX6_8, or \b UART_FIFO_RX7_8. +//! +//! This function sets the buffer level at which transmit and receive +//! ``interrupts'' are generated. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTFIFOLevelSet(tSoftUART *psUART, uint32_t ui32TxLevel, + uint32_t ui32RxLevel) +{ + // + // Check the arguments. + // + ASSERT((ui32TxLevel == SOFTUART_FIFO_TX1_8) || + (ui32TxLevel == SOFTUART_FIFO_TX2_8) || + (ui32TxLevel == SOFTUART_FIFO_TX4_8) || + (ui32TxLevel == SOFTUART_FIFO_TX6_8) || + (ui32TxLevel == SOFTUART_FIFO_TX7_8)); + ASSERT((ui32RxLevel == SOFTUART_FIFO_RX1_8) || + (ui32RxLevel == SOFTUART_FIFO_RX2_8) || + (ui32RxLevel == SOFTUART_FIFO_RX4_8) || + (ui32RxLevel == SOFTUART_FIFO_RX6_8) || + (ui32RxLevel == SOFTUART_FIFO_RX7_8)); + + // + // Save the buffer "interrupt" levels. + // + psUART->ui16Config = ((psUART->ui16Config & SOFTUART_CONFIG_BASE_M) | + ((ui32TxLevel | ui32RxLevel) << 8)); + + // + // Compute the new buffer "interrupt" levels. + // + SoftUARTTxLevelSet(psUART); + SoftUARTRxLevelSet(psUART); +} + +//***************************************************************************** +// +//! Gets the buffer level at which ``interrupts'' are generated. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui32TxLevel is a pointer to storage for the transmit buffer level, +//! returned as one of \b UART_FIFO_TX1_8, \b UART_FIFO_TX2_8, +//! \b UART_FIFO_TX4_8, \b UART_FIFO_TX6_8, or \b UART_FIFO_TX7_8. +//! \param pui32RxLevel is a pointer to storage for the receive buffer level, +//! returned as one of \b UART_FIFO_RX1_8, \b UART_FIFO_RX2_8, +//! \b UART_FIFO_RX4_8, \b UART_FIFO_RX6_8, or \b UART_FIFO_RX7_8. +//! +//! This function gets the buffer level at which transmit and receive +//! ``interrupts'' are generated. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTFIFOLevelGet(tSoftUART *psUART, uint32_t *pui32TxLevel, + uint32_t *pui32RxLevel) +{ + // + // Extract the transmit and receive buffer levels. + // + *pui32TxLevel = (psUART->ui16Config & SOFTUART_CONFIG_TXLVL_M) >> 8; + *pui32RxLevel = (psUART->ui16Config & SOFTUART_CONFIG_RXLVL_M) >> 8; +} + +//***************************************************************************** +// +//! Gets the current configuration of a UART. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui32Config is a pointer to storage for the data format. +//! +//! Returns the data format of the SoftUART. The data format returned in +//! \e pui32Config is enumerated the same as the \e ui32Config parameter of +//! SoftUARTConfigSet(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTConfigGet(tSoftUART *psUART, uint32_t *pui32Config) +{ + // + // Get the data format. + // + *pui32Config = psUART->ui16Config & SOFTUART_CONFIG_BASE_M; +} + +//***************************************************************************** +// +//! Enables the SoftUART. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function enables the SoftUART, allowing data to be transmitted and +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTEnable(tSoftUART *psUART) +{ + // + // Enable the SoftUART. + // + psUART->ui8Flags |= SOFTUART_FLAG_ENABLE; +} + +//***************************************************************************** +// +//! Disables the SoftUART. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function disables the SoftUART after waiting for it to become idle. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTDisable(tSoftUART *psUART) +{ + // + // Wait for end of TX. + // + while(SoftUARTBusy(psUART)) + { + } + + // + // Disable the SoftUART. + // + psUART->ui8Flags &= ~(SOFTUART_FLAG_ENABLE); +} + +//***************************************************************************** +// +//! Determines if there are any characters in the receive buffer. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function returns a flag indicating whether or not there is data +//! available in the receive buffer. +//! +//! \return Returns \b true if there is data in the receive buffer or \b false +//! if there is no data in the receive buffer. +// +//***************************************************************************** +bool +SoftUARTCharsAvail(tSoftUART *psUART) +{ + // + // Return the availability of characters. + // + return((psUART->ui16RxBufferRead == psUART->ui16RxBufferWrite) ? false : + true); +} + +//***************************************************************************** +// +//! Determines if there is any space in the transmit buffer. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function returns a flag indicating whether or not there is space +//! available in the transmit buffer. +//! +//! \return Returns \b true if there is space available in the transmit buffer +//! or \b false if there is no space available in the transmit buffer. +// +//***************************************************************************** +bool +SoftUARTSpaceAvail(tSoftUART *psUART) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psUART->ui16TxBufferWrite + 1; + if(ui16Temp == psUART->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // Return the availability of space. + // + return((psUART->ui16TxBufferRead == ui16Temp) ? false : true); +} + +//***************************************************************************** +// +//! Handles the deassertion of the receive ``interrupts''. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function is used to determine when to deassert the receive +//! ``interrupt'' as a result of reading data from the receive buffer. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTRxReadInt(tSoftUART *psUART) +{ + uint32_t ui32Temp; + + // + // Determine the number of characters in the receive buffer. + // + if(psUART->ui16RxBufferWrite > psUART->ui16RxBufferRead) + { + ui32Temp = psUART->ui16RxBufferWrite - psUART->ui16RxBufferRead; + } + else + { + ui32Temp = (psUART->ui16RxBufferLen + psUART->ui16RxBufferWrite - + psUART->ui16RxBufferRead); + } + + // + // See if the number of characters in the receive buffer have dropped below + // the receive trigger level. + // + if(ui32Temp < psUART->ui16RxBufferLevel) + { + // + // Deassert the receive "interrupt". + // + psUART->ui16IntStatus &= ~(SOFTUART_INT_RX); + } + + // + // See if the receive buffer is now empty. + // + if(ui32Temp == 0) + { + // + // Deassert the receive timeout "interrupt". + // + psUART->ui16IntStatus &= ~(SOFTUART_INT_RT); + } +} + +//***************************************************************************** +// +//! Receives a character from the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! Gets a character from the receive buffer for the specified port. +//! +//! \return Returns the character read from the specified port, cast as a +//! \e int32_t. A \b -1 is returned if there are no characters present in the +//! receive buffer. The SoftUARTCharsAvail() function should be called before +//! attempting to call this function. +// +//***************************************************************************** +int32_t +SoftUARTCharGetNonBlocking(tSoftUART *psUART) +{ + int32_t i32Temp; + + // + // See if there are any characters in the receive buffer. + // + if(psUART->ui16RxBufferRead != psUART->ui16RxBufferWrite) + { + // + // Read the next character. + // + i32Temp = psUART->pui16RxBuffer[psUART->ui16RxBufferRead]; + psUART->ui16RxBufferRead++; + if(psUART->ui16RxBufferRead == psUART->ui16RxBufferLen) + { + psUART->ui16RxBufferRead = 0; + } + + // + // Deassert the receive "interrupt(s)" if appropriate. + // + SoftUARTRxReadInt(psUART); + + // + // Set the receive status to match this character. + // + psUART->ui8RxStatus = + ((psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN) | + ((i32Temp >> 8) & ~(SOFTUART_RXERROR_OVERRUN))); + + // + // Return this character. + // + return(i32Temp); + } + else + { + // + // There are no characters, so return a failure. + // + return(-1); + } +} + +//***************************************************************************** +// +//! Waits for a character from the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! Gets a character from the receive buffer for the specified port. If there +//! are no characters available, this function waits until a character is +//! received before returning. +//! +//! \return Returns the character read from the specified port, cast as a +//! \e int32_t. +// +//***************************************************************************** +int32_t +SoftUARTCharGet(tSoftUART *psUART) +{ + int32_t i32Temp; + + // + // Wait until a int8_t is available. + // + while(psUART->ui16RxBufferRead == + *(volatile uint16_t *)(&(psUART->ui16RxBufferWrite))) + { + } + + // + // Read the next character. + // + i32Temp = psUART->pui16RxBuffer[psUART->ui16RxBufferRead]; + psUART->ui16RxBufferRead++; + if(psUART->ui16RxBufferRead == psUART->ui16RxBufferLen) + { + psUART->ui16RxBufferRead = 0; + } + + // + // Deassert the receive "interrupt(s)" if appropriate. + // + SoftUARTRxReadInt(psUART); + + // + // Set the receive status to match this character. + // + psUART->ui8RxStatus = ((psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN) | + ((i32Temp >> 8) & ~(SOFTUART_RXERROR_OVERRUN))); + + // + // Return this character. + // + return(i32Temp); +} + +//***************************************************************************** +// +//! Sends a character to the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui8Data is the character to be transmitted. +//! +//! Writes the character \e ui8Data to the transmit buffer for the specified +//! port. This function does not block, so if there is no space available, +//! then a \b false is returned, and the application must retry the function +//! later. +//! +//! \return Returns \b true if the character was successfully placed in the +//! transmit buffer or \b false if there was no space available in the +//! transmit buffer. +// +//***************************************************************************** +bool +SoftUARTCharPutNonBlocking(tSoftUART *psUART, uint8_t ui8Data) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psUART->ui16TxBufferWrite + 1; + if(ui16Temp == psUART->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // See if there is space in the transmit buffer. + // + if(ui16Temp != psUART->ui16TxBufferRead) + { + // + // Write this character to the transmit buffer. + // + psUART->pui8TxBuffer[psUART->ui16TxBufferWrite] = ui8Data; + psUART->ui16TxBufferWrite = ui16Temp; + + // + // Success. + // + return(true); + } + else + { + // + // There is no space in the transmit buffer, so return a failure. + // + return(false); + } +} + +//***************************************************************************** +// +//! Waits to send a character from the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui8Data is the character to be transmitted. +//! +//! Sends the character \e ui8Data to the transmit buffer for the specified +//! port. If there is no space available in the transmit buffer, this function +//! waits until there is space available before returning. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTCharPut(tSoftUART *psUART, uint8_t ui8Data) +{ + uint16_t ui16Temp; + + // + // Wait until space is available. + // + ui16Temp = psUART->ui16TxBufferWrite + 1; + if(ui16Temp == psUART->ui16TxBufferLen) + { + ui16Temp = 0; + } + while(ui16Temp == *(volatile uint16_t *)(&(psUART->ui16TxBufferRead))) + { + } + + // + // Send the int8_t. + // + psUART->pui8TxBuffer[psUART->ui16TxBufferWrite] = ui8Data; + psUART->ui16TxBufferWrite = ui16Temp; +} + +//***************************************************************************** +// +//! Causes a BREAK to be sent. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param bBreakState controls the output level. +//! +//! Calling this function with \e bBreakState set to \b true asserts a break +//! condition on the SoftUART. Calling this function with \e bBreakState set +//! to \b false removes the break condition. For proper transmission of a +//! break command, the break must be asserted for at least two complete frames. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTBreakCtl(tSoftUART *psUART, bool bBreakState) +{ + // + // Set the break condition as requested. + // + if(bBreakState) + { + psUART->ui8Flags |= SOFTUART_FLAG_TXBREAK; + } + else + { + psUART->ui8Flags &= ~(SOFTUART_FLAG_TXBREAK); + } +} + +//***************************************************************************** +// +//! Determines whether the UART transmitter is busy or not. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! Allows the caller to determine whether all transmitted bytes have cleared +//! the transmitter hardware. If \b false is returned, the transmit buffer is +//! empty and all bits of the last transmitted character, including all stop +//! bits, have left the hardware shift register. +//! +//! \return Returns \b true if the UART is transmitting or \b false if all +//! transmissions are complete. +// +//***************************************************************************** +bool +SoftUARTBusy(tSoftUART *psUART) +{ + // + // Determine if the UART is busy. + // + return(((psUART->ui8TxState == SOFTUART_TXSTATE_IDLE) && + (((psUART->ui8Flags & SOFTUART_FLAG_ENABLE) == 0) || + (psUART->ui16TxBufferRead == psUART->ui16TxBufferWrite))) ? + false : true); +} + +//***************************************************************************** +// +//! Enables individual SoftUART ``interrupt'' sources. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32IntFlags is the bit mask of the ``interrupt'' sources to be +//! enabled. +//! +//! Enables the indicated SoftUART ``interrupt'' sources. Only the sources +//! that are enabled can be reflected to the SoftUART callback. +//! +//! The \e ui32IntFlags parameter is the logical OR of any of the following: +//! +//! - \b SOFTUART_INT_OE - Overrun Error ``interrupt'' +//! - \b SOFTUART_INT_BE - Break Error ``interrupt'' +//! - \b SOFTUART_INT_PE - Parity Error ``interrupt'' +//! - \b SOFTUART_INT_FE - Framing Error ``interrupt'' +//! - \b SOFTUART_INT_RT - Receive Timeout ``interrupt'' +//! - \b SOFTUART_INT_TX - Transmit ``interrupt'' +//! - \b SOFTUART_INT_RX - Receive ``interrupt'' +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntEnable(tSoftUART *psUART, uint32_t ui32IntFlags) +{ + // + // Enable the specified interrupts. + // + psUART->ui16IntMask |= ui32IntFlags; +} + +//***************************************************************************** +// +//! Disables individual SoftUART ``interrupt'' sources. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32IntFlags is the bit mask of the ``interrupt'' sources to be +//! disabled. +//! +//! Disables the indicated SoftUART ``interrupt'' sources. Only the sources +//! that are enabled can be reflected to the SoftUART callback. +//! +//! The \e ui32IntFlags parameter has the same definition as the +//! \e ui32IntFlags parameter to SoftUARTIntEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntDisable(tSoftUART *psUART, uint32_t ui32IntFlags) +{ + // + // Disable the specified interrupts. + // + psUART->ui16IntMask &= ~(ui32IntFlags); +} + +//***************************************************************************** +// +//! Gets the current SoftUART ``interrupt'' status. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param bMasked is \b false if the raw ``interrupt'' status is required and +//! \b true if the masked ``interrupt'' status is required. +//! +//! This returns the ``interrupt'' status for the SoftUART. Either the raw +//! ``interrupt'' status or the status of ``interrupts'' that are allowed to +//! reflect to the SoftUART callback can be returned. +//! +//! \return Returns the current ``interrupt'' status, enumerated as a bit field +//! of values described in SoftUARTIntEnable(). +// +//***************************************************************************** +uint32_t +SoftUARTIntStatus(tSoftUART *psUART, bool bMasked) +{ + // + // Return either the interrupt status or the raw interrupt status as + // requested. + // + if(bMasked) + { + return(psUART->ui16IntStatus & psUART->ui16IntMask); + } + else + { + return(psUART->ui16IntStatus); + } +} + +//***************************************************************************** +// +//! Clears SoftUART ``interrupt'' sources. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! cleared. +//! +//! The specified SoftUART ``interrupt'' sources are cleared, so that they no +//! longer assert. This function must be called in the callback function to +//! keep the ``interrupt'' from being recognized again immediately upon exit. +//! +//! The \e ui32IntFlags parameter has the same definition as the +//! \e ui32IntFlags parameter to SoftUARTIntEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntClear(tSoftUART *psUART, uint32_t ui32IntFlags) +{ + // + // Clear the requested interrupt sources. + // + psUART->ui16IntStatus &= ~(ui32IntFlags); +} + +//***************************************************************************** +// +//! Gets current receiver errors. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function returns the current state of each of the 4 receiver error +//! sources. The returned errors are equivalent to the four error bits +//! returned via the previous call to SoftUARTCharGet() or +//! SoftUARTCharGetNonBlocking() with the exception that the overrun error is +//! set immediately when the overrun occurs rather than when a character is +//! next read. +//! +//! \return Returns a logical OR combination of the receiver error flags, +//! \b SOFTUART_RXERROR_FRAMING, \b SOFTUART_RXERROR_PARITY, +//! \b SOFTUART_RXERROR_BREAK and \b SOFTUART_RXERROR_OVERRUN. +// +//***************************************************************************** +uint32_t +SoftUARTRxErrorGet(tSoftUART *psUART) +{ + // + // Return the current value of the receive status. + // + return(psUART->ui8RxStatus); +} + +//***************************************************************************** +// +//! Clears all reported receiver errors. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function is used to clear all receiver error conditions reported via +//! SoftUARTRxErrorGet(). If using the overrun, framing error, parity error or +//! break interrupts, this function must be called after clearing the interrupt +//! to ensure that later errors of the same type trigger another interrupt. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTRxErrorClear(tSoftUART *psUART) +{ + // + // Clear any receive error status. + // + psUART->ui8RxStatus = 0; +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pfnCallback is a pointer to the callback function. +//! +//! This function sets the address of the callback function that is called when +//! there is an ``interrupt'' produced by the SoftUART module. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTCallbackSet(tSoftUART *psUART, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + psUART->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftUART Tx signal. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used when the SoftUART must assert +//! the Tx signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTTxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Tx signal. + // + if(ui32Base == 0) + { + psUART->ui32TxGPIO = 0; + } + else + { + psUART->ui32TxGPIO = ui32Base + (ui8Pin << 2); + } +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftUART Rx signal. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used when the SoftUART must sample +//! the Rx signal. If there is not a GPIO pin allocated for Rx, the SoftUART +//! module will not read data from the slave device. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTRxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Rx signal. + // + if(ui32Base == 0) + { + psUART->ui32RxGPIOPort = 0; + psUART->ui8RxPin = 0; + } + else + { + psUART->ui32RxGPIOPort = ui32Base; + psUART->ui8RxPin = ui8Pin; + } +} + +//***************************************************************************** +// +//! Sets the transmit buffer for a SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui8TxBuffer is the address of the transmit buffer. +//! \param ui16Len is the size, in 8-bit bytes, of the transmit buffer. +//! +//! This function sets the address and size of the transmit buffer. It also +//! resets the read and write pointers, marking the transmit buffer as empty. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTTxBufferSet(tSoftUART *psUART, uint8_t *pui8TxBuffer, uint16_t ui16Len) +{ + // + // Save the transmit buffer address and length. + // + psUART->pui8TxBuffer = pui8TxBuffer; + psUART->ui16TxBufferLen = ui16Len; + + // + // Reset the transmit buffer read and write pointers. + // + psUART->ui16TxBufferRead = 0; + psUART->ui16TxBufferWrite = 0; + + // + // Compute the new buffer "interrupt" level. + // + SoftUARTTxLevelSet(psUART); +} + +//***************************************************************************** +// +//! Sets the receive buffer for a SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui16RxBuffer is the address of the receive buffer. +//! \param ui16Len is the size, in 16-bit half-words, of the receive buffer. +//! +//! This function sets the address and size of the receive buffer. It also +//! resets the read and write pointers, marking the receive buffer as empty. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTRxBufferSet(tSoftUART *psUART, uint16_t *pui16RxBuffer, + uint16_t ui16Len) +{ + // + // Save the receive buffer address and length. + // + psUART->pui16RxBuffer = pui16RxBuffer; + psUART->ui16RxBufferLen = ui16Len; + + // + // Reset the receive read and write pointers. + // + psUART->ui16RxBufferRead = 0; + psUART->ui16RxBufferWrite = 0; + + // + // Compute the new buffer "interrupt" level. + // + SoftUARTRxLevelSet(psUART); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softuart.h b/utils/softuart.h new file mode 100644 index 0000000..d9184d7 --- /dev/null +++ b/utils/softuart.h @@ -0,0 +1,375 @@ +//***************************************************************************** +// +// softuart.h - Defines and macros for the SoftUART. +// +// Copyright (c) 2010-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 Utility Library. +// +//***************************************************************************** + +#ifndef __SOFTUART_H__ +#define __SOFTUART_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup softuart_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure contains the state of a single instance of a SoftUART +//! module. +// +//***************************************************************************** +typedef struct +{ + // + //! The address of the callback function that is called to simulate the + //! interrupts that would be produced by a hardware UART implementation. + //! This address can be set via a direct structure access or using the + //! SoftUARTCallbackSet function. + // + void (*pfnIntCallback)(void); + + // + //! The address of the GPIO pin to be used for the Tx signal. This member + //! can be set via a direct structure access or using the SoftUARTTxGPIOSet + //! function. + // + uint32_t ui32TxGPIO; + + // + //! The address of the GPIO port to be used for the Rx signal. This member + //! can be set via a direct structure access or using the SoftUARTRxGPIOSet + //! function. + // + uint32_t ui32RxGPIOPort; + + // + //! The address of the data buffer used for the transmit buffer. This + //! member can be set via a direct structure access or using the + //! SoftUARTTxBufferSet function. + // + uint8_t *pui8TxBuffer; + + // + //! The address of the data buffer used for the receive buffer. This + //! member can be set via a direct structure access or using the + //! SoftUARTRxBufferSet function. + // + uint16_t *pui16RxBuffer; + + // + //! The length of the transmit buffer. This member can be set via a direct + //! structure access or using the SoftUARTTxBufferSet function. + // + uint16_t ui16TxBufferLen; + + // + //! The index into the transmit buffer of the next character to be + //! transmitted. This member should not be accessed or modified by the + //! application. + // + uint16_t ui16TxBufferRead; + + // + //! The index into the transmit buffer of the next location to store a + //! character into the buffer. This member should not be accessed or + //! modified by the application. + // + uint16_t ui16TxBufferWrite; + + // + //! The transmit buffer level at which the transmit interrupt is asserted. + //! This member should not be accessed or modified by the application. + // + uint16_t ui16TxBufferLevel; + + // + //! The length of the receive buffer. This member can be set via a direct + //! structure access or using the SoftUARTRxBufferSet function. + // + uint16_t ui16RxBufferLen; + + // + //! The index into the receive buffer of the next character to be read from + //! the buffer. This member should not be accessed or modified by the + //! application. + // + uint16_t ui16RxBufferRead; + + // + //! The index into the receive buffer of the lcoation to store the next + //! character received. This member should not be accessed or modified by + //! the application. + // + uint16_t ui16RxBufferWrite; + + // + //! The receive buffer level at which the receive interrupt is asserted. + //! This member should not be accessed or modified by the application. + // + uint16_t ui16RxBufferLevel; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + uint16_t ui16IntStatus; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + uint16_t ui16IntMask; + + // + //! The configuration of the SoftUART module. This member can be set via + //! the SoftUARTConfigSet and SoftUARTFIFOLevelSet functions. + // + uint16_t ui16Config; + + // + //! The flags that control the operation of the SoftUART module. This + //! member should not be be accessed or modified by the application. + // + uint8_t ui8Flags; + + // + //! The current state of the SoftUART transmit state machine. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8TxState; + + // + //! The value that is written to the Tx pin at the start of the next + //! transmit timer tick. This member should not be accessed or modified + //! by the application. + // + uint8_t ui8TxNext; + + // + //! The character that is currently be sent via the Tx pin. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8TxData; + + // + //! The GPIO pin to be used for the Rx signal. This member can be set via + //! a direct structure access or using the SoftUARTRxGPIOSet function. + // + uint8_t ui8RxPin; + + // + //! The current state of the SoftUART receive state machine. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8RxState; + + // + //! The character that is currently being received via the Rx pin. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8RxData; + + // + //! The flags that indicate any errors that have occurred during the + //! reception of the current character via the Rx pin. This member should + //! not be accessed or modified by the application. + // + uint8_t ui8RxFlags; + + // + //! The receive error status. This member should only be accessed via the + //! SoftUARTRxErrorGet and SoftURATRxErrorClear functions. + // + uint8_t ui8RxStatus; +} +tSoftUART; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Values that can be passed to SoftUARTIntEnable(), SoftUARTIntDisable(), and +// SoftUARTIntClear() as the ui32IntFlags parameter, and returned from +// SoftUARTIntStatus(). +// +//***************************************************************************** +#define SOFTUART_INT_EOT 0x800 // End of transmission interrupt +#define SOFTUART_INT_OE 0x400 // Overrun error interrupt +#define SOFTUART_INT_BE 0x200 // Break error interrupt +#define SOFTUART_INT_PE 0x100 // Parity error interrupt +#define SOFTUART_INT_FE 0x080 // Framing error interrupt +#define SOFTUART_INT_RT 0x040 // Receive timeout interrupt +#define SOFTUART_INT_TX 0x020 // Transmit interrupt +#define SOFTUART_INT_RX 0x010 // Receive interrupt + +//***************************************************************************** +// +// Values that can be passed to SoftUARTConfigSet() as the ui32Config parameter +// and returned by SoftUARTConfigGet() in the pui32Config parameter. +// Additionally, the UART_CONFIG_PAR_* subset can be passed to +// SoftUARTParityModeSet() as the ui32Parity parameter, and are returned by +// SoftUARTParityModeGet(). +// +//***************************************************************************** +#define SOFTUART_CONFIG_WLEN_MASK \ + 0x00000060 // Mask for extracting word length +#define SOFTUART_CONFIG_WLEN_8 0x00000060 // 8 bit data +#define SOFTUART_CONFIG_WLEN_7 0x00000040 // 7 bit data +#define SOFTUART_CONFIG_WLEN_6 0x00000020 // 6 bit data +#define SOFTUART_CONFIG_WLEN_5 0x00000000 // 5 bit data +#define SOFTUART_CONFIG_STOP_MASK \ + 0x00000008 // Mask for extracting stop bits +#define SOFTUART_CONFIG_STOP_ONE \ + 0x00000000 // One stop bit +#define SOFTUART_CONFIG_STOP_TWO \ + 0x00000008 // Two stop bits +#define SOFTUART_CONFIG_PAR_MASK \ + 0x00000086 // Mask for extracting parity +#define SOFTUART_CONFIG_PAR_NONE \ + 0x00000000 // No parity +#define SOFTUART_CONFIG_PAR_EVEN \ + 0x00000006 // Even parity +#define SOFTUART_CONFIG_PAR_ODD 0x00000002 // Odd parity +#define SOFTUART_CONFIG_PAR_ONE 0x00000082 // Parity bit is one +#define SOFTUART_CONFIG_PAR_ZERO \ + 0x00000086 // Parity bit is zero +#define SOFTUART_CONFIG_WLEN_S 5 + +//***************************************************************************** +// +// Values that can be passed to SoftUARTFIFOLevelSet() as the ui32TxLevel +// parameter and returned by SoftUARTFIFOLevelGet() in the pui32TxLevel. +// +//***************************************************************************** +#define SOFTUART_FIFO_TX1_8 0x00000000 // Transmit interrupt at 1/8 Full +#define SOFTUART_FIFO_TX2_8 0x00000001 // Transmit interrupt at 1/4 Full +#define SOFTUART_FIFO_TX4_8 0x00000002 // Transmit interrupt at 1/2 Full +#define SOFTUART_FIFO_TX6_8 0x00000003 // Transmit interrupt at 3/4 Full +#define SOFTUART_FIFO_TX7_8 0x00000004 // Transmit interrupt at 7/8 Full + +//***************************************************************************** +// +// Values that can be passed to SoftUARTFIFOLevelSet() as the ui32RxLevel +// parameter and returned by SoftUARTFIFOLevelGet() in the pui32RxLevel. +// +//***************************************************************************** +#define SOFTUART_FIFO_RX1_8 0x00000000 // Receive interrupt at 1/8 Full +#define SOFTUART_FIFO_RX2_8 0x00000008 // Receive interrupt at 1/4 Full +#define SOFTUART_FIFO_RX4_8 0x00000010 // Receive interrupt at 1/2 Full +#define SOFTUART_FIFO_RX6_8 0x00000018 // Receive interrupt at 3/4 Full +#define SOFTUART_FIFO_RX7_8 0x00000020 // Receive interrupt at 7/8 Full + +//***************************************************************************** +// +// Values returned from SoftUARTRxErrorGet(). +// +//***************************************************************************** +#define SOFTUART_RXERROR_OVERRUN \ + 0x00000008 // An overrun error occurred +#define SOFTUART_RXERROR_BREAK 0x00000004 // A break was received +#define SOFTUART_RXERROR_PARITY 0x00000002 // A parity error occurred +#define SOFTUART_RXERROR_FRAMING \ + 0x00000001 // A framing error occurred + +//***************************************************************************** +// +// Values returned from SoftUARTRxTick(). +// +//***************************************************************************** +#define SOFTUART_RXTIMER_NOP 0 // The timer should continue to run +#define SOFTUART_RXTIMER_END 1 // The timer should be stopped + +//***************************************************************************** +// +// API Function prototypes +// +//***************************************************************************** +extern void SoftUARTInit(tSoftUART *psUART); +extern void SoftUARTParityModeSet(tSoftUART *psUART, uint32_t ui32Parity); +extern uint32_t SoftUARTParityModeGet(tSoftUART *psUART); +extern void SoftUARTFIFOLevelSet(tSoftUART *psUART, uint32_t ui32TxLevel, + uint32_t ui32RxLevel); +extern void SoftUARTFIFOLevelGet(tSoftUART *psUART, uint32_t *pui32TxLevel, + uint32_t *pui32RxLevel); +extern void SoftUARTConfigSet(tSoftUART *psUART, uint32_t ui32Config); +extern void SoftUARTConfigGet(tSoftUART *psUART, uint32_t *pui32Config); +extern void SoftUARTEnable(tSoftUART *psUART); +extern void SoftUARTDisable(tSoftUART *psUART); +extern void SoftUARTFIFOEnable(tSoftUART *psUART); +extern void SoftUARTFIFODisable(tSoftUART *psUART); +extern bool SoftUARTCharsAvail(tSoftUART *psUART); +extern bool SoftUARTSpaceAvail(tSoftUART *psUART); +extern int32_t SoftUARTCharGetNonBlocking(tSoftUART *psUART); +extern int32_t SoftUARTCharGet(tSoftUART *psUART); +extern bool SoftUARTCharPutNonBlocking(tSoftUART *psUART, + uint8_t ui8Data); +extern void SoftUARTCharPut(tSoftUART *psUART, uint8_t ui8Data); +extern void SoftUARTBreakCtl(tSoftUART *psUART, bool bBreakState); +extern bool SoftUARTBusy(tSoftUART *psUART); +extern void SoftUARTIntEnable(tSoftUART *psUART, uint32_t ui32IntFlags); +extern void SoftUARTIntDisable(tSoftUART *psUART, uint32_t ui32IntFlags); +extern uint32_t SoftUARTIntStatus(tSoftUART *psUART, bool bMasked); +extern void SoftUARTIntClear(tSoftUART *psUART, uint32_t ui32IntFlags); +extern uint32_t SoftUARTRxErrorGet(tSoftUART *psUART); +extern void SoftUARTRxErrorClear(tSoftUART *psUART); +extern uint32_t SoftUARTRxTick(tSoftUART *psUART, bool bEdgeInt); +extern void SoftUARTTxIntModeSet(tSoftUART *psUART, uint32_t ui32Mode); +extern uint32_t SoftUARTTxIntModeGet(tSoftUART *psUART); +extern void SoftUARTTxTimerTick(tSoftUART *psUART); +extern void SoftUARTCallbackSet(tSoftUART *psUART, void (*pfnCallback)(void)); +extern void SoftUARTTxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftUARTRxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftUARTTxBufferSet(tSoftUART *psUART, uint8_t *pui8TxBuffer, + uint16_t ui16Len); +extern void SoftUARTRxBufferSet(tSoftUART *psUART, uint16_t *pui16RxBuffer, + uint16_t ui16Len); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SOFTUART_H__ diff --git a/utils/speexlib.c b/utils/speexlib.c new file mode 100644 index 0000000..3a58ebc --- /dev/null +++ b/utils/speexlib.c @@ -0,0 +1,377 @@ +//***************************************************************************** +// +// speexlib.c - interface to the speex coder/encoder library. +// +// Copyright (c) 2009-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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "third_party/speex-1.2rc1/include/speex/speex.h" +#include "third_party/speex-1.2rc1/include/speex/speex_header.h" +#include "utils/speexlib.h" +#include "driverlib/debug.h" + +//***************************************************************************** +// +// The private structure that is used by the speex encoder or decoder for +// holding all state information for an encoder or decoder. +// +//***************************************************************************** +typedef struct +{ + // + // Holds the state of the decoder. + // + void *pvState; + + // + // Holds bits so they can be read and written to by the Speex routines + // + SpeexBits sBits; + + // + // Holds the header information for the current file. + // + SpeexHeader sHeader; + + // + // The current Segment table for the stream. + // + uint8_t pui8SegTable[256]; + + // + // The size of the current Segment table. + // + uint8_t ui8SegTableSize; + + // + // The current active page in a segment. + // + uint8_t ui8PageCurrent; + + // + // Current state flags. + // + uint32_t ui32Flags; +} +tSpeexInstance; + +//***************************************************************************** +// +// The decoder and encoder instance data. +// +//***************************************************************************** +tSpeexInstance g_sSpeexDecoder, g_sSpeexEncoder; + +//***************************************************************************** +// +//! Initialize the decoder's state to prepare for decoding new frames. +//! +//! This function will initializes the decoder so that it is prepared to start +//! receiving frames to decode. +//! +//! \return This function returns 0. +// +//***************************************************************************** +int32_t +SpeexDecodeInit(void) +{ + int iTemp; + + // + // Clear out the flags for this instance. + // + g_sSpeexDecoder.ui32Flags = 0; + + // + // Create a new decoder state in narrow band mode. + // + g_sSpeexDecoder.pvState = speex_decoder_init(&speex_nb_mode); + + // + // Disable enhanced decoding to reduce processing requirements. + // + iTemp = 0; + speex_decoder_ctl(g_sSpeexDecoder.pvState, SPEEX_SET_ENH, &iTemp); + + // + // Initialization of the structure that holds the bits. + // + speex_bits_init(&g_sSpeexDecoder.sBits); + + return(0); +} + +//***************************************************************************** +// +//! This function returns the current frame size from the decoder. +//! +//! This function queries the decoder for the current decode frame size in byte +//! and returns it to the caller. +//! +//! \return The current decoder frame size. +// +//***************************************************************************** +int32_t +SpeexDecodeFrameSizeGet(void) +{ + int iFrameSize; + + // + // Return 0 if the wrong request is made. + // + iFrameSize = 0; + + // + // Query the decoder for the current frame size. + // + speex_decoder_ctl(g_sSpeexDecoder.pvState, SPEEX_GET_FRAME_SIZE, + &iFrameSize); + + return(iFrameSize); +} + +//***************************************************************************** +// +//! This function decodes a single frame of Speex encoded audio. +//! +//! \param pui8InBuffer is the buffer that contains the Speex encoded audio. +//! \param ui32InSize is the number of valid bytes in the \e pui8InBuffer +//! buffer. +//! \param pui8OutBuffer is a pointer to the buffer to store decoded audio. +//! \param ui32OutSize is the size of the buffer pointed to by the +//! \e pui8OutBuffer pointer. +//! +//! This function will take a buffer of Speex encoded audio and decode it into +//! raw PCM audio. The \e pui16InBuffer parameter should contain a single +//! frame encoded Speex audio. The \e pui8OutBuffer will contain the decoded +//! audio after returning from this function. +//! +//! \return This function returns the number of decoded bytes in the +//! \e pui8OutBuffer buffer. +// +//***************************************************************************** +int32_t +SpeexDecode(uint8_t *pui8InBuffer, uint32_t ui32InSize, uint8_t *pui8OutBuffer, + uint32_t ui32OutSize) +{ + int32_t i32Bytes; + + // + // Read in the bit stream to the Speex library. + // + speex_bits_read_from(&g_sSpeexDecoder.sBits, (char *)pui8InBuffer, + ui32InSize); + + // + // Decode one frame of data. + // + i32Bytes = speex_decode_int(g_sSpeexDecoder.pvState, + &g_sSpeexDecoder.sBits, + (int16_t *)pui8OutBuffer); + + return(i32Bytes); +} + +//***************************************************************************** +// +//! This function sets the current quality setting for the Speex encoder. +//! +//! \param iQuality is the new Quality setting to use for the Speex encoder. +//! +//! This function will use the \e iQuality setting as the new quality setting +//! for the Speex encoder. +//! +//! \return This function returns 0. +// +//***************************************************************************** +int32_t +SpeexEncodeQualitySet(int iQuality) +{ + // + // Set the current encoder quality setting. + // + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_SET_QUALITY, &iQuality); + + return(0); +} + +//***************************************************************************** +// +//! This function returns the current frame size from the encoder. +//! +//! This function queries the encoder for the current encode frame size in byte +//! and returns it to the caller. +//! +//! \return The current encoder frame size. +// +//***************************************************************************** +int32_t +SpeexEncodeFrameSizeGet(void) +{ + int iFrameSize; + + // + // Return 0 if the wrong request is made. + // + iFrameSize = 0; + + // + // Query the encoder for the current frame size. + // + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_GET_FRAME_SIZE, + &iFrameSize); + + return(iFrameSize); +} + +//***************************************************************************** +// +//! Initialize the encoder's state to prepare for encoding new frames. +//! +//! \param iSampleRate is the sample rate of the incoming audio. +//! \param iComplexity is the complexity setting for the encoder. +//! \param iQuality is the quality setting for the encoder. +//! +//! This function will initializes the encoder by setting the sample rate, +//! complexity and quality settings. The \e iComplexity and \e iQuality +//! settings are explained further in the Speex documentation. +//! +//! \return This function returns 0. +// +//***************************************************************************** +int32_t +SpeexEncodeInit(int iSampleRate, int iComplexity, int iQuality) +{ + const SpeexMode *psMode; + + // + // Clear out the flags for this instance. + // + g_sSpeexEncoder.ui32Flags = 0; + + // + // Read out the current encoder mode. + // + psMode = speex_lib_get_mode(SPEEX_MODEID_NB); + + // + // Create a new decoder state in narrow band mode. + // + g_sSpeexEncoder.pvState = speex_encoder_init(psMode); + + // + // Initialize the bit stream. + // + speex_bits_init(&g_sSpeexEncoder.sBits); + + // + // Set the quality. + // + SpeexEncodeQualitySet(iQuality); + + // + // Set the complexity and sample rate for the encoder. + // + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_SET_COMPLEXITY, + &iComplexity); + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_SET_SAMPLING_RATE, + &iSampleRate); + + return(0); +} + +//***************************************************************************** +// +//! Encode a single frame of speex encoded audio. +//! +//! \param pui16InBuffer is the buffer that contains the raw PCM audio. +//! \param ui32InSize is the number of valid bytes in the \e pui16InBuffer +//! buffer. +//! \param pui8OutBuffer is a pointer to the buffer to store the encoded audio. +//! \param ui32OutSize is the size of the buffer pointed to by the +//! \e pui8OutBuffer pointer. +//! +//! This function will take a buffer of PCM audio and encode it into a frame +//! of speex compressed audio. The \e pui16InBuffer parameter should contain +//! a single frame of PCM audio. The \e pui8OutBuffer will contain the encoded +//! audio after returning from this function. +//! +//! \return This function returns the number of encoded bytes in the +//! \e pui8OutBuffer parameter. +// +//***************************************************************************** +int32_t +SpeexEncode(int16_t *pui16InBuffer, uint32_t ui32InSize, + uint8_t *pui8OutBuffer, uint32_t ui32OutSize) +{ + int32_t i32Bytes; + + // + // Reset the bit stream before encoding a new frame. + // + speex_bits_reset(&g_sSpeexEncoder.sBits); + + // + // Encode a single frame. + // + speex_encode_int(g_sSpeexEncoder.pvState, pui16InBuffer, + &g_sSpeexEncoder.sBits); + + // + // Read the PCM data from the encoded bit stream. + // + i32Bytes = speex_bits_write(&g_sSpeexEncoder.sBits, (char *)pui8OutBuffer, + ui32OutSize); + + // + // Return the number of bytes in the PCM data. + // + return(i32Bytes); +} + +//***************************************************************************** +// +// This is called by speex in the event of a fatal error. +// +//***************************************************************************** +void +_speex_fatal(const int8_t *str, const int8_t *file, int line) +{ + ASSERT(0); + while(1) + { + } +} + +//***************************************************************************** +// +// Speex wrapper for putc so that it does not use any file writing library +// functions. The speex library uses some file access for debug printing, this +// will disable this feature. +// +//***************************************************************************** +void +_speex_putc(int ch, void *file) +{ +} diff --git a/utils/speexlib.h b/utils/speexlib.h new file mode 100644 index 0000000..5b3f712 --- /dev/null +++ b/utils/speexlib.h @@ -0,0 +1,63 @@ +//***************************************************************************** +// +// speexlib.h - interface to the speex coder/encoder library. +// +// Copyright (c) 2009-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 Utility Library. +// +//***************************************************************************** + +#ifndef __SPEEXLIB_H__ +#define __SPEEXLIB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern int32_t SpeexEncodeInit(int iSampleRate, int iComplexity, int iQuality); +extern int32_t SpeexEncode(int16_t *pui16InBuffer, uint32_t ui32InSize, + uint8_t *pui8OutBuffer, uint32_t ui32OutSize); +extern int32_t SpeexEncodeQualitySet(int iQuality); +extern int32_t SpeexEncodeFrameSizeGet(void); +extern int32_t SpeexDecodeFrameSizeGet(void); +extern int32_t SpeexDecodeInit(void); +extern int32_t SpeexDecode(uint8_t *pui8InBuffer, uint32_t ui32InSize, + uint8_t *pui8OutBuffer, uint32_t ui32OutSize); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SPEEXLIB_H__ diff --git a/utils/spi_flash.c b/utils/spi_flash.c new file mode 100644 index 0000000..6e5b24a --- /dev/null +++ b/utils/spi_flash.c @@ -0,0 +1,2484 @@ +//***************************************************************************** +// +// spi_flash.c - Driver for a SPI flash that supports the "Intel" SPI flash +// command set, capable of utilizing Bi-SPI and Quad-SPI. +// +// Copyright (c) 2012-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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_ssi.h" +#include "inc/hw_types.h" +#include "inc/hw_udma.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/ssi.h" +#include "driverlib/udma.h" +#include "utils/spi_flash.h" + +//***************************************************************************** +// +//! \addtogroup spi_flash_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The commands that can be sent to the SPI flash. This is the "generic" +// command set that is supported by a wide number of SPI flashes. +// +//***************************************************************************** +#define CMD_WRSR 0x01 // Write status register +#define CMD_PP 0x02 // Page program +#define CMD_READ 0x03 // Read data +#define CMD_WRDI 0x04 // Disable writes +#define CMD_RDSR 0x05 // Read status register +#define CMD_WREN 0x06 // Enable writes +#define CMD_FREAD 0x0b // Fast read data +#define CMD_SE 0x20 // Sector erase (4K) +#define CMD_DREAD 0x3b // 1 in 2 out read data +#define CMD_BE32 0x52 // Block erase (32K) +#define CMD_QREAD 0x6b // 1 in 4 out read data +#define CMD_RDID 0x9f // Read JEDEC ID +#define CMD_CE 0xc7 // Chip erase +#define CMD_BE64 0xd8 // Block erase (64K) + +//***************************************************************************** +// +// The states for the SPI flash interrupt handler state machine. +// +//***************************************************************************** +#define STATE_IDLE 0 +#define STATE_CMD 1 +#define STATE_ADDR1 2 +#define STATE_ADDR2 3 +#define STATE_ADDR3 4 +#define STATE_READ_DUMMY 5 +#define STATE_READ_DATA_SETUP 6 +#define STATE_READ_DATA 7 +#define STATE_READ_DATA_DMA 8 +#define STATE_READ_DATA_END 9 +#define STATE_WRITE_DATA_SETUP 10 +#define STATE_WRITE_DATA 11 +#define STATE_WRITE_DATA_DMA 12 +#define STATE_WRITE_DATA_END 13 + +//***************************************************************************** +// +//! Handles SSI module interrupts for the SPI flash driver. +//! +//! \param pState is a pointer to the SPI flash driver instance data. +//! +//! This function handles SSI module interrupts that are generated as a result +//! of SPI flash driver operations. This must be called by the application in +//! response to the SSI module interrupt when using the SPIFlashxxxNonBlocking +//! APIs. +//! +//! \return Returns \b SPI_FLASH_IDLE if there is no transfer in progress, +//! \b SPI_FLASH_WORKING is the requested transfer is still in progress, or +//! \b SPI_FLASH_DONE if the requested transfer has completed. +// +//***************************************************************************** +uint32_t +SPIFlashIntHandler(tSPIFlashState *pState) +{ + uint32_t ui32Data, ui32Count; + + // + // Set the write count to four. This is the maximum number of bytes that + // will be written into the SSI transmit FIFO in the interrupt handler. + // Writing more might be possible but makes the latency of handling future + // SSI interrupt critical to preventing receive FIFO overruns. + // + ui32Count = 4; + + // + // Get the set of asserted and unmasked SSI module interrupts. Only some + // of these are directly handled; the others are implicitly handled via the + // operation of the state machine. + // + ui32Data = HWREG(pState->ui32Base + SSI_O_MIS); + + // + // See if the uDMA transmit complete interrupt has asserted. + // + if(ui32Data & SSI_MIS_DMATXMIS) + { + // + // Determine the size of the uDMA transfer based on the number of bytes + // left to write. + // + if(pState->ui32WriteCount > 1024) + { + // + // There are more than 1024 bytes left to transfer, so the uDMA + // transfer that just completed was for a full 1024 bytes. + // + pState->ui32WriteCount -= 1024; + + // + // If a page program is being performed, then the data buffer + // pointer needs to be incremented as well. + // + if(pState->ui16Cmd == CMD_PP) + { + // + // Increment the data buffer pointer. + // + pState->pui8Buffer += 1024; + + // + // See if there is more than one byte left to transfer. + // + if(pState->ui32WriteCount > 1) + { + // + // Configure the uDMA to transmit the next portion of the + // data buffer. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, + pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + } + } + } + else + { + // + // There are 1024 or less bytes left to transfer, so the uDMA + // transfer that just copmleted was for one less than the remaining + // transfer count. If a page program is being performed, then the + // data buffer pointer needs to be incremented. + // + if(pState->ui16Cmd == CMD_PP) + { + pState->pui8Buffer += (pState->ui32WriteCount - 1); + } + + // + // Set the remaining transfer count to 1. The final byte will be + // transferred with PIO since the end of frame flag needs to be set + // first. + // + pState->ui32WriteCount = 1; + } + + // + // Clear the uDMA transmit complete interrupt. + // + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + } + + // + // See if the uDMA receive complete interrupt has asserted. + // + if(ui32Data & SSI_MIS_DMARXMIS) + { + // + // Determine the size of the uDMA transfer based on the number of bytes + // left to read. + // + if(pState->ui32ReadCount >= 1024) + { + // + // There are 1024 or more bytes left to transfer, so the uDMA + // transfer that just completed was for a full 1024 bytes. + // + pState->ui32ReadCount -= 1024; + if(pState->ui32WriteCount != 0) + { + pState->ui32WriteCount -= 1024; + } + + // + // The data buffer pointer needs to be incremented as well. + // + pState->pui8Buffer += 1024; + + // + // See if there is additional data to transfer. + // + if(pState->ui32ReadCount != 0) + { + // + // Configure the transmit uDMA if there is more than one byte + // left to write. + // + if(pState->ui32WriteCount > 1) + { + // + // Configure the uDMA to transmit the next portion of the + // data buffer. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + } + + // + // Configure the uDMA to receive the next portion of the data + // buffer. + // + uDMAChannelTransferSet(pState->ui32RxChannel, UDMA_MODE_BASIC, + (void *)(pState->ui32Base + SSI_O_DR), + pState->pui8Buffer, + (pState->ui32ReadCount >= 1024) ? + 1024 : pState->ui32ReadCount); + + // + // Enable the uDMA receive channel. + // + uDMAChannelEnable(pState->ui32RxChannel); + + // + // If this is the final receive uDMA buffer and there is a + // transmit uDMA buffer associated, enable the DMA transmit + // interrupt. + // + if((pState->ui32ReadCount <= 1024) && + (pState->ui32WriteCount > 1)) + { + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM; + } + } + } + else + { + // + // There are less than 1024 bytes left to transfer, so the uDMA + // transfer that copmleted was for the remaining transfer count. + // + pState->ui32ReadCount = 0; + } + + // + // Clear the uDMA receive complete interrupt. + // + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC; + } + + // + // Drain the receive FIFO is not using uDMA. + // + if(!pState->bUseDMA) + { + // + // Loop while there is more data in the receive FIFO and more data to + // be read. + // + while((pState->ui32ReadCount != 0) && + (MAP_SSIDataGetNonBlocking(pState->ui32Base, &ui32Data) != 0)) + { + // + // Save this byte into the data buffer. + // + *(pState->pui8Buffer)++ = ui32Data & 0xff; + + // + // Decrement the read count. + // + pState->ui32ReadCount--; + } + } + + // + // The SPI flash state machine. Loop forever; the state machine will + // explicitly return to the caller when there is no further work that can + // be done without stalling. + // + while(1) + { + // + // Determine the current state. + // + switch(pState->ui16State) + { + // + // The state machine is idle. + // + case STATE_IDLE: + { + // + // Return indicating that the state machine is idle. This + // should never happen since no further interrupts should occur + // once the transfer has completed and the state machine goes + // into the idle state. + // + return(SPI_FLASH_IDLE); + } + + // + // The state machine is in the command state. + // + case STATE_CMD: + { + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_WRITE); + + // + // Attempt to write the command byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + pState->ui16Cmd) == 0) + { + // + // The command byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The command byte has been written, so move to the first + // address byte state. + // + pState->ui16State = STATE_ADDR1; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the first address byte state. + // + case STATE_ADDR1: + { + // + // Attempt to write the first address byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + (pState->ui32Addr >> 16) & + 0xff) == 0) + { + // + // The first address byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The first address byte has been written, so move to the + // second address byte state. + // + pState->ui16State = STATE_ADDR2; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the second address byte state. + // + case STATE_ADDR2: + { + // + // Attempt to write the second address byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + (pState->ui32Addr >> 8) & 0xff) == + 0) + { + // + // The second address byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The second address byte has been written, so move to the + // third address byte state. + // + pState->ui16State = STATE_ADDR3; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the third address byte state. + // + case STATE_ADDR3: + { + // + // Attempt to write the third address byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + pState->ui32Addr & 0xff) == 0) + { + // + // The third address byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The third address byte has been written, so determine + // the next state based on the command byte. + // + if(pState->ui16Cmd == CMD_PP) + { + // + // A page program is being performed, so move to the + // write data setup state. + // + pState->ui16State = STATE_WRITE_DATA_SETUP; + } + else if(pState->ui16Cmd == CMD_READ) + { + // + // A read is being performed, so move to the read data + // setup state. + // + pState->ui16State = STATE_READ_DATA_SETUP; + } + else + { + // + // The other forms of read (fast read, dual read, and + // quad read) all require a dummy byte. Move to the + // dummy byte state. + // + pState->ui16State = STATE_READ_DUMMY; + } + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the dummy byte state. + // + case STATE_READ_DUMMY: + { + // + // Attempt to write the dummy byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0) + { + // + // THe dummy byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The dummy byte has been written, so move to the read + // data setup state. + // + pState->ui16State = STATE_READ_DATA_SETUP; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the read data setup state. + // + case STATE_READ_DATA_SETUP: + { + // + // Set the SSI module into the appropriate mode based on the + // command byte. + // + if(pState->ui16Cmd == CMD_DREAD) + { + // + // Bi-SPI read mode is used for the dual read command. + // + MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_BI_READ); + } + else if(pState->ui16Cmd == CMD_QREAD) + { + // + // Quad-SPI read mode is used for the quad read command. + // + MAP_SSIAdvModeSet(pState->ui32Base, + SSI_ADV_MODE_QUAD_READ); + } + else + { + // + // Advanced read/write mode is used for the read and fast + // read commands. + // + MAP_SSIAdvModeSet(pState->ui32Base, + SSI_ADV_MODE_READ_WRITE); + } + + // + // See if a single byte is being transferred. + // + if(pState->ui32ReadCount == 1) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // Move to the read data end state to transfer the single + // byte. This uses PIO even if uDMA has been requested. + // + pState->ui16State = STATE_READ_DATA_END; + } + + // + // See if uDMA has been requested for this transfer. + // + else if(!pState->bUseDMA || (pState->ui32ReadCount < 4)) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // Move to the read data state. + // + pState->ui16State = STATE_READ_DATA; + } + + // + // This transfer should use uDMA. + // + else + { + // + // If the transfer is larger than 1024 bytes, enable the + // uDMA receive complete interrupt which will be used to + // move to the next block of the transfer. Otherwise, + // enable the uDMA transmit complete interrupt which will + // be used to complete the transaction. + // + if(pState->ui32ReadCount > 1024) + { + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMARXIM; + } + else + { + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM; + } + + // + // Disable the uDMA channels. + // + HWREG(UDMA_ENACLR) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + + // + // Configure the attributes for the transmit uDMA channel. + // + HWREG(UDMA_USEBURSTSET) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + HWREG(UDMA_ALTCLR) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel; + HWREG(UDMA_PRIOSET) = 1 << pState->ui32RxChannel; + HWREG(UDMA_REQMASKCLR) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + + // + // Configure the control parameters of the uDMA channels. + // + uDMAChannelControlSet(pState->ui32TxChannel, + UDMA_SRC_INC_NONE | + UDMA_DST_INC_NONE | + UDMA_SIZE_8 | UDMA_ARB_2); + uDMAChannelControlSet(pState->ui32RxChannel, + UDMA_SRC_INC_NONE | + UDMA_DST_INC_8 | + UDMA_SIZE_8 | UDMA_ARB_4); + + // + // Configure the uDMA receive channel to transfer the first + // portion of the data buffer. + // + uDMAChannelTransferSet(pState->ui32RxChannel, + UDMA_MODE_BASIC, + (void *)(pState->ui32Base + + SSI_O_DR), + pState->pui8Buffer, + (pState->ui32ReadCount >= 1024) ? + 1024 : pState->ui32ReadCount); + + // + // Enable the uDMA receive channel. + // + uDMAChannelEnable(pState->ui32RxChannel); + + // + // Configure the uDMA channel to transfer the dummy bytes + // for the first portion of the data buffer. The last + // dummy byte will not be included since it must be treated + // special. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, + pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + + // + // Clear any previously pending uDMA completion interrupt. + // + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC; + + // + // Enable uDMA transmit and receive in the SSI module. + // + MAP_SSIDMAEnable(pState->ui32Base, + SSI_DMA_TX | SSI_DMA_RX); + + // + // Move to the uDMA data read state. + // + pState->ui16State = STATE_READ_DATA_DMA; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the read data state. + // + case STATE_READ_DATA: + { + // + // Loop while there is more than one byte left to write. + // + while(pState->ui32WriteCount != 1) + { + // + // Dummy bytes are written into the FIFO in order to + // trigger the read operation. Attempt to write another + // dummy byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0) + { + // + // The dummy byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Decrement the count of dummy bytes to write. + // + pState->ui32WriteCount--; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Move to the read data end state. + // + pState->ui16State = STATE_READ_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the uDMA read data state. + // + case STATE_READ_DATA_DMA: + { + // + // See if the write count is greater than one. + // + if(pState->ui32WriteCount > 1) + { + // + // Return indicating that the transfer is still in + // progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Disable uDMA transmit in the SSI module. + // + MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX); + + // + // Enable the uDMA receive done and FIFO transmit interrupt. + // + HWREG(pState->ui32Base + SSI_O_IM) = + SSI_IM_DMARXIM | SSI_IM_TXIM; + + // + // Move to the read data end state. + // + pState->ui16State = STATE_READ_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the data read end state. + // + case STATE_READ_DATA_END: + { + // + // See if the final dummy byte still needs to be written. + // + if(pState->ui32WriteCount != 0) + { + // + // Attempt to write the final dummy byte into the FIFO and + // mark it as the end of the frame. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base, + 0) == 0) + { + // + // The dummy byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // The write portion of the transfer has completed. + // + pState->ui32WriteCount = 0; + + // + // Disable the transmit interrupt now that the write + // write portion of the transfer has completed. + // + HWREG(pState->ui32Base + SSI_O_IM) &= ~(SSI_IM_TXIM); + } + + // + // Return indicating that the transfer is still in progress if + // there are still data bytes to be read. + // + if(pState->ui32ReadCount != 0) + { + return(SPI_FLASH_WORKING); + } + + // + // Disable uDMA receive in the SSI module. + // + MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_RX); + + // + // The transfer is complete, so disable all interrupts. + // + HWREG(pState->ui32Base + SSI_O_IM) = 0; + + // + // Move to the idle state. + // + pState->ui16State = STATE_IDLE; + + // + // Return indicating that the transfer has completed. + // + return(SPI_FLASH_DONE); + } + + // + // The state machine is in the write data setup state. + // + case STATE_WRITE_DATA_SETUP: + { + // + // See if a single data byte is being transferred. + // + if(pState->ui32WriteCount == 1) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // Move to the write data end state to transfer the single + // byte. This uses PIO even if uDMA has been requested. + // + pState->ui16State = STATE_WRITE_DATA_END; + } + + // + // See if uDMA has been requested for this transfer. + // + else if(!pState->bUseDMA || (pState->ui32WriteCount < 4)) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // uDMA is not being used, so move to the write data state. + // + pState->ui16State = STATE_WRITE_DATA; + } + + // + // This transfer should use uDMA. + // + else + { + // + // Enable the uDMA transmit complete interrupt. + // + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM; + + // + // Disable the transmit uDMA channel. + // + HWREG(UDMA_ENACLR) = 1 << pState->ui32TxChannel; + + // + // Configure the attributes for the transmit uDMA channel. + // + HWREG(UDMA_USEBURSTSET) = 1 << pState->ui32TxChannel; + HWREG(UDMA_ALTCLR) = 1 << pState->ui32TxChannel; + HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel; + HWREG(UDMA_REQMASKCLR) = 1 << pState->ui32TxChannel; + + // + // Configure the control parameters of the uDMA channel. + // + uDMAChannelControlSet(pState->ui32TxChannel, + UDMA_SRC_INC_8 | + UDMA_DST_INC_NONE | + UDMA_SIZE_8 | UDMA_ARB_4); + + // + // Configure the uDMA channel to transfer the next portion + // of the data buffer. The last byte in the buffer will + // not be included since it must be treated special. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, + pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + + // + // Enable uDMA in the SSI module. + // + MAP_SSIDMAEnable(pState->ui32Base, SSI_DMA_TX); + + // + // Move to the uDMA data write state. + // + pState->ui16State = STATE_WRITE_DATA_DMA; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the write data state. + // + case STATE_WRITE_DATA: + { + // + // Loop while there is more than one byte left to write. + // + while(pState->ui32WriteCount != 1) + { + // + // Attempt to write the next data byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + *(pState->pui8Buffer)) == 0) + { + // + // The next data byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Increment the buffer pointer and decrement the byte + // count. + // + pState->pui8Buffer++; + pState->ui32WriteCount--; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Move to the write data end state. + // + pState->ui16State = STATE_WRITE_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the uDMA write data state. + // + case STATE_WRITE_DATA_DMA: + { + // + // See if the write count is greater than one. + // + if(pState->ui32WriteCount > 1) + { + // + // Return indicating that the transfer is still in + // progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Disable uDMA in the SSI module. + // + MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX); + + // + // Disable the uDMA transmit complete interrupt and enable the + // FIFO interrupt. + // + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_TXIM; + + // + // Move to the write data end state. + // + pState->ui16State = STATE_WRITE_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the write data end state. + // + case STATE_WRITE_DATA_END: + { + // + // Attempt to write the final data byte into the FIFO. + // + if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base, + *(pState->pui8Buffer)) == + 0) + { + // + // The final data byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // The transfer is complete, so disable all interrupts. + // + HWREG(pState->ui32Base + SSI_O_IM) = 0; + + // + // Move to the idle state. + // + pState->ui16State = STATE_IDLE; + + // + // Return indicating that the transfer has completed. + // + return(SPI_FLASH_DONE); + } + } + } +} + +//***************************************************************************** +// +//! Initializes the SPI flash driver. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Clock is the rate of the clock supplied to the SSI module. +//! \param ui32BitRate is the SPI clock rate. +//! +//! This function configures the SSI module for use by the SPI flash driver. +//! The SSI module will be placed into the correct mode of operation to allow +//! communication with the SPI flash. This function must be called prior to +//! calling the remaining SPI flash driver APIs. It can be called at a later +//! point to reconfigure the SSI module, such as to increase the SPI clock rate +//! once it has been determined that it is safe to use a higher speed clock. +//! +//! It is the responsibility of the caller to enable the SSI module and +//! configure the pins that it will utilize. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashInit(uint32_t ui32Base, uint32_t ui32Clock, uint32_t ui32BitRate) +{ + // + // Configure the SPI module. + // + MAP_SSIConfigSetExpClk(ui32Base, ui32Clock, SSI_FRF_MOTO_MODE_0, + SSI_MODE_MASTER, ui32BitRate, 8); + + // + // Enable the advanced mode of operation, defaulting to read/write mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // Enable the frame hold feature. + // + MAP_SSIAdvFrameHoldEnable(ui32Base); + + // + // Enable the SPI module. + // + MAP_SSIEnable(ui32Base); +} + +//***************************************************************************** +// +//! Writes the SPI flash status register. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui8Status is the value to write to the status register. +//! +//! This function writes the SPI flash status register. This uses the 0x01 SPI +//! flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashWriteStatus(uint32_t ui32Base, uint8_t ui8Status) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write status register command. + // + MAP_SSIDataPut(ui32Base, CMD_WRSR); + + // + // Send the new status register value, marking this byte as the end of the + // frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui8Status); +} + +//***************************************************************************** +// +//! Programs the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to be programmed. +//! \param pui8Data is a pointer to the data to be programmed. +//! \param ui32Count is the number of bytes to be programmed. +//! +//! This function programs data into the SPI flash, using PIO mode. This +//! function will not return until the entire program command has been written +//! into the SSI transmit FIFO. This uses the 0x02 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashPageProgram(uint32_t ui32Base, uint32_t ui32Addr, + const uint8_t *pui8Data, uint32_t ui32Count) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the page program command. + // + MAP_SSIDataPut(ui32Base, CMD_PP); + + // + // Send the address of the first byte to program. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Loop while there is more than one data byte left to be sent. + // + while(ui32Count-- != 1) + { + // + // Send the next data byte. + // + MAP_SSIDataPut(ui32Base, *pui8Data++); + } + + // + // Send the last data byte, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, *pui8Data); +} + +//***************************************************************************** +// +//! Programs the SPI flash in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to be programmed. +//! \param pui8Data is a pointer to the data to be programmed. +//! \param ui32Count is the number of bytes to be programmed. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! +//! This function programs data into the SPI flash, using either interrupts or +//! uDMA to transfer the data. This function will return immediately and send +//! the data in the background. In order for this to complete successfully, +//! several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the programming operation is indicated when +//! SPIFlashIntHandler() returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashPageProgram(), this uses the 0x02 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashPageProgramNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, const uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel) +{ + // + // Save the parameters of this program operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_PP; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = (uint8_t *)pui8Data; + pState->ui32ReadCount = 0; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + + // + // Enable the SSI transmit interrupt. This will start the transfer. If + // uDMA is being used, the uDMA-related interrupt will be enabled at the + // appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash, using PIO mode. This function +//! will not return until the read has completed. This uses the 0x03 SPI flash +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the read command. + // + MAP_SSIDataPut(ui32Base, CMD_READ); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash, using either interrupts or +//! uDMA to transfer the data. This function will return immediately and read +//! the data in the background. In order for this to complete successfully, +//! several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashRead(), this uses the 0x03 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_READ; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Disables SPI flash write operations. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This function sets the SPI flash to disallow program and erase operations. +//! This uses the 0x04 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashWriteDisable(uint32_t ui32Base) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write disable command, marking this byte as the end of the + // frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WRDI); +} + +//***************************************************************************** +// +//! Reads the SPI flash status register. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This function reads the SPI flash status register. This uses the 0x05 SPI +//! flash command. +//! +//! \return Returns the value of the SPI flash status register. +// +//***************************************************************************** +uint8_t +SPIFlashReadStatus(uint32_t ui32Base) +{ + uint32_t ui32Data; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write status register command. + // + MAP_SSIDataPut(ui32Base, CMD_RDSR); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the value of the status register. + // + MAP_SSIDataGet(ui32Base, &ui32Data); + + // + // Return the status register value. + // + return(ui32Data & 0xff); +} + +//***************************************************************************** +// +//! Enables SPI flash write operations. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This function sets the SPI flash to allow program and erase operations. +//! This must be done prior to each SPI flash program or erase operation; the +//! SPI flash will automatically disable program and erase operations once a +//! program or erase operation has completed. This uses the 0x06 SPI flash +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashWriteEnable(uint32_t ui32Base) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write enable command, marking this byte as the end of the + // frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WREN); +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using the fast read command. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash with the fast read command, +//! using PIO mode. The fast read command allows the SPI flash to be read at +//! a higher SPI clock rate because of the addition of a dummy cycle during the +//! command setup. This function will not return until the read has completed. +//! This uses the 0x0b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashFastRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the fast read command. + // + MAP_SSIDataPut(ui32Base, CMD_FREAD); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Send a dummy byte. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using the fast read command in the +//! background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash with the fast read command, +//! using either interrupts or uDMA to transfer the data. The fast read +//! command allows the SPI flash to be read at a higher SPI clock rate because +//! of the addition of a dummy cycle during the command setup. This function +//! will return immediately and read the data in the background. In order for +//! this to complete successfully, several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashFastRead(), this uses the 0x0b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashFastReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_FREAD; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Erases a 4 KB sector of the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to erase. +//! +//! This function erases a sector of the SPI flash. Each sector is 4 KB with a +//! 4 KB alignment; the SPI flash will ignore the lower ten bits of the address +//! provided. The sector erase command is issued by this function; +//! SPIFlashReadStatus() must be used to query the SPI flash to determine when +//! the sector erase operation has completed. This uses the 0x20 SPI flash +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashSectorErase(uint32_t ui32Base, uint32_t ui32Addr) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the sector erase command. + // + MAP_SSIDataPut(ui32Base, CMD_SE); + + // + // Send the address of the sector to be erased, marking the last byte of + // the address as the end of the frame. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff); +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Bi-SPI. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash with Bi-SPI, using PIO mode. +//! This function will not return until the read has completed. This uses the +//! 0x3b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashDualRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the dual read command. + // + MAP_SSIDataPut(ui32Base, CMD_DREAD); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Send a dummy byte. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Set the SSI module into Bi-SPI read mode. In this mode, dummy writes + // are required in order to make the transfer occur; the SSI module will + // ignore the data (the SPI flash will never see the dummy data since + // Bi-SPI read mode is a uni-directional input mode). + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_BI_READ); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Bi-SPI in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash with Bi-SPI, using either +//! interrupts or uDMA to transfer the data. This function will return +//! immediately and read the data in the background. In order for this to +//! complete successfully, several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFLashDualRead(), this uses the 0x3b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashDualReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_DREAD; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Erases a 32 KB block of the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to erase. +//! +//! This function erases a 32 KB block of the SPI flash. Each 32 KB block has +//! a 32 KB alignment; the SPI flash will ignore the lower 15 bits of the +//! address provided. The 32 KB block erase command is issued by this +//! function; SPIFlashReadStatus() must be used to query the SPI flash to +//! determine when the 32 KB block erase operation has completed. This uses +//! the 0x52 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashBlockErase32(uint32_t ui32Base, uint32_t ui32Addr) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the 32 KB block erase command command. + // + MAP_SSIDataPut(ui32Base, CMD_BE32); + + // + // Send the address of the 32 KB block to be erased, marking the last byte + // of the address as the end of the frame. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff); +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Quad-SPI. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash with Quad-SPI, using PIO mode. +//! This function will not return until the read has completed. This uses the +//! 0x6b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashQuadRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the quad read command. + // + MAP_SSIDataPut(ui32Base, CMD_QREAD); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Send a dummy byte. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Set the SSI module into Quad-SPI read mode. In this mode, dummy writes + // are required in order to make the transfer occur; the SSI module will + // ignore the data (the SPI flash will never see the dummy data since + // Quad-SPI read mode is a uni-directional input mode). + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_QUAD_READ); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Quad-SPI in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash with Quad-SPI, using either +//! interrupts or uDMA to transfer the data. This function will return +//! immediately and read the data in the background. In order for this to +//! complete successfully, several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashQuadRead(), this uses the 0x6b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashQuadReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_QREAD; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Reads the manufacturer and device IDs from the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param pui8ManufacturerID is a pointer to the location into which to store +//! the manufacturer ID. +//! \param pui16DeviceID is a pointer to the location into which to store the +//! device ID. +//! +//! This function reads the manufacturer and device IDs from the SPI flash. +//! These values can be used to identify the SPI flash that is attached, as +//! well as determining if a SPI flash is attached (if the \b SSIRx pin is +//! pulled up or down, either using the pad's weak pull up/down or using an +//! external resistor, which will cause the returned IDs to be either all zeros +//! or all ones if the SPI flash is not attached). This uses the 0x9f SPI +//! flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashReadID(uint32_t ui32Base, uint8_t *pui8ManufacturerID, + uint16_t *pui16DeviceID) +{ + uint32_t ui32Data1, ui32Data2; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data1) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the read ID command. + // + MAP_SSIDataPut(ui32Base, CMD_RDID); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // Send three dummy bytes, marking the last as the end of the frame. + // + MAP_SSIDataPut(ui32Base, 0); + MAP_SSIDataPut(ui32Base, 0); + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the first returned data byte, which contains the manufacturer ID. + // + MAP_SSIDataGet(ui32Base, &ui32Data1); + *pui8ManufacturerID = ui32Data1 & 0xff; + + // + // Read the remaining two data bytes, which contain the device ID. + // + MAP_SSIDataGet(ui32Base, &ui32Data1); + MAP_SSIDataGet(ui32Base, &ui32Data2); + *pui16DeviceID = ((ui32Data1 & 0xff) << 8) | (ui32Data2 & 0xff); +} + +//***************************************************************************** +// +//! Erases the entire SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This command erase the entire SPI flash. The chip erase command is issued +//! by this function; SPIFlashReadStatus() must be used to query the SPI flash +//! to determine when the chip erase operation has completed. This uses the +//! 0xc7 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashChipErase(uint32_t ui32Base) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the chip erase command, marking this byte as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_CE); +} + +//***************************************************************************** +// +//! Erases a 64 KB block of the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to erase. +//! +//! This function erases a 64 KB block of the SPI flash. Each 64 KB block has +//! a 64 KB alignment; the SPI flash will ignore the lower 16 bits of the +//! address provided. The 64 KB block erase command is issued by this +//! function; SPIFlashReadStatus() must be used to query the SPI flash to +//! determine when the 64 KB block erase operation has completed. This uses +//! the 0xd8 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashBlockErase64(uint32_t ui32Base, uint32_t ui32Addr) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the 64 KB block erase command command. + // + MAP_SSIDataPut(ui32Base, CMD_BE64); + + // + // Send the address of the 64 KB block to be erased, marking the last byte + // of the address as the end of the frame. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/spi_flash.h b/utils/spi_flash.h new file mode 100644 index 0000000..59311fb --- /dev/null +++ b/utils/spi_flash.h @@ -0,0 +1,166 @@ +//***************************************************************************** +// +// spi_flash.h - Prototypes for the SPI flash driver. +// +// Copyright (c) 2012-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 Utility Library. +// +//***************************************************************************** + +#ifndef __SPI_FLASH_H__ +#define __SPI_FLASH_H__ + +//***************************************************************************** +// +//! \addtogroup spi_flash_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The state structure used when performing non-blocking SPI flash operations. +// +//***************************************************************************** +typedef struct +{ + // + //! The base address of the SSI module that is being used. + // + uint32_t ui32Base; + + // + //! The command that is being send to the SPI flash. + // + uint16_t ui16Cmd; + + // + //! The current state of the SPI flash state machine. + // + uint16_t ui16State; + + // + //! The SPI flash address associated with the command. + // + uint32_t ui32Addr; + + // + //! A pointer to the data buffer that is being read or written. + // + uint8_t *pui8Buffer; + + // + //! The count of bytes left to be read. + // + uint32_t ui32ReadCount; + + // + //! The count of bytes left to be written. + // + uint32_t ui32WriteCount; + + // + //! A flag that is true if uDMA used be used for the transfer. + // + bool bUseDMA; + + // + //! The uDMA channel to use for transmitting when using uDMA for the + //! transfer. + // + uint32_t ui32TxChannel; + + // + //! The uDMA channel to use for receiving when using uDMA for the transfer. + // + uint32_t ui32RxChannel; +} +tSPIFlashState; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The possible return values from the SPI flash interrupt handler. +// +//***************************************************************************** +#define SPI_FLASH_IDLE 0 +#define SPI_FLASH_WORKING 1 +#define SPI_FLASH_DONE 3 + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern uint32_t SPIFlashIntHandler(tSPIFlashState *pState); +extern void SPIFlashInit(uint32_t ui32Base, uint32_t ui32Clock, + uint32_t ui32BitRate); +extern void SPIFlashWriteStatus(uint32_t ui32Base, uint8_t ui8Status); +extern void SPIFlashPageProgram(uint32_t ui32Base, uint32_t ui32Addr, + const uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashPageProgramNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, + uint32_t ui32Addr, + const uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel); +extern void SPIFlashRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashWriteDisable(uint32_t ui32Base); +extern uint8_t SPIFlashReadStatus(uint32_t ui32Base); +extern void SPIFlashWriteEnable(uint32_t ui32Base); +extern void SPIFlashFastRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashFastReadNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count, + bool bUseDMA, uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashSectorErase(uint32_t ui32Base, uint32_t ui32Addr); +extern void SPIFlashDualRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashDualReadNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count, + bool bUseDMA, uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashBlockErase32(uint32_t ui32Base, uint32_t ui32Addr); +extern void SPIFlashQuadRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashQuadReadNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count, + bool bUseDMA, uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashReadID(uint32_t ui32Base, uint8_t *pui8ManufacturerID, + uint16_t *pui16DeviceID); +extern void SPIFlashChipErase(uint32_t ui32Base); +extern void SPIFlashBlockErase64(uint32_t ui32Base, uint32_t ui32Addr); + +#endif // __SPI_FLASH_H__ diff --git a/utils/swupdate.c b/utils/swupdate.c new file mode 100644 index 0000000..edd523b --- /dev/null +++ b/utils/swupdate.c @@ -0,0 +1,355 @@ +//***************************************************************************** +// +// swupdate.c - A module wrapping the Ethernet bootloader software update +// functionality. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_nvic.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "driverlib/flash.h" +#include "driverlib/rom.h" +#include "driverlib/sysctl.h" +#include "driverlib/systick.h" +#include "utils/lwiplib.h" +#include "utils/swupdate.h" + +//***************************************************************************** +// +//! \addtogroup swupdate_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The UDP port used to send the remote firmware update request signal. This +// is the well-known port associated with "discard" function and is also used +// by some Wake-On-LAN implementations. +// +//***************************************************************************** +#define MPACKET_PORT 9 + +//***************************************************************************** +// +// The length of the various parts of the remote firmware update request magic +// packet and its total length. This contains a 6 byte header followed by 4 +// copies of the target MAC address. +// +//***************************************************************************** +#define MPACKET_HEADER_LEN 6 +#define MPACKET_MAC_REP 4 +#define MPACKET_MAC_LEN 6 +#define MPACKET_LEN (MPACKET_HEADER_LEN + \ + (MPACKET_MAC_REP * MPACKET_MAC_LEN)) + +//***************************************************************************** +// +// The marker byte used at the start of the magic packet. This is repeated +// MPACKET_HEADER_LEN times. +// +//***************************************************************************** +#define MPACKET_MARKER 0xAA + +//***************************************************************************** +// +// The callback function which is used to determine whether or not the +// application wants to allow a remotely-requested firmware update. +// +//***************************************************************************** +tSoftwareUpdateRequested g_pfnUpdateCallback = NULL; + +//***************************************************************************** +// +// A pointer to the remote firmware update signal PCB data structure. +// +//***************************************************************************** +static struct udp_pcb *g_psMagicPacketPCB = NULL; + +//***************************************************************************** +// +// The MAC address for this board. +// +//***************************************************************************** +static uint8_t g_pui8MACAddr[6]; + +//***************************************************************************** +// +// Receives a UDP port 9 packet from lwIP. +// +// \param arg is not used in this implementation. +// \param pcb is the pointer to the UDB control structure. +// \param p is the pointer to the PBUF structure containing the packet data. +// \param addr is the source (remote) IP address for this packet. +// \param port is the source (remote) port for this packet. +// +// This function is called when the lwIP TCP/IP stack has an incoming +// UDP packet to be processed on the remote firmware update signal port. +// +// \return None. +// +//***************************************************************************** +static void +SoftwareUpdateUDPReceive(void *arg, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + int8_t *pi8Data = p->payload; + uint32_t ui32Loop, ui32MACLoop; + + // + // Check that the packet length is what we expect. If not, ignore the + // packet. + // + if(p->len == MPACKET_LEN) + { + // + // The length matches so now look for the 6 byte header + // + for(ui32Loop = 0; ui32Loop < MPACKET_HEADER_LEN; ui32Loop++) + { + // + // Does this header byte match the expected marker? + // + if((*pi8Data & 0x000000FF)!= MPACKET_MARKER) + { + // + // No - free the buffer and return - this is not a packet + // we are interested in. + // + pbuf_free(p); + return; + } + else + { + // + // Byte matched so move on to the next one. + // + pi8Data++; + } + } + } + else + { + // + // No - free the buffer and return - this is not a packet + // we are interested in. + // + pbuf_free(p); + return; + } + + // + // If we get here, the packet length and header markers indicate + // that this is a remote firmware update request. Now check that it + // is for us and that it contains the required number of copies of + // the MAC address. + // + + // + // Loop through each of the expected MAC address copies. + // + for(ui32Loop = 0; ui32Loop < MPACKET_MAC_REP; ui32Loop++) + { + // + // Loop through each byte of the MAC address in this + // copy. + // + for(ui32MACLoop = 0; ui32MACLoop < MPACKET_MAC_LEN; ui32MACLoop++) + { + // + // Does the payload MAC address byte match what we expect? + // + if((*pi8Data & 0x000000FF) != g_pui8MACAddr[ui32MACLoop]) + { + // + // No match - free the packet and return. + // + pbuf_free(p); + return; + } + else + { + // + // Byte matched so move on to the next one. + // + pi8Data++; + } + } + } + + // + // Free the pbuf since we are finished with it now. + // + pbuf_free(p); + + // + // If we get this far, we've received a valid remote firmare update + // request targetted at this board. Signal this to the application + // if we have a valid callback pointer. + // + if(g_pfnUpdateCallback) + { + g_pfnUpdateCallback(); + } +} + +//***************************************************************************** +// +//! Initializes the remote Ethernet software update notification feature. +//! +//! \param pfnCallback is a pointer to a function which will be called whenever +//! a remote firmware update request is received. If the application wishes +//! to allow the update to go ahead, it must call SoftwareUpdateBegin() from +//! non-interrupt context after the callback is received. Note that the +//! callback will most likely be made in interrupt context so it is not safe +//! to call SoftwareUpdateBegin() from within the callback itself. +//! +//! This function may be used on Ethernet-enabled parts to support +//! remotely-signaled firmware updates over Ethernet. The LM Flash Programmer +//! (LMFlash.exe) application sends a magic packet to UDP port 9 whenever the +//! user requests an Ethernet-based firmware update. This packet consists of +//! 6 bytes of 0xAA followed by the target MAC address repeated 4 times. +//! This function starts listening on UDP port 9 and, if a magic packet +//! matching the MAC address of this board is received, makes a call to the +//! provided callback function to indicate that an update has been requested. +//! +//! The callback function provided here will typically be called in the context +//! of the lwIP Ethernet interrupt handler. It is not safe to call +//! SoftwareUpdateBegin() in this context so the application should use the +//! callback to signal code running in a non-interrupt context to perform the +//! update if it is to be allowed. +//! +//! UDP port 9 is chosen for this function since this is the well-known port +//! associated with ``discard'' operation. In other words, any other system +//! receiving the magic packet will simply ignore it. The actual magic packet +//! used is modeled on Wake-On-LAN which uses a similar structure (6 bytes of +//! 0xFF followed by 16 repetitions of the target MAC address). Some +//! Wake-On-LAN implementations also use UDP port 9 for their signaling. +//! +//! \note Applications using this function must initialize the lwIP stack prior +//! to making this call and must ensure that the lwIPTimer() function is called +//! periodically. lwIP UDP must be enabled in lwipopts.h to ensure that the +//! magic packets can be received. +//! +//! \return None. +// +//***************************************************************************** +void +SoftwareUpdateInit(tSoftwareUpdateRequested pfnCallback) +{ + uint32_t ui32User0, ui32User1; + + // + // Remember the callback function pointer we have been given. + // + g_pfnUpdateCallback = pfnCallback; + + // + // Get the MAC address from the user registers in NV ram. + // + FlashUserGet(&ui32User0, &ui32User1); + + // + // Convert the 24/24 split MAC address from NV ram into a MAC address + // array. + // + g_pui8MACAddr[0] = ui32User0 & 0xff; + g_pui8MACAddr[1] = (ui32User0 >> 8) & 0xff; + g_pui8MACAddr[2] = (ui32User0 >> 16) & 0xff; + g_pui8MACAddr[3] = ui32User1 & 0xff; + g_pui8MACAddr[4] = (ui32User1 >> 8) & 0xff; + g_pui8MACAddr[5] = (ui32User1 >> 16) & 0xff; + + // + // Set up a UDP PCB to allow us to receive the magic packets sent from + // LMFlash. These may be sent to port 9 from any port on the source + // machine so we do not call udp_connect here (since this causes lwIP to + // filter any packet that did not originate from port 9 too). + // + g_psMagicPacketPCB = udp_new(); + udp_recv(g_psMagicPacketPCB, SoftwareUpdateUDPReceive, NULL); + udp_bind(g_psMagicPacketPCB, IP_ADDR_ANY, MPACKET_PORT); +} + +//***************************************************************************** +// +//! Passes control to the bootloader and initiates a remote software update +//! over Ethernet. +//! +//! This function passes control to the bootloader and initiates an update of +//! the main application firmware image via BOOTP across Ethernet. This +//! function may only be used on parts supporting Ethernet and in cases where +//! the Ethernet boot loader is in use alongside the main application image. +//! It must not be called in interrupt context. +//! +//! Applications wishing to make use of this function must be built to +//! operate with the bootloader. If this function is called on a system +//! which does not include the bootloader, the results are unpredictable. +//! +//! \note It is not safe to call this function from within the callback +//! provided on the initial call to SoftwareUpdateInit(). The application +//! must use the callback to signal a pending update (assuming the update is to +//! be permitted) to some other code running in a non-interrupt context. +//! +//! \return Never returns. +// +//***************************************************************************** +void +SoftwareUpdateBegin(uint32_t ui32SysClock) +{ + // + // Disable all processor interrupts. Instead of disabling them + // one at a time (and possibly missing an interrupt if new sources + // are added), a direct write to NVIC is done to disable all + // peripheral interrupts. + // + HWREG(NVIC_DIS0) = 0xffffffff; + HWREG(NVIC_DIS1) = 0xffffffff; + HWREG(NVIC_DIS2) = 0xffffffff; + HWREG(NVIC_DIS3) = 0xffffffff; + HWREG(NVIC_DIS4) = 0xffffffff; + + // + // Also disable the SysTick interrupt. + // + SysTickIntDisable(); + SysTickDisable(); + + // + // Return control to the boot loader. This is a call to the SVC + // handler in the flashed-based boot loader, or to the ROM if configured. + // +#if ((defined ROM_UpdateEthernet) && !(defined USE_FLASH_BOOT_LOADER)) + ROM_UpdateEMAC(ui32SysClock); +#else + (*((void (*)(void))(*(uint32_t *)0x2c)))(); +#endif +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/swupdate.h b/utils/swupdate.h new file mode 100644 index 0000000..1cd44db --- /dev/null +++ b/utils/swupdate.h @@ -0,0 +1,66 @@ +//***************************************************************************** +// +// swupdate.h - Prototypes for the bootloader software update module. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SWUPDATE_H__ +#define __SWUPDATE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// This function pointer represents the callback made to the application in +// cases where a remote host requests a software update be performed. The +// application should use this to trigger a call to SoftwareUpdateBegin from +// a non-interrupt context. +// +//***************************************************************************** +typedef void (*tSoftwareUpdateRequested)(void); + +//***************************************************************************** +// +// Public function prototypes. +// +//***************************************************************************** +extern void SoftwareUpdateInit(tSoftwareUpdateRequested pfnCallback); +extern void SoftwareUpdateBegin(uint32_t ui32SysClock); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SWUPDATE_H__ diff --git a/utils/tftp.c b/utils/tftp.c new file mode 100644 index 0000000..674c307 --- /dev/null +++ b/utils/tftp.c @@ -0,0 +1,710 @@ +//***************************************************************************** +// +// tftp.c - A very simple lwIP TFTP server. +// +// Copyright (c) 2009-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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "utils/uartstdio.h" +#include "utils/lwiplib.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup tftp_api +//! @{ +// +//***************************************************************************** +#include "utils/tftp.h" + +//***************************************************************************** +// +// The TFTP commands. +// +//***************************************************************************** +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +//***************************************************************************** +// +// The UDP port for the TFTP server. +// +//***************************************************************************** +#define TFTP_PORT 69 + +//***************************************************************************** +// +// Application connection notification callback. +// +//***************************************************************************** +static tTFTPRequest g_pfnRequest; + +//***************************************************************************** +// +// Close the TFTP connection and free associated resources. +// +//***************************************************************************** +static void +TFTPClose(tTFTPConnection *psTFTP) +{ + // + // Tell the application we are closing the connection. + // + if(psTFTP->pfnClose) + { + psTFTP->pfnClose(psTFTP); + } + + // + // Close the underlying UDP connection. + // + udp_remove(psTFTP->psPCB); + + // + // Free the instance data structure. + // + mem_free(psTFTP); +} + +//***************************************************************************** +// +// Sends a TFTP error packet. +// +//***************************************************************************** +static void +TFTPErrorSend(tTFTPConnection *psTFTP, tTFTPError eError) +{ + uint32_t ui32Length; + uint8_t *pui8Data; + struct pbuf *p; + + // + // How big is this packet going to be? + // + ui32Length = 5 + strlen(psTFTP->pcErrorString); + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, ui32Length, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet. + // + pui8Data[0] = (TFTP_ERROR >> 8) & 0xff; + pui8Data[1] = TFTP_ERROR & 0xff; + pui8Data[2] = ((uint32_t)eError >> 8) & 0xff; + pui8Data[3] = (uint32_t)eError & 0xff; + memcpy(&pui8Data[4], psTFTP->pcErrorString, ui32Length - 5); + + // + // Send the data packet. + // + udp_send(psTFTP->psPCB, p); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Sends a TFTP data packet. +// +//***************************************************************************** +static void +TFTPDataSend(tTFTPConnection *psTFTP) +{ + uint32_t ui32Length; + uint8_t *pui8Data; + tTFTPError eError; + struct pbuf *p; + + // + // Determine the number of bytes to place into this packet. + // + if(psTFTP->ui32DataRemaining < (psTFTP->ui32BlockNum * TFTP_BLOCK_SIZE)) + { + ui32Length = psTFTP->ui32DataRemaining & (TFTP_BLOCK_SIZE - 1); + } + else + { + ui32Length = TFTP_BLOCK_SIZE; + } + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, ui32Length + 4, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet header. + // + pui8Data[0] = (TFTP_DATA >> 8) & 0xff; + pui8Data[1] = TFTP_DATA & 0xff; + pui8Data[2] = (psTFTP->ui32BlockNum >> 8) & 0xff; + pui8Data[3] = psTFTP->ui32BlockNum & 0xff; + + // + // Ask the application to provide the data we need. + // + psTFTP->pui8Data = pui8Data + 4; + psTFTP->ui32DataLength = ui32Length; + eError = psTFTP->pfnGetData(psTFTP); + + // + // Send the data packet or, if an error was reported, send an error. + // + if(eError == TFTP_OK) + { + udp_send(psTFTP->psPCB, p); + } + else + { + TFTPErrorSend(psTFTP, eError); + TFTPClose(psTFTP); + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Send an ACK packet back to the TFTP client. +// +//***************************************************************************** +static void +TFTPDataAck(tTFTPConnection *psTFTP) +{ + uint8_t *pui8Data; + struct pbuf *p; + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, 4, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet header. + // + pui8Data[0] = (TFTP_ACK >> 8) & 0xff; + pui8Data[1] = TFTP_ACK & 0xff; + pui8Data[2] = (psTFTP->ui32BlockNum >> 8) & 0xff; + pui8Data[3] = psTFTP->ui32BlockNum & 0xff; + + // + // Send the data packet. + // + udp_send(psTFTP->psPCB, p); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Handles datagrams received from the TFTP data connection. +// +//***************************************************************************** +static void +TFTPDataRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + uint8_t *pui8Data; + uint32_t ui32Block; + struct pbuf *pBuf; + tTFTPConnection *psTFTP; + tTFTPError eRetcode; + + // + // Initialize our return code. + // + eRetcode = TFTP_ERR_NOT_DEFINED; + + // + // Get a pointer to the connection instance data. + // + psTFTP = (tTFTPConnection *)arg; + + // + // Get a pointer to the TFTP packet. + // + pui8Data = (uint8_t *)(p->payload); + + // + // If this is an ACK packet, send back the next block to satisfy an + // ongoing GET (read) request. + // + if((pui8Data[0] == ((TFTP_ACK >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_ACK & 0xff))) + { + // + // Extract the block number from the acknowledge. + // + ui32Block = (pui8Data[2] << 8) + pui8Data[3]; + + // + // DEBUG ONLY! + // + UARTprintf("ACK %d\n", ui32Block); + + // + // See if there is more data to be sent. Note that we need the "<=" + // here to ensure that we send back a zero length packet in the case + // that the file is a multiple of 512 bytes (in other words, the last + // packet of valid data was a full packet). + // + if((ui32Block * TFTP_BLOCK_SIZE) <= psTFTP->ui32DataRemaining) + { + // + // Send the next block of the file. + // + psTFTP->ui32BlockNum = ui32Block + 1; + TFTPDataSend(psTFTP); + } + else + { + // + // The transfer is complete, so close the data connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + else + { + // + // If this is a DATA packet, get the payload and write it to the + // appropriate location in the serial flash. + // + if((pui8Data[0] == ((TFTP_DATA >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_DATA & 0xff))) + { + // + // This is a data packet. Extract the block number from the packet + // and set the offset within the block (stored in + // ui32DataRemaining) to zero. + // + psTFTP->ui32BlockNum = (pui8Data[2] << 8) + pui8Data[3]; + psTFTP->ui32DataRemaining = 0; + psTFTP->ui32DataLength = p->len - 4; + + // + // Pass the data back to the application for handling. Remember + // that the data may be stored across several pbufs in the chain. + // We can't assume it is in a contiguous block. + // + psTFTP->pui8Data = pui8Data + 4; + pBuf = p; + + // + // Keep writing until we run out of data. + // + while(pBuf) + { + // + // Pass this block to the application. + // + eRetcode = psTFTP->pfnPutData(psTFTP); + + // + // Was the data written successfully? + // + if(eRetcode != TFTP_OK) + { + // + // No - drop out. + // + break; + } + + // + // Update the offset so that it is correct for the next pbuf + // in the chain. + // + psTFTP->ui32DataRemaining += psTFTP->ui32DataLength; + + // + // Move to the next pbuf in the chain + // + pBuf = pBuf->next; + if(pBuf) + { + psTFTP->pui8Data = pBuf->payload; + psTFTP->ui32DataLength = pBuf->len; + } + } + + // + // If we get here and there was an error reported, pass the error + // back to the TFTP client. + // + if(psTFTP && (eRetcode != TFTP_OK)) + { + // + // Send the error code to the client. + // + TFTPErrorSend(psTFTP, eRetcode); + + // + // Close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + else + { + // + // Acknowledge this block. + // + TFTPDataAck(psTFTP); + + // + // Is the transfer finished? + // + if(p->tot_len < (TFTP_BLOCK_SIZE + 4)) + { + // + // We got a short packet so the transfer is complete. + // Close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + } + else + { + // + // Is the client reporting an error? + // + if((pui8Data[0] == ((TFTP_ERROR >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_ERROR & 0xff))) + { + // + // Yes - we got an error so close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Parses the request string to determine the transfer mode, netascii, octet or +// mail, for this request. +// +//***************************************************************************** +static tTFTPMode +TFTPModeGet(uint8_t *pui8Request, uint32_t ui32Len) +{ + uint32_t ui32Loop, ui32Max; + + // + // Look for the first zero after the start of the filename string (skipping + // the first two bytes of the request packet). + // + for(ui32Loop = 2; ui32Loop < ui32Len; ui32Loop++) + { + if(pui8Request[ui32Loop] == (uint8_t)0) + { + break; + } + } + + // + // Skip past the zero. + // + ui32Loop++; + + // + // Did we run off the end of the string? + // + if(ui32Loop >= ui32Len) + { + // + // Yes - this appears to be an invalid request. + // + return(TFTP_MODE_INVALID); + } + + // + // How much data do we have left to look for the mode string? + // + ui32Max = ui32Len - ui32Loop; + + // + // Now determine which of the modes this request asks for. Is it ASCII? + // + if(!ustrncasecmp("netascii", (char *)&pui8Request[ui32Loop], ui32Max)) + { + // + // This is an ASCII file transfer. + // + return(TFTP_MODE_NETASCII); + } + + // + // Binary transfer? + // + if(!ustrncasecmp("octet", (char *)&pui8Request[ui32Loop], ui32Max)) + { + // + // This is a binary file transfer. + // + return(TFTP_MODE_OCTET); + } + + // + // All other strings are invalid or obsolete ("mail" for example). + // + return(TFTP_MODE_INVALID); +} + +//***************************************************************************** +// +// Handles datagrams received on the TFTP server port. +// +//***************************************************************************** +static void +TFTPRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, + u16_t port) +{ + uint8_t *pui8Data; + bool bGetRequest; + tTFTPMode eMode; + tTFTPError eRetcode; + tTFTPConnection *psTFTP; + + // + // Get a pointer to the TFTP packet. + // + pui8Data = (uint8_t *)(p->payload); + + // + // Is this a read (GET) request? + // + if((pui8Data[0] == ((TFTP_RRQ >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_RRQ & 0xff))) + { + // + // Yes - remember that this is a GET request. + // + bGetRequest = true; + } + + // + // Is this a write (PUT) request? + // + else if((pui8Data[0] == ((TFTP_WRQ >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_WRQ & 0xff))) + { + // + // Yes - remember that this is a PUT request. + // + bGetRequest = false; + } + else + { + // + // The request is neither GET nor PUT so just ignore it. + // + pbuf_free(p); + return; + } + + // + // What is the mode for this request? + // + eMode = TFTPModeGet(pui8Data, p->len); + + // + // Was the transfer mode valid? + // + if(eMode != TFTP_MODE_INVALID) + { + // + // The transfer mode is valid so allocate a new connection instance + // and pass this to the client to have it tell us how to proceed. + // + psTFTP = (tTFTPConnection *)mem_malloc(sizeof(tTFTPConnection)); + + // + // If we can't allocate the connection instance, all we can do is + // ignore the datagram. + // + if(!psTFTP) + { + pbuf_free(p); + return; + } + + // + // Clear out the structure and initialize a few fields. + // + memset(psTFTP, 0, sizeof(tTFTPConnection)); + psTFTP->pcErrorString = "Unknown error"; + + // + // Yes - create the new UDP connection and set things up to + // handle this request. + // + psTFTP->psPCB = udp_new(); + udp_recv(psTFTP->psPCB, TFTPDataRecv, psTFTP); + udp_connect(psTFTP->psPCB, addr, port); + + // + // Ask the application if it wants to proceed with this request. + // + eRetcode = g_pfnRequest(psTFTP, bGetRequest, (int8_t *)(pui8Data + 2), + eMode); + + // + // Does it want to go on? + // + if(eRetcode == TFTP_OK) + { + // + // Yes - what kind of request is this? + // + if(bGetRequest) + { + // + // For a GET request, we send back the first block of data. + // + psTFTP->ui32BlockNum = 1; + TFTPDataSend(psTFTP); + } + else + { + // + // For a PUT request, we acknowledge the transfer which tells + // the TFTP client that it can start sending us data. + // + psTFTP->ui32BlockNum = 0; + TFTPDataAck(psTFTP); + } + } + else + { + // + // The application indicated that there was an error. Send the + // error report and close the connection. + // + TFTPErrorSend(psTFTP, eRetcode); + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +//! Initializes the TFTP server module. +//! +//! \param pfnRequest - A pointer to the function which the server will call +//! whenever a new incoming TFTP request is received. This function must +//! determine whether the request can be handled and return a value telling the +//! server whether to continue processing the request or ignore it. +//! +//! This function initializes the lwIP TFTP server and starts listening for +//! incoming requests from clients. It must be called after the network stack +//! is initialized using a call to lwIPInit(). +//! +//! \return None. +// +//***************************************************************************** +void +TFTPInit(tTFTPRequest pfnRequest) +{ + void *pcb; + + // + // Remember the application's notification callback. + // + g_pfnRequest = pfnRequest; + + // + // Start listening for incoming TFTP requests. + // + pcb = udp_new(); + udp_recv(pcb, TFTPRecv, NULL); + udp_bind(pcb, IP_ADDR_ANY, TFTP_PORT); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/tftp.h b/utils/tftp.h new file mode 100644 index 0000000..b02b8ea --- /dev/null +++ b/utils/tftp.h @@ -0,0 +1,215 @@ +//***************************************************************************** +// +// tftp.h - Public function prototypes and globals related to the lwIP TFTP +// server. +// +// Copyright (c) 2009-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 Utility Library. +// +//***************************************************************************** + +#ifndef __TFTP_H__ +#define __TFTP_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup tftp_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! TFTP error codes. Note that this enum is mapped so that all positive +//! values match the TFTP protocol-defined error codes. +// +//***************************************************************************** +typedef enum +{ + TFTP_OK = -1, + TFTP_ERR_NOT_DEFINED = 0, + TFTP_FILE_NOT_FOUND = 1, + TFTP_ACCESS_VIOLATION = 2, + TFTP_DISK_FULL = 3, + TFTP_ILLEGAL_OP = 4, + TFTP_UNKNOWN_TID = 5, + TFTP_FILE_EXISTS = 6, + TFTP_NO_SUCH_USER = 7 +} +tTFTPError; + +//***************************************************************************** +// +//! TFTP file transfer modes. This enum contains members defining ASCII +//! text transfer mode (TFTP_MODE_NETASCII), binary transfer mode +//! (TFTP_MODE_OCTET) and a marker for an invalid mode (TFTP_MODE_INVALID). +// +//***************************************************************************** +typedef enum +{ + TFTP_MODE_NETASCII, + TFTP_MODE_OCTET, + TFTP_MODE_INVALID +} +tTFTPMode; + +//***************************************************************************** +// +//! Data transfer under TFTP is performed using fixed-size blocks. This label +//! defines the size of a block of TFTP data. +// +//***************************************************************************** +#define TFTP_BLOCK_SIZE 512 + +//***************************************************************************** +// +// Callback function prototypes passed to TFTPInit. These functions receive +// notification of incoming GET and PUT requests, allowing the client to decide +// whether to accept the request or not. +// +//***************************************************************************** +struct _tTFTPConnection; + +typedef tTFTPError (*tTFTPRequest)(struct _tTFTPConnection *psTFTP, bool bGet, + int8_t *pui8FileName, tTFTPMode eMode); +typedef tTFTPError (*tTFTPTransfer)(struct _tTFTPConnection *psTFTP); +typedef void (*tTFTPClose)(struct _tTFTPConnection *psTFTP); + +//***************************************************************************** +// +//! The TFTP connection control structure. This is passed to a client on all +//! callbacks relating to a given TFTP connection. Depending upon the +//! callback, the client may need to fill in values to various fields or use +//! field values to determine where to transfer data from or to. +// +//***************************************************************************** +typedef struct _tTFTPConnection +{ + // + //! Pointer to the start of the buffer into which GET data should be copied + //! or from which PUT data should be read. + // + uint8_t *pui8Data; + + // + //! The length of the data requested in response to a single pfnGetData + //! callback or the size of the received data for a pfnPutData callback. + // + uint32_t ui32DataLength; + + // + //! Count of remaining bytes to send during a GET request or the byte + //! offset within a block during a PUT request. The application must set + //! this field to the size of the requested file during the tTFTPRequest + // callback if a GET request is to be accepted. + // + uint32_t ui32DataRemaining; + + // + //! Application function which is called whenever more data is required to + //! satisfy a GET request. The function must copy ui32DataLength bytes + //! into the buffer pointed to by pui8Data. + // + tTFTPTransfer pfnGetData; + + // + //! Application function which is called whenever a packet of file data is + //! received during a PUT request. The function must save the data to the + //! target file using ui32BlockNum and ui32DataRemaining to indicate the + //! position of the data in the file, and return an appropriate error code. + //! Note that several calls to this function may be made for a given + //! received TFTP block since the underlying networking stack may have + //! split the TFTP packet between several packets and a callback is made + //! for each of these. This avoids the need for a 512 byte buffer. The + //! ui32DataRemaining is used in these cases to indicate the offset of the + //! data within the current block. + // + tTFTPTransfer pfnPutData; + + // + //! Application function which is called when the TFTP connection is to + //! be closed. The function should tidy up and free any resources + //! associated with the connection prior to returning. + // + tTFTPClose pfnClose; + + // + //! This field may be used by the client to store an application-specific + //! pointer that will be accessible on all callbacks from the TFTP module + //! relating to this connection. + // + uint8_t *pui8User; + + // + //! Pointer to an error string which the client must fill in if reporting + //! an error. This string will be sent to the TFTP client in any case + //! where pfnPutData or pfnGetData return a value other than TFTP_OK. + // + char *pcErrorString; + + // + //! A pointer to the underlying UDP connection. Applications must not + //! modify this field. + // + struct udp_pcb *psPCB; + + // + //! The current block number for an ongoing TFTP transfer. Applications + //! may read this value to determine which data to return on a pfnGetData + //! callback or where to write incoming data on a pfnPutData callback but + //! must not modify it. + // + uint32_t ui32BlockNum; +} +tTFTPConnection; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public function prototypes. +// +//***************************************************************************** +extern void TFTPInit(tTFTPRequest pfnRequest); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __TFTP_H__ diff --git a/utils/uartstdio.c b/utils/uartstdio.c new file mode 100644 index 0000000..8ec2eaa --- /dev/null +++ b/utils/uartstdio.c @@ -0,0 +1,1720 @@ +//***************************************************************************** +// +// uartstdio.c - Utility driver to provide simple UART console functions. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_uart.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/uart.h" +#include "utils/uartstdio.h" + +//***************************************************************************** +// +//! \addtogroup uartstdio_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If buffered mode is defined, set aside RX and TX buffers and read/write +// pointers to control them. +// +//***************************************************************************** +#ifdef UART_BUFFERED + +//***************************************************************************** +// +// This global controls whether or not we are echoing characters back to the +// transmitter. By default, echo is enabled but if using this module as a +// convenient method of implementing a buffered serial interface over which +// you will be running an application protocol, you are likely to want to +// disable echo by calling UARTEchoSet(false). +// +//***************************************************************************** +static bool g_bDisableEcho; + +//***************************************************************************** +// +// Output ring buffer. Buffer is full if g_ui32UARTTxReadIndex is one ahead of +// g_ui32UARTTxWriteIndex. Buffer is empty if the two indices are the same. +// +//***************************************************************************** +static unsigned char g_pcUARTTxBuffer[UART_TX_BUFFER_SIZE]; +static volatile uint32_t g_ui32UARTTxWriteIndex = 0; +static volatile uint32_t g_ui32UARTTxReadIndex = 0; + +//***************************************************************************** +// +// Input ring buffer. Buffer is full if g_ui32UARTTxReadIndex is one ahead of +// g_ui32UARTTxWriteIndex. Buffer is empty if the two indices are the same. +// +//***************************************************************************** +static unsigned char g_pcUARTRxBuffer[UART_RX_BUFFER_SIZE]; +static volatile uint32_t g_ui32UARTRxWriteIndex = 0; +static volatile uint32_t g_ui32UARTRxReadIndex = 0; + +//***************************************************************************** +// +// Macros to determine number of free and used bytes in the transmit buffer. +// +//***************************************************************************** +#define TX_BUFFER_USED (GetBufferCount(&g_ui32UARTTxReadIndex, \ + &g_ui32UARTTxWriteIndex, \ + UART_TX_BUFFER_SIZE)) +#define TX_BUFFER_FREE (UART_TX_BUFFER_SIZE - TX_BUFFER_USED) +#define TX_BUFFER_EMPTY (IsBufferEmpty(&g_ui32UARTTxReadIndex, \ + &g_ui32UARTTxWriteIndex)) +#define TX_BUFFER_FULL (IsBufferFull(&g_ui32UARTTxReadIndex, \ + &g_ui32UARTTxWriteIndex, \ + UART_TX_BUFFER_SIZE)) +#define ADVANCE_TX_BUFFER_INDEX(Index) \ + (Index) = ((Index) + 1) % UART_TX_BUFFER_SIZE + +//***************************************************************************** +// +// Macros to determine number of free and used bytes in the receive buffer. +// +//***************************************************************************** +#define RX_BUFFER_USED (GetBufferCount(&g_ui32UARTRxReadIndex, \ + &g_ui32UARTRxWriteIndex, \ + UART_RX_BUFFER_SIZE)) +#define RX_BUFFER_FREE (UART_RX_BUFFER_SIZE - RX_BUFFER_USED) +#define RX_BUFFER_EMPTY (IsBufferEmpty(&g_ui32UARTRxReadIndex, \ + &g_ui32UARTRxWriteIndex)) +#define RX_BUFFER_FULL (IsBufferFull(&g_ui32UARTRxReadIndex, \ + &g_ui32UARTRxWriteIndex, \ + UART_RX_BUFFER_SIZE)) +#define ADVANCE_RX_BUFFER_INDEX(Index) \ + (Index) = ((Index) + 1) % UART_RX_BUFFER_SIZE +#endif + +//***************************************************************************** +// +// The base address of the chosen UART. +// +//***************************************************************************** +static uint32_t g_ui32Base = 0; + +//***************************************************************************** +// +// A mapping from an integer between 0 and 15 to its ASCII character +// equivalent. +// +//***************************************************************************** +static const char * const g_pcHex = "0123456789abcdef"; + +//***************************************************************************** +// +// The list of possible base addresses for the console UART. +// +//***************************************************************************** +static const uint32_t g_ui32UARTBase[3] = +{ + UART0_BASE, UART1_BASE, UART2_BASE +}; + +#ifdef UART_BUFFERED +//***************************************************************************** +// +// The list of possible interrupts for the console UART. +// +//***************************************************************************** +static const uint32_t g_ui32UARTInt[3] = +{ + INT_UART0, INT_UART1, INT_UART2 +}; + +//***************************************************************************** +// +// The port number in use. +// +//***************************************************************************** +static uint32_t g_ui32PortNum; +#endif + +//***************************************************************************** +// +// The list of UART peripherals. +// +//***************************************************************************** +static const uint32_t g_ui32UARTPeriph[3] = +{ + SYSCTL_PERIPH_UART0, SYSCTL_PERIPH_UART1, SYSCTL_PERIPH_UART2 +}; + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is full or not. +//! +//! \param pui32Read points to the read index for the buffer. +//! \param pui32Write points to the write index for the buffer. +//! \param ui32Size is the size of the buffer in bytes. +//! +//! This function is used to determine whether or not a given ring buffer is +//! full. The structure of the code is specifically to ensure that we do not +//! see warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is full or \b false otherwise. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static bool +IsBufferFull(volatile uint32_t *pui32Read, + volatile uint32_t *pui32Write, uint32_t ui32Size) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + ui32Write = *pui32Write; + ui32Read = *pui32Read; + + return((((ui32Write + 1) % ui32Size) == ui32Read) ? true : false); +} +#endif + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is empty or not. +//! +//! \param pui32Read points to the read index for the buffer. +//! \param pui32Write points to the write index for the buffer. +//! +//! This function is used to determine whether or not a given ring buffer is +//! empty. The structure of the code is specifically to ensure that we do not +//! see warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is empty or \b false otherwise. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static bool +IsBufferEmpty(volatile uint32_t *pui32Read, + volatile uint32_t *pui32Write) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + ui32Write = *pui32Write; + ui32Read = *pui32Read; + + return((ui32Write == ui32Read) ? true : false); +} +#endif + +//***************************************************************************** +// +//! Determines the number of bytes of data contained in a ring buffer. +//! +//! \param pui32Read points to the read index for the buffer. +//! \param pui32Write points to the write index for the buffer. +//! \param ui32Size is the size of the buffer in bytes. +//! +//! This function is used to determine how many bytes of data a given ring +//! buffer currently contains. The structure of the code is specifically to +//! ensure that we do not see warnings from the compiler related to the order +//! of volatile accesses being undefined. +//! +//! \return Returns the number of bytes of data currently in the buffer. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static uint32_t +GetBufferCount(volatile uint32_t *pui32Read, + volatile uint32_t *pui32Write, uint32_t ui32Size) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + ui32Write = *pui32Write; + ui32Read = *pui32Read; + + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (ui32Size - (ui32Read - ui32Write))); +} +#endif + +//***************************************************************************** +// +// Take as many bytes from the transmit buffer as we have space for and move +// them into the UART transmit FIFO. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static void +UARTPrimeTransmit(uint32_t ui32Base) +{ + // + // Do we have any data to transmit? + // + if(!TX_BUFFER_EMPTY) + { + // + // Disable the UART interrupt. If we don't do this there is a race + // condition which can cause the read index to be corrupted. + // + MAP_IntDisable(g_ui32UARTInt[g_ui32PortNum]); + + // + // Yes - take some characters out of the transmit buffer and feed + // them to the UART transmit FIFO. + // + while(MAP_UARTSpaceAvail(ui32Base) && !TX_BUFFER_EMPTY) + { + MAP_UARTCharPutNonBlocking(ui32Base, + g_pcUARTTxBuffer[g_ui32UARTTxReadIndex]); + ADVANCE_TX_BUFFER_INDEX(g_ui32UARTTxReadIndex); + } + + // + // Reenable the UART interrupt. + // + MAP_IntEnable(g_ui32UARTInt[g_ui32PortNum]); + } +} +#endif + +//***************************************************************************** +// +//! Configures the UART console. +//! +//! \param ui32PortNum is the number of UART port to use for the serial console +//! (0-2) +//! \param ui32Baud is the bit rate that the UART is to be configured to use. +//! \param ui32SrcClock is the frequency of the source clock for the UART +//! module. +//! +//! This function will configure the specified serial port to be used as a +//! serial console. The serial parameters are set to the baud rate +//! specified by the \e ui32Baud parameter and use 8 bit, no parity, and 1 stop +//! bit. +//! +//! This function must be called prior to using any of the other UART console +//! functions: UARTprintf() or UARTgets(). This function assumes that the +//! caller has previously configured the relevant UART pins for operation as a +//! UART rather than as GPIOs. +//! +//! \return None. +// +//***************************************************************************** +void +UARTStdioConfig(uint32_t ui32PortNum, uint32_t ui32Baud, uint32_t ui32SrcClock) +{ + // + // Check the arguments. + // + ASSERT((ui32PortNum == 0) || (ui32PortNum == 1) || + (ui32PortNum == 2)); + +#ifdef UART_BUFFERED + // + // In buffered mode, we only allow a single instance to be opened. + // + ASSERT(g_ui32Base == 0); +#endif + + // + // Check to make sure the UART peripheral is present. + // + if(!MAP_SysCtlPeripheralPresent(g_ui32UARTPeriph[ui32PortNum])) + { + return; + } + + // + // Select the base address of the UART. + // + g_ui32Base = g_ui32UARTBase[ui32PortNum]; + + // + // Enable the UART peripheral for use. + // + MAP_SysCtlPeripheralEnable(g_ui32UARTPeriph[ui32PortNum]); + + // + // Configure the UART for 115200, n, 8, 1 + // + MAP_UARTConfigSetExpClk(g_ui32Base, ui32SrcClock, ui32Baud, + (UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE | + UART_CONFIG_WLEN_8)); + +#ifdef UART_BUFFERED + // + // Set the UART to interrupt whenever the TX FIFO is almost empty or + // when any character is received. + // + MAP_UARTFIFOLevelSet(g_ui32Base, UART_FIFO_TX1_8, UART_FIFO_RX1_8); + + // + // Flush both the buffers. + // + UARTFlushRx(); + UARTFlushTx(true); + + // + // Remember which interrupt we are dealing with. + // + g_ui32PortNum = ui32PortNum; + + // + // We are configured for buffered output so enable the master interrupt + // for this UART and the receive interrupts. We don't actually enable the + // transmit interrupt in the UART itself until some data has been placed + // in the transmit buffer. + // + MAP_UARTIntDisable(g_ui32Base, 0xFFFFFFFF); + MAP_UARTIntEnable(g_ui32Base, UART_INT_RX | UART_INT_RT); + MAP_IntEnable(g_ui32UARTInt[ui32PortNum]); +#endif + + // + // Enable the UART operation. + // + MAP_UARTEnable(g_ui32Base); +} + +//***************************************************************************** +// +//! Writes a string of characters to the UART output. +//! +//! \param pcBuf points to a buffer containing the string to transmit. +//! \param ui32Len is the length of the string to transmit. +//! +//! This function will transmit the string to the UART output. The number of +//! characters transmitted is determined by the \e ui32Len parameter. This +//! function does no interpretation or translation of any characters. Since +//! the output is sent to a UART, any LF (/n) characters encountered will be +//! replaced with a CRLF pair. +//! +//! Besides using the \e ui32Len parameter to stop transmitting the string, if +//! a null character (0) is encountered, then no more characters will be +//! transmitted and the function will return. +//! +//! In non-buffered mode, this function is blocking and will not return until +//! all the characters have been written to the output FIFO. In buffered mode, +//! the characters are written to the UART transmit buffer and the call returns +//! immediately. If insufficient space remains in the transmit buffer, +//! additional characters are discarded. +//! +//! \return Returns the count of characters written. +// +//***************************************************************************** +int +UARTwrite(const char *pcBuf, uint32_t ui32Len) +{ +#ifdef UART_BUFFERED + unsigned int uIdx; + + // + // Check for valid arguments. + // + ASSERT(pcBuf != 0); + ASSERT(g_ui32Base != 0); + + // + // Send the characters + // + for(uIdx = 0; uIdx < ui32Len; uIdx++) + { + // + // If the character to the UART is \n, then add a \r before it so that + // \n is translated to \n\r in the output. + // + if(pcBuf[uIdx] == '\n') + { + if(!TX_BUFFER_FULL) + { + g_pcUARTTxBuffer[g_ui32UARTTxWriteIndex] = '\r'; + ADVANCE_TX_BUFFER_INDEX(g_ui32UARTTxWriteIndex); + } + else + { + // + // Buffer is full - discard remaining characters and return. + // + break; + } + } + + // + // Send the character to the UART output. + // + if(!TX_BUFFER_FULL) + { + g_pcUARTTxBuffer[g_ui32UARTTxWriteIndex] = pcBuf[uIdx]; + ADVANCE_TX_BUFFER_INDEX(g_ui32UARTTxWriteIndex); + } + else + { + // + // Buffer is full - discard remaining characters and return. + // + break; + } + } + + // + // If we have anything in the buffer, make sure that the UART is set + // up to transmit it. + // + if(!TX_BUFFER_EMPTY) + { + UARTPrimeTransmit(g_ui32Base); + MAP_UARTIntEnable(g_ui32Base, UART_INT_TX); + } + + // + // Return the number of characters written. + // + return(uIdx); +#else + unsigned int uIdx; + + // + // Check for valid UART base address, and valid arguments. + // + ASSERT(g_ui32Base != 0); + ASSERT(pcBuf != 0); + + // + // Send the characters + // + for(uIdx = 0; uIdx < ui32Len; uIdx++) + { + // + // If the character to the UART is \n, then add a \r before it so that + // \n is translated to \n\r in the output. + // + if(pcBuf[uIdx] == '\n') + { + MAP_UARTCharPut(g_ui32Base, '\r'); + } + + // + // Send the character to the UART output. + // + MAP_UARTCharPut(g_ui32Base, pcBuf[uIdx]); + } + + // + // Return the number of characters written. + // + return(uIdx); +#endif +} + +//***************************************************************************** +// +//! A simple UART based get string function, with some line processing. +//! +//! \param pcBuf points to a buffer for the incoming string from the UART. +//! \param ui32Len is the length of the buffer for storage of the string, +//! including the trailing 0. +//! +//! This function will receive a string from the UART input and store the +//! characters in the buffer pointed to by \e pcBuf. The characters will +//! continue to be stored until a termination character is received. The +//! termination characters are CR, LF, or ESC. A CRLF pair is treated as a +//! single termination character. The termination characters are not stored in +//! the string. The string will be terminated with a 0 and the function will +//! return. +//! +//! In both buffered and unbuffered modes, this function will block until +//! a termination character is received. If non-blocking operation is required +//! in buffered mode, a call to UARTPeek() may be made to determine whether +//! a termination character already exists in the receive buffer prior to +//! calling UARTgets(). +//! +//! Since the string will be null terminated, the user must ensure that the +//! buffer is sized to allow for the additional null character. +//! +//! \return Returns the count of characters that were stored, not including +//! the trailing 0. +// +//***************************************************************************** +int +UARTgets(char *pcBuf, uint32_t ui32Len) +{ +#ifdef UART_BUFFERED + uint32_t ui32Count = 0; + int8_t cChar; + + // + // Check the arguments. + // + ASSERT(pcBuf != 0); + ASSERT(ui32Len != 0); + ASSERT(g_ui32Base != 0); + + // + // Adjust the length back by 1 to leave space for the trailing + // null terminator. + // + ui32Len--; + + // + // Process characters until a newline is received. + // + while(1) + { + // + // Read the next character from the receive buffer. + // + if(!RX_BUFFER_EMPTY) + { + cChar = g_pcUARTRxBuffer[g_ui32UARTRxReadIndex]; + ADVANCE_RX_BUFFER_INDEX(g_ui32UARTRxReadIndex); + + // + // See if a newline or escape character was received. + // + if((cChar == '\r') || (cChar == '\n') || (cChar == 0x1b)) + { + // + // Stop processing the input and end the line. + // + break; + } + + // + // Process the received character as long as we are not at the end + // of the buffer. If the end of the buffer has been reached then + // all additional characters are ignored until a newline is + // received. + // + if(ui32Count < ui32Len) + { + // + // Store the character in the caller supplied buffer. + // + pcBuf[ui32Count] = cChar; + + // + // Increment the count of characters received. + // + ui32Count++; + } + } + } + + // + // Add a null termination to the string. + // + pcBuf[ui32Count] = 0; + + // + // Return the count of int8_ts in the buffer, not counting the trailing 0. + // + return(ui32Count); +#else + uint32_t ui32Count = 0; + int8_t cChar; + static int8_t bLastWasCR = 0; + + // + // Check the arguments. + // + ASSERT(pcBuf != 0); + ASSERT(ui32Len != 0); + ASSERT(g_ui32Base != 0); + + // + // Adjust the length back by 1 to leave space for the trailing + // null terminator. + // + ui32Len--; + + // + // Process characters until a newline is received. + // + while(1) + { + // + // Read the next character from the console. + // + cChar = MAP_UARTCharGet(g_ui32Base); + + // + // See if the backspace key was pressed. + // + if(cChar == '\b') + { + // + // If there are any characters already in the buffer, then delete + // the last. + // + if(ui32Count) + { + // + // Rub out the previous character. + // + UARTwrite("\b \b", 3); + + // + // Decrement the number of characters in the buffer. + // + ui32Count--; + } + + // + // Skip ahead to read the next character. + // + continue; + } + + // + // If this character is LF and last was CR, then just gobble up the + // character because the EOL processing was taken care of with the CR. + // + if((cChar == '\n') && bLastWasCR) + { + bLastWasCR = 0; + continue; + } + + // + // See if a newline or escape character was received. + // + if((cChar == '\r') || (cChar == '\n') || (cChar == 0x1b)) + { + // + // If the character is a CR, then it may be followed by a LF which + // should be paired with the CR. So remember that a CR was + // received. + // + if(cChar == '\r') + { + bLastWasCR = 1; + } + + // + // Stop processing the input and end the line. + // + break; + } + + // + // Process the received character as long as we are not at the end of + // the buffer. If the end of the buffer has been reached then all + // additional characters are ignored until a newline is received. + // + if(ui32Count < ui32Len) + { + // + // Store the character in the caller supplied buffer. + // + pcBuf[ui32Count] = cChar; + + // + // Increment the count of characters received. + // + ui32Count++; + + // + // Reflect the character back to the user. + // + MAP_UARTCharPut(g_ui32Base, cChar); + } + } + + // + // Add a null termination to the string. + // + pcBuf[ui32Count] = 0; + + // + // Send a CRLF pair to the terminal to end the line. + // + UARTwrite("\r\n", 2); + + // + // Return the count of int8_ts in the buffer, not counting the trailing 0. + // + return(ui32Count); +#endif +} + +//***************************************************************************** +// +//! Read a single character from the UART, blocking if necessary. +//! +//! This function will receive a single character from the UART and store it at +//! the supplied address. +//! +//! In both buffered and unbuffered modes, this function will block until a +//! character is received. If non-blocking operation is required in buffered +//! mode, a call to UARTRxAvail() may be made to determine whether any +//! characters are currently available for reading. +//! +//! \return Returns the character read. +// +//***************************************************************************** +unsigned char +UARTgetc(void) +{ +#ifdef UART_BUFFERED + unsigned char cChar; + + // + // Wait for a character to be received. + // + while(RX_BUFFER_EMPTY) + { + // + // Block waiting for a character to be received (if the buffer is + // currently empty). + // + } + + // + // Read a character from the buffer. + // + cChar = g_pcUARTRxBuffer[g_ui32UARTRxReadIndex]; + ADVANCE_RX_BUFFER_INDEX(g_ui32UARTRxReadIndex); + + // + // Return the character to the caller. + // + return(cChar); +#else + // + // Block until a character is received by the UART then return it to + // the caller. + // + return(MAP_UARTCharGet(g_ui32Base)); +#endif +} + +//***************************************************************************** +// +//! A simple UART based vprintf function supporting \%c, \%d, \%p, \%s, \%u, +//! \%x, and \%X. +//! +//! \param pcString is the format string. +//! \param vaArgP is a variable argument list pointer whose content will depend +//! upon the format string passed in \e pcString. +//! +//! This function is very similar to the C library vprintf() function. +//! All of its output will be sent to the UART. Only the following formatting +//! characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%s, \%d, \%i, \%u, \%p, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeroes instead of spaces. +//! +//! The type of the arguments in the variable arguments list must match the +//! requirements of the format string. For example, if an integer was passed +//! where a string was expected, an error of some kind will most likely occur. +//! +//! \return None. +// +//***************************************************************************** +void +UARTvprintf(const char *pcString, va_list vaArgP) +{ + uint32_t ui32Idx, ui32Value, ui32Pos, ui32Count, ui32Base, ui32Neg; + char *pcStr, pcBuf[16], cFill; + + // + // Check the arguments. + // + ASSERT(pcString != 0); + + // + // Loop while there are more characters in the string. + // + while(*pcString) + { + // + // Find the first non-% character, or the end of the string. + // + for(ui32Idx = 0; + (pcString[ui32Idx] != '%') && (pcString[ui32Idx] != '\0'); + ui32Idx++) + { + } + + // + // Write this portion of the string. + // + UARTwrite(pcString, ui32Idx); + + // + // Skip the portion of the string that was written. + // + pcString += ui32Idx; + + // + // See if the next character is a %. + // + if(*pcString == '%') + { + // + // Skip the %. + // + pcString++; + + // + // Set the digit count to zero, and the fill character to space + // (in other words, to the defaults). + // + ui32Count = 0; + cFill = ' '; + + // + // It may be necessary to get back here to process more characters. + // Goto's aren't pretty, but effective. I feel extremely dirty for + // using not one but two of the beasts. + // +again: + + // + // Determine how to handle the next character. + // + switch(*pcString++) + { + // + // Handle the digit characters. + // + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + // + // If this is a zero, and it is the first digit, then the + // fill character is a zero instead of a space. + // + if((pcString[-1] == '0') && (ui32Count == 0)) + { + cFill = '0'; + } + + // + // Update the digit count. + // + ui32Count *= 10; + ui32Count += pcString[-1] - '0'; + + // + // Get the next character. + // + goto again; + } + + // + // Handle the %c command. + // + case 'c': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Print out the character. + // + UARTwrite((char *)&ui32Value, 1); + + // + // This command has been handled. + // + break; + } + + // + // Handle the %d and %i commands. + // + case 'd': + case 'i': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Reset the buffer position. + // + ui32Pos = 0; + + // + // If the value is negative, make it positive and indicate + // that a minus sign is needed. + // + if((int32_t)ui32Value < 0) + { + // + // Make the value positive. + // + ui32Value = -(int32_t)ui32Value; + + // + // Indicate that the value is negative. + // + ui32Neg = 1; + } + else + { + // + // Indicate that the value is positive so that a minus + // sign isn't inserted. + // + ui32Neg = 0; + } + + // + // Set the base to 10. + // + ui32Base = 10; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %s command. + // + case 's': + { + // + // Get the string pointer from the varargs. + // + pcStr = va_arg(vaArgP, char *); + + // + // Determine the length of the string. + // + for(ui32Idx = 0; pcStr[ui32Idx] != '\0'; ui32Idx++) + { + } + + // + // Write the string. + // + UARTwrite(pcStr, ui32Idx); + + // + // Write any required padding spaces + // + if(ui32Count > ui32Idx) + { + ui32Count -= ui32Idx; + while(ui32Count--) + { + UARTwrite(" ", 1); + } + } + + // + // This command has been handled. + // + break; + } + + // + // Handle the %u command. + // + case 'u': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Reset the buffer position. + // + ui32Pos = 0; + + // + // Set the base to 10. + // + ui32Base = 10; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ui32Neg = 0; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %x and %X commands. Note that they are treated + // identically; in other words, %X will use lower case letters + // for a-f instead of the upper case letters it should use. We + // also alias %p to %x. + // + case 'x': + case 'X': + case 'p': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Reset the buffer position. + // + ui32Pos = 0; + + // + // Set the base to 16. + // + ui32Base = 16; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ui32Neg = 0; + + // + // Determine the number of digits in the string version of + // the value. + // +convert: + for(ui32Idx = 1; + (((ui32Idx * ui32Base) <= ui32Value) && + (((ui32Idx * ui32Base) / ui32Base) == ui32Idx)); + ui32Idx *= ui32Base, ui32Count--) + { + } + + // + // If the value is negative, reduce the count of padding + // characters needed. + // + if(ui32Neg) + { + ui32Count--; + } + + // + // If the value is negative and the value is padded with + // zeros, then place the minus sign before the padding. + // + if(ui32Neg && (cFill == '0')) + { + // + // Place the minus sign in the output buffer. + // + pcBuf[ui32Pos++] = '-'; + + // + // The minus sign has been placed, so turn off the + // negative flag. + // + ui32Neg = 0; + } + + // + // Provide additional padding at the beginning of the + // string conversion if needed. + // + if((ui32Count > 1) && (ui32Count < 16)) + { + for(ui32Count--; ui32Count; ui32Count--) + { + pcBuf[ui32Pos++] = cFill; + } + } + + // + // If the value is negative, then place the minus sign + // before the number. + // + if(ui32Neg) + { + // + // Place the minus sign in the output buffer. + // + pcBuf[ui32Pos++] = '-'; + } + + // + // Convert the value into a string. + // + for(; ui32Idx; ui32Idx /= ui32Base) + { + pcBuf[ui32Pos++] = + g_pcHex[(ui32Value / ui32Idx) % ui32Base]; + } + + // + // Write the string. + // + UARTwrite(pcBuf, ui32Pos); + + // + // This command has been handled. + // + break; + } + + // + // Handle the %% command. + // + case '%': + { + // + // Simply write a single %. + // + UARTwrite(pcString - 1, 1); + + // + // This command has been handled. + // + break; + } + + // + // Handle all other commands. + // + default: + { + // + // Indicate an error. + // + UARTwrite("ERROR", 5); + + // + // This command has been handled. + // + break; + } + } + } + } +} + +//***************************************************************************** +// +//! A simple UART based printf function supporting \%c, \%d, \%p, \%s, \%u, +//! \%x, and \%X. +//! +//! \param pcString is the format string. +//! \param ... are the optional arguments, which depend on the contents of the +//! format string. +//! +//! This function is very similar to the C library fprintf() function. +//! All of its output will be sent to the UART. Only the following formatting +//! characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%s, \%d, \%i, \%u, \%p, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeroes instead of spaces. +//! +//! The type of the arguments after \e pcString must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! \return None. +// +//***************************************************************************** +void +UARTprintf(const char *pcString, ...) +{ + va_list vaArgP; + + // + // Start the varargs processing. + // + va_start(vaArgP, pcString); + + UARTvprintf(pcString, vaArgP); + + // + // We're finished with the varargs now. + // + va_end(vaArgP); +} + +//***************************************************************************** +// +//! Returns the number of bytes available in the receive buffer. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to determine the number +//! of bytes of data currently available in the receive buffer. +//! +//! \return Returns the number of available bytes. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +int +UARTRxBytesAvail(void) +{ + return(RX_BUFFER_USED); +} +#endif + +#if defined(UART_BUFFERED) || defined(DOXYGEN) +//***************************************************************************** +// +//! Returns the number of bytes free in the transmit buffer. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to determine the amount +//! of space currently available in the transmit buffer. +//! +//! \return Returns the number of free bytes. +// +//***************************************************************************** +int +UARTTxBytesFree(void) +{ + return(TX_BUFFER_FREE); +} +#endif + +//***************************************************************************** +// +//! Looks ahead in the receive buffer for a particular character. +//! +//! \param ucChar is the character that is to be searched for. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to look ahead in the +//! receive buffer for a particular character and report its position if found. +//! It is typically used to determine whether a complete line of user input is +//! available, in which case ucChar should be set to CR ('\\r') which is used +//! as the line end marker in the receive buffer. +//! +//! \return Returns -1 to indicate that the requested character does not exist +//! in the receive buffer. Returns a non-negative number if the character was +//! found in which case the value represents the position of the first instance +//! of \e ucChar relative to the receive buffer read pointer. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +int +UARTPeek(unsigned char ucChar) +{ + int iCount; + int iAvail; + uint32_t ui32ReadIndex; + + // + // How many characters are there in the receive buffer? + // + iAvail = (int)RX_BUFFER_USED; + ui32ReadIndex = g_ui32UARTRxReadIndex; + + // + // Check all the unread characters looking for the one passed. + // + for(iCount = 0; iCount < iAvail; iCount++) + { + if(g_pcUARTRxBuffer[ui32ReadIndex] == ucChar) + { + // + // We found it so return the index + // + return(iCount); + } + else + { + // + // This one didn't match so move on to the next character. + // + ADVANCE_RX_BUFFER_INDEX(ui32ReadIndex); + } + } + + // + // If we drop out of the loop, we didn't find the character in the receive + // buffer. + // + return(-1); +} +#endif + +//***************************************************************************** +// +//! Flushes the receive buffer. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to discard any data +//! received from the UART but not yet read using UARTgets(). +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTFlushRx(void) +{ + uint32_t ui32Int; + + // + // Temporarily turn off interrupts. + // + ui32Int = MAP_IntMasterDisable(); + + // + // Flush the receive buffer. + // + g_ui32UARTRxReadIndex = 0; + g_ui32UARTRxWriteIndex = 0; + + // + // If interrupts were enabled when we turned them off, turn them + // back on again. + // + if(!ui32Int) + { + MAP_IntMasterEnable(); + } +} +#endif + +//***************************************************************************** +// +//! Flushes the transmit buffer. +//! +//! \param bDiscard indicates whether any remaining data in the buffer should +//! be discarded (\b true) or transmitted (\b false). +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to flush the transmit +//! buffer, either discarding or transmitting any data received via calls to +//! UARTprintf() that is waiting to be transmitted. On return, the transmit +//! buffer will be empty. +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTFlushTx(bool bDiscard) +{ + uint32_t ui32Int; + + // + // Should the remaining data be discarded or transmitted? + // + if(bDiscard) + { + // + // The remaining data should be discarded, so temporarily turn off + // interrupts. + // + ui32Int = MAP_IntMasterDisable(); + + // + // Flush the transmit buffer. + // + g_ui32UARTTxReadIndex = 0; + g_ui32UARTTxWriteIndex = 0; + + // + // If interrupts were enabled when we turned them off, turn them + // back on again. + // + if(!ui32Int) + { + MAP_IntMasterEnable(); + } + } + else + { + // + // Wait for all remaining data to be transmitted before returning. + // + while(!TX_BUFFER_EMPTY) + { + } + } +} +#endif + +//***************************************************************************** +// +//! Enables or disables echoing of received characters to the transmitter. +//! +//! \param bEnable must be set to \b true to enable echo or \b false to +//! disable it. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to control whether or not +//! received characters are automatically echoed back to the transmitter. By +//! default, echo is enabled and this is typically the desired behavior if +//! the module is being used to support a serial command line. In applications +//! where this module is being used to provide a convenient, buffered serial +//! interface over which application-specific binary protocols are being run, +//! however, echo may be undesirable and this function can be used to disable +//! it. +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTEchoSet(bool bEnable) +{ + g_bDisableEcho = !bEnable; +} +#endif + +//***************************************************************************** +// +//! Handles UART interrupts. +//! +//! This function handles interrupts from the UART. It will copy data from the +//! transmit buffer to the UART transmit FIFO if space is available, and it +//! will copy data from the UART receive FIFO to the receive buffer if data is +//! available. +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTStdioIntHandler(void) +{ + uint32_t ui32Ints; + int8_t cChar; + int32_t i32Char; + static bool bLastWasCR = false; + + // + // Get and clear the current interrupt source(s) + // + ui32Ints = MAP_UARTIntStatus(g_ui32Base, true); + MAP_UARTIntClear(g_ui32Base, ui32Ints); + + // + // Are we being interrupted because the TX FIFO has space available? + // + if(ui32Ints & UART_INT_TX) + { + // + // Move as many bytes as we can into the transmit FIFO. + // + UARTPrimeTransmit(g_ui32Base); + + // + // If the output buffer is empty, turn off the transmit interrupt. + // + if(TX_BUFFER_EMPTY) + { + MAP_UARTIntDisable(g_ui32Base, UART_INT_TX); + } + } + + // + // Are we being interrupted due to a received character? + // + if(ui32Ints & (UART_INT_RX | UART_INT_RT)) + { + // + // Get all the available characters from the UART. + // + while(MAP_UARTCharsAvail(g_ui32Base)) + { + // + // Read a character + // + i32Char = MAP_UARTCharGetNonBlocking(g_ui32Base); + cChar = (unsigned char)(i32Char & 0xFF); + + // + // If echo is disabled, we skip the various text filtering + // operations that would typically be required when supporting a + // command line. + // + if(!g_bDisableEcho) + { + // + // Handle backspace by erasing the last character in the + // buffer. + // + if(cChar == '\b') + { + // + // If there are any characters already in the buffer, then + // delete the last. + // + if(!RX_BUFFER_EMPTY) + { + // + // Rub out the previous character on the users + // terminal. + // + UARTwrite("\b \b", 3); + + // + // Decrement the number of characters in the buffer. + // + if(g_ui32UARTRxWriteIndex == 0) + { + g_ui32UARTRxWriteIndex = UART_RX_BUFFER_SIZE - 1; + } + else + { + g_ui32UARTRxWriteIndex--; + } + } + + // + // Skip ahead to read the next character. + // + continue; + } + + // + // If this character is LF and last was CR, then just gobble up + // the character since we already echoed the previous CR and we + // don't want to store 2 characters in the buffer if we don't + // need to. + // + if((cChar == '\n') && bLastWasCR) + { + bLastWasCR = false; + continue; + } + + // + // See if a newline or escape character was received. + // + if((cChar == '\r') || (cChar == '\n') || (cChar == 0x1b)) + { + // + // If the character is a CR, then it may be followed by an + // LF which should be paired with the CR. So remember that + // a CR was received. + // + if(cChar == '\r') + { + bLastWasCR = 1; + } + + // + // Regardless of the line termination character received, + // put a CR in the receive buffer as a marker telling + // UARTgets() where the line ends. We also send an + // additional LF to ensure that the local terminal echo + // receives both CR and LF. + // + cChar = '\r'; + UARTwrite("\n", 1); + } + } + + // + // If there is space in the receive buffer, put the character + // there, otherwise throw it away. + // + if(!RX_BUFFER_FULL) + { + // + // Store the new character in the receive buffer + // + g_pcUARTRxBuffer[g_ui32UARTRxWriteIndex] = + (unsigned char)(i32Char & 0xFF); + ADVANCE_RX_BUFFER_INDEX(g_ui32UARTRxWriteIndex); + + // + // If echo is enabled, write the character to the transmit + // buffer so that the user gets some immediate feedback. + // + if(!g_bDisableEcho) + { + UARTwrite((const char *)&cChar, 1); + } + } + } + + // + // If we wrote anything to the transmit buffer, make sure it actually + // gets transmitted. + // + UARTPrimeTransmit(g_ui32Base); + MAP_UARTIntEnable(g_ui32Base, UART_INT_TX); + } +} +#endif + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/uartstdio.h b/utils/uartstdio.h new file mode 100644 index 0000000..fbadabc --- /dev/null +++ b/utils/uartstdio.h @@ -0,0 +1,86 @@ +//***************************************************************************** +// +// uartstdio.h - Prototypes for the UART console functions. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +#ifndef __UARTSTDIO_H__ +#define __UARTSTDIO_H__ + +#include + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// If built for buffered operation, the following labels define the sizes of +// the transmit and receive buffers respectively. +// +//***************************************************************************** +#ifdef UART_BUFFERED +#ifndef UART_RX_BUFFER_SIZE +#define UART_RX_BUFFER_SIZE 128 +#endif +#ifndef UART_TX_BUFFER_SIZE +#define UART_TX_BUFFER_SIZE 1024 +#endif +#endif + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern void UARTStdioConfig(uint32_t ui32Port, uint32_t ui32Baud, + uint32_t ui32SrcClock); +extern int UARTgets(char *pcBuf, uint32_t ui32Len); +extern unsigned char UARTgetc(void); +extern void UARTprintf(const char *pcString, ...); +extern void UARTvprintf(const char *pcString, va_list vaArgP); +extern int UARTwrite(const char *pcBuf, uint32_t ui32Len); +#ifdef UART_BUFFERED +extern int UARTPeek(unsigned char ucChar); +extern void UARTFlushTx(bool bDiscard); +extern void UARTFlushRx(void); +extern int UARTRxBytesAvail(void); +extern int UARTTxBytesFree(void); +extern void UARTEchoSet(bool bEnable); +#endif + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __UARTSTDIO_H__ diff --git a/utils/ustdlib.c b/utils/ustdlib.c new file mode 100644 index 0000000..0fa6f05 --- /dev/null +++ b/utils/ustdlib.c @@ -0,0 +1,1826 @@ +//***************************************************************************** +// +// ustdlib.c - Simple standard library functions. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +#include +#include "driverlib/debug.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup ustdlib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// A mapping from an integer between 0 and 15 to its ASCII character +// equivalent. +// +//***************************************************************************** +static const char * const g_pcHex = "0123456789abcdef"; + +//***************************************************************************** +// +//! Copies a certain number of characters from one string to another. +//! +//! \param s1 is a pointer to the destination buffer into which characters +//! are to be copied. +//! \param s2 is a pointer to the string from which characters are to be +//! copied. +//! \param n is the number of characters to copy to the destination buffer. +//! +//! This function copies at most \e n characters from the string pointed to +//! by \e s2 into the buffer pointed to by \e s1. If the end of \e s2 is found +//! before \e n characters have been copied, remaining characters in \e s1 +//! will be padded with zeroes until \e n characters have been written. Note +//! that the destination string will only be NULL terminated if the number of +//! characters to be copied is greater than the length of \e s2. +//! +//! \return Returns \e s1. +// +//***************************************************************************** +char * +ustrncpy(char * restrict s1, const char * restrict s2, size_t n) +{ + size_t count; + + // + // Check the arguments. + // + ASSERT(s1); + ASSERT(s2); + + // + // Start at the beginning of the source string. + // + count = 0; + + // + // Copy the source string until we run out of source characters or + // destination space. + // + while(n && s2[count]) + { + s1[count] = s2[count]; + count++; + n--; + } + + // + // Pad the destination if we are not yet done. + // + while(n) + { + s1[count++] = (char)0; + n--; + } + + // + // Pass the destination pointer back to the caller. + // + return(s1); +} + +//***************************************************************************** +// +//! A simple vsnprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param s points to the buffer where the converted string is stored. +//! \param n is the size of the buffer. +//! \param format is the format string. +//! \param arg is the list of optional arguments, which depend on the +//! contents of the format string. +//! +//! This function is very similar to the C library vsnprintf() +//! function. Only the following formatting characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeroes instead of spaces. +//! +//! The type of the arguments after \e format must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! The \e n parameter limits the number of characters that will be +//! stored in the buffer pointed to by \e s to prevent the possibility of +//! a buffer overflow. The buffer size should be large enough to hold the +//! expected converted output string, including the null termination character. +//! +//! The function will return the number of characters that would be converted +//! as if there were no limit on the buffer size. Therefore it is possible for +//! the function to return a count that is greater than the specified buffer +//! size. If this happens, it means that the output was truncated. +//! +//! \return Returns the number of characters that were to be stored, not +//! including the NULL termination character, regardless of space in the +//! buffer. +// +//***************************************************************************** +int +uvsnprintf(char * restrict s, size_t n, const char * restrict format, + va_list arg) +{ + unsigned long ulIdx, ulValue, ulCount, ulBase, ulNeg; + char *pcStr, cFill; + int iConvertCount = 0; + + // + // Check the arguments. + // + ASSERT(s); + ASSERT(n); + ASSERT(format); + + // + // Adjust buffer size limit to allow one space for null termination. + // + if(n) + { + n--; + } + + // + // Initialize the count of characters converted. + // + iConvertCount = 0; + + // + // Loop while there are more characters in the format string. + // + while(*format) + { + // + // Find the first non-% character, or the end of the string. + // + for(ulIdx = 0; (format[ulIdx] != '%') && (format[ulIdx] != '\0'); + ulIdx++) + { + } + + // + // Write this portion of the string to the output buffer. If there are + // more characters to write than there is space in the buffer, then + // only write as much as will fit in the buffer. + // + if(ulIdx > n) + { + ustrncpy(s, format, n); + s += n; + n = 0; + } + else + { + ustrncpy(s, format, ulIdx); + s += ulIdx; + n -= ulIdx; + } + + // + // Update the conversion count. This will be the number of characters + // that should have been written, even if there was not room in the + // buffer. + // + iConvertCount += ulIdx; + + // + // Skip the portion of the format string that was written. + // + format += ulIdx; + + // + // See if the next character is a %. + // + if(*format == '%') + { + // + // Skip the %. + // + format++; + + // + // Set the digit count to zero, and the fill character to space + // (that is, to the defaults). + // + ulCount = 0; + cFill = ' '; + + // + // It may be necessary to get back here to process more characters. + // Goto's aren't pretty, but effective. I feel extremely dirty for + // using not one but two of the beasts. + // +again: + + // + // Determine how to handle the next character. + // + switch(*format++) + { + // + // Handle the digit characters. + // + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + // + // If this is a zero, and it is the first digit, then the + // fill character is a zero instead of a space. + // + if((format[-1] == '0') && (ulCount == 0)) + { + cFill = '0'; + } + + // + // Update the digit count. + // + ulCount *= 10; + ulCount += format[-1] - '0'; + + // + // Get the next character. + // + goto again; + } + + // + // Handle the %c command. + // + case 'c': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // Copy the character to the output buffer, if there is + // room. Update the buffer size remaining. + // + if(n != 0) + { + *s++ = (char)ulValue; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + + // + // This command has been handled. + // + break; + } + + // + // Handle the %d and %i commands. + // + case 'd': + case 'i': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // If the value is negative, make it positive and indicate + // that a minus sign is needed. + // + if((long)ulValue < 0) + { + // + // Make the value positive. + // + ulValue = -(long)ulValue; + + // + // Indicate that the value is negative. + // + ulNeg = 1; + } + else + { + // + // Indicate that the value is positive so that a + // negative sign isn't inserted. + // + ulNeg = 0; + } + + // + // Set the base to 10. + // + ulBase = 10; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %s command. + // + case 's': + { + // + // Get the string pointer from the varargs. + // + pcStr = va_arg(arg, char *); + + // + // Determine the length of the string. + // + for(ulIdx = 0; pcStr[ulIdx] != '\0'; ulIdx++) + { + } + + // + // Update the convert count to include any padding that + // should be necessary (regardless of whether we have space + // to write it or not). + // + if(ulCount > ulIdx) + { + iConvertCount += (ulCount - ulIdx); + } + + // + // Copy the string to the output buffer. Only copy as much + // as will fit in the buffer. Update the output buffer + // pointer and the space remaining. + // + if(ulIdx > n) + { + ustrncpy(s, pcStr, n); + s += n; + n = 0; + } + else + { + ustrncpy(s, pcStr, ulIdx); + s += ulIdx; + n -= ulIdx; + + // + // Write any required padding spaces assuming there is + // still space in the buffer. + // + if(ulCount > ulIdx) + { + ulCount -= ulIdx; + if(ulCount > n) + { + ulCount = n; + } + n = -ulCount; + + while(ulCount--) + { + *s++ = ' '; + } + } + } + + // + // Update the conversion count. This will be the number of + // characters that should have been written, even if there + // was not room in the buffer. + // + iConvertCount += ulIdx; + + // + // This command has been handled. + // + break; + } + + // + // Handle the %u command. + // + case 'u': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // Set the base to 10. + // + ulBase = 10; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ulNeg = 0; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %x and %X commands. Note that they are treated + // identically; that is, %X will use lower case letters for a-f + // instead of the upper case letters is should use. We also + // alias %p to %x. + // + case 'x': + case 'X': + case 'p': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // Set the base to 16. + // + ulBase = 16; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ulNeg = 0; + + // + // Determine the number of digits in the string version of + // the value. + // +convert: + for(ulIdx = 1; + (((ulIdx * ulBase) <= ulValue) && + (((ulIdx * ulBase) / ulBase) == ulIdx)); + ulIdx *= ulBase, ulCount--) + { + } + + // + // If the value is negative, reduce the count of padding + // characters needed. + // + if(ulNeg) + { + ulCount--; + } + + // + // If the value is negative and the value is padded with + // zeros, then place the minus sign before the padding. + // + if(ulNeg && (n != 0) && (cFill == '0')) + { + // + // Place the minus sign in the output buffer. + // + *s++ = '-'; + n--; + + // + // Update the conversion count. + // + iConvertCount++; + + // + // The minus sign has been placed, so turn off the + // negative flag. + // + ulNeg = 0; + } + + // + // See if there are more characters in the specified field + // width than there are in the conversion of this value. + // + if((ulCount > 1) && (ulCount < 65536)) + { + // + // Loop through the required padding characters. + // + for(ulCount--; ulCount; ulCount--) + { + // + // Copy the character to the output buffer if there + // is room. + // + if(n != 0) + { + *s++ = cFill; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + } + + // + // If the value is negative, then place the minus sign + // before the number. + // + if(ulNeg && (n != 0)) + { + // + // Place the minus sign in the output buffer. + // + *s++ = '-'; + n--; + + // + // Update the conversion count. + // + iConvertCount++; + } + + // + // Convert the value into a string. + // + for(; ulIdx; ulIdx /= ulBase) + { + // + // Copy the character to the output buffer if there is + // room. + // + if(n != 0) + { + *s++ = g_pcHex[(ulValue / ulIdx) % ulBase]; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + + // + // This command has been handled. + // + break; + } + + // + // Handle the %% command. + // + case '%': + { + // + // Simply write a single %. + // + if(n != 0) + { + *s++ = format[-1]; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + + // + // This command has been handled. + // + break; + } + + // + // Handle all other commands. + // + default: + { + // + // Indicate an error. + // + if(n >= 5) + { + ustrncpy(s, "ERROR", 5); + s += 5; + n -= 5; + } + else + { + ustrncpy(s, "ERROR", n); + s += n; + n = 0; + } + + // + // Update the conversion count. + // + iConvertCount += 5; + + // + // This command has been handled. + // + break; + } + } + } + } + + // + // Null terminate the string in the buffer. + // + *s = 0; + + // + // Return the number of characters in the full converted string. + // + return(iConvertCount); +} + +//***************************************************************************** +// +//! A simple sprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and \%X. +//! +//! \param s is the buffer where the converted string is stored. +//! \param format is the format string. +//! \param ... are the optional arguments, which depend on the contents of the +//! format string. +//! +//! This function is very similar to the C library sprintf() function. +//! Only the following formatting characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeros instead of spaces. +//! +//! The type of the arguments after \e format must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! The caller must ensure that the buffer \e s is large enough to hold the +//! entire converted string, including the null termination character. +//! +//! \return Returns the count of characters that were written to the output +//! buffer, not including the NULL termination character. +// +//***************************************************************************** +int +usprintf(char * restrict s, const char *format, ...) +{ + va_list arg; + int ret; + + // + // Start the varargs processing. + // + va_start(arg, format); + + // + // Call vsnprintf to perform the conversion. Use a large number for the + // buffer size. + // + ret = uvsnprintf(s, 0xffff, format, arg); + + // + // End the varargs processing. + // + va_end(arg); + + // + // Return the conversion count. + // + return(ret); +} + +//***************************************************************************** +// +//! A simple snprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param s is the buffer where the converted string is stored. +//! \param n is the size of the buffer. +//! \param format is the format string. +//! \param ... are the optional arguments, which depend on the contents of the +//! format string. +//! +//! This function is very similar to the C library sprintf() function. +//! Only the following formatting characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeros instead of spaces. +//! +//! The type of the arguments after \e format must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! The function will copy at most \e n - 1 characters into the buffer +//! \e s. One space is reserved in the buffer for the null termination +//! character. +//! +//! The function will return the number of characters that would be converted +//! as if there were no limit on the buffer size. Therefore it is possible for +//! the function to return a count that is greater than the specified buffer +//! size. If this happens, it means that the output was truncated. +//! +//! \return Returns the number of characters that were to be stored, not +//! including the NULL termination character, regardless of space in the +//! buffer. +// +//***************************************************************************** +int +usnprintf(char * restrict s, size_t n, const char * restrict format, ...) +{ + va_list arg; + int ret; + + // + // Start the varargs processing. + // + va_start(arg, format); + + // + // Call vsnprintf to perform the conversion. + // + ret = uvsnprintf(s, n, format, arg); + + // + // End the varargs processing. + // + va_end(arg); + + // + // Return the conversion count. + // + return(ret); +} + +//***************************************************************************** +// +// This array contains the number of days in a year at the beginning of each +// month of the year, in a non-leap year. +// +//***************************************************************************** +static const time_t g_psDaysToMonth[12] = +{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +//***************************************************************************** +// +//! Converts from seconds to calendar date and time. +//! +//! \param timer is the number of seconds. +//! \param tm is a pointer to the time structure that is filled in with the +//! broken down date and time. +//! +//! This function converts a number of seconds since midnight GMT on January 1, +//! 1970 (traditional Unix epoch) into the equivalent month, day, year, hours, +//! minutes, and seconds representation. +//! +//! \return None. +// +//***************************************************************************** +void +ulocaltime(time_t timer, struct tm *tm) +{ + time_t temp, months; + + // + // Extract the number of seconds, converting time to the number of minutes. + // + temp = timer / 60; + tm->tm_sec = timer - (temp * 60); + timer = temp; + + // + // Extract the number of minutes, converting time to the number of hours. + // + temp = timer / 60; + tm->tm_min = timer - (temp * 60); + timer = temp; + + // + // Extract the number of hours, converting time to the number of days. + // + temp = timer / 24; + tm->tm_hour = timer - (temp * 24); + timer = temp; + + // + // Compute the day of the week. + // + tm->tm_wday = (timer + 4) % 7; + + // + // Compute the number of leap years that have occurred since 1968, the + // first leap year before 1970. For the beginning of a leap year, cut the + // month loop below at March so that the leap day is classified as February + // 29 followed by March 1, instead of March 1 followed by another March 1. + // + timer += 366 + 365; + temp = timer / ((4 * 365) + 1); + if((timer - (temp * ((4 * 365) + 1))) > (31 + 28)) + { + temp++; + months = 12; + } + else + { + months = 2; + } + + // + // Extract the year. + // + tm->tm_year = ((timer - temp) / 365) + 68; + timer -= ((tm->tm_year - 68) * 365) + temp; + + // + // Extract the month. + // + for(temp = 0; temp < months; temp++) + { + if(g_psDaysToMonth[temp] > timer) + { + break; + } + } + tm->tm_mon = temp - 1; + + // + // Extract the day of the month. + // + tm->tm_mday = timer - g_psDaysToMonth[temp - 1] + 1; +} + +//***************************************************************************** +// +//! Compares two time structures and determines if one is greater than, +//! less than, or equal to the other. +//! +//! \param t1 is the first time structure to compare. +//! \param t2 is the second time structure to compare. +//! +//! This function compares two time structures and returns a signed number +//! to indicate the result of the comparison. If the time represented by +//! \e t1 is greater than the time represented by \e t2 then a positive +//! number is returned. Likewise if \e t1 is less than \e t2 then a +//! negative number is returned. If the two times are equal then the function +//! returns 0. +//! +//! \return Returns 0 if the two times are equal, +1 if \e t1 is greater +//! than \e t2, and -1 if \e t1 is less than \e t2. +// +//***************************************************************************** +static int +ucmptime(struct tm *t1, struct tm *t2) +{ + // + // Compare each field in descending signficance to determine if + // greater than, less than, or equal. + // + if(t1->tm_year > t2->tm_year) + { + return(1); + } + else if(t1->tm_year < t2->tm_year) + { + return(-1); + } + else if(t1->tm_mon > t2->tm_mon) + { + return(1); + } + else if(t1->tm_mon < t2->tm_mon) + { + return(-1); + } + else if(t1->tm_mday > t2->tm_mday) + { + return(1); + } + else if(t1->tm_mday < t2->tm_mday) + { + return(-1); + } + else if(t1->tm_hour > t2->tm_hour) + { + return(1); + } + else if(t1->tm_hour < t2->tm_hour) + { + return(-1); + } + else if(t1->tm_min > t2->tm_min) + { + return(1); + } + else if(t1->tm_min < t2->tm_min) + { + return(-1); + } + else if(t1->tm_sec > t2->tm_sec) + { + return(1); + } + else if(t1->tm_sec < t2->tm_sec) + { + return(-1); + } + else + { + // + // Reaching this branch of the conditional means that all of the + // fields are equal, and thus the two times are equal. + // + return(0); + } +} + +//***************************************************************************** +// +//! Converts calendar date and time to seconds. +//! +//! \param timeptr is a pointer to the time structure that is filled in with +//! the broken down date and time. +//! +//! This function converts the date and time represented by the \e timeptr +//! structure pointer to the number of seconds since midnight GMT on January 1, +//! 1970 (traditional Unix epoch). +//! +//! \return Returns the calendar time and date as seconds. If the conversion +//! was not possible then the function returns (uint32_t)(-1). +// +//***************************************************************************** +time_t +umktime(struct tm *timeptr) +{ + struct tm sTimeGuess; + unsigned long ulTimeGuess = 0x80000000; + unsigned long ulAdjust = 0x40000000; + int iSign; + + // + // Seed the binary search with the first guess. + // + ulocaltime(ulTimeGuess, &sTimeGuess); + iSign = ucmptime(timeptr, &sTimeGuess); + + // + // While the time is not yet found, execute a binary search. + // + while(iSign && ulAdjust) + { + // + // Adjust the time guess up or down depending on the result of the + // last compare. + // + ulTimeGuess = ((iSign > 0) ? (ulTimeGuess + ulAdjust) : + (ulTimeGuess - ulAdjust)); + ulAdjust /= 2; + + // + // Compare the new time guess against the time pointed at by the + // function parameters. + // + ulocaltime(ulTimeGuess, &sTimeGuess); + iSign = ucmptime(timeptr, &sTimeGuess); + } + + // + // If the above loop was exited with iSign == 0, that means that the + // time in seconds was found, so return that value to the caller. + // + if(iSign == 0) + { + return(ulTimeGuess); + } + + // + // Otherwise the time could not be converted so return an error. + // + else + { + return((unsigned long)-1); + } +} + +//***************************************************************************** +// +//! Converts a string into its numeric equivalent. +//! +//! \param nptr is a pointer to the string containing the integer. +//! \param endptr is a pointer that will be set to the first character past +//! the integer in the string. +//! \param base is the radix to use for the conversion; can be zero to +//! auto-select the radix or between 2 and 16 to explicitly specify the radix. +//! +//! This function is very similar to the C library strtoul() function. +//! It scans a string for the first token (that is, non-white space) and +//! converts the value at that location in the string into an integer value. +//! +//! \return Returns the result of the conversion. +// +//***************************************************************************** +unsigned long +ustrtoul(const char * restrict nptr, const char ** restrict endptr, int base) +{ + unsigned long ulRet, ulDigit, ulNeg, ulValid; + const char *pcPtr; + + // + // Check the arguments. + // + ASSERT(nptr); + ASSERT((base == 0) || ((base > 1) && (base <= 16))); + + // + // Initially, the result is zero. + // + ulRet = 0; + ulNeg = 0; + ulValid = 0; + + // + // Skip past any leading white space. + // + pcPtr = nptr; + while((*pcPtr == ' ') || (*pcPtr == '\t')) + { + pcPtr++; + } + + // + // Take a leading + or - from the value. + // + if(*pcPtr == '-') + { + ulNeg = 1; + pcPtr++; + } + else if(*pcPtr == '+') + { + pcPtr++; + } + + // + // See if the radix was not specified, or is 16, and the value starts with + // "0x" or "0X" (to indicate a hex value). + // + if(((base == 0) || (base == 16)) && (*pcPtr == '0') && + ((pcPtr[1] == 'x') || (pcPtr[1] == 'X'))) + { + // + // Skip the leading "0x". + // + pcPtr += 2; + + // + // Set the radix to 16. + // + base = 16; + } + + // + // See if the radix was not specified. + // + if(base == 0) + { + // + // See if the value starts with "0". + // + if(*pcPtr == '0') + { + // + // Values that start with "0" are assumed to be radix 8. + // + base = 8; + } + else + { + // + // Otherwise, the values are assumed to be radix 10. + // + base = 10; + } + } + + // + // Loop while there are more valid digits to consume. + // + while(1) + { + // + // See if this character is a number. + // + if((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Convert the character to its integer equivalent. + // + ulDigit = *pcPtr++ - '0'; + } + + // + // Otherwise, see if this character is an upper case letter. + // + else if((*pcPtr >= 'A') && (*pcPtr <= 'Z')) + { + // + // Convert the character to its integer equivalent. + // + ulDigit = *pcPtr++ - 'A' + 10; + } + + // + // Otherwise, see if this character is a lower case letter. + // + else if((*pcPtr >= 'a') && (*pcPtr <= 'z')) + { + // + // Convert the character to its integer equivalent. + // + ulDigit = *pcPtr++ - 'a' + 10; + } + + // + // Otherwise, this is not a valid character. + // + else + { + // + // Stop converting this value. + // + break; + } + + // + // See if this digit is valid for the chosen radix. + // + if(ulDigit >= base) + { + // + // Since this was not a valid digit, move the pointer back to the + // character that therefore should not have been consumed. + // + pcPtr--; + + // + // Stop converting this value. + // + break; + } + + // + // Add this digit to the converted value. + // + ulRet *= base; + ulRet += ulDigit; + + // + // Since a digit has been added, this is now a valid result. + // + ulValid = 1; + } + + // + // Set the return string pointer to the first character not consumed. + // + if(endptr) + { + *endptr = ulValid ? pcPtr : nptr; + } + + // + // Return the converted value. + // + return(ulNeg ? (0 - ulRet) : ulRet); +} + +//***************************************************************************** +// +// An array of the value of ten raised to the power-of-two exponents. This is +// used for converting the decimal exponent into the floating-point value of +// 10^exp. +// +//***************************************************************************** +static const float g_pfExponents[] = +{ + 1.0e+01, + 1.0e+02, + 1.0e+04, + 1.0e+08, + 1.0e+16, + 1.0e+32, +}; + +//***************************************************************************** +// +//! Converts a string into its floating-point equivalent. +//! +//! \param nptr is a pointer to the string containing the floating-point +//! value. +//! \param endptr is a pointer that will be set to the first character past +//! the floating-point value in the string. +//! +//! This function is very similar to the C library strtof() function. +//! It scans a string for the first token (that is, non-white space) and +//! converts the value at that location in the string into a floating-point +//! value. +//! +//! \return Returns the result of the conversion. +// +//***************************************************************************** +float +ustrtof(const char *nptr, const char **endptr) +{ + unsigned long ulNeg, ulExp, ulExpNeg, ulValid, ulIdx; + float fRet, fDigit, fExp; + const char *pcPtr; + + // + // Check the arguments. + // + ASSERT(nptr); + + // + // Initially, the result is zero. + // + fRet = 0; + ulNeg = 0; + ulValid = 0; + + // + // Skip past any leading white space. + // + pcPtr = nptr; + while((*pcPtr == ' ') || (*pcPtr == '\t')) + { + pcPtr++; + } + + // + // Take a leading + or - from the value. + // + if(*pcPtr == '-') + { + ulNeg = 1; + pcPtr++; + } + else if(*pcPtr == '+') + { + pcPtr++; + } + + // + // Loop while there are valid digits to consume. + // + while((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Add this digit to the converted value. + // + fRet *= 10; + fRet += *pcPtr++ - '0'; + + // + // Since a digit has been added, this is now a valid result. + // + ulValid = 1; + } + + // + // See if the next character is a period and the character after that is a + // digit, indicating the start of the fractional portion of the value. + // + if((*pcPtr == '.') && (pcPtr[1] >= '0') && (pcPtr[1] <= '9')) + { + // + // Skip the period. + // + pcPtr++; + + // + // Loop while there are valid fractional digits to consume. + // + fDigit = 0.1; + while((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Add this digit to the converted value. + // + fRet += (*pcPtr++ - '0') * fDigit; + fDigit /= (float)10.0; + + // + // Since a digit has been added, this is now a valid result. + // + ulValid = 1; + } + } + + // + // See if the next character is an "e" and a valid number has been + // converted, indicating the start of the exponent. + // + if(((pcPtr[0] == 'e') || (pcPtr[0] == 'E')) && (ulValid == 1) && + (((pcPtr[1] >= '0') && (pcPtr[1] <= '9')) || + (((pcPtr[1] == '+') || (pcPtr[1] == '-')) && + (pcPtr[2] >= '0') && (pcPtr[2] <= '9')))) + { + // + // Skip the "e". + // + pcPtr++; + + // + // Take a leading + or - from the exponenet. + // + ulExpNeg = 0; + if(*pcPtr == '-') + { + ulExpNeg = 1; + pcPtr++; + } + else if(*pcPtr == '+') + { + pcPtr++; + } + + // + // Loop while there are valid digits in the exponent. + // + ulExp = 0; + while((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Add this digit to the converted value. + // + ulExp *= 10; + ulExp += *pcPtr++ - '0'; + } + + // + // Raise ten to the power of the exponent. Do this via binary + // decomposition; for each binary bit set in the exponent, multiply the + // floating-point representation by ten raised to that binary value + // (extracted from the table above). + // + fExp = 1; + for(ulIdx = 0; ulIdx < 7; ulIdx++) + { + if(ulExp & (1 << ulIdx)) + { + fExp *= g_pfExponents[ulIdx]; + } + } + + // + // If the exponent is negative, then the exponent needs to be inverted. + // + if(ulExpNeg == 1) + { + fExp = 1 / fExp; + } + + // + // Multiply the result by the computed exponent value. + // + fRet *= fExp; + } + + // + // Set the return string pointer to the first character not consumed. + // + if(endptr) + { + *endptr = ulValid ? pcPtr : nptr; + } + + // + // Return the converted value. + // + return(ulNeg ? (0 - fRet) : fRet); +} + +//***************************************************************************** +// +//! Returns the length of a null-terminated string. +//! +//! \param s is a pointer to the string whose length is to be found. +//! +//! This function is very similar to the C library strlen() function. +//! It determines the length of the null-terminated string passed and returns +//! this to the caller. +//! +//! This implementation assumes that single byte character strings are passed +//! and will return incorrect values if passed some UTF-8 strings. +//! +//! \return Returns the length of the string pointed to by \e s. +// +//***************************************************************************** +size_t +ustrlen(const char *s) +{ + size_t len; + + // + // Check the arguments. + // + ASSERT(s); + + // + // Initialize the length. + // + len = 0; + + // + // Step throug the string looking for a zero character (marking its end). + // + while(s[len]) + { + // + // Zero not found so move on to the next character. + // + len++; + } + + return(len); +} + +//***************************************************************************** +// +//! Finds a substring within a string. +//! +//! \param s1 is a pointer to the string that will be searched. +//! \param s2 is a pointer to the substring that is to be found within +//! \e s1. +//! +//! This function is very similar to the C library strstr() function. +//! It scans a string for the first instance of a given substring and returns +//! a pointer to that substring. If the substring cannot be found, a NULL +//! pointer is returned. +//! +//! \return Returns a pointer to the first occurrence of \e s2 within +//! \e s1 or NULL if no match is found. +// +//***************************************************************************** +char * +ustrstr(const char *s1, const char *s2) +{ + size_t n; + + // + // Get the length of the string to be found. + // + n = ustrlen(s2); + + // + // Loop while we have not reached the end of the string. + // + while(*s1) + { + // + // Check to see if the substring appears at this position. + // + if(ustrncmp(s2, s1, n) == 0) + { + // + // It does so return the pointer. + // + return((char *)s1); + } + + // + // Move to the next position in the string being searched. + // + s1++; + } + + // + // We reached the end of the string without finding the substring so + // return NULL. + // + return((char *)0); +} + +//***************************************************************************** +// +//! Compares two strings without regard to case. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! \param n is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strncasecmp() +//! function. It compares at most \e n characters of two strings without +//! regard to case. The comparison ends if a terminating NULL character is +//! found in either string before \e n characters are compared. In this case, +//! the shorter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrncasecmp(const char *s1, const char *s2, size_t n) +{ + char c1, c2; + + // + // Loop while there are more characters to compare. + // + while(n) + { + // + // If we reached a NULL in both strings, they must be equal so + // we end the comparison and return 0 + // + if(!*s1 && !*s2) + { + return(0); + } + + // + // Lower case the characters at the current position before we compare. + // + c1 = (((*s1 >= 'A') && (*s1 <= 'Z')) ? (*s1 + ('a' - 'A')) : *s1); + c2 = (((*s2 >= 'A') && (*s2 <= 'Z')) ? (*s2 + ('a' - 'A')) : *s2); + + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(c2 < c1) + { + return(1); + } + if(c1 < c2) + { + return(-1); + } + + // + // Move on to the next character. + // + s1++; + s2++; + n--; + } + + // + // If we fall out, the strings must be equal for at least the first n + // characters so return 0 to indicate this. + // + return(0); +} + +//***************************************************************************** +// +//! Compares two strings without regard to case. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! +//! This function is very similar to the C library strcasecmp() +//! function. It compares two strings without regard to case. The comparison +//! ends if a terminating NULL character is found in either string. In this +//! case, the int16_ter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrcasecmp(const char *s1, const char *s2) +{ + // + // Just let ustrncasecmp() handle this. + // + return(ustrncasecmp(s1, s2, (size_t)-1)); +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! \param n is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strncmp() function. +//! It compares at most \e n characters of two strings taking case into +//! account. The comparison ends if a terminating NULL character is found in +//! either string before \e n characters are compared. In this case, the +//! int16_ter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrncmp(const char *s1, const char *s2, size_t n) +{ + // + // Loop while there are more characters. + // + while(n) + { + // + // If we reached a NULL in both strings, they must be equal so we end + // the comparison and return 0 + // + if(!*s1 && !*s2) + { + return(0); + } + + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(*s2 < *s1) + { + return(1); + } + if(*s1 < *s2) + { + return(-1); + } + + // + // Move on to the next character. + // + s1++; + s2++; + n--; + } + + // + // If we fall out, the strings must be equal for at least the first n + // characters so return 0 to indicate this. + // + return(0); +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! +//! This function is very similar to the C library strcmp() +//! function. It compares two strings, taking case into account. The +//! comparison ends if a terminating NULL character is found in either string. +//! In this case, the int16_ter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrcmp(const char *s1, const char *s2) +{ + // + // Pass this on to ustrncmp. + // + return(ustrncmp(s1, s2, (size_t)-1)); +} + +//***************************************************************************** +// +// Random Number Generator Seed Value +// +//***************************************************************************** +static unsigned int g_iRandomSeed = 1; + +//***************************************************************************** +// +//! Set the random number generator seed. +//! +//! \param seed is the new seed value to use for the random number +//! generator. +//! +//! This function is very similar to the C library srand() function. +//! It will set the seed value used in the urand() function. +//! +//! \return None +// +//***************************************************************************** +void +usrand(unsigned int seed) +{ + g_iRandomSeed = seed; +} + +//***************************************************************************** +// +//! Generate a new (pseudo) random number +//! +//! This function is very similar to the C library rand() function. +//! It will generate a pseudo-random number sequence based on the seed value. +//! +//! \return A pseudo-random number will be returned. +// +//***************************************************************************** +int +urand(void) +{ + // + // Generate a new pseudo-random number with a linear congruence random + // number generator. This new random number becomes the seed for the next + // random number. + // + g_iRandomSeed = (g_iRandomSeed * 1664525) + 1013904223; + + // + // Return the new random number. + // + return((int)g_iRandomSeed); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/ustdlib.h b/utils/ustdlib.h new file mode 100644 index 0000000..b9227f9 --- /dev/null +++ b/utils/ustdlib.h @@ -0,0 +1,82 @@ +//***************************************************************************** +// +// ustdlib.h - Prototypes for simple standard library functions. +// +// Copyright (c) 2007-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 Utility Library. +// +//***************************************************************************** + +#ifndef __USTDLIB_H__ +#define __USTDLIB_H__ + +//***************************************************************************** +// +// Include the standard C headers upon which these replacements are based. +// +//***************************************************************************** +#include +#include + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern void ulocaltime(time_t timer, struct tm *tm); +extern time_t umktime(struct tm *timeptr); +extern int urand(void); +extern int usnprintf(char * restrict s, size_t n, const char * restrict format, + ...); +extern int usprintf(char * restrict s, const char * restrict format, ...); +extern void usrand(unsigned int seed); +extern int ustrcasecmp(const char *s1, const char *s2); +extern int ustrcmp(const char *s1, const char *s2); +extern size_t ustrlen(const char *s); +extern int ustrncasecmp(const char *s1, const char *s2, size_t n); +extern int ustrncmp(const char *s1, const char *s2, size_t n); +extern char *ustrncpy(char * restrict s1, const char * restrict s2, size_t n); +extern char *ustrstr(const char *s1, const char *s2); +extern float ustrtof(const char * restrict nptr, + const char ** restrict endptr); +extern unsigned long int ustrtoul(const char * restrict nptr, + const char ** restrict endptr, int base); +extern int uvsnprintf(char * restrict s, size_t n, + const char * restrict format, va_list arg); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USTDLIB_H__ diff --git a/utils/wavfile.c b/utils/wavfile.c new file mode 100644 index 0000000..b2ce150 --- /dev/null +++ b/utils/wavfile.c @@ -0,0 +1,291 @@ +//****************************************************************************** +// +// wavfile.c - This file supports reading audio data from a .wav file and +// reading the file format. +// +// Copyright (c) 2012-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 Utility Library. +// +//****************************************************************************** + +#include +#include "inc/hw_types.h" +#include "third_party/fatfs/src/ff.h" +#include "third_party/fatfs/src/diskio.h" +#include "wavfile.h" + +//****************************************************************************** +// +// The flag values for the ui32Flags member of the tWavFile structure. +// +//****************************************************************************** +#define WAV_FLAG_FILEOPEN 0x00000001 + +//****************************************************************************** +// +// Basic wav file RIFF header information used to open and read a wav file. +// +//****************************************************************************** +#define RIFF_CHUNK_ID_RIFF 0x46464952 +#define RIFF_CHUNK_ID_FMT 0x20746d66 +#define RIFF_CHUNK_ID_DATA 0x61746164 +#define RIFF_TAG_WAVE 0x45564157 +#define RIFF_FORMAT_UNKNOWN 0x0000 +#define RIFF_FORMAT_PCM 0x0001 +#define RIFF_FORMAT_MSADPCM 0x0002 +#define RIFF_FORMAT_IMAADPCM 0x0011 + +//****************************************************************************** +// +// This function returns the format of a wav file that has been opened with +// the WavOpen() function. +// +// \param psWavData is the structure that was passed to the WavOpen() function. +// \param psWavHeader is the structure to fill with the format of the wav file. +// +// This function is used to get the audio format of a file that was opened +// with the WavOpen() function. The \e psWavData parameter should be the +// same structure that was passed to the WavOpen() function. The +// \e psWavHeader function will be filled with the format of the open file if +// the \e psWavData is a valid open file. If this function is called with +// an invalid \e psWavData then the results will be undetermined. +// +// \return None. +// +//****************************************************************************** +void +WavGetFormat(tWavFile *psWavData, tWavHeader *psWavHeader) +{ + // + // Only return data if the file is open. + // + psWavHeader->ui32DataSize = psWavData->sWavHeader.ui32DataSize; + psWavHeader->ui16NumChannels = psWavData->sWavHeader.ui16NumChannels; + psWavHeader->ui32SampleRate = psWavData->sWavHeader.ui32SampleRate; + psWavHeader->ui32AvgByteRate = psWavData->sWavHeader.ui32AvgByteRate; + psWavHeader->ui16BitsPerSample = psWavData->sWavHeader.ui16BitsPerSample; +} + +//****************************************************************************** +// +// This function is called to open and determine if a file is a valid .wav +// file. +// +// \param pcFileName is the null terminated string for the file to open. +// \param psWavData is the structure used to hold the file state information. +// +// This function is used to open a file and determine if it is a valid .wav +// file. The \e pcFileName will be opened and read to look for a valid .wav +// file header and prepared for calling the WavRead() or WavGetFormat() +// functions. When an application is done with the .wav file it should call +// the WavClose() function to free up the file. The function will return +// zero if the function successfully opened a .wav file and a non-zero value +// indicates that the file was a valid .wav file or the file could not be +// opened. +// +// \return A value of zero indicates that the file was successfully opened and +// any other value indicates that the file was not opened. +// +//****************************************************************************** +int +WavOpen(const char *pcFileName, tWavFile *psWavData) +{ + unsigned char pucBuffer[16]; + uint32_t *pui32Buffer; + uint16_t *pui16Buffer; + uint32_t ui32ChunkSize; + uint32_t ui32Count; + + // + // Create some local pointers using in parsing values. + // + pui32Buffer = (uint32_t *)pucBuffer; + pui16Buffer = (uint16_t *)pucBuffer; + + // + // Open the file as read only. + // + if(f_open(&psWavData->i16File, pcFileName, FA_READ) != FR_OK) + { + return(-1); + } + + // + // File is open. + // + psWavData->ui32Flags = WAV_FLAG_FILEOPEN; + + // + // Read the first 12 bytes. + // + if(f_read(&psWavData->i16File, pucBuffer, 12, (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Look for RIFF tag. + // + if((pui32Buffer[0] != RIFF_CHUNK_ID_RIFF) || + (pui32Buffer[2] != RIFF_TAG_WAVE)) + { + return(-1); + } + + // + // Read the next chunk header. + // + if(f_read(&psWavData->i16File, pucBuffer, 8, (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Now look for the RIFF ID format tag. + // + if(pui32Buffer[0] != RIFF_CHUNK_ID_FMT) + { + return(-1); + } + + // + // Read the format chunk size and insure that it is 16. + // + ui32ChunkSize = pui32Buffer[1]; + + if(ui32ChunkSize > 16) + { + return(-1); + } + + // + // Read the next chunk header. + // + if(f_read(&psWavData->i16File, pucBuffer, ui32ChunkSize, + (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Save the audio format data so that it can be returned later if + // requested. + // + psWavData->sWavHeader.ui16Format = pui16Buffer[0]; + psWavData->sWavHeader.ui16NumChannels = pui16Buffer[1]; + psWavData->sWavHeader.ui32SampleRate = pui32Buffer[1]; + psWavData->sWavHeader.ui32AvgByteRate = pui32Buffer[2]; + psWavData->sWavHeader.ui16BitsPerSample = pui16Buffer[7]; + + // + // Only mono and stereo supported. + // + if(psWavData->sWavHeader.ui16NumChannels > 2) + { + return(-1); + } + + // + // Read the next chunk header. + // + if(f_read(&psWavData->i16File, pucBuffer, 8, (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Now make sure that the file has a data chunk. + // + if(pui32Buffer[0] != RIFF_CHUNK_ID_DATA) + { + return(-1); + } + + // + // Save the size of the data. + // + psWavData->sWavHeader.ui32DataSize = pui32Buffer[1]; + + return(0); +} + +//****************************************************************************** +// +// This is used to close a .wav file that was opened with WavOpen(). +// +// \param psWavData is the file structure that was passed into the WavOpen() +// function. +// +// This function should be called when a function has completed using a .wav +// file that was opened with the WavOpen() function. This will free up any +// file system data that is held while the file is open. +// +// \return None. +// +//****************************************************************************** +void +WavClose(tWavFile *psWavData) +{ + if(psWavData->ui32Flags & WAV_FLAG_FILEOPEN) + { + // + // Close out the file. + // + f_close(&psWavData->i16File); + + // + // Mark file as no longer open. + // + psWavData->ui32Flags &= ~WAV_FLAG_FILEOPEN; + } +} + +//****************************************************************************** +// +// This function is used to read audio data from a file that was opened with +// the WavOpen() function. +// +// \param psWavData is the file structure that was passed into the WavOpen() +// function. +// \param pucBuffer is the buffer to read data into. +// \param ui32Size is the amount of data to read in bytes. +// +// +// This function handles reading data from a .wav file that was opened with +// the WavOpen() function. The function will return the actual number of +// of bytes read from the file. +// +// \return This function returns the number of bytes read from the file. +// +//****************************************************************************** +uint16_t +WavRead(tWavFile *psWavData, unsigned char *pucBuffer, uint32_t ui32Size) +{ + uint32_t ui32Count; + + // + // Read in another buffer from the file. + // + if(f_read(&psWavData->i16File, pucBuffer, ui32Size, + (UINT *)&ui32Count) != FR_OK) + { + return(0); + } + + return(ui32Count); +} diff --git a/utils/wavfile.h b/utils/wavfile.h new file mode 100644 index 0000000..f044301 --- /dev/null +++ b/utils/wavfile.h @@ -0,0 +1,97 @@ +//***************************************************************************** +// +// wavfile.h - This file supports reading audio data from a .wav file and +// reading the file format. +// +// Copyright (c) 2012-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 Utility Library. +// +//***************************************************************************** + +#ifndef WAVEFILE_H_ +#define WAVEFILE_H_ + +//***************************************************************************** +// +// The wav file header information. +// +//***************************************************************************** +typedef struct +{ + // + // Sample rate in bytes per second. + // + uint32_t ui32SampleRate; + + // + // The average byte rate for the wav file. + // + uint32_t ui32AvgByteRate; + + // + // The size of the wav data in the file. + // + uint32_t ui32DataSize; + + // + // The number of bits per sample. + // + uint16_t ui16BitsPerSample; + + // + // The wav file format. + // + uint16_t ui16Format; + + // + // The number of audio channels. + // + uint16_t ui16NumChannels; +} +tWavHeader; + +//***************************************************************************** +// +// The structure used to hold the wav file state. +// +//***************************************************************************** +typedef struct +{ + // + // The wav files header information + // + tWavHeader sWavHeader; + + // + // The file information for the current file. + // + FIL i16File; + + // + // Current state flags, a combination of the WAV_FLAG_* values. + // + uint32_t ui32Flags; +} tWavFile; + +void WavGetFormat(tWavFile *psWavData, tWavHeader *psWaveHeader); +int WavOpen(const char *pcFileName, tWavFile *psWavData); +void WavClose(tWavFile *psWavData); +uint16_t WavRead(tWavFile *psWavData, unsigned char *pucBuffer, + uint32_t ui32Size); + +#endif -- cgit v1.3.1