From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/Makefile | 35 + usblib/Makefile.usblib-cm3 | 110 + usblib/Makefile.usblib-cm4f | 110 + usblib/ccs-cm3/.ccsproject | 11 + usblib/ccs-cm3/.cproject | 142 + usblib/ccs-cm3/.project | 226 + .../.settings/org.eclipse.cdt.codan.core.prefs | 3 + usblib/ccs-cm3/Debug/usblib-cm3.lib | Bin 0 -> 1517058 bytes usblib/ccs-cm3/macros.ini_initial | 1 + usblib/ccs-cm4f/.ccsproject | 11 + usblib/ccs-cm4f/.cproject | 161 + usblib/ccs-cm4f/.project | 226 + .../.settings/org.eclipse.cdt.codan.core.prefs | 3 + usblib/ccs-cm4f/Debug/usblib-cm4f.lib | Bin 0 -> 1482638 bytes usblib/ccs-cm4f/macros.ini_initial | 1 + usblib/device/usbdaudio.c | 1485 +++++ usblib/device/usbdaudio.h | 362 ++ usblib/device/usbdbulk.c | 1447 +++++ usblib/device/usbdbulk.h | 269 + usblib/device/usbdcdc.c | 2972 ++++++++++ usblib/device/usbdcdc.h | 353 ++ usblib/device/usbdcdesc.c | 649 +++ usblib/device/usbdcomp.c | 1543 ++++++ usblib/device/usbdcomp.h | 297 + usblib/device/usbdconfig.c | 654 +++ usblib/device/usbddfu-rt.c | 589 ++ usblib/device/usbddfu-rt.h | 163 + usblib/device/usbdenum.c | 2900 ++++++++++ usblib/device/usbdevice.h | 223 + usblib/device/usbdevicepriv.h | 223 + usblib/device/usbdhandler.c | 82 + usblib/device/usbdhid.c | 2579 +++++++++ usblib/device/usbdhid.h | 974 ++++ usblib/device/usbdhidkeyb.c | 1119 ++++ usblib/device/usbdhidkeyb.h | 382 ++ usblib/device/usbdhidmouse.c | 843 +++ usblib/device/usbdhidmouse.h | 321 ++ usblib/device/usbdmsc.c | 2130 ++++++++ usblib/device/usbdmsc.h | 391 ++ usblib/ewarm-cm3/Exe/usblib-cm3.a | Bin 0 -> 1121020 bytes usblib/ewarm-cm4f/Exe/usblib-cm4f.a | Bin 0 -> 1124376 bytes usblib/gcc-cm3/libusb-cm3.a | Bin 0 -> 157906 bytes usblib/gcc-cm4f/libusb-cm4f.a | Bin 0 -> 158070 bytes usblib/host/usbhaudio.c | 1528 ++++++ usblib/host/usbhaudio.h | 162 + usblib/host/usbhhid.c | 723 +++ usblib/host/usbhhid.h | 164 + usblib/host/usbhhidkeyboard.c | 719 +++ usblib/host/usbhhidkeyboard.h | 77 + usblib/host/usbhhidmouse.c | 415 ++ usblib/host/usbhhidmouse.h | 68 + usblib/host/usbhhub.c | 1251 +++++ usblib/host/usbhhub.h | 206 + usblib/host/usbhmsc.c | 713 +++ usblib/host/usbhmsc.h | 95 + usblib/host/usbhost.h | 348 ++ usblib/host/usbhostenum.c | 5694 ++++++++++++++++++++ usblib/host/usbhostpriv.h | 257 + usblib/host/usbhscsi.c | 778 +++ usblib/host/usbhscsi.h | 102 + usblib/readme.txt | 21 + usblib/rvmdk-cm3/usblib-cm3.lib | Bin 0 -> 2271910 bytes usblib/rvmdk-cm4f/usblib-cm4f.lib | Bin 0 -> 2291182 bytes usblib/usb-ids.h | 63 + usblib/usbaudio.h | 717 +++ usblib/usbbuffer.c | 1184 ++++ usblib/usbcdc.h | 789 +++ usblib/usbdesc.c | 478 ++ usblib/usbdfu.h | 430 ++ usblib/usbhid.h | 657 +++ usblib/usbkeyboardmap.c | 143 + usblib/usblib-cm3.ewp | 851 +++ usblib/usblib-cm3.sgxx | Bin 0 -> 3524 bytes usblib/usblib-cm3.uvopt | 608 +++ usblib/usblib-cm3.uvproj | 540 ++ usblib/usblib-cm4f.ewp | 851 +++ usblib/usblib-cm4f.sgxx | Bin 0 -> 3544 bytes usblib/usblib-cm4f.uvopt | 608 +++ usblib/usblib-cm4f.uvproj | 540 ++ usblib/usblib.h | 1813 +++++++ usblib/usblibpriv.h | 130 + usblib/usbmode.c | 1115 ++++ usblib/usbmsc.h | 393 ++ usblib/usbringbuf.c | 712 +++ usblib/usbtick.c | 216 + 85 files changed, 50149 insertions(+) create mode 100644 usblib/Makefile create mode 100644 usblib/Makefile.usblib-cm3 create mode 100644 usblib/Makefile.usblib-cm4f create mode 100644 usblib/ccs-cm3/.ccsproject create mode 100644 usblib/ccs-cm3/.cproject create mode 100644 usblib/ccs-cm3/.project create mode 100644 usblib/ccs-cm3/.settings/org.eclipse.cdt.codan.core.prefs create mode 100644 usblib/ccs-cm3/Debug/usblib-cm3.lib create mode 100644 usblib/ccs-cm3/macros.ini_initial create mode 100644 usblib/ccs-cm4f/.ccsproject create mode 100644 usblib/ccs-cm4f/.cproject create mode 100644 usblib/ccs-cm4f/.project create mode 100644 usblib/ccs-cm4f/.settings/org.eclipse.cdt.codan.core.prefs create mode 100644 usblib/ccs-cm4f/Debug/usblib-cm4f.lib create mode 100644 usblib/ccs-cm4f/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/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-cm3/Exe/usblib-cm3.a create mode 100644 usblib/ewarm-cm4f/Exe/usblib-cm4f.a create mode 100644 usblib/gcc-cm3/libusb-cm3.a create mode 100644 usblib/gcc-cm4f/libusb-cm4f.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-cm3/usblib-cm3.lib create mode 100644 usblib/rvmdk-cm4f/usblib-cm4f.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/usbhid.h create mode 100644 usblib/usbkeyboardmap.c create mode 100644 usblib/usblib-cm3.ewp create mode 100644 usblib/usblib-cm3.sgxx create mode 100644 usblib/usblib-cm3.uvopt create mode 100644 usblib/usblib-cm3.uvproj create mode 100644 usblib/usblib-cm4f.ewp create mode 100644 usblib/usblib-cm4f.sgxx create mode 100644 usblib/usblib-cm4f.uvopt create mode 100644 usblib/usblib-cm4f.uvproj create mode 100644 usblib/usblib.h 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 diff --git a/usblib/Makefile b/usblib/Makefile new file mode 100644 index 0000000..48582b9 --- /dev/null +++ b/usblib/Makefile @@ -0,0 +1,35 @@ +#****************************************************************************** +# +# Makefile - Rules for building the USB library. +# +# Copyright (c) 2011-2012 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 9453 of the Stellaris USB Library. +# +#****************************************************************************** + +all: + @make -f Makefile.usblib-cm3 + @make -f Makefile.usblib-cm4f + +clean: + @make -f Makefile.usblib-cm3 clean + @make -f Makefile.usblib-cm4f clean + +realclean: + @make -f Makefile.usblib-cm3 realclean + @make -f Makefile.usblib-cm4f realclean diff --git a/usblib/Makefile.usblib-cm3 b/usblib/Makefile.usblib-cm3 new file mode 100644 index 0000000..24bf999 --- /dev/null +++ b/usblib/Makefile.usblib-cm3 @@ -0,0 +1,110 @@ +#****************************************************************************** +# +# Makefile.usblib-cm3 - Rules for building the USB library. +# +# Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +# +#****************************************************************************** + +# +# Defines the directory suffix that this project uses. +# +SUFFIX=-cm3 + +# +# Set the processor variant. +# +VARIANT=cm3 + +# +# The base directory for StellarisWare. +# +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}-cm3 +all: ${COMPILER}-cm3/libusb-cm3.a + +# +# The rule to clean out all the build products. +# +clean: + @rm -rf ${COMPILER}-cm3 ${wildcard *~} + +# +# The rule to create the target directory. +# +${COMPILER}-cm3: + @mkdir -p ${COMPILER}-cm3 + +# +# Rules for building the USB library. +# +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbbuffer.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdaudio.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdbulk.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdcdc.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdcdesc.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdcomp.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdconfig.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbddfu-rt.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdenum.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdesc.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdhandler.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdhid.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdhidkeyb.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdhidmouse.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbdmsc.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhaudio.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhhid.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhhidkeyboard.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhhidmouse.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhhub.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhmsc.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhostenum.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbhscsi.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbkeyboardmap.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbmode.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbringbuf.o +${COMPILER}-cm3/libusb-cm3.a: ${COMPILER}-cm3/usbtick.o + +# +# Include the automatically generated dependency files. +# +ifneq (${MAKECMDGOALS},clean) +-include ${wildcard ${COMPILER}-cm3/*.d} __dummy__ +endif diff --git a/usblib/Makefile.usblib-cm4f b/usblib/Makefile.usblib-cm4f new file mode 100644 index 0000000..bd71556 --- /dev/null +++ b/usblib/Makefile.usblib-cm4f @@ -0,0 +1,110 @@ +#****************************************************************************** +# +# Makefile.usblib-cm4f - Rules for building the USB library. +# +# Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +# +#****************************************************************************** + +# +# Defines the directory suffix that this project uses. +# +SUFFIX=-cm4f + +# +# Set the processor variant. +# +VARIANT=cm4f + +# +# The base directory for StellarisWare. +# +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}-cm4f +all: ${COMPILER}-cm4f/libusb-cm4f.a + +# +# The rule to clean out all the build products. +# +clean: + @rm -rf ${COMPILER}-cm4f ${wildcard *~} + +# +# The rule to create the target directory. +# +${COMPILER}-cm4f: + @mkdir -p ${COMPILER}-cm4f + +# +# Rules for building the USB library. +# +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbbuffer.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdaudio.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdbulk.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdcdc.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdcdesc.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdcomp.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdconfig.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbddfu-rt.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdenum.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdesc.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdhandler.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdhid.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdhidkeyb.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdhidmouse.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbdmsc.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhaudio.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhhid.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhhidkeyboard.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhhidmouse.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhhub.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhmsc.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhostenum.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbhscsi.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbkeyboardmap.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbmode.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbringbuf.o +${COMPILER}-cm4f/libusb-cm4f.a: ${COMPILER}-cm4f/usbtick.o + +# +# Include the automatically generated dependency files. +# +ifneq (${MAKECMDGOALS},clean) +-include ${wildcard ${COMPILER}-cm4f/*.d} __dummy__ +endif diff --git a/usblib/ccs-cm3/.ccsproject b/usblib/ccs-cm3/.ccsproject new file mode 100644 index 0000000..1273946 --- /dev/null +++ b/usblib/ccs-cm3/.ccsproject @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/usblib/ccs-cm3/.cproject b/usblib/ccs-cm3/.cproject new file mode 100644 index 0000000..b33550b --- /dev/null +++ b/usblib/ccs-cm3/.cproject @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usblib/ccs-cm3/.project b/usblib/ccs-cm3/.project new file mode 100644 index 0000000..2e0b42e --- /dev/null +++ b/usblib/ccs-cm3/.project @@ -0,0 +1,226 @@ + + + usblib-cm3 + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + -k + + + org.eclipse.cdt.make.core.buildCommand + ${CCS_UTILS_DIR}/bin/gmake + + + org.eclipse.cdt.make.core.buildLocation + ${BuildDirectory} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + true + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + false + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + + + 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 + + + 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 + + + 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/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-cm3/.settings/org.eclipse.cdt.codan.core.prefs b/usblib/ccs-cm3/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000..98b6350 --- /dev/null +++ b/usblib/ccs-cm3/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +inEditor=false +onBuild=false diff --git a/usblib/ccs-cm3/Debug/usblib-cm3.lib b/usblib/ccs-cm3/Debug/usblib-cm3.lib new file mode 100644 index 0000000..55277bf Binary files /dev/null and b/usblib/ccs-cm3/Debug/usblib-cm3.lib differ diff --git a/usblib/ccs-cm3/macros.ini_initial b/usblib/ccs-cm3/macros.ini_initial new file mode 100644 index 0000000..08b716d --- /dev/null +++ b/usblib/ccs-cm3/macros.ini_initial @@ -0,0 +1 @@ +SW_ROOT = ../.. diff --git a/usblib/ccs-cm4f/.ccsproject b/usblib/ccs-cm4f/.ccsproject new file mode 100644 index 0000000..b16ea80 --- /dev/null +++ b/usblib/ccs-cm4f/.ccsproject @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/usblib/ccs-cm4f/.cproject b/usblib/ccs-cm4f/.cproject new file mode 100644 index 0000000..dc33339 --- /dev/null +++ b/usblib/ccs-cm4f/.cproject @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usblib/ccs-cm4f/.project b/usblib/ccs-cm4f/.project new file mode 100644 index 0000000..78e336b --- /dev/null +++ b/usblib/ccs-cm4f/.project @@ -0,0 +1,226 @@ + + + usblib-cm4f + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + -k + + + org.eclipse.cdt.make.core.buildCommand + ${CCS_UTILS_DIR}/bin/gmake + + + org.eclipse.cdt.make.core.buildLocation + ${BuildDirectory} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + true + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + false + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + + + 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 + + + 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 + + + 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/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-cm4f/.settings/org.eclipse.cdt.codan.core.prefs b/usblib/ccs-cm4f/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000..98b6350 --- /dev/null +++ b/usblib/ccs-cm4f/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +inEditor=false +onBuild=false diff --git a/usblib/ccs-cm4f/Debug/usblib-cm4f.lib b/usblib/ccs-cm4f/Debug/usblib-cm4f.lib new file mode 100644 index 0000000..9512e5e Binary files /dev/null and b/usblib/ccs-cm4f/Debug/usblib-cm4f.lib differ diff --git a/usblib/ccs-cm4f/macros.ini_initial b/usblib/ccs-cm4f/macros.ini_initial new file mode 100644 index 0000000..08b716d --- /dev/null +++ b/usblib/ccs-cm4f/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..a1ef199 --- /dev/null +++ b/usblib/device/usbdaudio.c @@ -0,0 +1,1485 @@ +//***************************************************************************** +// +// usbdaudio.c - USB audio device class driver. +// +// Copyright (c) 2009-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 "driverlib/udma.h" +#include "usblib/usblib.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 +#define ISOC_OUT_DMA_CHANNEL UDMA_CHANNEL_USBEP1RX + +//***************************************************************************** +// +// 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 unsigned char g_pAudioDeviceDescriptor[] = +{ + 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 unsigned char g_pAudioDescriptor[] = +{ + // + // 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. +// +//***************************************************************************** +unsigned char g_pIADAudioDescriptor[] = +{ + + 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_pIADAudioDescriptor), + g_pIADAudioDescriptor +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const unsigned char g_pAudioControlInterface[] = +{ + // + // 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_pAudioControlInterface + 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 unsigned char g_pAudioStreamInterface[] = +{ + // + // 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 | USB_EP_TO_INDEX(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_pAudioDescriptor), + g_pAudioDescriptor +}; + +const tConfigSection g_sAudioStreamInterfaceSection = +{ + sizeof(g_pAudioStreamInterface), + g_pAudioStreamInterface +}; + +const tConfigSection g_sAudioControlInterfaceSection = +{ + sizeof(g_pAudioControlInterface), + g_pAudioControlInterface +}; + +//***************************************************************************** +// +// 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(tConfigSection *)) + +//***************************************************************************** +// +// 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_pAudioConfigDescriptors[] = +{ + &g_sAudioConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void InterfaceChange(void *pvInstance, unsigned char ucInterface, + unsigned char ucAlternateSetting); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The FIFO configuration for USB audio device class. +// +//***************************************************************************** +const tFIFOConfig g_sUSBAudioFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT | USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//***************************************************************************** +// +// The device information structure for the USB Audio device. +// +//***************************************************************************** +tDeviceInfo g_sAudioDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // 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 + }, + g_pAudioDeviceDescriptor, + g_pAudioConfigDescriptors, + 0, + 0, + &g_sUSBAudioFIFOConfig +}; + +//***************************************************************************** +// +// 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 *pvInstance, unsigned long ulInfo) +{ + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = psDevice->psPrivateData; + + // + // If there is an update pending and the request was to set a current + // value then check which value was set. + // + if(psInst->usUpdate && (psInst->ucRequest == USB_AC_SET_CUR)) + { + // + // Only handling interface requests. + // + if((psInst->usRequestType & USB_RTYPE_RECIPIENT_M) == + USB_RTYPE_INTERFACE) + { + if(psInst->usUpdate == VOLUME_CONTROL) + { + // + // Inform the callback of the new volume. + // + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_VOLUME, + psInst->sVolume, 0); + } + else if(psDevice->psPrivateData->usUpdate == MUTE_CONTROL) + { + // + // Inform the callback of the new data. + // + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_MUTE, psInst->ucMute, + 0); + } + } + psInst->usUpdate = 0; + } +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the isochronous endpoint +// for the audio device class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + tAudioInstance *psInst; + unsigned char *pucData; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use later in this function. + // + psInst = psDevice->psPrivateData; + + // + // Make sure this was for the isochronous out endpoint. + // + if((psInst->sBuffer.pvData != 0) && + (MAP_uDMAChannelModeGet(psInst->ucOUTDMA) == UDMA_MODE_STOP)) + { + // + // Save the pointer to the data buffer. + // + pucData = psInst->sBuffer.pvData; + + // + // Clear out the buffer pointer to indicate it is no longer in use. + // + psInst->sBuffer.pvData = 0; + + // + // Inform the callback of the new data. + // + psInst->sBuffer.pfnCallback(pucData, psInst->sBuffer.ulSize, + USBD_AUDIO_EVENT_DATAOUT); + + // + // Read out the current endpoint status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Acknowledge that the data was read, this will not cause a bus + // acknowledgment. + // + MAP_USBDevEndpointDataAck(USB0_BASE, psInst->ucOUTEndpoint, 0); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, + ulEPStatus); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tAudioInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDAudioDevice *)pvInstance)->psPrivateData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pucData[0] == AUDIO_INTERFACE_CONTROL) + { + psInst->ucInterfaceControl = pucData[1]; + } + else if(pucData[0] == AUDIO_INTERFACE_OUTPUT) + { + psInst->ucInterfaceAudio = pucData[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((pucData[0] & USB_EP_DESC_IN) == 0) + { + // + // Extract the new endpoint number without the DIR bit. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + + // + // Extract the new DMA channel. + // + psInst->ucOUTDMA = UDMA_CHANNEL_USBEP1RX + + (((pucData[1] & 0x7f) - 1) * 2); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects + // devices that have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, + psInst->ucOUTDMA); + } + 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. + // + pucData[2] = psInst->ucInterfaceControl; + + break; + } + + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Inform the application that the device has been disconnected. + // + psDevice->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 *pvInstance, unsigned char ucInterface, + unsigned char ucAlternateSetting) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Check which interface to change into. + // + if(ucAlternateSetting == 0) + { + // + // Alternate setting 0 is an inactive state. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_IDLE, 0, 0); + } + } + else + { + // + // Alternate setting 1 is the active state. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_ACTIVE, 0, 0); + } + + // + // Enable uDMA on the endpoint now that the active configuration + // has been selected. + // + MAP_USBEndpointDMAEnable(USB0_BASE, + psDevice->psPrivateData->ucOUTEndpoint, + USB_EP_DEV_OUT); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvInstance, unsigned long ulValue) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnCallback(pvInstance, 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 ulIndex is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psDevice 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 psDevice 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(unsigned long ulIndex, const tUSBDAudioDevice *psDevice) +{ + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + USBDAudioCompositeInit(ulIndex, psDevice); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateData->psDevInfo); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psDevice->psPrivateData->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects devices that + // have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psDevice->psPrivateData->ucOUTEndpoint, + psDevice->psPrivateData->ucOUTDMA); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psDevice 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 psDevice 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 * +USBDAudioCompositeInit(unsigned long ulIndex, const tUSBDAudioDevice *psDevice) +{ + tAudioInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pAudioDescriptor; + psInst->psDevInfo = &g_sAudioDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + + // + // The Control interface is at index 0. + // + psInst->ucInterfaceControl = AUDIO_INTERFACE_CONTROL; + + // + // The Audio interface is at index 1. + // + psInst->ucInterfaceAudio = AUDIO_INTERFACE_OUTPUT; + + // + // Set the default Isochronous OUT endpoint. + // + psInst->ucOUTEndpoint = ISOC_OUT_ENDPOINT; + psInst->ucOUTDMA = ISOC_OUT_DMA_CHANNEL; + + // + // Set the initial buffer to null. + // + psInst->sBuffer.pvData = 0; + + // + // Save the volume settings. + // + psInst->sVolumeMax = psDevice->sVolumeMax; + psInst->sVolumeMin = psDevice->sVolumeMin; + psInst->sVolumeStep = psDevice->sVolumeStep; + + // + // No update pending to any command. + // + psInst->usUpdate = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors = + psDevice->ulNumStringDescriptors; + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the audio device. +//! +//! \param pvInstance 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 pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDAudioTerm(void *pvInstance) +{ + ASSERT(pvInstance != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance is the instance data for this request. +// \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulControl; + unsigned long ulRecipient; + unsigned long ulStall; + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = psDevice->psPrivateData; + + // + // 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. + // + ulStall = 0; + + // + // Get the request type. + // + ulRecipient = pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M; + + // + // Save the request type and request value. + // + psInst->usRequestType = pUSBRequest->bmRequestType; + psInst->ucRequest = pUSBRequest->bRequest; + + // + // Check if this is an endpoint request to the audio streaming endpoint. + // + if((ulRecipient == USB_RTYPE_ENDPOINT) && + (pUSBRequest->wIndex == USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Determine the type of request. + // + switch(psInst->ucRequest) + { + case USB_AC_SET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Retrieve the requested sample rate. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->ulSampleRate, + 3); + + // + // Save what we are updating. + // + psInst->usUpdate = SAMPLING_FREQ_CONTROL; + } + break; + } + case USB_AC_GET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Send back the current sample rate. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->ulSampleRate, + 3); + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ulStall = 1; + break; + } + } + } + else if(ulRecipient == USB_RTYPE_INTERFACE) + { + // + // Make sure the request was for the control interface. + // + if((unsigned char)pUSBRequest->wIndex != psInst->ucInterfaceControl) + { + return; + } + + // + // Extract the control value from the message. + // + ulControl = pUSBRequest->wValue & USB_CS_CONTROL_M; + + // + // Handle an audio control request to the feature control unit. + // + if((AUDIO_CONTROL_ID << 8) == (pUSBRequest->wIndex & USB_CS_CONTROL_M)) + { + // + // Determine the type of request. + // + switch(psInst->ucRequest) + { + case USB_AC_GET_MAX: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the maximum volume setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeMax, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_MIN: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the minimum volume setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeMin, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_RES: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the volume step setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeStep, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_CUR: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Send back the current volume level. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolume, + 2); + } + else if(ulControl == MUTE_CONTROL) + { + // + // Send back the current mute value. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->ucMute, 1); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_SET_CUR: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Read the new volume level. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->sVolume, + 2); + + // + // Save what we are updating. + // + psInst->usUpdate = VOLUME_CONTROL; + } + else if(ulControl == MUTE_CONTROL) + { + // + // Read the new mute setting. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->ucMute, + 1); + + // + // Save what we are updating. + // + psInst->usUpdate = MUTE_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_SET_RES: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Read the new volume step setting. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->sVolumeStep, 2); + + // + // Save what we are updating. + // + psInst->usUpdate = VOLUME_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ulStall = 1; + break; + } + } + } + } + + // + // Stall on all unknown commands. + // + if(ulStall) + { + USBDCDStallEP0(0); + } +} + +//***************************************************************************** +// +//! This function is used to supply buffers to the audio class to be filled +//! from the USB host device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDAudioInit() or USBDAudioInitComposite(). +//! \param pvBuffer is a pointer to the buffer to fill with audio data. +//! \param ulSize is the size in bytes of the buffer pointed to by the 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 ulSize one packet of data from the host controller. The ulSize +//! 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. +// +//***************************************************************************** +long +USBAudioBufferOut(void *pvInstance, void *pvBuffer, unsigned long ulSize, + tUSBAudioBufferCallback pfnCallback) +{ + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + // + // Make sure we were not passed NULL pointers. + // + ASSERT(pvInstance != 0); + ASSERT(pvBuffer != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Buffer must be at least one packet in size. + // + ASSERT(ulSize >= ISOC_OUT_EP_MAX_SIZE); + ASSERT(pfnCallback); + + // + // Create a pointer of the correct type from the private pointer. + // + psInst = psDevice->psPrivateData; + + // + // Initialize the buffer instance. + // + psInst->sBuffer.pvData = pvBuffer; + psInst->sBuffer.ulSize = ulSize; + psInst->sBuffer.ulNumBytes = 0; + psInst->sBuffer.pfnCallback = pfnCallback; + + // + // Configure and enable DMA for the OUT transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, UDMA_MODE_BASIC, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucOUTEndpoint), + psInst->sBuffer.pvData, ulSize >> 2); + + // + // Start the DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucOUTDMA); + + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/device/usbdaudio.h b/usblib/device/usbdaudio.h new file mode 100644 index 0000000..4406597 --- /dev/null +++ b/usblib/device/usbdaudio.h @@ -0,0 +1,362 @@ +//***************************************************************************** +// +// usbdaudio.h - USB audio device class driver. +// +// Copyright (c) 2009-2012 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 9453 of the Stellaris 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, unsigned long ulParam, + unsigned long ulEvent); + +//***************************************************************************** +// +// 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 psPrivateData field in the tUSBDAudioDevice structure passed on +// USBDAudioInit() and should not be modified by any code outside of the audio +// device. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + + // + // The maximum volume expressed as an 8.8 signed value. + // + short sVolumeMax; + + // + // The minimum volume expressed as an 8.8 signed value. + // + short sVolumeMin; + + // + // The minimum volume step expressed as an 8.8 signed value. + // + short sVolumeStep; + + struct + { + // + // Pointer to a buffer provided by caller. + // + void *pvData; + + // + // Size of the data area provided in pvData in bytes. + // + unsigned long ulSize; + + // + // Number of valid bytes copied into the pvData area. + // + unsigned long ulNumBytes; + + // + // The buffer callback for this function. + // + tUSBAudioBufferCallback pfnCallback; + } sBuffer; + + // + // Pending request type. + // + unsigned short usRequestType; + + // + // Pending request. + // + unsigned char ucRequest; + + // + // Pending update value. + // + unsigned short usUpdate; + + // + // Current Volume setting. + // + short sVolume; + + // + // Current Mute setting. + // + unsigned char ucMute; + + // + // Current Sample rate, this is not writable but the host will try. + // + unsigned long ulSampleRate; + + // + // The OUT endpoint in use by this instance. + // + unsigned char ucOUTEndpoint; + + // + // The OUT endpoint DMA channel in use by this instance. + // + unsigned char ucOUTDMA; + + // + // The control interface number associated with this instance. + // + unsigned char ucInterfaceControl; + + // + // The audio interface number associated with this instance. + // + unsigned char ucInterfaceAudio; +} +tAudioInstance; + +//***************************************************************************** +// +//! This is the size in bytes of the private data for the device audio class. +// +//***************************************************************************** +#define USB_AUDIO_INSTANCE_SIZE sizeof(tAudioInstance); + +//***************************************************************************** +// +//! 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. +// +// This value must be at least sizeof(g_pIADAudioDescriptor) + +// sizeof(g_pAudioControlInterface) + +// sizeof(g_sAudioStreamInterfaceSection) +// +//***************************************************************************** +#define COMPOSITE_DAUDIO_SIZE (8 + 52 + 52) + +//***************************************************************************** +// +//! 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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! 8 byte vendor string. + // + unsigned char pucVendor[8]; + + // + //! 16 byte vendor string. + // + unsigned char pucProduct[16]; + + // + //! 4 byte vendor string. + // + unsigned char pucVersion[4]; + + // + //! The maximum power consumption of the device, expressed in mA. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the audio + //! device. + // + 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! The maximum volume expressed as an 8.8 signed value. + // + short sVolumeMax; + + // + //! The minimum volume expressed as an 8.8 signed value. + // + short sVolumeMin; + + // + //! The minimum volume step expressed as an 8.8 signed value. + // + short sVolumeStep; + + // + //! A pointer to private instance data for the audio device. This memory + //! must remain accessible for as long as the audio device is in use and + //! must not be modified by any code outside the audio class driver. + // + tAudioInstance *psPrivateData; +} +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 ulParam 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 occured. The +//! \e ulParam 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 occured. The +//! \e ulParam 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) + +extern tDeviceInfo g_sAudioDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDAudioInit(unsigned long ulIndex, + const tUSBDAudioDevice *psAudioDevice); +extern void *USBDAudioCompositeInit(unsigned long ulIndex, + const tUSBDAudioDevice *psAudioDevice); +extern void USBDAudioTerm(void *pvInstance); +extern long USBAudioBufferOut(void *pvInstance, void *pvBuffer, + unsigned long ulSize, + 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..f623516 --- /dev/null +++ b/usblib/device/usbdbulk.c @@ -0,0 +1,1447 @@ +//***************************************************************************** +// +// usbdbulk.c - USB bulk device class driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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/device/usbdevice.h" +#include "usblib/device/usbdbulk.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 usDeferredOpFlags 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->usDeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define BULK_DO_PACKET_RX 5 + +//***************************************************************************** +// +// 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 USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//***************************************************************************** +// +// 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 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 DATA_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_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. +// +//***************************************************************************** +unsigned char g_pBulkDeviceDescriptor[] = +{ + 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. +// +//***************************************************************************** +unsigned char g_pBulkDescriptor[] = +{ + // + // 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 unsigned char g_pBulkInterface[] = +{ + // + // 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 | USB_EP_TO_INDEX(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 | USB_EP_TO_INDEX(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 serial config 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_pBulkDescriptor), + g_pBulkDescriptor +}; + +const tConfigSection g_sBulkInterfaceSection = +{ + sizeof(g_pBulkInterface), + g_pBulkInterface +}; + +//***************************************************************************** +// +// 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(tConfigSection *)) + +//***************************************************************************** +// +// 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_pBulkConfigDescriptors[] = +{ + &g_sBulkConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleDisconnect(void *pvInstance); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleSuspend(void *pvInstance); +static void HandleResume(void *pvInstance); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB serial device. +// +//***************************************************************************** +tDeviceInfo g_sBulkDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + 0, // GetDescriptor + 0, // RequestHandler + 0, // InterfaceChange + HandleConfigChange, // ConfigChange + 0, // DataReceived + 0, // DataSentCallback + 0, // ResetHandler + HandleSuspend, // SuspendHandler + HandleResume, // ResumeHandler + HandleDisconnect, // DisconnectHandler + HandleEndpoints, // EndpointHandler + HandleDevice // Device handler. + }, + g_pBulkDeviceDescriptor, + g_pBulkConfigDescriptors, + 0, // Will be completed during USBDBulkInit(). + 0, // Will be completed during USBDBulkInit(). + &g_sUSBDefaultFIFOConfig +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pusDeferredOp points to the flags variable which is to be modified. +// \param usBit 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 unsigned short *pusDeferredOp, unsigned short usBit, + tBoolean bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pusDeferredOp, usBit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus 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 tBoolean +ProcessDataFromHost(const tUSBDBulkDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tBulkInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, ulEPStatus); + + // + // Has a packet been received? + // + if(ulEPStatus & 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 signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, BULK_DO_PACKET_RX, true); + + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // 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. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ulEPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_ERROR, + (ulEPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus 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 tBoolean +ProcessDataToHost(const tUSBDBulkDevice *psDevice, unsigned long ulStatus) +{ + tBulkInstance *psInst; + unsigned long ulEPStatus; + unsigned long ulSize; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, psInst->ucINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucINEndpoint, + ulEPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->eBulkTxState = BULK_STATE_IDLE; + + // + // Notify the client that the last transmission completed. + // + ulSize = psInst->usLastTxSize; + psInst->usLastTxSize = 0; + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ulSize, (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 ulStatus. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDBulkDevice *psBulkInst; + tBulkInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psBulkInst = (const tUSBDBulkDevice *)pvInstance; + psInst = psBulkInst->psPrivateBulkData; + + // + // Handler for the bulk OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvInstance, ulStatus); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) + { + ProcessDataToHost(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tBulkInstance *psInst; + const tUSBDBulkDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Set all our endpoints to idle state. + // + psInst->eBulkRxState = BULK_STATE_IDLE; + psInst->eBulkTxState = BULK_STATE_IDLE; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnRxCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_CONNECTED, 0, + (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tBulkInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ucInterface = pucData[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(pucData[0] & USB_EP_DESC_IN) + { + psInst->ucINEndpoint = INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + tBulkInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psBulkDevice->psPrivateBulkData; + + // + // 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 *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // 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 *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // 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 ulIndex is the index of the USB controller for which this tick +// is being generated. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +BulkTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tBulkInstance *psInst; + unsigned long ulSize; + const tUSBDBulkDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateBulkData; + + // + // Do we have a deferred receive waiting + // + if(psInst->usDeferredOpFlags & (1 << BULK_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_RX_AVAILABLE, + ulSize, (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psDevice 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 psDevice 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 +//! 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 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 the psDevice pointer on success. +// +//***************************************************************************** +void * +USBDBulkInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice) +{ + void *pvInstance; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + + pvInstance = USBDBulkCompositeInit(ulIndex, psDevice); + + if(pvInstance) + { + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateBulkData->psDevInfo); + } + + // + // Return the pointer to the instance indicating that everything went well. + // + return(pvInstance); +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the bulk 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. +//! +//! \return Returns zero on failure or a non-zero value that should be +//! used with the remaining USB HID Bulk APIs. +// +//***************************************************************************** +void * +USBDBulkCompositeInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice) +{ + tBulkInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateBulkData); + ASSERT(psDevice->pfnRxCallback); + ASSERT(psDevice->pfnTxCallback); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateBulkData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pBulkDescriptor; + psInst->psDevInfo = &g_sBulkDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eBulkRxState = BULK_STATE_UNCONFIGURED; + psInst->eBulkTxState = BULK_STATE_UNCONFIGURED; + psInst->usDeferredOpFlags = 0; + psInst->bConnected = false; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucINEndpoint = DATA_IN_ENDPOINT; + psInst->ucOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucInterface = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psDevice->ulNumStringDescriptors; + + // + // Set the device instance. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // 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 *)psDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shut down the bulk device. +//! +//! \param pvInstance 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 pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkTerm(void *pvInstance) +{ + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USB_BASE_TO_INDEX(psInst->ulUSBBase)); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; + psInst->psConfDescriptor = (tConfigDescriptor *)0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvInstance 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 pvInstance structure passed to USBDBulkInit() 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 receive callback. +// +//***************************************************************************** +void * +USBDBulkSetRxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvInstance)->pvRxCBData; + ((tUSBDBulkDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the transmit callback. +//! +//! \param pvInstance 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 pvInstance structure passed to USBDBulkInit() 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 transmit callback. +// +//***************************************************************************** +void * +USBDBulkSetTxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback pointer for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvInstance)->pvTxCBData; + ((tUSBDBulkDevice *)pvInstance)->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 pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pcData points to the first byte of data which is to be transmitted. +//! \param ulLength 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 +//! 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 ulLength 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. +// +//***************************************************************************** +unsigned long +USBDBulkPacketWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tBulkInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Can we send the data provided? + // + if((ulLength > DATA_IN_EP_MAX_SIZE) || + (psInst->eBulkTxState != BULK_STATE_IDLE)) + { + // + // 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. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucINEndpoint, + pcData, ulLength); + + // + // Did we copy the data successfully? + // + if(lRetcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->usLastTxSize += (unsigned short)ulLength; + + // + // 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->eBulkTxState = BULK_STATE_WAIT_DATA; + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucINEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(lRetcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ulLength); + } + else + { + // + // Yes - tell the caller we couldn't send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the bulk data +//! interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pcData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pcData. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ulLength 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. +// +//***************************************************************************** +unsigned long +USBDBulkPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tBulkInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + pcData, &ulCount); + + // + // Did we read the last of the packet data? + // + if(ulCount == ulPkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, psInst->ucOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, BULK_DO_PACKET_RX, + false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(lRetcode != -1) + { + return(ulCount); + } + } + + // + // 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 pvInstance 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. +// +//***************************************************************************** +unsigned long +USBDBulkTxPacketAvailable(void *pvInstance) +{ + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eBulkTxState != BULK_STATE_IDLE) + { + // + // 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 pvInstance 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. +// +//***************************************************************************** +unsigned long +USBDBulkRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + return(ulSize); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the bulk device instance structure. +//! \param ucPower 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 *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance 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. +// +//***************************************************************************** +tBoolean +USBDBulkRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // 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..abfe85f --- /dev/null +++ b/usblib/device/usbdbulk.h @@ -0,0 +1,269 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for a generic bulk device. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 +{ + // + // Unconfigured. + // + BULK_STATE_UNCONFIGURED, + + // + // No outstanding transaction remains to be completed. + // + BULK_STATE_IDLE, + + // + // Waiting on completion of a send or receive transaction. + // + BULK_STATE_WAIT_DATA, + + // + // Waiting for client to process data. + // + BULK_STATE_WAIT_CLIENT +} tBulkState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// Bulk only example device. The memory for this structure is pointed to by +// the psPrivateBulkData field in the tUSBDBulkDevice structure passed on +// USBDBulkInit(). +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + volatile tBulkState eBulkRxState; + volatile tBulkState eBulkTxState; + volatile unsigned short usDeferredOpFlags; + unsigned short usLastTxSize; + volatile tBoolean bConnected; + unsigned char ucINEndpoint; + unsigned char ucOUTEndpoint; + unsigned char ucInterface; +} +tBulkInstance; + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the bulk device class driver. +// The client must provide a block of RAM of at least this size in the +// tBulkInstance field of the tUSBBulkDevice structure passed on USBDBulkInit. +// +// This value is deprecated and should not be used, any new code should just +// pass in a tBulkInstance structure in the psPrivateBulkData field. +// +//***************************************************************************** +#define USB_BULK_WORKSPACE_SIZE (sizeof(tBulkInstance)) +#endif + +//***************************************************************************** +// +//! 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. +// +// For reference this is sizeof(g_sCDCSerIfaceHeaderSectionNOINT) + +// sizeof(g_sCDCSerInterfaceSection) + sizeof(g_sCDCSerIfaceEndpointsNOINT) +// +//***************************************************************************** +#define COMPOSITE_DBULK_SIZE (23) + +//***************************************************************************** +// +//! 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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data receive channel. + // + 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. + // + 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors array. + //! This must be 1 + (5 * number of supported languages). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to private instance data for this device. This memory must + //! remain accessible for as long as the bulk device is in use and must not + //! be modified by any code outside the bulk class driver. + // + tBulkInstance *psPrivateBulkData; +} +tUSBDBulkDevice; + +extern tDeviceInfo g_sBulkDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDBulkInit(unsigned long ulIndex, + const tUSBDBulkDevice *psDevice); +extern void *USBDBulkCompositeInit(unsigned long ulIndex, + const tUSBDBulkDevice *psDevice); +extern void USBDBulkTerm(void *pvInstance); +extern void *USBDBulkSetRxCBData(void *pvInstance, void *pvCBData); +extern void *USBDBulkSetTxCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDBulkPacketWrite(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDBulkPacketRead(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDBulkTxPacketAvailable(void *pvInstance); +extern unsigned long USBDBulkRxPacketAvailable(void *pvInstance); +extern void USBDBulkPowerStatusSet(void *pvInstance, unsigned char ucPower); +extern tBoolean USBDBulkRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..f25b4bc --- /dev/null +++ b/usblib/device/usbdcdc.c @@ -0,0 +1,2972 @@ +//***************************************************************************** +// +// usbdcdc.c - USB CDC ACM (serial) device class driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 usDeferredOpFlags 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)) + +//***************************************************************************** +// +// 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 USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//***************************************************************************** +// +// 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 USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) +#define CTL_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(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. +// +//***************************************************************************** +unsigned char g_pCDCSerDeviceDescriptor[] = +{ + 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. +// +//***************************************************************************** +unsigned char g_pCDCSerDescriptor[] = +{ + // + // 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_pCDCSerDescriptor), + g_pCDCSerDescriptor +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +unsigned char g_pIADSerDescriptor[] = +{ + + 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_pIADSerDescriptor), + g_pIADSerDescriptor +}; + +//***************************************************************************** +// +// This is the control interface for the serial device. +// +//***************************************************************************** +const unsigned char g_pCDCSerCommInterface[] = +{ + // + // 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 | USB_EP_TO_INDEX(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_pCDCSerCommInterface), + g_pCDCSerCommInterface +}; + +//***************************************************************************** +// +// This is the Data interface for the serial device. +// +//***************************************************************************** +const unsigned char g_pCDCSerDataInterface[] = +{ + // + // 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 | USB_EP_TO_INDEX(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 | USB_EP_TO_INDEX(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_pCDCSerDataInterface), + g_pCDCSerDataInterface +}; + +//***************************************************************************** +// +// 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(tConfigSection *)) + +//***************************************************************************** +// +// 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(tConfigSection *)) + +//***************************************************************************** +// +// 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_pCDCSerConfigDescriptors[] = +{ + &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 *pvInstance, tUSBRequest *pUSBRequest); +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleEP0Data(void *pvInstance, unsigned long ulDataSize); +static void HandleDisconnect(void *pvInstance); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleSuspend(void *pvInstance); +static void HandleResume(void *pvInstance); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB serial device. +// +//***************************************************************************** +tDeviceInfo g_sCDCSerDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // 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 + }, + + // + // The common device descriptor. + // + g_pCDCSerDeviceDescriptor, + + // + // Default to no interrupt endpoint. + // + g_pCDCCompSerConfigDescriptors, + + // + // String descriptors will be passed in. + // + 0, + 0, + + // + // Use the default USB FIFO configuration. + // + &g_sUSBDefaultFIFOConfig, + + // + // Zero out the instance pointer by default. + // + 0 +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pusDeferredOp points to the flags variable which is to be modified. +// \param usBit 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 unsigned short *pusDeferredOp, + unsigned short usBit, tBoolean bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pusDeferredOp, usBit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Determines whether or not a client has consumed all received data previously +// passed to it. +// +//! \param psDevice 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 tBoolean +DeviceConsumedAllData(const tUSBDCDCDevice *psDevice) +{ + unsigned long ulRemaining; + + // + // Send the device an event requesting that it tell us how many bytes + // of data it still has to process. + // + ulRemaining = psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_DATA_REMAINING, 0, (void *)0); + + // + // If any data remains to be processed, return false, else return true. + // + return(ulRemaining ? false : true); +} + +//***************************************************************************** +// +// Notifies the client that it should set or clear a break condition. +// +// \param psDevice 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(const tUSBDCDCDevice *psDevice, tBoolean bSend) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // 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->usDeferredOpFlags, CDC_DO_SEND_BREAK, false); + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_CLEAR_BREAK, bSend); + + // + // Tell the client to start or stop sending the break. + // + psDevice->pfnControlCallback(psDevice->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 psDevice 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(const tUSBDCDCDevice *psDevice) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_LINE_CODING_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psDevice->pfnControlCallback(psDevice->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 psDevice 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(const tUSBDCDCDevice *psDevice) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_LINE_STATE_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_SET_CONTROL_LINE_STATE, + psInst->usControlLineState, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a break request if no data remains to be processed. +// +// \param psDevice 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 tBoolean +CheckAndSendBreak(const tUSBDCDCDevice *psDevice, unsigned short usDuration) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the break request on to the client since no data remains to be + // consumed. + // + SendBreak(psDevice, (usDuration ? 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 psDevice 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 tBoolean +CheckAndSendLineCodingChange(const tUSBDCDCDevice *psDevice) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineCodingChange(psDevice); + } + + // + // 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 psDevice 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 tBoolean +CheckAndSendLineStateChange(const tUSBDCDCDevice *psDevice) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineStateChange(psDevice); + } + + // + // 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 tBoolean +SendSerialState(const tUSBDCDCDevice *psDevice) +{ + tUSBRequest sRequest; + unsigned short usSerialState; + tCDCSerInstance *psInst; + long lRetcode; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Remember that we are in the middle of sending a notification. + // + psInst->eCDCInterruptState = CDC_STATE_WAIT_DATA; + + // + // Clear the flag we use to indicate that a send is required. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + false); + // + // Take a snapshot of the serial state. + // + usSerialState = psInst->usSerialState; + + // + // 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. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, + psInst->ucControlEndpoint, + (unsigned char *)&sRequest, + sizeof(tUSBRequest)); + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucControlEndpoint, + (unsigned char *)&usSerialState, + USB_CDC_NOTIFY_SERIAL_STATE_SIZE); + + // + // Did we correctly write the data to the endpoint FIFO? + // + if(lRetcode != -1) + { + // + // We put the data into the FIFO so now schedule it to be + // sent. + // + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucControlEndpoint, + USB_TRANS_IN); + } + + // + // If an error occurred, mark the endpoint as idle (to prevent possible + // lockup) and return an error. + // + if(lRetcode == -1) + { + psInst->eCDCInterruptState = CDC_STATE_IDLE; + return(false); + } + else + { + // + // Everything went fine. Clear the error bits that we just notified + // and return true. + // + psInst->usSerialState &= ~(usSerialState & USB_CDC_SERIAL_ERRORS); + return(true); + } +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus 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. +// +//***************************************************************************** +tBoolean +ProcessDataFromHost(const tUSBDCDCDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tCDCSerInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucBulkOUTEndpoint, + ulEPStatus); + + // + // Has a packet been received? + // + if(ulEPStatus & 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->usDeferredOpFlags, CDC_DO_PACKET_RX, true); + + // + // Is the receive channel currently blocked? + // + if(!psInst->bControlBlocked && !psInst->bRxBlocked) + { + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // 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. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ulEPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_ERROR, + (ulEPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to interrupt messages sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus 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. +// +//***************************************************************************** +tBoolean +ProcessNotificationToHost(const tUSBDCDCDevice *psDevice, + unsigned long ulStatus) +{ + unsigned long ulEPStatus; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + // + // Assume all will go well until we have reason to believe otherwise. + // + bRetcode = true; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucControlEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucControlEndpoint, ulEPStatus); + + // + // Did the state change while we were waiting for the previous notification + // to complete? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_SERIAL_STATE_CHANGE)) + { + // + // The state changed while we were waiting so we need to schedule + // another notification immediately. + // + bRetcode = SendSerialState(psDevice); + } + else + { + // + // Our last notification completed and we didn't have any new + // notifications to make so the interrupt channel is now idle again. + // + psInst->eCDCInterruptState = CDC_STATE_IDLE; + } + + // + // Tell the caller how things went. + // + return(bRetcode); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus 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. +// +//***************************************************************************** +tBoolean +ProcessDataToHost(const tUSBDCDCDevice *psDevice, unsigned long ulStatus) +{ + tCDCSerInstance *psInst; + unsigned long ulEPStatus, ulSize; + tBoolean bSentFullPacket; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, ulEPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->eCDCTxState = CDC_STATE_IDLE; + + // + // If this notification isn't 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->usLastTxSize) + { + // + // Have we just sent a 64 byte packet? + // + bSentFullPacket = (psInst->usLastTxSize == DATA_IN_EP_MAX_SIZE) ? + true : false; + + // + // Notify the client that the last transmission completed. + // + ulSize = (unsigned long)psInst->usLastTxSize; + psInst->usLastTxSize = 0; + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ulSize, (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->usLastTxSize) + { + // + // We can expect another transmit complete notification after doing + // this. + // + psInst->eCDCTxState = CDC_STATE_WAIT_DATA; + + // + // Send the zero-length packet. + // + MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, + 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 ulStatus. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDCDCDevice *psDeviceInst; + tCDCSerInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psDeviceInst = pvInstance; + psInst = psDeviceInst->psPrivateCDCSerData; + + // + // Handler for the interrupt IN notification endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucControlEndpoint))) + { + // + // We have sent an interrupt notification to the host. + // + ProcessNotificationToHost(psDeviceInst, ulStatus); + } + + // + // Handler for the bulk OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucBulkOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(psDeviceInst, ulStatus); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucBulkINEndpoint))) + { + ProcessDataToHost(psDeviceInst, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tCDCSerInstance *psInst; + const tUSBDCDCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Set all our endpoints to idle state. + // + psInst->eCDCInterruptState = CDC_STATE_IDLE; + psInst->eCDCRequestState = CDC_STATE_IDLE; + psInst->eCDCRxState = CDC_STATE_IDLE; + psInst->eCDCTxState = CDC_STATE_IDLE; + + // + // 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. + // + psDevice->pfnControlCallback(psDevice->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 *pvInstance, unsigned long ulDataSize) +{ + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // If we were not passed any data, just return. + // + if(ulDataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Make sure we are actually expecting something. + // + if(psInst->eCDCRequestState != CDC_STATE_WAIT_DATA) + { + return; + } + + // + // Process the data received. This will be a request-specific data + // block associated with the last request received. + // + switch (psInst->ucPendingRequest) + { + // + // 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(ulDataSize != sizeof(tLineCoding)) + { + USBDCDStallEP0(0); + } + else + { + // + // Set the flag telling us that we need to send a line coding + // notification to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_LINE_CODING_CHANGE, true); + + // + // See if we can send the notification immediately. + // + bRetcode = CheckAndSendLineCodingChange(psDevice); + + // + // If we couldn't 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->eCDCRequestState = CDC_STATE_IDLE; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tCDCSerInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pucData[0] == SERIAL_INTERFACE_CONTROL) + { + psInst->ucInterfaceControl = pucData[1]; + } + else if(pucData[0] == SERIAL_INTERFACE_DATA) + { + psInst->ucInterfaceData = pucData[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(pucData[0] & USB_EP_DESC_IN) + { + // + // Determine which IN endpoint to modify. + // + if((pucData[0] & 0x7f) == USB_EP_TO_INDEX(CONTROL_ENDPOINT)) + { + psInst->ucControlEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + psInst->ucBulkINEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucBulkOUTEndpoint = + INDEX_TO_USB_EP(pucData[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. + // + pucData[2] = psInst->ucInterfaceControl; + + // + // 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. + // + pucData[29] = psInst->ucInterfaceControl; + pucData[30] = psInst->ucInterfaceData; + + // + // This sets the bDataInterface of the Union descriptor to the + // Data interface used by this instance. + pucData[35] = psInst->ucInterfaceData; + 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Only handle requests meant for this interface. + // + if(pUSBRequest->wIndex != psInst->ucInterfaceControl) + { + 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->ucPendingRequest = USB_CDC_SET_LINE_CODING; + + // + // Set the state to indicate we are waiting for data. + // + psInst->eCDCRequestState = CDC_STATE_WAIT_DATA; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, (unsigned char *)&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->ulUSBBase, 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->ulUSBBase, USB_EP_0, false); + + // + // Ask the client for the current line coding. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_GET_LINE_CODING, 0, + &sLineCoding); + + // + // Send the line coding information back to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&sLineCoding, + sizeof(tLineCoding)); + + break; + } + + case USB_CDC_SET_CONTROL_LINE_STATE: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + // + // Set the handshake lines as required. + // + psInst->usControlLineState = pUSBRequest->wValue; + + // + // Remember that we are due to notify the client of a line + // state change. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_LINE_STATE_CHANGE, true); + + // + // See if we can notify now. + // + bRetcode = CheckAndSendLineStateChange(psDevice); + + // + // If we couldn't 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->ulUSBBase, USB_EP_0, false); + + // + // Keep a copy of the requested break duration. + // + psInst->usBreakDuration = pUSBRequest->wValue; + + // + // Remember that we need to send a break request. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_SEND_BREAK, true); + + // + // Send the break request if all outstanding receive data has been + // processed. + // + bRetcode = CheckAndSendBreak(psDevice, pUSBRequest->wValue); + + // + // If we couldn't 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 *pvInstance) +{ + const tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // 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 *pvInstance) +{ + const tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // 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 *pvInstance) +{ + tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvInstance; + + // + // 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 ulIndex is the index of the USB controller for which this tick +// is being generated. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +CDCTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tBoolean bCanSend; + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + unsigned long ulSize; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Is there any outstanding operation that we should try to perform? + // + if(psInst->usDeferredOpFlags) + { + // + // 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->usDeferredOpFlags & (1 << CDC_DO_CLEAR_BREAK)) + { + // + // Will our break timer expire this time? + // + if(psInst->usBreakDuration <= ulTimemS) + { + // + // Yes - turn off the break condition. + // + SendBreak(psDevice, false); + } + else + { + // + // We have not timed out yet. Decrement the break timer. + // + psInst->usBreakDuration -= (unsigned short)ulTimemS; + } + } + + // 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(psDevice); + + // + // 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->usDeferredOpFlags & (1 << CDC_DO_SEND_BREAK)) + { + SendBreak(psDevice, true); + } + + // + // Do we need to set the RTS/DTR states? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_LINE_STATE_CHANGE)) + { + SendLineStateChange(psDevice); + } + + // + // Do we need to change the line coding parameters? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_LINE_CODING_CHANGE)) + { + SendLineCodingChange(psDevice); + } + + // + // 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->usDeferredOpFlags & 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->usDeferredOpFlags & (1 << CDC_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + } + } + + return; +} + +//***************************************************************************** +// +//! Initializes CDC device operation when used with a composite device. +//! +//! \param ulIndex is the index of the USB controller in use. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC 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. +//! +//! \return Returns NULL on failure or the psCDCDevice pointer on success. +// +//***************************************************************************** +void * +USBDCDCCompositeInit(unsigned long ulIndex, const tUSBDCDCDevice *psCDCDevice) +{ + tCDCSerInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psCDCDevice); + ASSERT(psCDCDevice->psPrivateCDCSerData); + ASSERT(psCDCDevice->pfnControlCallback); + ASSERT(psCDCDevice->pfnRxCallback); + ASSERT(psCDCDevice->pfnTxCallback); + + // + // Create an instance pointer to the private data area. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucBulkINEndpoint = DATA_IN_ENDPOINT; + psInst->ucBulkOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucInterfaceControl = SERIAL_INTERFACE_CONTROL; + psInst->ucInterfaceData = 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->ucControlEndpoint = CONTROL_ENDPOINT; + + // + // Initialize the workspace in the passed instance structure. + // + psInst->psConfDescriptor = (tConfigDescriptor *)g_pCDCSerDescriptor; + psInst->psDevInfo = &g_sCDCSerDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eCDCRxState = CDC_STATE_UNCONFIGURED; + psInst->eCDCTxState = CDC_STATE_UNCONFIGURED; + psInst->eCDCInterruptState = CDC_STATE_UNCONFIGURED; + psInst->eCDCRequestState = CDC_STATE_UNCONFIGURED; + psInst->ucPendingRequest = 0; + psInst->usBreakDuration = 0; + psInst->usSerialState = 0; + psInst->usDeferredOpFlags = 0; + psInst->usControlLineState = 0; + psInst->bRxBlocked = false; + psInst->bControlBlocked = false; + psInst->bConnected = false; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psCDCDevice->usVID; + psDevDesc->idProduct = psCDCDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psCDCDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psCDCDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psCDCDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psCDCDevice->ulNumStringDescriptors; + + // + // 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 ulIndex 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(unsigned long ulIndex, const tUSBDCDCDevice *psCDCDevice) +{ + void *pvRet; + tCDCSerInstance *psInst; + + // + // Initialize the internal state for this class. + // + pvRet = USBDCDCCompositeInit(ulIndex, psCDCDevice); + + if(pvRet) + { + // + // Create an instance pointer to the private data area. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // Set the instance data for this device so that USBDCDInit() call can + // have the instance data. + // + psInst->psDevInfo->pvInstance = (void *)psCDCDevice; + + // + // Enable the default interrupt control endpoint if this class is not + // being used in a composite device. + // + psInst->ucControlEndpoint = CONTROL_ENDPOINT; + + // + // Use the configuration descriptor with the interrupt control endpoint. + // + psInst->psDevInfo->ppConfigDescriptors = g_pCDCSerConfigDescriptors; + + // + // All is well so now pass the descriptors to the lower layer and put + // the CDC device on the bus. + // + USBDCDInit(ulIndex, psInst->psDevInfo); + } + + return(pvRet); +} + +//***************************************************************************** +// +//! Shuts down the CDC device instance. +//! +//! \param pvInstance 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 pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCTerm(void *pvInstance) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USB_BASE_TO_INDEX(psInst->ulUSBBase)); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; + psInst->psConfDescriptor = (tConfigDescriptor *)0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer for the control callback. +//! +//! \param pvInstance 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 *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback pointer for the control channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvControlCBData; + ((tUSBDCDCDevice *)pvInstance)->pvControlCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the receive channel callback. +//! +//! \param pvInstance 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 *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvRxCBData; + ((tUSBDCDCDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the transmit callback. +//! +//! \param pvInstance 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 *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvTxCBData; + ((tUSBDCDCDevice *)pvInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the CDC data interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pcData points to the first byte of data which is to be transmitted. +//! \param ulLength 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 +//! 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 ulLength 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. +// +//***************************************************************************** +unsigned long +USBDCDCPacketWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tCDCSerInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Can we send the data provided? + // + if((ulLength > DATA_IN_EP_MAX_SIZE) || + (psInst->eCDCTxState != CDC_STATE_IDLE)) + { + // + // 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. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, pcData, + ulLength); + + // + // Did we copy the data successfully? + // + if(lRetcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->usLastTxSize += (unsigned short)ulLength; + + // + // 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->eCDCTxState = CDC_STATE_WAIT_DATA; + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(lRetcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ulLength); + } + else + { + // + // Yes - tell the caller we couldn't send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the CDC data +//! interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pcData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pcData. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to ulLength 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. +// +//***************************************************************************** +unsigned long +USBDCDCPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tCDCSerInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + if(ulEPStatus & 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->usDeferredOpFlags, + 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? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + pcData, &ulCount); + + // + // Did we read the last of the packet data? + // + if(ulCount == ulPkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_PACKET_RX, false); + + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(lRetcode != -1) + { + return(ulCount); + } + } + } + + // + // 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 pvInstance 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 (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. +// +//***************************************************************************** +unsigned long +USBDCDCTxPacketAvailable(void *pvInstance) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eCDCTxState != CDC_STATE_IDLE) + { + // + // 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 pvInstance 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. +// +//***************************************************************************** +unsigned long +USBDCDCRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // 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? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + return(ulSize); + } + 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 pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param usState 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 usState 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 *pvInstance, unsigned short usState) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // 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->usSerialState |= (usState & USB_CDC_SERIAL_ERRORS); + psInst->usSerialState &= ~USB_CDC_SERIAL_ERRORS; + psInst->usSerialState |= (usState & ~USB_CDC_SERIAL_ERRORS); + + // + // Set the flag indicating that a serial state change is to be sent. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + true); + + // + // Can we send the state change immediately? + // + if(psInst->eCDCInterruptState == CDC_STATE_IDLE) + { + // + // 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->eCDCInterruptState = CDC_STATE_WAIT_DATA; + SendSerialState(pvInstance); + } + + return; +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the CDC device instance structure. +//! \param ucPower 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 *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wakeup to resume communication when in suspended state. +//! +//! \param pvInstance 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 config 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. +// +//***************************************************************************** +tBoolean +USBDCDCRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // 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..f2f9b00 --- /dev/null +++ b/usblib/device/usbdcdc.h @@ -0,0 +1,353 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for generic CDC ACM (serial) device. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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. + // + CDC_STATE_UNCONFIGURED, + + // + // No outstanding transaction remains to be completed. + // + CDC_STATE_IDLE, + + // + // Waiting on completion of a send or receive transaction. + // + CDC_STATE_WAIT_DATA, + + // + // Waiting for client to process data. + // + CDC_STATE_WAIT_CLIENT +} +tCDCState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// CDC Serial device. The memory for this structure is pointed to by the +// psPrivateCDCSerData field in the tUSBDCDCDevice structure passed on +// USBDCDCInit(). +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + volatile tCDCState eCDCRxState; + volatile tCDCState eCDCTxState; + volatile tCDCState eCDCRequestState; + volatile tCDCState eCDCInterruptState; + volatile unsigned char ucPendingRequest; + unsigned short usBreakDuration; + unsigned short usControlLineState; + unsigned short usSerialState; + volatile unsigned short usDeferredOpFlags; + unsigned short usLastTxSize; + tLineCoding sLineCoding; + volatile tBoolean bRxBlocked; + volatile tBoolean bControlBlocked; + volatile tBoolean bConnected; + unsigned char ucControlEndpoint; + unsigned char ucBulkINEndpoint; + unsigned char ucBulkOUTEndpoint; + unsigned char ucInterfaceControl; + unsigned char ucInterfaceData; +} +tCDCSerInstance; + + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the CDC device class driver. +// The client must provide a block of RAM of at least this size in the +// psPrivateCDCSerData field of the tUSBCDCDevice structure passed on +// USBDCDCInit(). +// +// This value is deprecated and should not be used, any new code should just +// pass in a tUSBCDCDevice structure in the psPrivateCDCSerData field. +// +//***************************************************************************** +#define USB_CDCSER_WORKSPACE_SIZE (sizeof(tCDCSerInstance)) +#endif + +//***************************************************************************** +// +// The following defines are used when working with composite devices. +// +//***************************************************************************** + +//***************************************************************************** +// +//! 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. +// +// For reference this is sizeof(g_pIADSerDescriptor) + +// sizeof(g_pCDCSerCommInterface) + sizeof(g_pCDCSerDataInterface) +// +//***************************************************************************** +#define COMPOSITE_DCDC_SIZE (8 + 35 + 23) + +//***************************************************************************** +// +// CDC-specific events These events are provided to the application in the +// \e ulMsg 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 ulMsgValue 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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + // + //! 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. + // + 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. + // + 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. + // + 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + (5 * number of supported languages). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to 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 *psPrivateCDCSerData; +} +tUSBDCDCDevice; + +extern tDeviceInfo g_sCDCSerDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void * USBDCDCCompositeInit(unsigned long ulIndex, + const tUSBDCDCDevice *psCDCDevice); +extern void *USBDCDCInit(unsigned long ulIndex, + const tUSBDCDCDevice *psCDCDevice); +extern void USBDCDCTerm(void *pvInstance); +extern void *USBDCDCSetControlCBData(void *pvInstance, void *pvCBData); +extern void *USBDCDCSetRxCBData(void *pvInstance, void *pvCBData); +extern void *USBDCDCSetTxCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDCDCPacketWrite(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDCDCPacketRead(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDCDCTxPacketAvailable(void *pvInstance); +extern unsigned long USBDCDCRxPacketAvailable(void *pvInstance); +extern void USBDCDCSerialStateChange(void *pvInstance, + unsigned short usState); +extern void USBDCDCPowerStatusSet(void *pvInstance, unsigned char ucPower); +extern tBoolean USBDCDCRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..2bb1731 --- /dev/null +++ b/usblib/device/usbdcdesc.c @@ -0,0 +1,649 @@ +//***************************************************************************** +// +// usbcdesc.c - Config descriptor parsing functions. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" + +//***************************************************************************** +// +// The functions in this file mirror the descriptor parsing APIs available +// in usblib.h but parse config 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 psDesc. +//! \param pulSec points to a variable containing the section within \e +//! psConfig which contains \e psDesc. +//! \param psDesc points to the descriptor that we want to step past. +//! +//! This function walks forward one descriptor within a config descriptor. The +//! value returned is a pointer to the header of the next descriptor after the +//! descriptor supplied in \e psDesc. If the next descriptor is in the next +//! section, \e *pulSec will be incremented accordlingly. +//! +//! \return Returns a pointer to the next descriptor in the config descrptor. +// +//***************************************************************************** +static tDescriptorHeader * +NextConfigDescGet(const tConfigHeader *psConfig, unsigned long *pulSec, + 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((unsigned char *)psDesc >= (psConfig->psSections[*pulSec]->pucData + + psConfig->psSections[*pulSec]->usSize)) + { + // + // Yes - move to the next section. + // + (*pulSec)++; + + // + // Are we still within the config descriptor? + // + if(*pulSec < psConfig->ucNumSections) + { + // + // Yes - the new descriptor is at the start of the new section. + // + psDesc = + (tDescriptorHeader *)psConfig->psSections[*pulSec]->pucData; + } + 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 config descriptor +//! with the supplied interface number. +//! +//! \param psConfig points to the header structure for the config descriptor +//! that is to be searched. +//! \param ucInterfaceNumber is the interface number of the descriptor that is +//! being queried. +//! \param ulIndex is the zero based index of the descriptor to return. +//! \param pulSection points to storage which will be 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, + unsigned char ucInterfaceNumber, + unsigned long ulIndex, + unsigned long *pulSection) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulCount; + unsigned long ulSec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pucData; + ulCount = 0; + ulSec = 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 == + ucInterfaceNumber)) + { + // + // This is an interface descriptor for interface ucInterfaceNumber. + // Determine if this is the n-th one we have found and, if so, + // return its pointer. + // + if(ulCount == ulIndex) + { + // + // Found it - return the pointer and section number. + // + *pulSection = ulSec; + 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. + // + ulCount++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ulSec, 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 config 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 config descriptor will result +//! from concatenating the required sections. +// +//***************************************************************************** +unsigned long +USBDCDConfigDescGetSize(const tConfigHeader *psConfig) +{ + unsigned long ulLoop; + unsigned long ulLen; + + ulLen = 0; + + // + // Determine the size of the whole descriptor by adding the sizes of + // each section which will be concatenated to produce it. + // + for(ulLoop = 0; ulLoop < psConfig->ucNumSections; ulLoop++) + { + ulLen += psConfig->psSections[ulLoop]->usSize; + } + + return(ulLen); +} + +//***************************************************************************** +// +//! \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 ulType 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. +// +//***************************************************************************** +unsigned long +USBDCDConfigDescGetNum(const tConfigHeader *psConfig, unsigned long ulType) +{ + unsigned long ulSection; + unsigned long ulNumDescs; + + // + // Initialize our counts. + // + ulNumDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the config descriptor. Note that this assumes each + // section contains only whole descriptors! + // + for(ulSection = 0; ulSection < (unsigned long)psConfig->ucNumSections; + ulSection++) + { + ulNumDescs += USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ulSection]->pucData, + psConfig->psSections[ulSection]->usSize, + ulType); + } + + return(ulNumDescs); +} + +//***************************************************************************** +// +//! \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 ulType 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 ulIndex is the zero based index of the descriptor whose pointer is +//! to be returned. For example, passing value 1 in \e ulIndex returns the +//! second matching descriptor. +//! \param pulSection 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, unsigned long ulType, + unsigned long ulIndex, unsigned long *pulSection) +{ + unsigned long ulSection; + unsigned long ulTotalDescs; + unsigned long ulNumDescs; + + // + // Initialize our counts. + // + ulTotalDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the config descriptor. This allows us to determine + // which section contains the descriptor we are being asked for. + // + for(ulSection = 0; ulSection < (unsigned long)psConfig->ucNumSections; + ulSection++) + { + // + // How many descriptors of the requested type exist in this section? + // + ulNumDescs = USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ulSection]->pucData, + psConfig->psSections[ulSection]->usSize, + ulType); + + // + // Does this section contain the descriptor whose index we are looking + // for? + // + if((ulTotalDescs + ulNumDescs) > ulIndex) + { + // + // We know the requested descriptor exists in the current + // block so write the section number to the caller's storage. + // + *pulSection = ulSection; + + // + // Now find the actual descriptor requested and return its pointer. + // + return(USBDescGet( + (tDescriptorHeader *)psConfig->psSections[ulSection]->pucData, + psConfig->psSections[ulSection]->usSize, + ulType, + ulIndex - ulTotalDescs)); + } + + // + // 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. + // + ulTotalDescs += ulNumDescs; + } + + // + // 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 config descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ucInterfaceNumber 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 config +//! descriptor. +// +//***************************************************************************** +unsigned long +USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + unsigned char ucInterfaceNumber) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulCount; + unsigned long ulSec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pucData; + ulSec = 0; + ulCount = 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 == + ucInterfaceNumber)) + { + // + // Yes - increment our count. + // + ulCount++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ulSec, psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ulCount); +} + +//***************************************************************************** +// +//! \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 ulIndex is the zero based index of the interface that is to be +//! found. If \e ulAlt is set to a value other than \b USB_DESC_ANY, this will +//! be equivalent to the interface number being searched for. +//! \param ulAlt 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 pulSection 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 ulAlt 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 +//! ulAlt if \e ulIndex is set to the required interface number and \e ulAlt +//! 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, unsigned long ulIndex, + unsigned long ulAlt, unsigned long *pulSection) +{ + // + // If we are being told to ignore the alternate configuration, this boils + // down to a very simple query. + // + if(ulAlt == USB_DESC_ANY) + { + // + // Return the ulIndex-th interface descriptor we find in the + // configuration descriptor. + // + return((tInterfaceDescriptor *)USBDCDConfigDescGet(psConfig, + USB_DTYPE_INTERFACE, + ulIndex, + pulSection)); + } + else + { + // + // In this case, a specific alternate setting number is required. + // Given that interface numbers are zero based indices, we can + // pass the supplied ulIndex parameter directly as the interface + // number to USBDescGetAlternateInterface to retrieve the requested + // interface descriptor pointer. + // + return(ConfigAlternateInterfaceGet(psConfig, ulIndex, ulAlt, + pulSection)); + } +} + +//***************************************************************************** +// +//! \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 ulInterfaceNumber is the interface number whose endpoint is to be +//! found. +//! \param ulAltCfg is the alternate setting number which is to be searched +//! for. This must be a valid alternate setting number for the requested +//! interface. +//! \param ulIndex 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 ulIndex 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, + unsigned long ulInterfaceNumber, + unsigned long ulAltCfg, unsigned long ulIndex) +{ + tInterfaceDescriptor *psInterface; + tDescriptorHeader *psEndpoint; + unsigned long ulSection; + unsigned long ulCount; + + // + // Find the requested interface descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ulInterfaceNumber, + ulAltCfg, &ulSection); + + // + // Did we find the requested interface? + // + if(psInterface) + { + // + // Is the index passed valid? + // + if(ulIndex >= 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. + // + ulCount = 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(ulCount == ulIndex) + { + // + // Yes - return the descriptor pointer to the caller. + // + return((tEndpointDescriptor *)psEndpoint); + } + + // + // Move on to look for the next endpoint. + // + ulCount++; + } + + // + // Move to the next descriptor. + // + psEndpoint = NextConfigDescGet(psConfig, &ulSection, + psEndpoint); + } + } + } + + // + // We couldn't 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..feb22bf --- /dev/null +++ b/usblib/device/usbdcomp.c @@ -0,0 +1,1543 @@ +//**************************************************************************** +// +// usbdcomp.c - USB composite device class driver. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris USB Library. +// +//**************************************************************************** + +#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/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 unsigned char g_pCompDeviceDescriptor[] = +{ + 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 unsigned char g_pCompConfigDescriptor[] = +{ + // + // 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.pulDeviceWorkspace). 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 + +//***************************************************************************** +// +// 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 USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//**************************************************************************** +// +// Various internal handlers needed by this class. +// +//**************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void InterfaceChange(void *pvInstance, unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void DataSent(void *pvInstance, unsigned long ulInfo); +static void DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void SuspendHandler(void *pvInstance); +static void ResumeHandler(void *pvInstance); +static void ResetHandler(void *pvInstance); +static void GetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); + +//**************************************************************************** +// +// Configuration Descriptor. +// +//**************************************************************************** +tConfigHeader *g_pCompConfigDescriptors[1]; + +//**************************************************************************** +// +// The FIFO configuration for USB mass storage class device. +// +//**************************************************************************** +tFIFOConfig g_sUSBCompositeFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//**************************************************************************** +// +// The device information structure for the USB Composite device. +// +//**************************************************************************** +tDeviceInfo g_sCompositeDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + GetDescriptor, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + DataSent, + + // + // ResetHandler + // + ResetHandler, + + // + // SuspendHandler + // + SuspendHandler, + + // + // ResumeHandler + // + ResumeHandler, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints + }, + g_pCompDeviceDescriptor, + (const tConfigHeader **)g_pCompConfigDescriptors, + 0, + 0, + &g_sUSBCompositeFIFOConfig +}; + +//**************************************************************************** +// +// Use the lookup table from the field pulDeviceWorkspace 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 unsigned long +InterfaceToIndex(tUSBDCompositeDevice *psDevice, unsigned long ulInterface) +{ + unsigned long ulLoop; + unsigned char *pucLookupEntry; + + // + // Get a pointer to the lookup table. + // + pucLookupEntry = (unsigned char *)psDevice->pulDeviceWorkspace; + + // + // Check each lookup entry in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ulNumDevices; ulLoop++) + { + // + // 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(ulInterface < (unsigned long)pucLookupEntry[LOOKUP_INTERFACE_BYTE]) + { + return(ulLoop); + } + + // + // Move to the next lookup table entry. + // + pucLookupEntry += sizeof(unsigned long); + } + + // + // 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 pulDeviceWorkspace 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 unsigned long +EndpointToIndex(tUSBDCompositeDevice *psDevice, unsigned long ulEndpoint, + tBoolean bInEndpoint) +{ + unsigned long ulLoop, ulEndpointByte; + unsigned char *pucLookupEntry; + + // + // Get a pointer to the lookup table. + // + pucLookupEntry = (unsigned char *)psDevice->pulDeviceWorkspace; + + // + // Are we considering an IN or OUT endpoint? + // + ulEndpointByte = bInEndpoint ? LOOKUP_IN_END_BYTE : LOOKUP_OUT_END_BYTE; + + // + // Check each lookup entry in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ulNumDevices; ulLoop++) + { + // + // 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(ulEndpoint < (unsigned long)pucLookupEntry[ulEndpointByte]) + { + return(ulLoop); + } + + // + // Move to the next lookup table entry. + // + pucLookupEntry += sizeof(unsigned long); + } + + // + // 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // 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(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ulIdx = InterfaceToIndex(psDevice, (pUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ulIdx = EndpointToIndex(psDevice, (pUSBRequest->wIndex & 0x0F), + (pUSBRequest->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: + { + ulIdx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ulIdx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + // + // Does this device have a GetDescriptor callback? + // + if(pDeviceInfo->sCallbacks.pfnGetDescriptor) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psDevice->psPrivateData->ulEP0Owner = ulIdx; + + // + // Call the device to retrieve the descriptor. + // + pDeviceInfo->sCallbacks.pfnGetDescriptor( + psDevice->psDevices[ulIdx].pvInstance, pUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0( + USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an suspend handler +// called. +// +//**************************************************************************** +static void +SuspendHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has resumed. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_SUSPEND, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnSuspendHandler) + { + pDeviceInfo->sCallbacks.pfnSuspendHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an resume handler +// called. +// +//**************************************************************************** +static void +ResumeHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has resumed. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_RESUME, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnResumeHandler) + { + pDeviceInfo->sCallbacks.pfnResumeHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an reset handler +// called. +// +//**************************************************************************** +static void +ResetHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has been connected. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnResetHandler) + { + pDeviceInfo->sCallbacks.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 *pvInstance, unsigned long ulInfo) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ulIdx = psDevice->psPrivateData->ulEP0Owner; + + if(ulIdx != INVALID_DEVICE_INDEX) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDataSent) + { + pDeviceInfo->sCallbacks.pfnDataSent( + psDevice->psDevices[ulIdx].pvInstance, ulInfo); + } + } +} + +//**************************************************************************** +// +// 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 *pvInstance, unsigned long ulInfo) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ulIdx = psDevice->psPrivateData->ulEP0Owner; + + if(ulIdx != INVALID_DEVICE_INDEX) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDataReceived) + { + pDeviceInfo->sCallbacks.pfnDataReceived( + psDevice->psDevices[ulIdx].pvInstance, ulInfo); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an endpoint handler +// called. +// +//**************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // 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 ulStatus will be 0). Since the handlers are + // set up to ignore any callback that isn't for them, this is safe. + // + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnEndpointHandler) + { + pDeviceInfo->sCallbacks.pfnEndpointHandler( + psDevice->psDevices[ulIdx].pvInstance, ulStatus); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//**************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has been disconnected. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_DISCONNECTED, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDisconnectHandler) + { + pDeviceInfo->sCallbacks.pfnDisconnectHandler( + psDevice->psDevices[ulIdx].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 *pvInstance, unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnInterfaceChange) + { + pDeviceInfo->sCallbacks.pfnInterfaceChange( + psDevice->psDevices[ulIdx].pvInstance, ucInterfaceNum, + ucAlternateSetting); + } + } +} + +//**************************************************************************** +// +// 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 *pvInstance, unsigned long ulValue) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnConfigChange) + { + pDeviceInfo->sCallbacks.pfnConfigChange( + psDevice->psDevices[ulIdx].pvInstance, ulValue); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance +// \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // 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(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ulIdx = InterfaceToIndex(psDevice, (pUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ulIdx = EndpointToIndex(psDevice, (pUSBRequest->wIndex & 0x0F), + (pUSBRequest->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: + { + ulIdx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ulIdx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + // + // Does this device have a RequestHandler callback? + // + if(pDeviceInfo->sCallbacks.pfnRequestHandler) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psDevice->psPrivateData->ulEP0Owner = ulIdx; + + // + // Yes - call the device to retrieve the descriptor. + // + pDeviceInfo->sCallbacks.pfnRequestHandler( + psDevice->psDevices[ulIdx].pvInstance, pUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0( + USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + +} + +//**************************************************************************** +// +// This function handles sending interface number changes to device instances. +// +//**************************************************************************** +static void +CompositeIfaceChange(tCompositeEntry *pCompDevice, unsigned char ucOld, + unsigned char ucNew) +{ + unsigned char pucInterfaces[2]; + + if(pCompDevice->psDevice->sCallbacks.pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pucInterfaces[0] = ucOld; + pucInterfaces[1] = ucNew; + + // + // Call the device handler to inform the class of the interface number + // change. + // + pCompDevice->psDevice->sCallbacks.pfnDeviceHandler( + pCompDevice->pvInstance, USB_EVENT_COMP_IFACE_CHANGE, + (void *)pucInterfaces); + } +} + +//**************************************************************************** +// +// This function handles sending endpoint number changes to device instances. +// +//**************************************************************************** +static void +CompositeEPChange(tCompositeEntry *pCompDevice, unsigned char ucOld, + unsigned char ucNew) +{ + unsigned char pucInterfaces[2]; + unsigned char ucIndex; + + if(pCompDevice->psDevice->sCallbacks.pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pucInterfaces[0] = ucOld; + pucInterfaces[1] = ucNew; + + ucNew--; + + if(ucOld & USB_RTYPE_DIR_IN) + { + ucIndex = (ucOld & ~USB_RTYPE_DIR_IN) - 1; + + g_sUSBCompositeFIFOConfig.sIn[ucNew].bDoubleBuffer = + pCompDevice->psDevice->psFIFOConfig->sIn[ucIndex].bDoubleBuffer; + + g_sUSBCompositeFIFOConfig.sIn[ucNew].usEPFlags = + pCompDevice->psDevice->psFIFOConfig->sIn[ucIndex].usEPFlags; + } + else + { + ucIndex = ucOld - 1; + + g_sUSBCompositeFIFOConfig.sOut[ucNew].bDoubleBuffer = + pCompDevice->psDevice->psFIFOConfig->sOut[ucIndex].bDoubleBuffer; + + g_sUSBCompositeFIFOConfig.sOut[ucNew].usEPFlags = + pCompDevice->psDevice->psFIFOConfig->sOut[ucIndex].usEPFlags; + } + // + // Call the device handler to inform the class of the interface number + // change. + // + pCompDevice->psDevice->sCallbacks.pfnDeviceHandler( + pCompDevice->pvInstance, USB_EVENT_COMP_EP_CHANGE, + (void *)pucInterfaces); + } +} + +//**************************************************************************** +// +// This function merges the configuration descriptors into a single multiple +// instance device. +// +//**************************************************************************** +unsigned long +BuildCompositeDescriptor(tUSBDCompositeDevice *psCompDevice) +{ + unsigned long ulIdx, ulOffset, ulCPIdx, ulFixINT, ulDev; + unsigned short usTotalLength, usBytes; + unsigned char ucInterface, ucINEndpoint, ucOUTEndpoint; + unsigned char *pucData, *pucConfig; + const tConfigHeader *pConfigHeader; + tDescriptorHeader *psHeader; + const unsigned char *pucDescriptor; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + const tDeviceInfo *psDevice; + + // + // Save the number of devices to look through. + // + ulDev = 0; + ulIdx = 0; + ucInterface = 0; + ucINEndpoint = 1; + ucOUTEndpoint = 1; + ulOffset = 0; + ulFixINT = 0; + + // + // This puts the first section pointer in the first entry in the list + // of sections. + // + psCompDevice->psPrivateData->ppsCompSections[0] = + &psCompDevice->psPrivateData->psCompSections[0]; + + // + // Put the pointer to this instances configuration descriptor into the + // front of the list. + // + psCompDevice->psPrivateData->ppsCompSections[0]->pucData = + (unsigned char *)&psCompDevice->psPrivateData->sConfigDescriptor; + + psCompDevice->psPrivateData->ppsCompSections[0]->usSize = + psCompDevice->psPrivateData->sConfigDescriptor.bLength; + + // + // The configuration descriptor is 9 bytes so initialize the total length + // to 9 bytes. + // + usTotalLength = 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->psPrivateData->ppsCompSections[1] = + &psCompDevice->psPrivateData->psCompSections[1]; + + // + // Copy the pointer to the application supplied space into the section + // list. + // + psCompDevice->psPrivateData->ppsCompSections[1]->usSize = 0; + psCompDevice->psPrivateData->ppsCompSections[1]->pucData = + psCompDevice->psPrivateData->pucData; + + // + // Create a local pointer to the data that is used to copy data from + // the other devices into the composite descriptor. + // + pucData = psCompDevice->psPrivateData->pucData; + + // + // Consider each device in turn. + // + while(ulDev < psCompDevice->ulNumDevices) + { + // + // Save the current starting address of this descriptor. + // + pucConfig = pucData + ulOffset; + + // + // Create a local pointer to the configuration header. + // + psDevice = psCompDevice->psDevices[ulDev].psDevice; + pConfigHeader = psDevice->ppConfigDescriptors[0]; + + // + // Loop through each of the sections in this device's configuration + // descriptor. + // + for(ulIdx = 0; ulIdx < pConfigHeader->ucNumSections; ulIdx++) + { + // + // Initialize the local offset in this descriptor. We include + // a special case here to ignore the initial 9 byte config + // descriptor since this has already been handled. + // + if(ulIdx) + { + // + // This is not the first section so we handle everything in + // it. + // + usBytes = 0; + } + else + { + // + // This is the first section for this device so skip the 9 + // byte config descriptor since we've already handled this. + // + usBytes = 9; + + // + // If this section includes only the config descriptor, skip + // it entirely. + // + if(pConfigHeader->psSections[ulIdx]->usSize <= usBytes) + { + continue; + } + } + + // + // Get a pointer to the configuration descriptor. + // + pucDescriptor = pConfigHeader->psSections[ulIdx]->pucData; + + // + // Bounds check the allocated space and return if there is not + // enough space. + // + if(ulOffset > psCompDevice->psPrivateData->ulDataSize) + { + return(1); + } + + // + // Copy the descriptor from the device into the descriptor list. + // + for(ulCPIdx = 0; + ulCPIdx < pConfigHeader->psSections[ulIdx]->usSize; + ulCPIdx++) + { + pucData[ulCPIdx + ulOffset] = pucDescriptor[ulCPIdx]; + } + + // + // Read out the descriptors in this section. + // + while(usBytes < pConfigHeader->psSections[ulIdx]->usSize) + { + // + // Create a descriptor header pointer. + // + psHeader = (tDescriptorHeader *)&pucData[ulOffset + usBytes]; + + // + // 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 = ucInterface - 1; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeIfaceChange(&psCompDevice->psDevices[ulDev], + psInterface->bInterfaceNumber, + ucInterface); + // + // This was the non-alternate setting so save the + // value and move to the next interface number. + // + psInterface->bInterfaceNumber = ucInterface; + + // + // No strings allowed on interface descriptors for + // composite devices. + // + psInterface->iInterface = 0; + + ucInterface++; + } + } + // + // 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->usPID == USB_PID_COMP_SERIAL)) + { + // + // Check if the Fixed Interrupt endpoint has been + // set yet. + // + if(ulFixINT == 0) + { + // + // Allocate the fixed interrupt endpoint and + // save its number. + // + ulFixINT = ucINEndpoint++; + } + + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ulFixINT); + + psEndpoint->bEndpointAddress = ulFixINT | + USB_RTYPE_DIR_IN; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ucINEndpoint); + + psEndpoint->bEndpointAddress = ucINEndpoint++ | + USB_RTYPE_DIR_IN; + } + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ucOUTEndpoint); + psEndpoint->bEndpointAddress = ucOUTEndpoint++; + } + } + + // + // Move on to the next descriptor. + // + usBytes += psHeader->bLength; + } + + ulOffset += pConfigHeader->psSections[ulIdx]->usSize; + + usTotalLength += usBytes; + } + + // + // Allow the device class to make adjustments to the configuration + // descriptor. + // + psCompDevice->psDevices[ulDev].psDevice->sCallbacks.pfnDeviceHandler( + psCompDevice->psDevices[ulDev].pvInstance, + USB_EVENT_COMP_CONFIG, (void *)pucConfig); + + // + // Add an entry into the device workspace array to allow us to quickly + // map interface and endpoint numbers to device instances later. + // + psCompDevice->pulDeviceWorkspace[ulDev] = + (ulDev << (LOOKUP_INDEX_BYTE * 8)) | + (ucInterface << (LOOKUP_INTERFACE_BYTE * 8)) | + (ucOUTEndpoint << (LOOKUP_OUT_END_BYTE * 8)) | + (ucINEndpoint << (LOOKUP_IN_END_BYTE * 8)); + + // + // Move on to the next device. + // + ulDev++; + } + + // + // Modify the configuration descriptor to match the number of interfaces + // and the new total size. + // + psCompDevice->psPrivateData->sCompConfigHeader.ucNumSections = 2; + psCompDevice->psPrivateData->ppsCompSections[1]->usSize = ulOffset; + psCompDevice->psPrivateData->sConfigDescriptor.bNumInterfaces = + ucInterface; + psCompDevice->psPrivateData->sConfigDescriptor.wTotalLength = + usTotalLength; + + + return(0); +} + +//**************************************************************************** +// +//! This function should be called once for the composite class device to +//! initialize basic operation and prepare for enumeration. +//! +//! \param ulIndex 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 ulSize is the size in bytes of the data pointed to by the +//! \e pucData parameter. +//! \param pucData 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 ulSize and \e pucData +//! parameters should be large enough to hold all of the class instances +//! passed in via the 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(unsigned long ulIndex, tUSBDCompositeDevice *psDevice, + unsigned long ulSize, unsigned char *pucData) +{ + tCompositeInstance *psInst; + long lIdx; + unsigned char *pucTemp; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the work space in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->ulDataSize = ulSize; + psInst->pucData = pucData; + + // + // Save the base address of the USB controller. + // + psInst->ulUSBBase = USB_INDEX_TO_BASE(ulIndex); + + // + // No device is currently transfering data on EP0. + // + psInst->ulEP0Owner = INVALID_DEVICE_INDEX; + + // + // Set the device information for the composite device. + // + psInst->psDevInfo = &g_sCompositeDeviceInfo; + + g_pCompConfigDescriptors[0] = &psInst->sCompConfigHeader; + g_pCompConfigDescriptors[0]->ucNumSections = 0; + g_pCompConfigDescriptors[0]->psSections = + (const tConfigSection * const *)psDevice->psPrivateData->ppsCompSections; + + // + // Create a byte pointer to use with the copy. + // + pucTemp = (unsigned char *)&psInst->sConfigDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(lIdx = 0; lIdx < g_pCompConfigDescriptor[0]; lIdx++) + { + pucTemp[lIdx] = g_pCompConfigDescriptor[lIdx]; + } + + // + // Create a byte pointer to use with the copy. + // + pucTemp = (unsigned char *)&psInst->sDeviceDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(lIdx = 0; lIdx < g_pCompDeviceDescriptor[0]; lIdx++) + { + pucTemp[lIdx] = g_pCompDeviceDescriptor[lIdx]; + } + + // + // Fix up the device descriptor with the client-supplied values. + // + psInst->sDeviceDescriptor.idVendor = psDevice->usVID; + psInst->sDeviceDescriptor.idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->sConfigDescriptor.bmAttributes = psDevice->ucPwrAttributes; + psInst->sConfigDescriptor.bMaxPower = + (unsigned char)(psDevice->usMaxPowermA>>1); + + g_sCompositeDeviceInfo.pDeviceDescriptor = + (const unsigned char *)&psInst->sDeviceDescriptor; + + // + // Plug in the client's string table to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors = + psDevice->ulNumStringDescriptors; + + // + // 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); + } + + // + // Set the instance data for this device. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psInst->psDevInfo); + + // + // Return the pointer to the instance indicating that everything went + // well. + // + return((void *)psDevice); +} + +//**************************************************************************** +// +//! Shuts down the composite device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCompositeInit(). +//! +//! This function terminates composite device interface for the instance +//! supplied. Following this call, the \e pvInstance instance should not me +//! used in any other calls. +//! +//! \return None. +// +//**************************************************************************** +void +USBDCompositeTerm(void *pvInstance) +{ + ASSERT(pvInstance != 0); + +} + +//**************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//**************************************************************************** + diff --git a/usblib/device/usbdcomp.h b/usblib/device/usbdcomp.h new file mode 100644 index 0000000..5d26352 --- /dev/null +++ b/usblib/device/usbdcomp.h @@ -0,0 +1,297 @@ +//***************************************************************************** +// +// usbdcomp.h - USB composite device class driver. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris 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 *pDeviceInfo; + + // + // 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. + // + unsigned char ucIfaceOffset; + +} tUSBDCompositeEntry; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// composite device class. The memory for this structure is pointed to by +// the psPrivateData 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. + // + unsigned long ulUSBBase; + + // + // The device information pointer. + // + tDeviceInfo *psDevInfo; + + // + // 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. + // + unsigned long ulDataSize; + unsigned char *pucData; + + // + // The current "owner" of endpoint 0. This is used to track the device + // class which is currently transferring data on EP0. + // + unsigned long ulEP0Owner; +} +tCompositeInstance; + +//***************************************************************************** +// +//! 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 *psDevice; + + // + //! This is the instance data for the device structure. + // + void *pvInstance; +} +tCompositeEntry; + +//***************************************************************************** +// +//! 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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in mA. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the composite + //! device. + // + 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! The number of devices in the psDevices array. + // + unsigned long ulNumDevices; + + // + //! This application supplied array holds the the top level device class + //! information as well as the Instance data for that class. + // + tCompositeEntry *psDevices; + + // + //! A pointer to per-device workspace used by the composite device. This + //! buffer must be sized to contain at least ulNumDevices long integers + //! (or 4 * ulNumDevices bytes). It 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. + // + unsigned long *pulDeviceWorkspace; + + // + //! A pointer to RAM work space for this device instance. The client + //! must fill in this field with a pointer to at least + //! sizeof(tCompositeInstance) bytes of read/write storage that the + //! library can use for driver work space. 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 *psPrivateData; +} +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(unsigned long ulIndex, + tUSBDCompositeDevice *psCompDevice, + unsigned long ulSize, + unsigned char *pucData); +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..9737607 --- /dev/null +++ b/usblib/device/usbdconfig.c @@ -0,0 +1,654 @@ +//***************************************************************************** +// +// usbdconfig.c - High level USB device configuration function. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Mask used to preserve various endpoint configuration flags. +// +//***************************************************************************** +#define EP_FLAGS_MASK (USB_EP_MODE_MASK | USB_EP_DEV_IN | \ + USB_EP_DEV_OUT) + +//***************************************************************************** +// +// Structure used in compiling FIFO size and endpoint properties from a +// configuration descriptor. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulSize[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 unsigned long +GetEndpointFIFOSize(unsigned long ulMaxPktSize, const tFIFOEntry *psFIFOParams, + unsigned long *pupBytesUsed) +{ + unsigned long ulBytes; + unsigned long ulLoop; + unsigned long ulFIFOSize; + + // + // What is the basic size required for a single buffered FIFO entry + // containing the required number of packets? + // + ulBytes = ulMaxPktSize; + + // + // 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(ulLoop = USB_FIFO_SZ_8; ulLoop <= USB_FIFO_SZ_4096; ulLoop++) + { + // + // How many bytes does this FIFO value represent? + // + ulFIFOSize = USB_FIFO_SZ_TO_BYTES(ulLoop); + + // + // Is this large enough to satisfy the request? + // + if(ulFIFOSize >= ulBytes) + { + // + // Yes - are we being asked to double-buffer the FIFO for this + // endpoint? + // + if(psFIFOParams->bDoubleBuffer) + { + // + // Yes - FIFO requirement is double in this case. + // + *pupBytesUsed = ulFIFOSize * 2; + return(ulLoop | USB_FIFO_SIZE_DB_FLAG); + } + else + { + // + // No double buffering so just return the size and associated + // flag. + // + *pupBytesUsed = ulFIFOSize; + return(ulLoop); + } + } + } + + // + // 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, unsigned long *pulEPIndex, + unsigned long *pulMaxPktSize, unsigned long *pulFlags) +{ + // + // Get the endpoint index. + // + *pulEPIndex = psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + + // + // Extract the maximum packet size. + // + *pulMaxPktSize = psEndpoint->wMaxPacketSize & USB_EP_MAX_PACKET_COUNT_M; + + // + // Is this an IN or an OUT endpoint? + // + *pulFlags = (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: + *pulFlags |= USB_EP_MODE_CTRL; + break; + + case USB_EP_ATTR_BULK: + *pulFlags |= USB_EP_MODE_BULK; + break; + + case USB_EP_ATTR_INT: + *pulFlags |= USB_EP_MODE_INT; + break; + + case USB_EP_ATTR_ISOC: + *pulFlags |= USB_EP_MODE_ISOC; + break; + } +} + +//***************************************************************************** +// +//! Configure the USB controller appropriately for the device whose config +//! 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. +//! +//! USBDCDConfig() is an optional call and applications may chose to make +//! direct calls to SysCtlPeripheralEnable(), SysCtlUSBPLLEnable(), +//! USBDevEndpointConfigSet() and USBFIFOConfigSet() instead of using this +//! function. If this function is used, it must be called prior to +//! USBDCDInit() since this call assumes that the low level hardware +//! configuration has been completed before it is made. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +tBoolean +USBDeviceConfig(tDeviceInstance *psDevInst, const tConfigHeader *psConfig) +{ + unsigned long ulLoop; + unsigned long ulCount; + unsigned long ulNumInterfaces; + unsigned long ulEpIndex; + unsigned long ulEpType; + unsigned long ulMaxPkt; + unsigned long ulNumEndpoints; + unsigned long ulFlags; + unsigned long ulBytesUsed; + unsigned long ulSection; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + tFIFOConfig const *psFIFOConfig; + tUSBEndpointInfo psEPInfo[NUM_USB_EP - 1]; + + // + // A valid device instance is required. + // + ASSERT(psDevInst != 0); + + // + // Catch bad pointers in a debug build. + // + ASSERT(psConfig); + + psFIFOConfig = psDevInst->psInfo->psFIFOConfig; + + // + // Clear out our endpoint info. + // + for(ulLoop = 0; ulLoop < (NUM_USB_EP - 1); ulLoop++) + { + psEPInfo[ulLoop].ulSize[EP_INFO_IN] = 0; + psEPInfo[ulLoop].ulSize[EP_INFO_OUT] = 0; + } + + // + // How many (total) endpoints does this configuration describe? + // + ulNumEndpoints = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_ENDPOINT); + + // + // How many interfaces are included? + // + ulNumInterfaces = 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(ulLoop = 0; ulLoop < ulNumEndpoints; ulLoop++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = (tEndpointDescriptor *)USBDCDConfigDescGet( + psConfig, USB_DTYPE_ENDPOINT, ulLoop, + &ulSection); + + // + // Extract the endpoint number and whether it is an IN or OUT + // endpoint. + // + ulEpIndex = (unsigned long) + psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + ulEpType = (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((ulEpIndex >= NUM_USB_EP) || (ulEpIndex == 0)) + { + return(false); + } + + // + // Does this endpoint have a max packet size requirement larger than + // any previous use we have seen? + // + if(psEndpoint->wMaxPacketSize > + psEPInfo[ulEpIndex - 1].ulSize[ulEpType]) + { + // + // Yes - remember the new maximum packet size. + // + psEPInfo[ulEpIndex - 1].ulSize[ulEpType] = + 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(ulLoop = 0; ulLoop < ulNumInterfaces; ulLoop++) + { + // + // Get the next interface descriptor in the config descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, + ulLoop, + USB_DESC_ANY, + &ulSection); + + // + // 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. + // + ulNumEndpoints = (unsigned long)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in this interface and configure + // it appropriately. + // + for(ulCount = 0; ulCount < ulNumEndpoints; ulCount++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ulCount); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ulEpIndex, &ulMaxPkt, + &ulFlags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ulEpIndex) + { + return(false); + } + + // + // Include any additional flags that the user wants. + // + if((ulFlags & (USB_EP_DEV_IN | USB_EP_DEV_OUT)) == + USB_EP_DEV_IN) + { + // + // This is an IN endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sIn[ulEpIndex - 1].usEPFlags); + } + else + { + // + // This is an OUT endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sOut[ulEpIndex - 1].usEPFlags); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + INDEX_TO_USB_EP(ulEpIndex), + ulMaxPkt, ulFlags); + } + } + } + } + + // + // 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. + // + ulCount = MAX_PACKET_SIZE_EP0; + for(ulLoop = 1; ulLoop < NUM_USB_EP; ulLoop++) + { + // + // Configure the IN endpoint at this index if it is referred to + // anywhere. + // + if(psEPInfo[ulLoop - 1].ulSize[EP_INFO_IN]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ulMaxPkt = GetEndpointFIFOSize( + psEPInfo[ulLoop - 1].ulSize[EP_INFO_IN], + &(psFIFOConfig->sIn[ulLoop - 1]), + &ulBytesUsed); + + // + // If we are told that 0 bytes of FIFO will be used, this implies + // that there is an error in psFIFOConfig or the descriptor + // somewhere so return an error indicator to the caller. + // + if(!ulBytesUsed) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(ulLoop), ulCount, + ulMaxPkt, USB_EP_DEV_IN); + ulCount += ulBytesUsed; + } + + // + // Configure the OUT endpoint at this index. + // + if(psEPInfo[ulLoop - 1].ulSize[EP_INFO_OUT]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ulMaxPkt = GetEndpointFIFOSize( + psEPInfo[ulLoop - 1].ulSize[EP_INFO_OUT], + &(psFIFOConfig->sOut[ulLoop - 1]), + &ulBytesUsed); + + // + // If we are told that 0 bytes of FIFO will be used, this implies + // that there is an error in psFIFOConfig or the descriptor + // somewhere so return an error indicator to the caller. + // + if(!ulBytesUsed) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(ulLoop), ulCount, + ulMaxPkt, USB_EP_DEV_OUT); + ulCount += ulBytesUsed; + } + + } + + // + // 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 ucInterfaceNum 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 ucAlternateSetting 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. +// +//***************************************************************************** +tBoolean +USBDeviceConfigAlternate(tDeviceInstance *psDevInst, const tConfigHeader *psConfig, + unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting) +{ + unsigned long ulNumInterfaces; + unsigned long ulNumEndpoints; + unsigned long ulLoop; + unsigned long ulCount; + unsigned long ulMaxPkt; + unsigned long ulFlags; + unsigned long ulSection; + unsigned long ulEpIndex; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + tFIFOConfig const *psFIFOConfig; + + // + // How many interfaces are included in the descriptor? + // + ulNumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + psFIFOConfig = psDevInst->psInfo->psFIFOConfig; + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + + for(ulLoop = 0; ulLoop < ulNumInterfaces; ulLoop++) + { + // + // Get the next interface descriptor in the config descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ulLoop, USB_DESC_ANY, + &ulSection); + + // + // Is this the default interface (bAlternateSetting set to 0)? + // + if(psInterface && + (psInterface->bInterfaceNumber == ucInterfaceNum) && + (psInterface->bAlternateSetting == ucAlternateSetting)) + { + // + // 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? + // + ulNumEndpoints = (unsigned long)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in turn. + // + for(ulCount = 0; ulCount < ulNumEndpoints; ulCount++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ulCount); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ulEpIndex, &ulMaxPkt, + &ulFlags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ulEpIndex) + { + return(false); + } + + // + // Include any additional flags that the user wants. + // + if((ulFlags & (USB_EP_DEV_IN | USB_EP_DEV_OUT)) == + USB_EP_DEV_IN) + { + // + // This is an IN endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sIn[ulEpIndex - 1].usEPFlags); + } + else + { + // + // This is an OUT endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sOut[ulEpIndex - 1].usEPFlags); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + INDEX_TO_USB_EP(ulEpIndex), + ulMaxPkt, ulFlags); + } + } + + // + // 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..8060eca --- /dev/null +++ b/usblib/device/usbddfu-rt.c @@ -0,0 +1,589 @@ +//***************************************************************************** +// +// usbddfu-rt.c - USB Device Firmware Update runtime device class driver. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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_map.h" +#include "usblib/usblib.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 unsigned char g_pDFUDeviceDescriptor[] = +{ + 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 indentifier. + 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. +// +//***************************************************************************** +unsigned char g_pDFUConfigDescriptor[] = +{ + // + // 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. +// +//***************************************************************************** +unsigned char g_pDFUInterface[] = +{ + // + // 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. +// +//***************************************************************************** +unsigned char g_pDFUFunctionalDesc[] = +{ + // + // 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_pDFUConfigDescriptor), + g_pDFUConfigDescriptor +}; + +const tConfigSection g_sDFUInterfaceSection = +{ + sizeof(g_pDFUInterface), + g_pDFUInterface +}; + +const tConfigSection g_sDFUFunctionalDescSection = +{ + sizeof(g_pDFUFunctionalDesc), + g_pDFUFunctionalDesc +}; + +//***************************************************************************** +// +// 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(tConfigSection *)) + +//***************************************************************************** +// +// 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_pDFUConfigDescriptors[] = +{ + &g_sDFUConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleRequest(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB DFU devices. +// +//***************************************************************************** +tDeviceInfo g_sDFUDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + HandleGetDescriptor, // GetDescriptor + HandleRequest, // RequestHandler + 0, // InterfaceChange + 0, // ConfigChange + 0, // DataReceived + 0, // DataSentCallback + 0, // ResetHandler + 0, // SuspendHandler + 0, // ResumeHandler + 0, // DisconnectHandler + 0, // EndpointHandler + HandleDevice, // Device handler. + }, + 0, // Device descriptor (unused - composite device) + g_pDFUConfigDescriptors, + 0, // Completed during USBDDFUCompositeInit(). + 0, // Completed during USBDDFUCompositeInit(). + &g_sUSBDefaultFIFOConfig, + 0 +}; + +//***************************************************************************** +// +// 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 *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tDFUInstance *psInst; + unsigned char *pucData; + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDDFUDevice *)pvInstance)->psPrivateDFUData; + + // + // Get a byte pointer to the data. + // + pucData = (unsigned char *)pvRequestData; + + // + // Which request event have we been passed? + // + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the interface number. + // + psInst->ucInterface = pucData[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 pvInstance is the instance data for this request. +// \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulSize; + + ASSERT(pvInstance != 0); + + // + // Which type of class descriptor are we being asked for? We only support + // 1 type - the DFU functional descriptor. + // + if(((pUSBRequest->wValue >> 8) == USB_DFU_FUNC_DESCRIPTOR_TYPE) && + ((pUSBRequest->wValue & 0xFF) == 0)) + { + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if((unsigned short)g_pDFUFunctionalDesc[0] > pUSBRequest->wLength) + { + ulSize = (unsigned long)pUSBRequest->wLength; + } + else + { + ulSize = (unsigned long)g_pDFUFunctionalDesc[0]; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, g_pDFUFunctionalDesc, ulSize); + } + 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 pvInstance is the instance data for this HID device. +// \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + tDFUInstance *psInst; + const tUSBDDFUDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateDFUData; + + // + // Make sure the request was for this interface. + // + if(pUSBRequest->wIndex != psInst->ucInterface) + { + return; + } + + // + // Determine the type of request. + // + switch(pUSBRequest->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 ulIndex is the index of the USB controller which is to be +//! initialized for DFU runtime device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the DFU device. +//! +//! \return Returns NULL on failure or the \e psDevice pointer on success. +// +//***************************************************************************** +void * +USBDDFUCompositeInit(unsigned long ulIndex, const tUSBDDFUDevice *psDevice) +{ + tDFUInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->psPrivateDFUData); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateDFUData; + psInst->psDevInfo = &g_sDFUDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->bConnected = false; + psInst->ucInterface = 0; + + // + // Tag the device information with our device data structure pointer. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the DFU device. +//! +//! \param pvInstance 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 pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDDFUCompositeTerm(void *pvInstance) +{ + tDFUInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDDFUDevice *)pvInstance)->psPrivateDFUData; + + // + // Terminate the requested instance. + // + USBDCDTerm(0); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)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))(*(unsigned long *)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..561d1a9 --- /dev/null +++ b/usblib/device/usbddfu-rt.h @@ -0,0 +1,163 @@ +//***************************************************************************** +// +// usbddfu-rt.h - Definitions used by runtime DFU class devices. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! 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 ulSize parameter. +// +// For reference this is sizeof(g_pDFUInterface) + sizeof(g_pDFUFunctionalDesc) +// +//***************************************************************************** +#define COMPOSITE_DDFU_SIZE (9 + 9) + +//***************************************************************************** +// +//! 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 pointed to by the +// psPrivateDFUData field in the tUSBDDFUDevice structure passed in the +// USBDDFUCompositeInit() function. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + unsigned char ucInterface; + tBoolean 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. + // + tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the pfnCallback function. + // + void *pvCBData; + + // + //! A pointer to private instance data for this device instance. 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 *psPrivateDFUData; +} +tUSBDDFUDevice; + +//***************************************************************************** +// +// Device information structure required to construct the composite device. +// +//***************************************************************************** +extern tDeviceInfo g_sDFUDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDDFUCompositeInit(unsigned long ulIndex, + const tUSBDDFUDevice *psDevice); +extern void USBDDFUCompositeTerm(void *pvInstance); +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..e8aa64e --- /dev/null +++ b/usblib/device/usbdenum.c @@ -0,0 +1,2900 @@ +//***************************************************************************** +// +// usbenum.c - Enumeration code to handle all endpoint zero traffic. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.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/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_eUSBMode; + +//***************************************************************************** +// +// Local functions prototypes. +// +//***************************************************************************** +static void USBDGetStatus(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDClearFeature(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetFeature(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetAddress(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDGetConfiguration(void *pvInstance, + tUSBRequest *pUSBRequest); +static void USBDSetConfiguration(void *pvInstance, + tUSBRequest *pUSBRequest); +static void USBDGetInterface(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetInterface(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSyncFrame(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDEP0StateTx(unsigned long ulIndex); +static void USBDEP0StateTxConfig(unsigned long ulIndex); +static long USBDStringIndexFromRequest(unsigned short usLang, + unsigned short usIndex); + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The default USB endpoint FIFO configuration structure. This structure +//! contains definitions to set all USB FIFOs into single buffered mode with +//! no DMA use. Each endpoint's FIFO is sized to hold the largest maximum +//! packet size for any interface alternate setting in the current +//! configuration descriptor. A pointer to this structure may be passed in the +//! psFIFOConfig field of the tDeviceInfo structure passed to USBCDCInit if the +//! application does not require any special handling of the USB controller +//! FIFO. +// +//***************************************************************************** +const tFIFOConfig g_sUSBDefaultFIFOConfig = +{ + { + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 } + }, + { + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 } + }, +}; + +//***************************************************************************** +// +// Indices into the ucHalt 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.ulDevAddress 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 buffer for reading data coming into EP0 +// +//***************************************************************************** +static unsigned char g_pucDataBufferIn[EP0_MAX_PACKET_SIZE]; + +tDeviceInstance g_psUSBDevice[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 the USB library device control driver for a given hardware +//! controller. +//! +//! \param ulIndex 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. +//! +//! This function must be called by any application which wishes to operate +//! as a USB device. It 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 device information structure passed in \e psDevice must remain +//! unchanged between this call and any matching call to USBDCDTerm() since +//! it is not copied by the USB library. +//! +//! The USBStackModeSet() function can be called with USB_MODE_FORCE_DEVICE 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. +//! +//! \note Forcing of the USB controller mode feature is not available on all +//! Stellaris microcontrollers. Consult the data sheet for the microcontroller +//! that the application is using to determine if this feature is available. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDInit(unsigned long ulIndex, tDeviceInfo *psDevice) +{ + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice != 0); + + // + // Should not call this if the stack is in host mode. + // + ASSERT(g_eUSBMode != USB_MODE_HOST); + ASSERT(g_eUSBMode != USB_MODE_FORCE_HOST); + + // + // Initialize a couple of fields in the device state structure. + // + g_psUSBDevice[0].ulConfiguration = DEFAULT_CONFIG_ID; + g_psUSBDevice[0].ulDefaultConfiguration = DEFAULT_CONFIG_ID; + + // + // Remember the device information pointer. + // + g_psUSBDevice[0].psInfo = psDevice; + g_psUSBDevice[0].pvInstance = psDevice->pvInstance; + g_psUSBDevice[0].eEP0State = USB_STATE_IDLE; + + // + // Default to device mode if no mode was set. + // + if(g_eUSBMode == USB_MODE_NONE) + { + g_eUSBMode = USB_MODE_DEVICE; + } + + // + // Only do hardware update if the stack is in not in OTG mode. + // + if(g_eUSBMode != USB_MODE_OTG) + { + // + // 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(); + + // + // If the application is requesting OTG or DEVICE mode then set the mode + // detection to OTG. If the mode was device then the rest of the library + // should make sure that no OTG signaling actually occurs. + // + if((g_eUSBMode == USB_MODE_DEVICE) || (g_eUSBMode == USB_MODE_OTG)) + { + // + // Switch to OTG mode to detect VBUS changes. + // + MAP_USBOTGMode(USB0_BASE); + } + else + { + // + // Force device mode on devices that support forcing mode. + // + MAP_USBDevMode(USB0_BASE); + } + + // + // In all other cases, set the mode to device this function should not + // be called in OTG mode. + // + g_eUSBMode = USB_MODE_DEVICE; + } + + // + // Initialize the USB tick module. + // + InternalUSBTickInit(); + + // + // Get a pointer to the default configuration descriptor. + // + psHdr = psDevice->ppConfigDescriptors[ + g_psUSBDevice[0].ulDefaultConfiguration - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pucData); + + // + // Default to the state where remote wake up is disabled. + // + g_psUSBDevice[0].ucStatus = 0; + g_psUSBDevice[0].bRemoteWakeup = false; + + // + // Determine the self- or bus-powered state based on the flags the + // user provided. + // + g_psUSBDevice[0].bPwrSrcSet = false; + + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == USB_CONF_ATTR_SELF_PWR) + { + g_psUSBDevice[0].ucStatus |= USB_STATUS_SELF_PWR; + } + else + { + g_psUSBDevice[0].ucStatus &= ~USB_STATUS_SELF_PWR; + } + + // + // Only do hardware update if the stack is not in OTG mode. + // + if(g_eUSBMode != USB_MODE_OTG) + { + // + // 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); + + // + // Make sure we disconnect from the host for a while. This ensures + // that the host will enumerate us even if we were previously + // connected to the bus. + // + MAP_USBDevDisconnect(USB0_BASE); + + // + // Wait about 100mS. + // + OS_DELAY(MAP_SysCtlClockGet() / 30); + + // + // Attach the device using the soft connect. + // + MAP_USBDevConnect(USB0_BASE); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } +} + +//***************************************************************************** +// +//! Free the USB library device control driver for a given hardware controller. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + + // + // Disable the USB interrupts. + // + OS_INT_DISABLE(INT_USB0); + + // + // Reset the tick handlers so that they can be reconfigured when and if + // USBDCDInit() is called. + // + InternalUSBTickReset(); + + g_psUSBDevice[0].psInfo = (tDeviceInfo *)0; + g_psUSBDevice[0].pvInstance = 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 ulIndex is the index of the USB controller from which the data +//! is being requested. +//! \param pucData is a pointer to the buffer to fill with data from the USB +//! host. +//! \param ulSize 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, +//! tDeviceInfo.sCallbacks.pfnDataReceived should 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(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize) +{ + ASSERT(ulIndex == 0); + + // + // Enter the RX state on end point 0. + // + g_psUSBDevice[0].eEP0State = USB_STATE_RX; + + // + // Save the pointer to the data. + // + g_psUSBDevice[0].pEP0Data = pucData; + + // + // Location to save the current number of bytes received. + // + g_psUSBDevice[0].ulOUTDataSize = ulSize; + + // + // Bytes remaining to be received. + // + g_psUSBDevice[0].ulEP0DataRemain = ulSize; +} + +//***************************************************************************** +// +//! This function requests transfer of data to the host on endpoint zero. +//! +//! \param ulIndex is the index of the USB controller which is to be used to +//! send the data. +//! \param pucData is a pointer to the buffer to send via endpoint zero. +//! \param ulSize 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, +//! tDeviceInfo.sCallbacks.pfnDataSent should contain a valid function +//! pointer. This callback could be used to free up the buffer passed into +//! this function in the \e pucData parameter. The contents of the \e pucData +//! buffer must remain unchanged until the pfnDataSent callback is +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSendDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize) +{ + ASSERT(ulIndex == 0); + + // + // Return the externally provided device descriptor. + // + g_psUSBDevice[0].pEP0Data = pucData; + + // + // The size of the device descriptor is in the first byte. + // + g_psUSBDevice[0].ulEP0DataRemain = ulSize; + + // + // Save the total size of the data sent. + // + g_psUSBDevice[0].ulOUTDataSize = ulSize; + + // + // Now in the transmit data state. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +//! This function sets the default configuration for the device. +//! +//! \param ulIndex is the index of the USB controller whose default +//! configuration is to be set. +//! \param ulDefaultConfig 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 +//! ppConfigDescriptors array for the device. +//! +//! If this function is not called, the USB library will return the first +//! configuration in the ppConfigDescriptors 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, ppConfigDescriptors, +//! 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(unsigned long ulIndex, + unsigned long ulDefaultConfig) +{ + ASSERT(ulIndex == 0); + + g_psUSBDevice[0].ulDefaultConfiguration = ulDefaultConfig; +} + +//***************************************************************************** +// +//! This function generates a stall condition on endpoint zero. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // Stall the endpoint in question. + // + MAP_USBDevEndpointStall(USB0_BASE, USB_EP_0, USB_EP_DEV_OUT); + + // + // Enter the stalled state. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STALL; +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the library. +//! +//! \param ulIndex is the index of the USB controller whose device power +//! status is being reported. +//! \param ucPower 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(unsigned long ulIndex, unsigned char ucPower) +{ + // + // Check for valid parameters. + // + ASSERT((ucPower == USB_STATUS_BUS_PWR) || + (ucPower == USB_STATUS_SELF_PWR)); + ASSERT(ulIndex == 0); + + // + // Update the device status with the new power status flag. + // + g_psUSBDevice[0].bPwrSrcSet = true; + g_psUSBDevice[0].ucStatus &= ~USB_STATUS_PWR_M; + g_psUSBDevice[0].ucStatus |= ucPower; +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param ulIndex 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. +// +//***************************************************************************** +tBoolean +USBDCDRemoteWakeupRequest(unsigned long ulIndex) +{ + // + // Check for parameter validity. + // + ASSERT(ulIndex == 0); + + // + // Is remote wake up signaling currently enabled? + // + if(g_psUSBDevice[0].ucStatus & 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_psUSBDevice[0].bRemoteWakeup) + { + // + // No - we are not in the middle of a wake up sequence so start + // one here. + // + g_psUSBDevice[0].ucRemoteWakeupCount = 0; + g_psUSBDevice[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(tDeviceInstance *psDevInst) +{ + if(g_psUSBDevice[0].bRemoteWakeup) + { + // + // Increment the millisecond counter we use to time the resume + // signaling. + // + g_psUSBDevice[0].ucRemoteWakeupCount++; + + // + // Have we reached the 10mS mark? If so, we need to turn the signaling + // off again. + // + if(g_psUSBDevice[0].ucRemoteWakeupCount == 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 doesn't 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_psUSBDevice[0].ucRemoteWakeupCount == REMOTE_WAKEUP_READY_MS) + { + // + // We are now finished with the remote wake up signaling. + // + g_psUSBDevice[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_psUSBDevice[0].psInfo->sCallbacks.pfnResumeHandler) + { + g_psUSBDevice[0].psInfo->sCallbacks.pfnResumeHandler( + g_psUSBDevice[0].pvInstance); + } + } + } +} + +//***************************************************************************** +// +// 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(unsigned long ulIndex) +{ + unsigned long ulSize; + tUSBRequest *pRequest; + + // + // Cast the buffer to a request structure. + // + pRequest = (tUSBRequest *)g_pucDataBufferIn; + + // + // Set the buffer size. + // + ulSize = EP0_MAX_PACKET_SIZE; + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, + USB_EP_0, + g_pucDataBufferIn, + &ulSize); + + // + // If there was a null setup packet then just return. + // + if(!ulSize) + { + return; + } + + // + // See if this is a standard request or not. + // + if((pRequest->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_psUSBDevice[0].psInfo->sCallbacks.pfnRequestHandler) + { + g_psUSBDevice[0].psInfo->sCallbacks.pfnRequestHandler( + g_psUSBDevice[0].pvInstance, pRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } + else + { + // + // Assure that the jump table is not out of bounds. + // + if((pRequest->bRequest < + (sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) && + (g_psUSBDStdRequests[pRequest->bRequest] != 0)) + { + // + // Jump table to the appropriate handler. + // + g_psUSBDStdRequests[pRequest->bRequest](&g_psUSBDevice[0], + pRequest); + } + 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 USB_STATE_IDLE, \b USB_STATE_TX, \b USB_STATE_RX, +// \b USB_STATE_STALL, and \b USB_STATE_STATUS. In the \b USB_STATE_IDLE +// 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 USB_STATE_TX, \b USB_STATE_RX, or \b USB_STATE_STALL depending on the +// command. If the controller enters the \b USB_STATE_TX or \b USB_STATE_RX +// then once all data has been sent or received, it must pass through the +// \b USB_STATE_STATUS state to allow the host to acknowledge completion of +// the request. The \b USB_STATE_STALL is entered from \b USB_STATE_IDLE in +// the event that the USB request was not valid. Both the \b USB_STATE_STALL +// and \b USB_STATE_STATUS are transitional states that return to the +// \b USB_STATE_IDLE state. +// +// \return None. +// +// USB_STATE_IDLE -*--> USB_STATE_TX -*-> USB_STATE_STATUS -*->USB_STATE_IDLE +// | | | +// |--> USB_STATE_RX - | +// | | +// |--> USB_STATE_STALL ---------->--------- +// +// ---------------------------------------------------------------- +// | Current State | State 0 | State 1 | +// | --------------------|-------------------|---------------------- +// | USB_STATE_IDLE | USB_STATE_TX/RX | USB_STATE_STALL | +// | USB_STATE_TX | USB_STATE_STATUS | | +// | USB_STATE_RX | USB_STATE_STATUS | | +// | USB_STATE_STATUS | USB_STATE_IDLE | | +// | USB_STATE_STALL | USB_STATE_IDLE | | +// ---------------------------------------------------------------- +// +//***************************************************************************** +void +USBDeviceEnumHandler(tDeviceInstance *pDevInstance) +{ + unsigned long ulEPStatus; + + // + // Get the end point 0 status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + switch(pDevInstance->eEP0State) + { + // + // Handle the status state, this is a transitory state from + // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE. + // + case USB_STATE_STATUS: + { + // + // Just go back to the idle state. + // + pDevInstance->eEP0State = USB_STATE_IDLE; + + // + // If there is a pending address change then set the address. + // + if(pDevInstance->ulDevAddress & DEV_ADDR_PENDING) + { + // + // Clear the pending address change and set the address. + // + pDevInstance->ulDevAddress &= ~DEV_ADDR_PENDING; + MAP_USBDevAddrSet(USB0_BASE, pDevInstance->ulDevAddress); + } + + // + // If a new packet is already pending, we need to read it + // and handle whatever request it contains. + // + if(ulEPStatus & 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 USB_STATE_IDLE: + { + // + // Is there a packet waiting for us? + // + if(ulEPStatus & 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 USB_STATE_TX: + { + USBDEP0StateTx(0); + break; + } + + // + // We are still in the middle of sending the configuration descriptor + // so handle this in the EP0StateTxConfig() function. + // + case USB_STATE_TX_CONFIG: + { + USBDEP0StateTxConfig(0); + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint zero. + // + case USB_STATE_RX: + { + unsigned long ulDataSize; + + // + // Set the number of bytes to get out of this next packet. + // + if(pDevInstance->ulEP0DataRemain > EP0_MAX_PACKET_SIZE) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ulDataSize = EP0_MAX_PACKET_SIZE; + } + else + { + // + // There was space so send the remaining bytes. + // + ulDataSize = pDevInstance->ulEP0DataRemain; + } + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, pDevInstance->pEP0Data, + &ulDataSize); + + // + // 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->ulEP0DataRemain < EP0_MAX_PACKET_SIZE) + { + // + // 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); + + // + // Return to the idle state. + // + pDevInstance->eEP0State = USB_STATE_STATUS; + + // + // If there is a receive callback then call it. + // + if((pDevInstance->psInfo->sCallbacks.pfnDataReceived) && + (pDevInstance->ulOUTDataSize != 0)) + { + // + // Call the custom receive handler to handle the data + // that was received. + // + pDevInstance->psInfo->sCallbacks.pfnDataReceived( + pDevInstance->pvInstance, + pDevInstance->ulOUTDataSize); + + // + // Indicate that there is no longer any data being waited + // on. + // + pDevInstance->ulOUTDataSize = 0; + } + } + 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->pEP0Data += ulDataSize; + + // + // Decrement the number of bytes that are being waited on. + // + pDevInstance->ulEP0DataRemain -= ulDataSize; + + break; + } + // + // The device stalled endpoint zero so check if the stall needs to be + // cleared once it has been successfully sent. + // + case USB_STATE_STALL: + { + // + // If we sent a stall then acknowledge this interrupt. + // + if(ulEPStatus & 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->eEP0State = USB_STATE_IDLE; + + } + break; + } + // + // Halt on an unknown state, but only in DEBUG mode builds. + // + default: + { + ASSERT(0); + } + } +} + +//***************************************************************************** +// +// 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(tDeviceInstance *pDevInstance) +{ + unsigned long ulLoop; + + // + // Disable remote wake up signaling (as per USB 2.0 spec 9.1.1.6). + // + pDevInstance->ucStatus &= ~USB_STATUS_REMOTE_WAKE; + pDevInstance->bRemoteWakeup = false; + + // + // Call the device dependent code to indicate a bus reset has occurred. + // + if(pDevInstance->psInfo->sCallbacks.pfnResetHandler) + { + pDevInstance->psInfo->sCallbacks.pfnResetHandler( + pDevInstance->pvInstance); + } + + // + // Reset the default configuration identifier and alternate function + // selections. + // + pDevInstance->ulConfiguration = pDevInstance->ulDefaultConfiguration; + + for(ulLoop = 0; ulLoop < USB_MAX_INTERFACES_PER_DEVICE; ulLoop++) + { + pDevInstance->pucAltSetting[ulLoop] = (unsigned char)0; + } +} + +//***************************************************************************** +// +// This function handles the GET_STATUS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest 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 pUSBRequest 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 *pUSBRequest) +{ + unsigned short usData; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)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(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This was a Device Status request. + // + case USB_RTYPE_DEVICE: + { + // + // Return the current status for the device. + // + usData = (unsigned short)psUSBControl->ucStatus; + + break; + } + + // + // This was a Interface status request. + // + case USB_RTYPE_INTERFACE: + { + // + // Interface status always returns 0. + // + usData = (unsigned short)0; + + break; + } + + // + // This was an endpoint status request. + // + case USB_RTYPE_ENDPOINT: + { + unsigned short usIndex; + unsigned long ulDir; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Check if this was a valid endpoint request. + // + if((usIndex == 0) || (usIndex >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + return; + } + else + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->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. + // + usData = + (unsigned short)psUSBControl->ucHalt[ulDir][usIndex - 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->ulEP0DataRemain = 2; + psUSBControl->pEP0Data = (unsigned char *)&usData; + + // + // Send the response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the CLEAR_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the options for the Clear Feature USB request. +// +// This function handles device or endpoint clear feature requests. The +// \e pUSBRequest 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 *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)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(pUSBRequest->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 & pUSBRequest->wValue) + { + // + // Clear the remote wake up state. + // + psUSBControl->ucStatus &= ~USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a clear feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + unsigned long ulDir; + unsigned short usIndex; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint. + // + if((usIndex == 0) || (usIndex > NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the halt feature is supported. + // + if(USB_FEATURE_EP_HALT == pUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->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->ucHalt[ulDir][usIndex - 1] = 0; + + if(ulDir == HALT_EP_IN) + { + MAP_USBDevEndpointStallClear(USB0_BASE, + INDEX_TO_USB_EP(usIndex), + USB_EP_DEV_IN); + } + else + { + MAP_USBDevEndpointStallClear(USB0_BASE, + INDEX_TO_USB_EP(usIndex), + 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 pUSBRequest holds the feature in the wValue field of the USB +// request. +// +// This function handles device or endpoint set feature requests. The +// \e pUSBRequest 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 *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)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(pUSBRequest->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 & pUSBRequest->wValue) + { + // + // Set the remote wake up state. + // + psUSBControl->ucStatus |= USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a set feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + unsigned short usIndex; + unsigned long ulDir; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint? + // + if((usIndex == 0) || (usIndex >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the Halt feature can be set. + // + if(USB_FEATURE_EP_HALT == pUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->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->ucHalt[ulDir][usIndex - 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 pUSBRequest 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 *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)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->ulDevAddress = pUSBRequest->wValue | DEV_ADDR_PENDING; + + // + // Transition directly to the status state since there is no data phase + // for this request. + // + psUSBControl->eEP0State = USB_STATE_STATUS; +} + +//***************************************************************************** +// +// This function handles the GET_DESCRIPTOR standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest 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->pDeviceDescriptor 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->sCallbacks.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 *pUSBRequest) +{ + tBoolean bConfig; + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // 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(pUSBRequest->wValue >> 8) + { + // + // This request was for a device descriptor. + // + case USB_DTYPE_DEVICE: + { + // + // Return the externally provided device descriptor. + // + psUSBControl->pEP0Data = + (unsigned char *)psDevice->pDeviceDescriptor; + + // + // The size of the device descriptor is in the first byte. + // + psUSBControl->ulEP0DataRemain = psDevice->pDeviceDescriptor[0]; + + break; + } + + // + // This request was for a configuration descriptor. + // + case USB_DTYPE_CONFIGURATION: + { + const tConfigHeader *psConfig; + const tDeviceDescriptor *psDeviceDesc; + unsigned char ucIndex; + + // + // Which configuration are we being asked for? + // + ucIndex = (unsigned char)(pUSBRequest->wValue & 0xFF); + + // + // Is this valid? + // + psDeviceDesc = + (const tDeviceDescriptor *)psDevice->pDeviceDescriptor; + + if(ucIndex >= psDeviceDesc->bNumConfigurations) + { + // + // This is an invalid configuration index. Stall EP0 to + // indicate a request error. + // + USBDCDStallEP0(0); + psUSBControl->pEP0Data = 0; + psUSBControl->ulEP0DataRemain = 0; + } + else + { + // + // Return the externally specified configuration descriptor. + // + psConfig = psDevice->ppConfigDescriptors[ucIndex]; + + // + // Start by sending data from the beginning of the first + // descriptor. + // + psUSBControl->ucConfigSection = 0; + psUSBControl->ucSectionOffset = 0; + psUSBControl->pEP0Data = (unsigned char *) + psConfig->psSections[0]->pucData; + + // + // Determine the total size of the configuration descriptor + // by counting the sizes of the sections comprising it. + // + psUSBControl->ulEP0DataRemain = + USBDCDConfigDescGetSize(psConfig); + + // + // Remember that we need to send the configuration descriptor + // and which descriptor we need to send. + // + psUSBControl->ucConfigIndex = ucIndex; + + bConfig = true; + } + break; + } + + // + // This request was for a string descriptor. + // + case USB_DTYPE_STRING: + { + long lIndex; + + // + // Determine the correct descriptor index based on the requested + // language ID and index. + // + lIndex = USBDStringIndexFromRequest(pUSBRequest->wIndex, + pUSBRequest->wValue & 0xFF); + + // + // If the mapping function returned -1 then stall the request to + // indicate that the request was not valid. + // + if(lIndex == -1) + { + USBDCDStallEP0(0); + break; + } + + // + // Return the externally specified configuration descriptor. + // + psUSBControl->pEP0Data = + (unsigned char *)psDevice->ppStringDescriptors[lIndex]; + + // + // The total size of a string descriptor is in byte 0. + // + psUSBControl->ulEP0DataRemain = + psDevice->ppStringDescriptors[lIndex][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->sCallbacks.pfnGetDescriptor) + { + psDevice->sCallbacks.pfnGetDescriptor(psUSBControl->pvInstance, + pUSBRequest); + } + 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->pEP0Data) + { + // + // If there is more data to send than is requested then just + // send the requested amount of data. + // + if(psUSBControl->ulEP0DataRemain > pUSBRequest->wLength) + { + psUSBControl->ulEP0DataRemain = pUSBRequest->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 usLang is the requested string language ID. +// \param usIndex 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 long +USBDStringIndexFromRequest(unsigned short usLang, unsigned short usIndex) +{ + tString0Descriptor *pLang; + unsigned long ulNumLangs; + unsigned long ulNumStringsPerLang; + unsigned long ulLoop; + + // + // Make sure we have a string table at all. + // + if((g_psUSBDevice[0].psInfo == 0) || + (g_psUSBDevice[0].psInfo->ppStringDescriptors == 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(usIndex == 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). + // + ulNumLangs = (g_psUSBDevice[0].psInfo->ppStringDescriptors[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. + // + ulNumStringsPerLang = ((g_psUSBDevice[0].psInfo->ulNumStringDescriptors - 1) / + ulNumLangs); + + // + // 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 + (ulNumStringsPerLang * ulNumLangs)) != + g_psUSBDevice[0].psInfo->ulNumStringDescriptors) + { + 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_psUSBDevice[0].psInfo->ppStringDescriptors[0]); + + // + // Look through the supported languages looking for the one we were asked + // for. + // + for(ulLoop = 0; ulLoop < ulNumLangs; ulLoop++) + { + // + // Have we found the requested language? + // + if(pLang->wLANGID[ulLoop] == usLang) + { + // + // Yes - calculate the index of the descriptor to send. + // + return((ulNumStringsPerLang * ulLoop) + usIndex); + } + } + + // + // 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 pUSBRequest 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 *pUSBRequest) +{ + // + // 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 pUSBRequest 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 *pUSBRequest) +{ + unsigned char ucValue; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)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->ulDevAddress & DEV_ADDR_PENDING) + { + ucValue = 0; + } + else + { + ucValue = (unsigned char)psUSBControl->ulConfiguration; + } + + psUSBControl->ulEP0DataRemain = 1; + psUSBControl->pEP0Data = &ucValue; + + // + // 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 pUSBRequest 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 pUSBRequest. 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->sCallbacks, +// it will be called so that the application can respond to a change in +// configuration. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetConfiguration(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // 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(pUSBRequest->wValue > psUSBControl->psInfo->pDeviceDescriptor[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->ulConfiguration = pUSBRequest->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->ulConfiguration) + { + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Get a pointer to the configuration descriptor. This will always + // be the first section in the current configuration. + // + psHdr = psDevice->ppConfigDescriptors[pUSBRequest->wValue - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pucData); + + // + // 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->ucStatus |= USB_STATUS_SELF_PWR; + } + else + { + psUSBControl->ucStatus &= ~USB_STATUS_SELF_PWR; + } + } + + // + // Configure endpoints for the new configuration. + // + USBDeviceConfig(psUSBControl, + psDevice->ppConfigDescriptors[pUSBRequest->wValue - 1]); + } + + // + // If there is a configuration change callback then call it. + // + if(psDevice->sCallbacks.pfnConfigChange) + { + psDevice->sCallbacks.pfnConfigChange( + psUSBControl->pvInstance, psUSBControl->ulConfiguration); + } + } +} + +//***************************************************************************** +// +// This function handles the GET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest 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 *pUSBRequest) +{ + unsigned char ucValue; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)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->ulDevAddress & DEV_ADDR_PENDING) + { + ucValue = (unsigned char)0; + } + else + { + // + // Is the interface number valid? + // + if(pUSBRequest->wIndex < USB_MAX_INTERFACES_PER_DEVICE) + { + // + // Read the current alternate setting for the required interface. + // + ucValue = psUSBControl->pucAltSetting[pUSBRequest->wIndex]; + } + else + { + // + // An invalid interface number was specified. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the single byte response. + // + psUSBControl->ulEP0DataRemain = 1; + psUSBControl->pEP0Data = &ucValue; + + // + // 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 pUSBRequest 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->sCallbacks 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 *pUSBRequest) +{ + const tConfigHeader *psConfig; + tInterfaceDescriptor *psInterface; + unsigned long ulLoop; + unsigned long ulSection; + unsigned long ulNumInterfaces; + unsigned char ucInterface; + tBoolean bRetcode; + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // 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->ppConfigDescriptors[psUSBControl->ulConfiguration - 1]; + + // + // How many interfaces are included in the descriptor? + // + ulNumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + for(ulLoop = 0; ulLoop < ulNumInterfaces; ulLoop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ulLoop, USB_DESC_ANY, + &ulSection); + + // + // Is this the required interface with the correct alternate setting? + // + if(psInterface && + (psInterface->bInterfaceNumber == pUSBRequest->wIndex) && + (psInterface->bAlternateSetting == pUSBRequest->wValue)) + { + ucInterface = psInterface->bInterfaceNumber; + + // + // Make sure we don't write outside the bounds of the pucAltSetting + // array (in a debug build, anyway, since this indicates an error + // in the device descriptor). + // + ASSERT(ucInterface < USB_MAX_INTERFACES_PER_DEVICE); + + // + // This is the correct interface descriptor so save the + // setting. + // + psUSBControl->pucAltSetting[ucInterface] = + psInterface->bAlternateSetting; + + // + // Reconfigure the endpoints to match the requirements of the + // new alternate setting for the interface. + // + bRetcode = USBDeviceConfigAlternate(psUSBControl, psConfig, ucInterface, + psInterface->bAlternateSetting); + + // + // If there is a callback then notify the application of the + // change to the alternate interface. + // + if(bRetcode && psDevice->sCallbacks.pfnInterfaceChange) + { + psDevice->sCallbacks.pfnInterfaceChange( + psUSBControl->pvInstance, + pUSBRequest->wIndex, + pUSBRequest->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 pUSBRequest 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 *pUSBRequest) +{ + // + // 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 ulIndex is the index of the USB controller which is to be +// initialized. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTx(unsigned long ulIndex) +{ + unsigned long ulNumBytes; + unsigned char *pData; + + ASSERT(ulIndex == 0); + + // + // In the TX state on endpoint zero. + // + g_psUSBDevice[0].eEP0State = USB_STATE_TX; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = g_psUSBDevice[0].ulEP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ulNumBytes > EP0_MAX_PACKET_SIZE) + { + ulNumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pData = (unsigned char *)g_psUSBDevice[0].pEP0Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_psUSBDevice[0].ulEP0DataRemain -= ulNumBytes; + g_psUSBDevice[0].pEP0Data += ulNumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulNumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ulNumBytes == 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_psUSBDevice[0].eEP0State = USB_STATE_STATUS; + + // + // 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_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent) && + (g_psUSBDevice[0].ulOUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent( + g_psUSBDevice[0].pvInstance, g_psUSBDevice[0].ulOUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psUSBDevice[0].ulOUTDataSize = 0; + } + } +} + +//***************************************************************************** +// +// This internal function handles sending the configuration descriptor on +// endpoint zero. +// +// \param ulIndex is the index of the USB controller which is to be used. +// +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTxConfig(unsigned long ulIndex) +{ + unsigned long ulNumBytes; + unsigned long ulSecBytes; + unsigned long ulToSend; + unsigned char *pData; + tConfigDescriptor sConfDesc; + const tConfigHeader *psConfig; + const tConfigSection *psSection; + + ASSERT(ulIndex == 0); + + // + // In the TX state on endpoint zero. + // + g_psUSBDevice[0].eEP0State = USB_STATE_TX_CONFIG; + + // + // Find the current configuration descriptor definition. + // + psConfig = g_psUSBDevice[0].psInfo->ppConfigDescriptors[ + g_psUSBDevice[0].ucConfigIndex]; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = g_psUSBDevice[0].ulEP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ulNumBytes > EP0_MAX_PACKET_SIZE) + { + ulNumBytes = 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.ulEP0DataRemain. + // + if((g_psUSBDevice[0].ucSectionOffset == 0) && + (g_psUSBDevice[0].ucConfigSection == 0)) + { + // + // Copy the USB configuration descriptor from the beginning of the + // first section of the current configuration. + // + sConfDesc = *(tConfigDescriptor *)g_psUSBDevice[0].pEP0Data; + + // + // Update the total size. + // + sConfDesc.wTotalLength = (unsigned short)USBDCDConfigDescGetSize( + psConfig); + + // + // Write the descriptor to the USB FIFO. + // + ulToSend = (ulNumBytes < sizeof(tConfigDescriptor)) ? ulNumBytes : + sizeof(tConfigDescriptor); + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (unsigned char *)&sConfDesc, + ulToSend); + + // + // Did we reach the end of the first section? + // + if(psConfig->psSections[0]->usSize == ulToSend) + { + // + // Update our tracking indices to point to the start of the next + // section. + // + g_psUSBDevice[0].ucSectionOffset = 0; + g_psUSBDevice[0].ucConfigSection = 1; + } + else + { + // + // Note that we have sent the first few bytes of the descriptor. + // + g_psUSBDevice[0].ucSectionOffset = (unsigned char)ulToSend; + } + + // + // How many bytes do we have remaining to send on this iteration? + // + ulToSend = ulNumBytes - ulToSend; + } + else + { + // + // Set the number of bytes we still have to send on this call. + // + ulToSend = ulNumBytes; + } + + // + // Add the relevant number of bytes to the USB FIFO + // + while(ulToSend) + { + // + // Get a pointer to the current configuration section. + // + psSection = psConfig->psSections[g_psUSBDevice[0].ucConfigSection]; + + // + // Calculate bytes are available in the current configuration section. + // + ulSecBytes = (unsigned long)(psSection->usSize - + g_psUSBDevice[0].ucSectionOffset); + + // + // Save the pointer so that it can be passed to the + // USBEndpointDataPut() function. + // + pData = (unsigned char *)psSection->pucData + + g_psUSBDevice[0].ucSectionOffset; + + // + // Are there more bytes in this section that we still have to send? + // + if(ulSecBytes > ulToSend) + { + // + // Yes - send only the remaining bytes in the transfer. + // + ulSecBytes = ulToSend; + } + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulSecBytes); + + // + // Fix up our pointers for the next iteration. + // + ulToSend -= ulSecBytes; + g_psUSBDevice[0].ucSectionOffset += (unsigned char)ulSecBytes; + + // + // Have we reached the end of a section? + // + if(g_psUSBDevice[0].ucSectionOffset == psSection->usSize) + { + // + // Yes - move to the next one. + // + g_psUSBDevice[0].ucConfigSection++; + g_psUSBDevice[0].ucSectionOffset = 0; + } + } + + // + // Fix up the number of bytes remaining to be sent and the start pointer. + // + g_psUSBDevice[0].ulEP0DataRemain -= ulNumBytes; + + // + // If we ran out of bytes in the configuration section, bail and just + // send out what we have. + // + if(psConfig->ucNumSections <= g_psUSBDevice[0].ucConfigSection) + { + g_psUSBDevice[0].ulEP0DataRemain = 0; + } + + // + // If there is no more data don't keep looking or ucConfigSection might + // overrun the available space. + // + if(g_psUSBDevice[0].ulEP0DataRemain != 0) + { + pData =(unsigned char *) + psConfig->psSections[g_psUSBDevice[0].ucConfigSection]->pucData; + ulToSend = g_psUSBDevice[0].ucSectionOffset; + g_psUSBDevice[0].pEP0Data = (pData + ulToSend); + } + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ulNumBytes == 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_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent) && + (g_psUSBDevice[0].ulOUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent( + g_psUSBDevice[0].pvInstance, g_psUSBDevice[0].ulOUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psUSBDevice[0].ulOUTDataSize = 0; + } + + // + // Now go to the status state and wait for the transmit to complete. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STATUS; + } +} + +//***************************************************************************** +// +// The internal USB device interrupt handler. +// +// \param ulIndex is the USB controller associated with this interrupt. +// \param ulStatus 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(unsigned long ulIndex, unsigned long ulStatus) +{ + static unsigned long ulSOFDivide = 0; + tDeviceInfo *psInfo; + void *pvInstance; + + // + // If device initialization has not been performed then just disconnect + // from the USB bus and return from the handler. + // + if(g_psUSBDevice[0].psInfo == 0) + { + MAP_USBDevDisconnect(USB0_BASE); + return; + } + + psInfo = g_psUSBDevice[0].psInfo; + pvInstance = g_psUSBDevice[0].pvInstance; + + // + // Received a reset from the host. + // + if(ulStatus & USB_INTCTRL_RESET) + { + USBDeviceEnumResetHandler(&g_psUSBDevice[0]); + } + + // + // Suspend was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_SUSPEND) + { + // + // Call the SuspendHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnSuspendHandler) + { + psInfo->sCallbacks.pfnSuspendHandler(pvInstance); + } + } + + // + // Resume was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_RESUME) + { + // + // Call the ResumeHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnResumeHandler) + { + psInfo->sCallbacks.pfnResumeHandler(pvInstance); + } + } + + // + // USB device was disconnected. + // + if(ulStatus & USB_INTCTRL_DISCONNECT) + { + // + // Call the DisconnectHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnDisconnectHandler) + { + psInfo->sCallbacks.pfnDisconnectHandler(pvInstance); + } + } + + // + // Start of Frame was received. + // + if(ulStatus & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ulUSBSOFCount++; + + // + // Increment our SOF divider. + // + ulSOFDivide++; + + // + // Handle resume signaling if required. + // + USBDeviceResumeTickHandler(&g_psUSBDevice[0]); + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ulSOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ulSOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Get the controller interrupt status. + // + ulStatus = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ulStatus & USB_INTEP_0) + { + USBDeviceEnumHandler(&g_psUSBDevice[0]); + } + + // + // 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(psInfo->sCallbacks.pfnEndpointHandler) + { + psInfo->sCallbacks.pfnEndpointHandler(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdevice.h b/usblib/device/usbdevice.h new file mode 100644 index 0000000..edffb1d --- /dev/null +++ b/usblib/device/usbdevice.h @@ -0,0 +1,223 @@ +//***************************************************************************** +// +// usbdevice.h - types and definitions used during USB enumeration. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 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" + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! 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. + // + tCustomHandlers sCallbacks; + + // + //! A pointer to the device descriptor for this device. + // + const unsigned char *pDeviceDescriptor; + + // + //! 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, pDeviceDescriptor. + // + const tConfigHeader * const *ppConfigDescriptors; + + // + //! 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 unsigned char * const *ppStringDescriptors; + + // + //! The total number of descriptors provided in the ppStringDescriptors + //! array. + // + unsigned long ulNumStringDescriptors; + + // + //! A structure defining how the USB controller FIFO is to be partitioned + //! between the various endpoints. This member can be set to point to + //! g_sUSBDefaultFIFOConfig if the default FIFO configuration is acceptable + //! This configuration sets each endpoint FIFO to be single buffered and + //! sized to hold the maximum packet size for the endpoint. + // + const tFIFOConfig *psFIFOConfig; + + // + //! This value will be passed back to all call back functions so that + //! they have access to individual instance data based on the this pointer. + // + void *pvInstance; + + // + //! The generic device instance data for this device. This will be set + //! by the call to DCDInit() which is usually made when the applicaiton + //! calls the class specific initialization function. + // + tDeviceInstance *psDevice; +}; + +//***************************************************************************** +// +// The default USB endpoint FIFO configuration structure. This structure +// contains definitions to set all USB FIFOs into single buffered mode with +// no DMA use. Each endpoint's FIFO is sized to hold the largest maximum +// packet size for any interface alternate setting in the current config +// descriptor. A pointer to this structure may be passed in the psFIFOConfig +// field of the tDeviceInfo structure passed to USBCDCInit if the application +// does not require any special handling of the USB controller FIFO. +// +//***************************************************************************** +extern const tFIFOConfig g_sUSBDefaultFIFOConfig; + +//***************************************************************************** +// +// Public APIs offered by the USB library device control driver. +// +//***************************************************************************** +extern void USBDCDInit(unsigned long ulIndex, tDeviceInfo *psDevice); +extern void USBDCDTerm(unsigned long ulIndex); +extern void USBDCDStallEP0(unsigned long ulIndex); +extern void USBDCDRequestDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize); +extern void USBDCDSendDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize); +extern void USBDCDSetDefaultConfiguration(unsigned long ulIndex, + unsigned long ulDefaultConfig); +extern unsigned long USBDCDConfigDescGetSize(const tConfigHeader *psConfig); +extern unsigned long USBDCDConfigDescGetNum(const tConfigHeader *psConfig, + unsigned long ulType); +extern tDescriptorHeader *USBDCDConfigDescGet(const tConfigHeader *psConfig, + unsigned long ulType, + unsigned long ulIndex, + unsigned long *pulSection); +extern unsigned long + USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + unsigned char ucInterfaceNumber); +extern tInterfaceDescriptor * + USBDCDConfigGetInterface(const tConfigHeader *psConfig, + unsigned long ulIndex, unsigned long ulAltCfg, + unsigned long *pulSection); +extern tEndpointDescriptor * + USBDCDConfigGetInterfaceEndpoint(const tConfigHeader *psConfig, + unsigned long ulInterfaceNumber, + unsigned long ulAltCfg, + unsigned long ulIndex); +extern void USBDCDPowerStatusSet(unsigned long ulIndex, unsigned char ucPower); +extern tBoolean USBDCDRemoteWakeupRequest(unsigned long ulIndex); + +//***************************************************************************** +// +// Early releases of the USB library had the following function named +// incorrectly. This macro ensures that any code which used the previous name +// will still operate as expected. +// +//***************************************************************************** +#ifndef DEPRECATED +#define USBCDCConfigGetInterfaceEndpoint(a, b, c, d) \ + USBDCDConfigGetInterfaceEndpoint((a), (b), (c), (d)) +#endif + +//***************************************************************************** +// +// Device mode interrupt handler for controller index 0. +// +//***************************************************************************** +extern void USB0DeviceIntHandler(void); + +//***************************************************************************** +// +// 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..341fb1b --- /dev/null +++ b/usblib/device/usbdevicepriv.h @@ -0,0 +1,223 @@ +//***************************************************************************** +// +// 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-2012 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 9453 of the Stellaris 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. + // + USB_STATE_IDLE, + + // + // The USB device is sending data back to the host due to an IN request. + // + USB_STATE_TX, + + // + // The USB device is sending the configuration descriptor back to the host + // due to an IN request. + // + USB_STATE_TX_CONFIG, + + // + // The USB device is receiving data from the host due to an OUT + // request from the host. + // + USB_STATE_RX, + + // + // 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. + // + USB_STATE_STATUS, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + USB_STATE_STALL +} +tEP0State; + +typedef struct tDeviceInfo tDeviceInfo; +typedef struct tDeviceInstance tDeviceInstance; + +//***************************************************************************** +// +// The USB controller device information. +// +//***************************************************************************** +struct tDeviceInstance +{ + // + // The device information for the USB device. + // + tDeviceInfo *psInfo; + + // + // The instance data for the USB device. + // + void *pvInstance; + + // + // The current state of endpoint zero. + // + volatile tEP0State eEP0State; + + // + // The devices current address, this also has a change pending bit in the + // MSB of this value specified by DEV_ADDR_PENDING. + // + volatile unsigned long ulDevAddress; + + // + // This holds the current active configuration for this device. + // + unsigned long ulConfiguration; + + // + // This holds the configuration id that will take effect after a reset. + // + unsigned long ulDefaultConfiguration; + + // + // This holds the current alternate interface for this device. + // + unsigned char pucAltSetting[USB_MAX_INTERFACES_PER_DEVICE]; + + // + // This is the pointer to the current data being sent out or received + // on endpoint zero. + // + unsigned char *pEP0Data; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_sUSBDeviceState.pEP0Data data buffer. + // + volatile unsigned long ulEP0DataRemain; + + // + // The amount of data being sent/received due to a custom request. + // + unsigned long ulOUTDataSize; + + // + // Holds the current device status. + // + unsigned char ucStatus; + + // + // Holds the endpoint status for the HALT condition. This array is sized + // to hold halt status for all IN and OUT endpoints. + // + unsigned char ucHalt[2][USBLIB_NUM_EP - 1]; + + // + // Holds the configuration descriptor section number currently being sent + // to the host. + // + unsigned char ucConfigSection; + + // + // Holds the offset within the configuration descriptor section currently + // being sent to the host. + // + unsigned char ucSectionOffset; + + // + // Holds the index of the configuration that we are currently sending back + // to the host. + // + unsigned char ucConfigIndex; + + // + // 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. + // + tBoolean bPwrSrcSet; + + // + // This flag indicates whether or not remote wake up signaling is in + // progress. + // + tBoolean bRemoteWakeup; + + // + // During remote wake up signaling, this counter is used to track the + // number of milliseconds since the signaling was initiated. + // + unsigned char ucRemoteWakeupCount; +}; + +extern tDeviceInstance g_psUSBDevice[]; + +//***************************************************************************** +// +// Device enumeration functions provided by device/usbenum.c and called from +// the interrupt handler in device/usbhandler.c +// +//***************************************************************************** +extern tBoolean USBDeviceConfig(tDeviceInstance *psDevInst, + const tConfigHeader *psConfig); +extern tBoolean USBDeviceConfigAlternate(tDeviceInstance *psDevInst, + const tConfigHeader *psConfig, + unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting); + +//***************************************************************************** +// +// 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..5ba33e5 --- /dev/null +++ b/usblib/device/usbdhandler.c @@ -0,0 +1,82 @@ +//***************************************************************************** +// +// usbhandler.c - General USB handling routines. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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/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) +{ + unsigned long ulStatus; + + // + // Get the controller interrupt status. + // + ulStatus = MAP_USBIntStatusControl(USB0_BASE); + + // + // Call the internal handler. + // + USBDeviceIntHandlerInternal(0, ulStatus); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhid.c b/usblib/device/usbdhid.c new file mode 100644 index 0000000..ccfca59 --- /dev/null +++ b/usblib/device/usbdhid.c @@ -0,0 +1,2579 @@ +//***************************************************************************** +// +// usbdhid.c - USB HID device class driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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/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 usDeferredOpFlags 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->usDeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define HID_DO_PACKET_RX 5 +#define HID_DO_SEND_IDLE_REPORT 6 + +//***************************************************************************** +// +// 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 USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//***************************************************************************** +// +// 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 + +//***************************************************************************** +// +// Maximum packet size for the interrupt endpoints used for report transmission +// and reception and the associated FIFO sizes to set aside for each endpoint. +// +//***************************************************************************** +#define INT_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define INT_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 + +#define INT_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(INT_IN_EP_FIFO_SIZE) +#define INT_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(INT_IN_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. +// +//***************************************************************************** +unsigned char g_pHIDDeviceDescriptor[] = +{ + 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 + 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. +}; + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +unsigned char g_pHIDDescriptor[] = +{ + // + // 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. +// +//***************************************************************************** +unsigned char g_pHIDInterface[] = +{ + // + // 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. + 2, // 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. +}; + +const unsigned char g_pHIDInEndpoint[] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(INT_IN_ENDPOINT), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(INT_IN_EP_MAX_SIZE), // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +const unsigned char g_pHIDOutEndpoint[] = +{ + // + // Interrupt OUT endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(INT_OUT_ENDPOINT), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(INT_OUT_EP_MAX_SIZE), // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// 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_pHIDDescriptor), + g_pHIDDescriptor +}; + +const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pHIDInterface), + g_pHIDInterface +}; + +const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pHIDInEndpoint), + g_pHIDInEndpoint +}; + +const tConfigSection g_sHIDOutEndpointSection = +{ + sizeof(g_pHIDOutEndpoint), + g_pHIDOutEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +tConfigSection g_sHIDDescriptorSection = +{ + 0, (void *)0 +}; + +//***************************************************************************** +// +// 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, + &g_sHIDOutEndpointSection +}; + +#define NUM_HID_SECTIONS (sizeof(g_psHIDSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// 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_pHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleRequest(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleEP0DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEP0DataSent(void *pvInstance, unsigned long ulInfo); +static void HandleReset(void *pvInstance); +static void HandleSuspend(void *pvInstance); +static void HandleResume(void *pvInstance); +static void HandleDisconnect(void *pvInstance); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB HID devices. +// +//***************************************************************************** +tDeviceInfo g_sHIDDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + HandleGetDescriptor, // GetDescriptor + HandleRequest, // RequestHandler + 0, // InterfaceChange + HandleConfigChange, // ConfigChange + HandleEP0DataReceived, // DataReceived + HandleEP0DataSent, // DataSentCallback + HandleReset, // ResetHandler + HandleSuspend, // SuspendHandler + HandleResume, // ResumeHandler + HandleDisconnect, // DisconnectHandler + HandleEndpoints, // EndpointHandler + HandleDevice // Device handler. + }, + g_pHIDDeviceDescriptor, + g_pHIDConfigDescriptors, + 0, // Will be completed during USBDHIDInit(). + 0, // Will be completed during USBDHIDInit(). + &g_sUSBDefaultFIFOConfig +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pusDeferredOp points to the flags variable which is to be modified. +// \param usBit 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 unsigned short *pusDeferredOp, + unsigned short usBit, tBoolean bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pusDeferredOp, usBit) = 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 psDevice points to the HID device structure whose report timer is to +// be cleared. +// \param ucReportID 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 psDevice structure. +// +// \return None. +// +//***************************************************************************** +static void +ClearReportTimer(const tUSBDHIDDevice *psDevice, unsigned char ucReportID) +{ + unsigned long ulLoop; + + if(psDevice->ucNumInputReports > 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(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + if(psDevice->psReportIdle[ulLoop].ucReportID == ucReportID) + { + break; + } + } + } + else + { + ulLoop = 0; + } + + // + // If we drop out of the loop with an index less than ucNumInputReports, + // we found the relevant report so clear its timer. + // + if(ulLoop < psDevice->ucNumInputReports) + { + psDevice->psReportIdle[ulLoop].ulTimeSinceReportmS = 0; + } +} + +//***************************************************************************** +// +// This function is called to clear the idle period timers for each input +// report supported by the device. +// +// \param psDevice points to the HID device structure whose timers are to be +// cleared. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +ClearIdleTimers(const tUSBDHIDDevice *psDevice) +{ + unsigned long ulLoop; + + // + // Clear the "time till next report" counters for each input report. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + psDevice->psReportIdle[ulLoop].usTimeTillNextmS = + psDevice->psReportIdle[ulLoop].ucDuration4mS * 4; + } +} + +//***************************************************************************** +// +// This function is called periodically to allow us to process the report idle +// timers. +// +// \param psDevice points to the HID device structure whose timers are to be +// updated. +// \param ulElapsedmS indicates the number of milliseconds that have elapsed +// since the last call to this function. +// +// \return None. +// +//***************************************************************************** +static void +ProcessIdleTimers(const tUSBDHIDDevice *psDevice, unsigned long ulElapsedmS) +{ + unsigned long ulLoop; + unsigned long ulSizeReport; + void *pvReport; + tHIDInstance *psInst; + tBoolean bDeferred; + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)psDevice)->psPrivateHIDData; + + // + // We have not had to defer any report transmissions yet. + // + bDeferred = false; + + // + // Look at each of the input report idle timers in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + // + // Update the time since the last report was sent. + // + psDevice->psReportIdle[ulLoop].ulTimeSinceReportmS += ulElapsedmS; + + // + // Is this timer running? + // + if(psDevice->psReportIdle[ulLoop].ucDuration4mS) + { + // + // Yes - is it about to expire? + // + if(psDevice->psReportIdle[ulLoop].usTimeTillNextmS <= ulElapsedmS) + { + // + // The timer is about to expire. Can we send a report right + // now? + // + if((psInst->eHIDTxState == HID_STATE_IDLE) && + (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. + // + ulSizeReport = psDevice->pfnRxCallback( + psDevice->pvRxCBData, + USBD_HID_EVENT_IDLE_TIMEOUT, + psDevice->psReportIdle[ulLoop].ucReportID, + &pvReport); + + // + // Schedule the report for transmission. + // + USBDHIDReportWrite((void *)psDevice, pvReport, + ulSizeReport, true); + + // + // Reload the timer for the next period. + // + psDevice->psReportIdle[ulLoop].usTimeTillNextmS = + psDevice->psReportIdle[ulLoop].ucDuration4mS * 4; + } + else + { + // + // We can't send the report straight away so flag it for + // transmission as soon as the previous transmission ends. + // + psDevice->psReportIdle[ulLoop].usTimeTillNextmS = 0; + bDeferred = true; + } + } + else + { + // + // The timer is not about to expire. Update the time till the + // next report transmission. + // + psDevice->psReportIdle[ulLoop].usTimeTillNextmS -= ulElapsedmS; + } + } + } + + // + // If we had to defer transmission of any report, remember this so that we + // will process it as soon as possible. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + HID_DO_SEND_IDLE_REPORT, bDeferred); +} + +static void +SetIdleTimeout(const tUSBDHIDDevice *psDevice, unsigned char ucReportID, + unsigned char ucTimeout4mS) +{ + unsigned long ulLoop; + tBoolean 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(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + psIdle = &psDevice->psReportIdle[ulLoop]; + + // + // 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(!ucReportID || (ucReportID == psIdle->ucReportID)) + { + // + // Save the new duration for the idle timer. + // + psIdle->ucDuration4mS = ucTimeout4mS; + + // + // Are we enabling the idle timer? If so, fix up the time until it + // needs to fire. + // + if(ucTimeout4mS) + { + // + // Determine what the timeout is for this report given the time + // since the last report of this type was sent. + // + if(psIdle->ulTimeSinceReportmS >= + ((unsigned long)ucTimeout4mS * 4)) + { + psIdle->usTimeTillNextmS = 0; + bReportNeeded = true; + } + else + { + psIdle->usTimeTillNextmS = + (((unsigned short)ucTimeout4mS * 4) - + psIdle->ulTimeSinceReportmS); + } + } + } + } + + // + // 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(psDevice, 0); + } +} + +//***************************************************************************** +// +// Find the idle timeout for a given HID input report. +// +// \param psDevice points to the HID device whose report idle timeout is to be +// found. +// \param ucReportID 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 unsigned long +GetIdleTimeout(const tUSBDHIDDevice *psDevice, unsigned char ucReportID) +{ + unsigned long ulLoop; + tHIDReportIdle *psIdle; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + psIdle = &psDevice->psReportIdle[ulLoop]; + + // + // 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(!ucReportID || (ucReportID == psIdle->ucReportID)) + { + // + // 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((unsigned long)psIdle->ucDuration4mS); + } + } + + // + // If we drop out, the report couldn't 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 psDevice points to the HID device which is to be searched for the +// required class descriptor. +// \param ucType is the type of class descriptor being requested. This will +// be either USB_HID_DTYPE_REPORT or USB_HID_DTYPE_PHYSICAL. +// \param ulIndex 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 unsigned long +FindHIDDescriptor(const tUSBDHIDDevice *psDevice, unsigned char ucType, + unsigned long ulIndex, unsigned long *pulLen) +{ + tBoolean bFoundType; + unsigned long ulLoop; + unsigned long ulCount; + unsigned long ulLastFound; + const tHIDClassDescriptorInfo *psDesc; + + // + // Remember that we have not found any descriptor with a matching type yet. + // + bFoundType = false; + ulCount = 0; + ulLastFound = 0; + + // + // Walk through all the class descriptors looking for the one which + // matches the requested index and type. + // + for(ulLoop = 0; ulLoop < psDevice->psHIDDescriptor->bNumDescriptors; + ulLoop++) + { + psDesc = &(psDevice->psHIDDescriptor->sClassDescriptor[ulLoop]); + if(psDesc->bDescriptorType == ucType) + { + // + // We found a descriptor of the correct type. Is this the + // correct index? + // + bFoundType = true; + + // + // Is this the descriptor we are looking for? + // + if(ulCount == ulIndex) + { + // + // Yes - we found it so return the index and size to the + // caller. + // + *pulLen = (unsigned long)psDesc->wDescriptorLength; + return(ulLoop); + } + 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. + // + ulCount++; + ulLastFound = ulLoop; + } + } + } + + // + // If we drop out, we didn't find the requested descriptor. Now handle + // the special case of a physical descriptor - if we found any physical + // descriptors, return the last one. + // + if((ucType == USB_HID_DTYPE_PHYSICAL) && bFoundType) + { + // + // Get the length of the last descriptor we found. + // + psDesc = &(psDevice->psHIDDescriptor->sClassDescriptor[ulLastFound]); + *pulLen = (unsigned long)psDesc->wDescriptorLength; + + // + // Return the index to the caller. + // + return(ulLastFound); + } + else + { + // + // We couldn't 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 psInst points to the 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 short packet or a +// packet with no data to indicate the end of the transaction. +// +//***************************************************************************** +static long +ScheduleReportTransmission(tHIDInstance *psInst) +{ + unsigned long ulNumBytes; + unsigned char *pucData; + long lRetcode; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = (unsigned long)(psInst->usInReportSize - + psInst->usInReportIndex); + + // + // Limit individual transfers to the maximum packet size for the endpoint. + // + if(ulNumBytes > INT_IN_EP_MAX_SIZE) + { + ulNumBytes = INT_IN_EP_MAX_SIZE; + } + + // + // Where are we sending this data from? + // + pucData = psInst->pucInReportData + psInst->usInReportIndex; + + // + // Put the data in the correct FIFO. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucINEndpoint, + pucData, ulNumBytes); + + if(lRetcode != -1) + { + // + // Update the count and index ready for the next time round. + // + psInst->usInReportIndex += ulNumBytes; + + // + // Send out the current data. + // + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucINEndpoint, + USB_TRANS_IN); + } + + // + // Tell the caller how we got on. + // + return(lRetcode); +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus 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 tBoolean +ProcessDataFromHost(const tUSBDHIDDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tHIDInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, ulEPStatus); + + // + // Has a packet been received? + // + if(ulEPStatus & 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 signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, HID_DO_PACKET_RX, true); + + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // 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. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ulEPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_ERROR, + (ulEPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus 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 tBoolean +ProcessDataToHost(const tUSBDHIDDevice *psDevice, unsigned long ulStatus) +{ + tHIDInstance *psInst; + unsigned long ulEPStatus; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, psInst->ucINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucINEndpoint, + ulEPStatus); + + // + // 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 usInReportIndex has reached the usInReportSize value. + // + if(psInst->usInReportSize == psInst->usInReportIndex) + { + // + // We finished sending the last report so are idle once again. + // + psInst->eHIDTxState = HID_STATE_IDLE; + + // + // Notify the client that the report transmission completed. + // + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + psInst->usInReportSize, (void *)0); + + // + // Do we have any reports to send as a result of idle timer timeouts? + // + if(psInst->usDeferredOpFlags & (1 << HID_DO_SEND_IDLE_REPORT)) + { + // + // Yes - send reports for any timers that expired recently. + // + ProcessIdleTimers(psDevice, 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 ulStatus. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDHIDDevice *psHIDInst; + tHIDInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psHIDInst = (const tUSBDHIDDevice *)pvInstance; + psInst = psHIDInst->psPrivateHIDData; + + // + // Handler for the interrupt OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvInstance, ulStatus); + } + + // + // Handler for the interrupt IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) + { + ProcessDataToHost(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tHIDInstance *psInst; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Set all our endpoints to idle state. + // + psInst->eHIDRxState = HID_STATE_IDLE; + psInst->eHIDTxState = HID_STATE_IDLE; + + // + // 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. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_CONNECTED, 0, + (void *)0); + } + + // + // Clear the idle timers for each input report. + // + ClearIdleTimers(psDevice); + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tHIDInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ucInterface = pucData[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(pucData[0] & USB_EP_DESC_IN) + { + psInst->ucINEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucOUTEndpoint = + INDEX_TO_USB_EP(pucData[1] & 0x7f); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psDevice->psPrivateHIDData->bConnected) + { + // + // Pass the disconnected event to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_DISCONNECTED, + 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psDevice->psPrivateHIDData->bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a request for a +// non-standard descriptor is received. +// +// \param pvInstance is the instance data for this request. +// \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulSize; + unsigned long ulDesc; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Which type of class descriptor are we being asked for? + // + switch(pUSBRequest->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. + // + ulSize = 0; + ulDesc = FindHIDDescriptor(psDevice, pUSBRequest->wValue >> 8, + pUSBRequest->wValue & 0xFF, + &ulSize); + + // + // Did we find the descriptor? + // + if(ulDesc == 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(ulSize > pUSBRequest->wLength) + { + ulSize = pUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, + (unsigned char *)psDevice->ppClassDescriptors[ulDesc], ulSize); + + 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? + // + ulSize = (unsigned long)psDevice->psHIDDescriptor->bLength; + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ulSize > pUSBRequest->wLength) + { + ulSize = pUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, (unsigned char *)psDevice->psHIDDescriptor, + ulSize); + 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 pvInstance is the instance data for this HID device. +// \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + tHIDInstance *psInst; + unsigned char ucProtocol; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Make sure the request was for this interface. + // + if(pUSBRequest->wIndex != psInst->ucInterface) + { + return; + } + + // + // 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_SET_REPORT: + { + // + // Ask the application for a buffer large enough to hold the + // report we are to be sent. + // + psInst->usOutReportSize = pUSBRequest->wLength; + psInst->pucOutReportData = + (unsigned char *)psDevice->pfnRxCallback( + psDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT_BUFFER, + pUSBRequest->wValue, + (void *)(unsigned long)(pUSBRequest->wLength)); + + // + // Did the client provide us a buffer? + // + if(!psInst->pucOutReportData) + { + // + // The application couldn't 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->eHIDRxState = HID_STATE_WAIT_DATA; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, psInst->pucOutReportData, + (unsigned long)pUSBRequest->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->ulUSBBase, 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: + { + unsigned long ulSize; + unsigned char *pucReport; + + // + // Get the latest report from the application. + // + ulSize = psDevice->pfnRxCallback(psDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT, + pUSBRequest->wValue, &pucReport); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // ..then send back the requested report. + // + psInst->bGetRequestPending = true; + USBDCDSendDataEP0(0, pucReport, ulSize); + + 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(psDevice, pUSBRequest->wValue & 0xFF, + (pUSBRequest->wValue >> 8) & 0xFF); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, 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: + { + unsigned long ulTimeout; + + // + // Determine the timeout for the requested report. + // + ulTimeout = GetIdleTimeout(psDevice, pUSBRequest->wValue); + + if(ulTimeout != HID_NOT_FOUND) + { + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&ulTimeout, 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(psDevice->ucSubclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USBD_HID_EVENT_SET_PROTOCOL, + pUSBRequest->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(psDevice->ucSubclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application callback to get the answer. + // + ucProtocol = (unsigned char)psDevice->pfnRxCallback( + psDevice->pvRxCBData, USBD_HID_EVENT_GET_PROTOCOL, 0, + (void *)0); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&ucProtocol, 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 *pvInstance, unsigned long ulDataSize) +{ + tHIDInstance *psInst; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // If we were not passed any data, just return. + // + if(ulDataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateHIDData; + + // + // Make sure we are actually expecting something. + // + if(psInst->eHIDRxState != HID_STATE_WAIT_DATA) + { + return; + } + + // + // Change the endpoint state back to idle now that we have been passed + // the data we were waiting for. + // + psInst->eHIDRxState = HID_STATE_IDLE; + + // + // 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. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USBD_HID_EVENT_SET_REPORT, + psInst->usOutReportSize, + psInst->pucOutReportData); +} + +//***************************************************************************** +// +// 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 *pvInstance, unsigned long ulInfo) +{ + tHIDInstance *psInst; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateHIDData; + + // + // 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; + + psDevice->pfnRxCallback(psDevice->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 *pvInstance) +{ + ASSERT(pvInstance != 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(pvInstance); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvInstance) +{ + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psDevice->pfnRxCallback(psDevice->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 *pvInstance) +{ + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psDevice->pfnRxCallback(psDevice->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 pvInstance is the instance data for this request. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +HIDTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tHIDInstance *psInst; + unsigned long ulSize; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateHIDData; + + // + // If we are connected, process our idle timers. + // + if(psInst->bConnected) + { + ProcessIdleTimers(psDevice, ulTimemS); + } + + // + // Do we have a deferred receive waiting + // + if(psInst->usDeferredOpFlags & (1 << HID_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psDevice 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 psDevice 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 64 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 +//! 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 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, 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 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 psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDInit(unsigned long ulIndex, const tUSBDHIDDevice *psDevice) +{ + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDData); + ASSERT(psDevice->pfnRxCallback); + ASSERT(psDevice->pfnTxCallback); + ASSERT(psDevice->ppClassDescriptors); + ASSERT(psDevice->psHIDDescriptor); + ASSERT((psDevice->ucNumInputReports == 0) || psDevice->psReportIdle); + + USBDHIDCompositeInit(ulIndex, psDevice); + + // + // All is well so now pass the descriptors to the lower layer and put + // the HID device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateHIDData->psDevInfo); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! +//! +//! \return Returns NULL on failure or the \e psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDCompositeInit(unsigned long ulIndex, const tUSBDHIDDevice *psDevice) +{ + tHIDInstance *psInst; + tDeviceDescriptor *psDevDesc; + tInterfaceDescriptor *psDevIf; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDData); + ASSERT(psDevice->pfnRxCallback); + ASSERT(psDevice->pfnTxCallback); + ASSERT(psDevice->ppClassDescriptors); + ASSERT(psDevice->psHIDDescriptor); + ASSERT((psDevice->ucNumInputReports == 0) || psDevice->psReportIdle); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateHIDData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pHIDDescriptor; + psInst->psDevInfo = &g_sHIDDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eHIDRxState = HID_STATE_UNCONFIGURED; + psInst->eHIDTxState = HID_STATE_UNCONFIGURED; + psInst->usDeferredOpFlags = 0; + psInst->bConnected = false; + psInst->bGetRequestPending = false; + psInst->bSendInProgress = false; + psInst->usInReportIndex = 0; + psInst->usInReportSize = 0; + psInst->pucInReportData = (unsigned char *)0; + psInst->usOutReportSize = 0; + psInst->pucOutReportData = (unsigned char *)0; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucINEndpoint = INT_IN_ENDPOINT; + psInst->ucOUTEndpoint = INT_OUT_ENDPOINT; + psInst->ucInterface = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Slot the client's HID descriptor into our standard configuration + // descriptor. + // + g_sHIDDescriptorSection.usSize = psDevice->psHIDDescriptor->bLength; + g_sHIDDescriptorSection.pucData = + (unsigned char *)psDevice->psHIDDescriptor; + + // + // Fix up the interface and endpoint descriptors depending upon client + // choices. + // + psDevIf = (tInterfaceDescriptor *)g_pHIDInterface; + psDevIf->bNumEndpoints = psDevice->bUseOutEndpoint ? 2 : 1; + psDevIf->bInterfaceSubClass = psDevice->ucSubclass; + psDevIf->bInterfaceProtocol = psDevice->ucProtocol; + + // + // If necessary, remove the interrupt OUT endpoint from the configuration + // descriptor. + // + if(psDevice->bUseOutEndpoint == false) + { + g_sHIDConfigHeader.ucNumSections = (NUM_HID_SECTIONS - 1); + } + else + { + g_sHIDConfigHeader.ucNumSections = NUM_HID_SECTIONS; + } + + // + // Plug in the client's string table to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psDevice->ulNumStringDescriptors; + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Initialize the input report idle timers if any input reports exist. + // + ClearIdleTimers(psDevice); + + // + // 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 *)psDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the HID device. +//! +//! \param pvInstance 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 pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDTerm(void *pvInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USB_BASE_TO_INDEX(psInst->ulUSBBase)); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; + psInst->psConfDescriptor = (tConfigDescriptor *)0; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvInstance 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 pvInstance 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 *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvInstance)->pvRxCBData; + ((tUSBDHIDDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data pointer for the transmit callback. +//! +//! \param pvInstance 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 pvInstance 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 *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvInstance)->pvTxCBData; + ((tUSBDHIDDevice *)pvInstance)->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 pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pcData points to the first byte of data which is to be transmitted. +//! \param ulLength 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 pucData 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. +// +//***************************************************************************** +unsigned long +USBDHIDReportWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tHIDInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // 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->eHIDTxState != HID_STATE_IDLE) + { + // + // 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(ulLength) + { + ClearReportTimer(pvInstance, *pcData); + } + + // + // Keep track of the whereabouts of the report so that we can send it in + // multiple packets if necessary. + // + psInst->pucInReportData = pcData; + psInst->usInReportIndex = 0; + psInst->usInReportSize = ulLength; + + // + // Schedule transmission of the first packet of the report. + // + psInst->eHIDTxState = HID_STATE_WAIT_DATA; + lRetcode = 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(lRetcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ulLength); + } + else + { + // + // Yes - tell the caller we couldn't send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the interrupt OUT +//! endpoint (if in use). +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pcData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pcData. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to ulLength 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. +// +//***************************************************************************** +unsigned long +USBDHIDPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tHIDInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + pcData, &ulCount); + + // + // Did we read the last of the packet data? + // + if(ulCount == ulPkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, psInst->ucOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + HID_DO_PACKET_RX, false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(lRetcode != -1) + { + return(ulCount); + } + } + + // + // 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 pvInstance 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 (64) 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 64 +//! if no transmission is currently in progress. +// +//***************************************************************************** +unsigned long +USBDHIDTxPacketAvailable(void *pvInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eHIDTxState != HID_STATE_IDLE) + { + // + // 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(INT_IN_EP_MAX_SIZE); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvInstance 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. +// +//***************************************************************************** +unsigned long +USBDHIDRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tHIDInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + return(ulSize); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the HID device instance structure. +//! \param ucPower 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 *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance 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. +// +//***************************************************************************** +tBoolean +USBDHIDRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // 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..80b1126 --- /dev/null +++ b/usblib/device/usbdhid.h @@ -0,0 +1,974 @@ +//***************************************************************************** +// +// usbdhid.h - Definitions used by HID class devices. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! 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. +// +// For reference this is +// sizeof(g_sHIDInterfaceSection) + sizeof(g_sHIDDescriptorSection) + +// sizeof(g_sHIDInEndpointSection) + sizeof(g_sHIDOutEndpointSection) +// +//***************************************************************************** +#define COMPOSITE_DHID_SIZE (32) + +//***************************************************************************** +// +// Macros used to create the static Report Descriptors. +// +//***************************************************************************** + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Page entries in HID report +//! descriptors. +//! +//! \param ucValue 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(ucValue) 0x05, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage entries in HID report descriptors. +//! +//! \param ucValue 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(ucValue) 0x09, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Minimum entries in HID report +//! descriptors. +//! +//! \param ucValue 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(ucValue) 0x19, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Maximum entries in HID report +//! descriptors. +//! +//! \param ucValue 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(ucValue) 0x29, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Minimum entries in HID report +//! descriptors. +//! +//! \param cValue 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(cValue) 0x15, ((cValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Maximum entries in HID report +//! descriptors. +//! +//! \param cValue 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(cValue) 0x25, ((cValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Minimum entries in HID report +//! descriptors. +//! +//! \param sValue 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(sValue) 0x36, ((sValue) & 0xFF), \ + (((sValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Maximum entries in HID report +//! descriptors. +//! +//! \param sValue 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(sValue) 0x46, ((sValue) & 0xFF), \ + (((sValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Collection entries in HID report +//! descriptors. +//! +//! \param ucValue 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(ucValue) 0xa1, ((ucValue) & 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 ucValue 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(ucValue) 0x95, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report ID entries in HID report +//! descriptors. +//! +//! \param ucValue 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(ucValue) 0x85, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report Size entries in HID report +//! descriptors. +//! +//! \param ucValue 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(ucValue) 0x75, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param ucValue 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(ucValue) 0x81, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param usValue 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(usValue) 0x82, ((usValue) & 0xff), \ + (((usValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param ucValue is bit mask to specify the type of a set of feature report +//! items. Note that if the 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(ucValue) 0xB1, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param usValue 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 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(usValue) 0xB2, ((usValue) & 0xff), \ + (((usValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param ucValue is bit mask to specify the type of a set of output report +//! items. Note that if the 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(ucValue) 0x91, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param usValue 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 USB_HID_OUTPUT_BITF (bit 8) 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(usValue) 0x92, ((usValue) & 0xff), \ + (((usValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit Exponent entries in HID report +//! descriptors. +//! +//! \param cValue 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(cValue) 0x55, ((cValue) & 0x0f) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit entries for uncommon units in HID +//! report descriptors. +//! +//! \param ulValue 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(ulValue) 0x67, (ulValue) & 0x0f), \ + (((ulValue) >> 8) & 0xFF), \ + (((ulValue) >> 16) & 0xFF), \ + (((ulValue) >> 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. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + HID_STATE_UNCONFIGURED, + + // + // No outstanding transaction remains to be completed. + // + HID_STATE_IDLE, + + // + // Waiting on completion of a send or receive transaction. + // + HID_STATE_WAIT_DATA +} +tHIDState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for +// HID devices. The memory for this structure is pointed to by the +// psPrivateHIDData field in the tUSBDHIDDevice structure passed in the +// USBDHIDInit() function. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + volatile tHIDState eHIDRxState; + volatile tHIDState eHIDTxState; + volatile unsigned short usDeferredOpFlags; + unsigned short usInReportSize; + unsigned short usInReportIndex; + unsigned short usOutReportSize; + unsigned char *pucInReportData; + unsigned char *pucOutReportData; + volatile tBoolean bConnected; + volatile tBoolean bSendInProgress; + tBoolean bGetRequestPending; + unsigned char ucINEndpoint; + unsigned char ucOUTEndpoint; + unsigned char ucInterface; +} +tHIDInstance; + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the HID device class driver. +// The client must provide a block of RAM of at least this size in the +// pvWorkspace field of the tUSBDHIDDevice structure passed on USBDHIDInit. +// +//***************************************************************************** +#define USB_HID_WORKSPACE_SIZE sizeof(tHIDInstance) +#endif + +//***************************************************************************** +// +//! 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. + // + unsigned char ucDuration4mS; + + // + //! 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. + // + unsigned char ucReportID; + + // + //! 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 USBD_HID_EVENT_IDLE_TIMEOUT. + // + unsigned short usTimeTillNextmS; + + // + //! 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. + // + unsigned long ulTimeSinceReportmS; +} +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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + // + //! The interface subclass to publish to the server for this HID device. + // + unsigned char ucSubclass; + + // + //! The interface protocol to publish to the server for this HID device. + // + unsigned char ucProtocol; + + // + //! 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 psReportIdle below. + // + unsigned char ucNumInputReports; + + // + //! 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 + //! ucDuration4mS and ucReportID 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 will update 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 ucNumInputReports field above. + // + tHIDReportIdle *psReportIdle; + + //! A pointer to the callback function which will be 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 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 transmission of Input reports + //! via the interrupt IN endpoint. + // + 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; + + // + //! 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 USB_EVENT_RX_AVAILABLE events. If false, + //! reports from the host are received via endpoint zero and passed to the + //! application via USBD_HID_EVENT_REPORT_SENT events. + // + tBoolean 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 psHIDDescriptor. + // + const unsigned char * const *ppClassDescriptors; + + // + //! 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to 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 *psPrivateHIDData; +} +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 ulMsgValue 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 +//! 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 +//! 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 ulMsgValue 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 "unsigned long" 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 ulMsgValue field indicates the +//! size of the report and pvMsgData points to the first byte of the report. +//! The report buffer will previously have been returned in response to an +//! earlier USBD_HID_EVENT_GET_REPORT_BUFFER callback. The HID device class +//! driver will 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, +//! USB_HID_PROTOCOL_BOOT or 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 ulMsgData value will contain the requested protocol, +//! USB_HID_PROTOCOL_BOOT or 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 ulMsgData value will contain 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) + +extern tDeviceInfo g_sHIDDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDInit(unsigned long ulIndex, + const tUSBDHIDDevice *psHIDDevice); +extern void *USBDHIDCompositeInit(unsigned long ulIndex, + const tUSBDHIDDevice *psDevice); + +extern void USBDHIDTerm(void *pvInstance); +extern void *USBDHIDSetRxCBData(void *pvInstance, void *pvCBData); +extern void *USBDHIDSetTxCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDHIDReportWrite(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDHIDPacketRead(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDHIDTxPacketAvailable(void *pvInstance); +extern unsigned long USBDHIDRxPacketAvailable(void *pvInstance); +extern void USBDHIDPowerStatusSet(void *pvInstance, unsigned char ucPower); +extern tBoolean USBDHIDRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHID_H__ diff --git a/usblib/device/usbdhidkeyb.c b/usblib/device/usbdhidkeyb.c new file mode 100644 index 0000000..8b4aa95 --- /dev/null +++ b/usblib/device/usbdhidkeyb.c @@ -0,0 +1,1119 @@ +//***************************************************************************** +// +// usbdhidkeyb.c - USB HID Keyboard device class driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The following is the HID report structure definition that is passed back +// to the host. +// +//***************************************************************************** +static const unsigned char g_pucKeybReportDescriptor[]= +{ + 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 class descriptor table. For the keyboard class, we have only a +// single report descriptor. +// +//***************************************************************************** +static const unsigned char * const g_pKeybClassDescriptors[] = +{ + g_pucKeybReportDescriptor +}; + +//***************************************************************************** +// +// 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_pucKeybReportDescriptor) // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// Forward references for keyboard device callback functions. +// +//***************************************************************************** +static unsigned long HIDKeyboardRxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + void *pvMsgData); +static unsigned long HIDKeyboardTxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + void *pvMsgData); + +//***************************************************************************** +// +// Main HID device class event handler function. +// +// \param pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDKeybDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData 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 unsigned long +HIDKeyboardRxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psDevice; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDKeyboardDevice *)pvCBData; + psInst = psDevice->psPrivateHIDKbdData; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ucUSBConfigured = true; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_CONNECTED, + 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ucUSBConfigured = false; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->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 ulMsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(unsigned char **)pvMsgData = psInst->pucReport; + 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((unsigned long)pvMsgData == KEYB_OUT_REPORT_SIZE ) + { + // + // Yes - return our pointer. + // + return((unsigned long)psInst->pucDataBuffer); + } + 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->ucLEDStates != psInst->pucDataBuffer[0]) + { + // + // Note the new LED states. + // + psInst->ucLEDStates = psInst->pucDataBuffer[0]; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, + USBD_HID_KEYB_EVENT_SET_LEDS, + psInst->pucDataBuffer[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->ucProtocol = ulMsgData; + 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->ucProtocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + { + return(psDevice->pfnCallback(psDevice->pvCBData, ulEvent, + ulMsgData, pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDKeybDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData 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 unsigned long +HIDKeyboardTxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + unsigned long ulCount; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDKeyboardDevice *)pvCBData; + psInst = psDevice->psPrivateHIDKbdData; + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // 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. + // + ulCount = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pucReport, + KEYB_IN_REPORT_SIZE, true); + + // + // If we scheduled the report for transmission, clear the + // change flag. + // + if(ulCount != 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. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_TX_COMPLETE, + ulMsgData, (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 ucUsageCode 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 tBoolean +AddKeyToPressedList(tHIDKeyboardInstance *psInst, unsigned char ucUsageCode) +{ + unsigned long ulLoop; + tBoolean 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(ulLoop = 0; ulLoop < (unsigned long)psInst->ucKeyCount; ulLoop++) + { + // + // Is this key already included in the list of keys in the pressed + // state? + // + if(ucUsageCode == psInst->pucKeysPressed[ulLoop]) + { + // + // 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(ulLoop >= psInst->ucKeyCount) + { + if(psInst->ucKeyCount < KEYB_MAX_CHARS_PER_REPORT) + { + // + // We have room so store the new key press in the list. + // + psInst->pucKeysPressed[psInst->ucKeyCount] = ucUsageCode; + psInst->ucKeyCount++; + 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 ucUsageCode 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 will have 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 will not have +// changed. +// +//***************************************************************************** +static tBoolean +RemoveKeyFromPressedList(tHIDKeyboardInstance *psInst, + unsigned char ucUsageCode) +{ + unsigned long ulLoop; + unsigned long ulPos; + + // + // Keep the compiler happy by setting ulPos to something. + // + ulPos = 0; + + // + // Find the usage code in the current list. + // + for(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + if(psInst->pucKeysPressed[ulLoop] == ucUsageCode) + { + ulPos = ulLoop; + break; + } + } + + // + // If we dropped out at the end of the loop, we couldn't find the code so + // just return false. + // + if(ulLoop == 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(ulLoop = (ulPos + 1); ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + psInst->pucKeysPressed[ulLoop - 1] = psInst->pucKeysPressed[ulLoop]; + } + + // + // Clear the last entry in the array and adjust the number of keys in the + // array. + // + psInst->pucKeysPressed[KEYB_MAX_CHARS_PER_REPORT - 1] = + HID_KEYB_USAGE_RESERVED; + psInst->ucKeyCount--; + + // + // Tell the caller we were successful. + // + return(true); +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psDevice 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 will return the \e psDevice 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 +//! will receive \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 will cause unpredictable (though almost certainly +//! unpleasant) behavior. +//! +//! \return Returns NULL on failure or the psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDKeyboardInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDKbdData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDKeyboardCompositeInit(ulIndex, psDevice); + + // + // 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(ulIndex, psHIDDevice); + + return((void *)psDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID keyboard device. +//! +//! This call is very similar to USBDKeyboardInit() except that it is used for +//! initializing an instance of the HID keyboard device for use in a composite +//! device. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Keyboard APIs. +// +//***************************************************************************** +void * +USBDHIDKeyboardCompositeInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice) +{ + tHIDKeyboardInstance *psInst; + unsigned long ulLoop; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDKbdData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDKbdData; + + // + // Initialize the various fields in our instance structure. + // + psInst->ucUSBConfigured = 0; + psInst->ucProtocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ucDuration4mS = 125; + psInst->sReportIdle.ucReportID = 0; + psInst->sReportIdle.ulTimeSinceReportmS = 0; + psInst->sReportIdle.usTimeTillNextmS = 0; + psInst->ucLEDStates = 0; + psInst->ucKeyCount = 0; + for(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + psInst->pucKeysPressed[ulLoop] = HID_KEYB_USAGE_RESERVED; + } + + psInst->eKeyboardState = HID_KEYBOARD_STATE_UNCONFIGURED; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->usPID = psDevice->usPID; + psHIDDevice->usVID = psDevice->usVID; + psHIDDevice->usMaxPowermA = psDevice->usMaxPowermA; + psHIDDevice->ucPwrAttributes = psDevice->ucPwrAttributes; + psHIDDevice->ucSubclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ucProtocol = USB_HID_PROTOCOL_KEYB; + psHIDDevice->ucNumInputReports = 1; + psHIDDevice->psReportIdle = 0; + psHIDDevice->pfnRxCallback = HIDKeyboardRxHandler; + psHIDDevice->pvRxCBData = (void *)psDevice; + psHIDDevice->pfnTxCallback = HIDKeyboardTxHandler; + psHIDDevice->pvTxCBData = (void *)psDevice; + psHIDDevice->bUseOutEndpoint = false, + psHIDDevice->psHIDDescriptor = &g_sKeybHIDDescriptor; + psHIDDevice->ppClassDescriptors = g_pKeybClassDescriptors; + psHIDDevice->ppStringDescriptors = psDevice->ppStringDescriptors; + psHIDDevice->ulNumStringDescriptors = psDevice->ulNumStringDescriptors; + psHIDDevice->psPrivateHIDData = &psInst->sHIDInstance; + 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(ulIndex, psHIDDevice)); +} + +//***************************************************************************** +// +//! Shuts down the HID keyboard device. +//! +//! \param pvInstance 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 +//! pvInstance instance may not me used in any other call to the HID keyboard +//! device other than USBDHIDKeyboardInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDKeyboardTerm(void *pvInstance) +{ + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get a pointer to the device. + // + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Mark the device as no longer configured. + // + psDevice->psPrivateHIDKbdData->ucUSBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the keyboard callback. +//! +//! \param pvInstance 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 pvInstance structure passed to USBDHIDKeyboardInit() resides +//! in RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDKeyboardSetCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDKeyboardDevice *psKeyboard; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvInstance); + + // + // Get a pointer to our keyboard device. + // + psKeyboard = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // 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 pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDKeyboardInit(). +//! \param ucModifiers 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 ucUsageCode 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 will contain the +//! rollover error code, HID_KEYB_USAGE_ROLLOVER instead of key usage codes +//! and the caller will receive return code 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. +// +//***************************************************************************** +unsigned long +USBDHIDKeyboardKeyStateChange(void *pvInstance, unsigned char ucModifiers, + unsigned char ucUsageCode, tBoolean bPress) +{ + tBoolean bRetcode; + unsigned long ulLoop; + unsigned long ulCount; + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Assume all is well until we determine otherwise. + // + bRetcode = true; + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDKbdData; + + // + // Update the global keyboard report with the information passed. + // + psInst->pucReport[0] = ucModifiers; + psInst->pucReport[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(ucUsageCode != 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, ucUsageCode); + } + else + { + // + // A key has been released - remove it from the list. + // + bRetcode = RemoveKeyFromPressedList(psInst, ucUsageCode); + + // + // 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(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + psInst->pucReport[2 + ulLoop] = (bRetcode ? + psInst->pucKeysPressed[ulLoop] : HID_KEYB_USAGE_ROLLOVER); + } + } + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ucUSBConfigured) + { + 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; + ulCount = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pucReport, KEYB_IN_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ulCount) + { + // + // 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); +} + +//***************************************************************************** +// +//! Reports the device power status (bus or self powered) to the USB library. +//! +//! \param pvInstance is the pointer to the keyboard device instance structure. +//! \param ucPower 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 *pvInstance, unsigned char ucPower) +{ + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance 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 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. +// +//***************************************************************************** +tBoolean +USBDHIDKeyboardRemoteWakeupRequest(void *pvInstance) +{ + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->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..1f03332 --- /dev/null +++ b/usblib/device/usbdhidkeyb.h @@ -0,0 +1,382 @@ +//***************************************************************************** +// +// usbdhidkeyb.h - Definitions used by HID keyboard class devices. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 will result 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. + // + unsigned char ucUSBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + unsigned char ucProtocol; + + // + // The current states that the keyboard LEDs are to be set to. + // + volatile unsigned char ucLEDStates; + + // + // The total number of keys currently pressed. This indicates the number + // of key press entries in the pucKeysPressed array. + // + unsigned char ucKeyCount; + + // + // 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 tBoolean bChangeMade; + + // + // A buffer used to receive output reports from the host. + // + unsigned char pucDataBuffer[KEYB_OUT_REPORT_SIZE]; + + // + // A buffer used to hold the last input report sent to the host. + // + unsigned char pucReport[KEYB_IN_REPORT_SIZE]; + + // + // A buffer containing the usage codes of all non-modifier keys currently + // in the pressed state. + // + unsigned char pucKeysPressed[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; + + // + // The lower level HID driver's instance data. + // + tHIDInstance sHIDInstance; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDKeyboardInstance; + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the HID keyboard driver. +// The client must provide a block of RAM of at least this size in the +// psPrivateHIDKbdData field of the tUSBHIDKeyboardDevice structure passed on +// USBDHIDKeyboardInit(). +// +// This value is deprecated and should not be used, any new code should just +// pass in a psPrivateHIDKbdData structure in the psPrivateHIDKbdData field. +// +//***************************************************************************** +#define USB_HID_KEYB_WORKSPACE_SIZE (sizeof(tHIDKeyboardInstance)) +#endif + +//***************************************************************************** +// +//! 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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + //! A pointer to the callback function which will be called to notify + //! the application of general events and those related to reception of + //! Output and Feature reports via the (optional) interrupt OUT endpoint. + // + tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to 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 *psPrivateHIDKbdData; +} +tUSBDHIDKeyboardDevice; + +//***************************************************************************** +// +// Keyboard-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the keyboard LED states are to be set. The +//! ulMsgValue 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 HID_KEYB_NUM_LOCK, +//! HID_KEYB_CAPS_LOCK, HID_KEYB_SCROLL_LOCK, HID_KEYB_COMPOSE and +//! 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 will report 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 ucUsageCode 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 will be stored and passed to the host once configuration completes. +// +//***************************************************************************** +#define KEYB_ERR_NOT_CONFIGURED 4 + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDKeyboardInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice); +extern void *USBDHIDKeyboardCompositeInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice); +extern void USBDHIDKeyboardTerm(void *pvInstance); +extern void *USBDHIDKeyboardSetCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDHIDKeyboardKeyStateChange(void *pvInstance, + unsigned char ucModifiers, + unsigned char ucUsageCode, + tBoolean bPressed); +extern void USBDHIDKeyboardPowerStatusSet(void *pvInstance, + unsigned char ucPower); +extern tBoolean USBDHIDKeyboardRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// 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..ebf7a9f --- /dev/null +++ b/usblib/device/usbdhidmouse.c @@ -0,0 +1,843 @@ +//***************************************************************************** +// +// usbdhidmouse.c - USB HID Mouse device class driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The report descriptor for the mouse class device. +// +//***************************************************************************** +static const unsigned char g_pucMouseReportDescriptor[]= +{ + 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), + + // + // 2 - 8 bit Values for x and y. // Padding + // + ReportSize(8), + ReportCount(MOUSE_REPORT_SIZE - 3), + Input(USB_HID_INPUT_CONSTANT | USB_HID_INPUT_ARRAY | + USB_HID_INPUT_ABS), + + EndCollection, + EndCollection, +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the mouse class, we have only a single +// report descriptor. +// +//***************************************************************************** +static const unsigned char * const g_pMouseClassDescriptors[] = +{ + g_pucMouseReportDescriptor +}; + +//***************************************************************************** +// +// 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_pucMouseReportDescriptor) // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// Forward references for mouse device callback functions. +// +//***************************************************************************** +static unsigned long HIDMouseRxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + void *pvMsgData); +static unsigned long HIDMouseTxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + 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 pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDMouseDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData 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 unsigned long +HIDMouseRxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psDevice; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDMouseDevice *)pvCBData; + psInst = psDevice->psPrivateHIDMouseData; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ucUSBConfigured = true; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_CONNECTED, + 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ucUSBConfigured = false; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->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 ulMsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(unsigned char **)pvMsgData = psInst->pucReport; + 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->ucProtocol = ulMsgData; + 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->ucProtocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + { + return(psDevice->pfnCallback(psDevice->pvCBData, ulEvent, + ulMsgData, pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDMouseDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData 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 unsigned long +HIDMouseTxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psDevice; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDMouseDevice *)pvCBData; + psInst = psDevice->psPrivateHIDMouseData; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // Our last transmission is complete. + // + psInst->eMouseState = HID_MOUSE_STATE_IDLE; + + // + // Pass the event on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_TX_COMPLETE, + ulMsgData, (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 ulIndex is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psDevice 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 will return the \e psDevice 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 +//! will receive \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 will cause unpredictable (though almost certainly unpleasant) +//! behavior. +//! +//! \return Returns NULL on failure or the psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDMouseInit(unsigned long ulIndex, const tUSBDHIDMouseDevice *psDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDMouseData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDMouseCompositeInit(ulIndex, psDevice); + + // + // 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(ulIndex, psHIDDevice); + + return((void *)psDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID mouse device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID mouse 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. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Mouse APIs. +// +//***************************************************************************** +void * +USBDHIDMouseCompositeInit(unsigned long ulIndex, + const tUSBDHIDMouseDevice *psDevice) +{ + tHIDMouseInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDMouseData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDMouseData; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Initialize the various fields in our instance structure. + // + psInst->ucUSBConfigured = 0; + psInst->ucProtocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ucDuration4mS = 0; + psInst->sReportIdle.ucReportID = 0; + psInst->sReportIdle.ulTimeSinceReportmS = 0; + psInst->sReportIdle.usTimeTillNextmS = 0; + psInst->eMouseState = HID_MOUSE_STATE_UNCONFIGURED; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->usPID = psDevice->usPID; + psHIDDevice->usVID = psDevice->usVID; + psHIDDevice->usMaxPowermA = psDevice->usMaxPowermA; + psHIDDevice->ucPwrAttributes = psDevice->ucPwrAttributes; + psHIDDevice->ucSubclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ucProtocol = USB_HID_PROTOCOL_MOUSE; + psHIDDevice->ucNumInputReports = 1; + psHIDDevice->psReportIdle = &psInst->sReportIdle; + psHIDDevice->pfnRxCallback = HIDMouseRxHandler; + psHIDDevice->pvRxCBData = (void *)psDevice; + psHIDDevice->pfnTxCallback = HIDMouseTxHandler; + psHIDDevice->pvTxCBData = (void *)psDevice; + psHIDDevice->bUseOutEndpoint = false; + psHIDDevice->psHIDDescriptor = &g_sMouseHIDDescriptor; + psHIDDevice->ppClassDescriptors= g_pMouseClassDescriptors; + psHIDDevice->ppStringDescriptors = psDevice->ppStringDescriptors; + psHIDDevice->ulNumStringDescriptors = psDevice->ulNumStringDescriptors; + psHIDDevice->psPrivateHIDData = &psInst->sHIDInstance; + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a keyboard. + // + return(USBDHIDCompositeInit(ulIndex, psHIDDevice)); +} + +//***************************************************************************** +// +//! Shuts down the HID mouse device. +//! +//! \param pvInstance 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 +//! pvInstance instance may not me used in any other call to the HID mouse +//! device other than USBDHIDMouseInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDMouseTerm(void *pvInstance) +{ + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get a pointer to the device. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Mark our device as no longer configured. + // + psDevice->psPrivateHIDMouseData->ucUSBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the mouse callback. +//! +//! \param pvInstance 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 pvInstance structure passed to USBDHIDMouseInit() resides +//! in RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDMouseSetCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDMouseDevice *psMouse; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvInstance); + + // + // Get a pointer to our mouse device. + // + psMouse = (tUSBDHIDMouseDevice *)pvInstance; + + // + // 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 pvInstance is the pointer to the mouse device instance structure. +//! \param cDeltaX 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 cDeltaY 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 ucButtons is a bit mask indicating which (if any) of the three +//! mouse buttons is pressed. Valid values are logical OR combinations of +//! \e MOUSE_REPORT_BUTTON_1, \e MOUSE_REPORT_BUTTON_2 and \e +//! 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 will be returned +//! and the state change will be 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. +// +//***************************************************************************** +unsigned long +USBDHIDMouseStateChange(void *pvInstance, char cDeltaX, char cDeltaY, + unsigned char ucButtons) +{ + unsigned long ulRetcode; + unsigned long ulCount; + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + // + // Get a pointer to the device. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDMouseData; + + // + // Update the global mouse report with the information passed. + // + psInst->pucReport[HID_REPORT_BUTTONS] = ucButtons; + psInst->pucReport[HID_REPORT_X] = (unsigned char)cDeltaX; + psInst->pucReport[HID_REPORT_Y] = (unsigned char)cDeltaY; + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ucUSBConfigured) + { + 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->eMouseState = HID_MOUSE_STATE_SEND; + ulCount = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pucReport, MOUSE_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ulCount) + { + // + // No - report the error to the caller. + // + ulRetcode = MOUSE_ERR_TX_ERROR; + } + else + { + ulRetcode = MOUSE_SUCCESS; + } + } + else + { + ulRetcode = MOUSE_ERR_TX_ERROR; + } + // + // Return the relevant error code to the caller. + // + return(ulRetcode); +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the mouse device instance structure. +//! \param ucPower 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 *pvInstance, unsigned char ucPower) +{ + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance 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 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. +// +//***************************************************************************** +tBoolean +USBDHIDMouseRemoteWakeupRequest(void *pvInstance) +{ + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->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..8ca80e6 --- /dev/null +++ b/usblib/device/usbdhidmouse.h @@ -0,0 +1,321 @@ +//***************************************************************************** +// +// usbdhidmouse.h - Public header file for the USB HID Mouse device class +// driver +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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. + // + HID_MOUSE_STATE_UNCONFIGURED, + + // + // No keys to send and not waiting on data. + // + HID_MOUSE_STATE_IDLE, + + // + // Waiting on report data from the host. + // + HID_MOUSE_STATE_WAIT_DATA, + + // + // Waiting on data to be sent out. + // + HID_MOUSE_STATE_SEND +} +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. + // + unsigned char ucUSBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + unsigned char ucProtocol; + + // + // A buffer used to hold the last input report sent to the host. + // + unsigned char pucReport[MOUSE_REPORT_SIZE]; + + // + // The current state of the mouse interrupt IN endpoint. + // + volatile tMouseState eMouseState; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; + + // + // The lower level HID driver's instance data. + // + tHIDInstance sHIDInstance; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDMouseInstance; + +#ifdef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the HID mouse driver. +// The client must provide a block of RAM of at least this size in the +// tHIDMouseInstance field of the tUSBHIDMouseDevice structure passed on +// USBDHIDMouseInit(). The HID mouse driver needs space for the generic HID +// interface + the Mouse Report Buffer + HID mouse interface. +// +// This value is deprecated and should not be used, any new code should just +// pass in a tHIDMouseInstance structure in the psPrivateHIDMouseData field. +// +//***************************************************************************** +#define USB_HID_MOUSE_WORKSPACE_SIZE \ + (sizeof(tHIDMouseInstance)) +#endif + +//***************************************************************************** +// +//! 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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the mouse. + // + tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to 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 *psPrivateHIDMouseData; +} +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 will have been ignored. +// +//***************************************************************************** +#define MOUSE_ERR_NOT_CONFIGURED \ + 4 + +//***************************************************************************** +// +//! Setting this bit in the ucButtons 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 ucButtons 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 ucButtons 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(unsigned long ulIndex, + const tUSBDHIDMouseDevice *psDevice); +extern void *USBDHIDMouseCompositeInit(unsigned long ulIndex, + const tUSBDHIDMouseDevice *psDevice); +extern void USBDHIDMouseTerm(void *pvInstance); +extern void *USBDHIDMouseSetCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDHIDMouseStateChange(void *pvInstance, char cDeltaX, + char cDeltaY, + unsigned char ucButtons); +extern void USBDHIDMousePowerStatusSet(void *pvInstance, + unsigned char ucPower); +extern tBoolean USBDHIDMouseRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// 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..e563423 --- /dev/null +++ b/usblib/device/usbdmsc.c @@ -0,0 +1,2130 @@ +//***************************************************************************** +// +// usbdmsc.c - USB mass storage device class driver. +// +// Copyright (c) 2009-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 "driverlib/udma.h" +#include "usblib/usblib.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 ulFlags member variable. +// +//***************************************************************************** +#define USBD_FLAG_DMA_IN 0x00000001 +#define USBD_FLAG_DMA_OUT 0x00000002 + +//***************************************************************************** +// +// 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_IN_DMA_CHANNEL UDMA_CHANNEL_USBEP1TX +#define DATA_OUT_ENDPOINT USB_EP_1 +#define DATA_OUT_DMA_CHANNEL UDMA_CHANNEL_USBEP1RX + +//***************************************************************************** +// +// 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 sizes of USB transfers for data and commands. +// +//***************************************************************************** +#define MAX_TRANSFER_SIZE 512 +#define COMMAND_BUFFER_SIZE 64 + +//***************************************************************************** +// +// The local buffer used to read in commands and process them. +// +//***************************************************************************** +static unsigned char g_pucCommand[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.ucSCSIState 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 unsigned char g_pMSCDeviceDescriptor[] = +{ + 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 unsigned char g_pMSCDescriptor[] = +{ + // + // 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 unsigned char g_pMSCInterface[] = +{ + // + // 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 | USB_EP_TO_INDEX(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 | USB_EP_TO_INDEX(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_pMSCDescriptor), + g_pMSCDescriptor +}; + +const tConfigSection g_sMSCInterfaceSection = +{ + sizeof(g_pMSCInterface), + g_pMSCInterface +}; + +//***************************************************************************** +// +// 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(tConfigSection *)) + +//***************************************************************************** +// +// 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_pMSCConfigDescriptors[] = +{ + &g_sMSCConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSCSISendStatus(const tUSBDMSCDevice *psDevice); +unsigned long USBDSCSICommand(const tUSBDMSCDevice *psDevice, + tMSCCBW *pSCSICBW); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The FIFO configuration for USB mass storage class device. +// +//***************************************************************************** +const tFIFOConfig g_sUSBMSCFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN | USB_EP_DMA_MODE_1 | USB_EP_AUTO_SET }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT | USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//***************************************************************************** +// +// The device information structure for the USB MSC device. +// +//***************************************************************************** +tDeviceInfo g_sMSCDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // 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 + }, + g_pMSCDeviceDescriptor, + g_pMSCConfigDescriptors, + 0, + 0, + &g_sUSBMSCFIFOConfig +}; + +//***************************************************************************** +// +//! This function is used by an application if it can detect insertion or +//! removal of the media. +//! +//! \param pvInstance is the mass storage device instance that had a media +//! change. +//! \param eMediaStatus 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 eMediaStatus 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 eMediaStatus +//! parameter: +//! - USBDMSC_MEDIA_PRESENT indicates that the media is present or has been +//! added. +//! - USBDMSC_MEDIA_NOTPRESENT indicates that the media is not present or was +//! removed. +//! - USBDMSC_MEDIA_UNKNOWN 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 USBDMSC_MEDIA_UNKNOWN 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 *pvInstance, tUSBDMSCMediaStatus eMediaStatus) +{ + const tUSBDMSCDevice *psDevice; + + // + // Create a device instance pointer. + // + psDevice = pvInstance; + + // + // Save the current media status. + // + psDevice->psPrivateData->eMediaStatus = eMediaStatus; +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the Bulk endpoints for +// the mass storage class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDMSCDevice *psDevice; + tMSCInstance *psInst; + tMSCCBW *pSCSICBW; + unsigned long ulEPStatus; + unsigned long ulSize; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psDevice = pvInstance; + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateData; + + // + // Get the endpoints status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Handler for the bulk IN data endpoint. + // + if((ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) || + ((psInst->ulFlags & USBD_FLAG_DMA_IN) && + (MAP_uDMAChannelModeGet(psInst->ucINDMA) == UDMA_MODE_STOP))) + { + switch(psInst->ucSCSIState) + { + // + // 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->ulBytesToTransfer -= MAX_TRANSFER_SIZE; + + // + // If we are done then move on to the status phase. + // + if(psInst->ulBytesToTransfer == 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->ulFlags &= ~USBD_FLAG_DMA_IN; + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucINEndpoint, + USB_EP_DEV_IN); + + // + // Send back the status once this transfer is complete. + // + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; + + if(psDevice->pfnEventCallback) + { + psDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, 0, + 0); + } + + // + // The transfer is complete so don't read anymore data. + // + break; + } + + // + // Move on to the next Logical Block. + // + psInst->ulCurrentLBA++; + + // + // Read the new data and send it out. + // + if(psDevice->sMediaFunctions.BlockRead(psInst->pvMedia, + (unsigned char *)psInst->pulBuffer, + psInst->ulCurrentLBA, 1) == 0) + { + } + + // + // Reset the DMA transfer and enable the DMA channel. + // + MAP_uDMAChannelTransferSet(psInst->ucINDMA, + UDMA_MODE_BASIC, + psInst->pulBuffer, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucINEndpoint), + (MAX_TRANSFER_SIZE >> 2)); + MAP_uDMAChannelEnable(psInst->ucINDMA); + + break; + } + + // + // Handle sending status. + // + case STATE_SCSI_SEND_STATUS: + { + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psDevice); + + break; + } + + // + // Handle completing sending status. + // + case STATE_SCSI_SENT_STATUS: + { + psInst->ucSCSIState = 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((ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) || + ((psInst->ulFlags & USBD_FLAG_DMA_OUT) && + (MAP_uDMAChannelModeGet(psInst->ucOUTDMA) == UDMA_MODE_STOP))) + { + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + switch(psInst->ucSCSIState) + { + // + // Receiving and writing bytes to the storage device. + // + case STATE_SCSI_RECEIVE_BLOCKS: + { + // + // Update the current status for the buffer. + // + psInst->ulBytesToTransfer -= MAX_TRANSFER_SIZE; + + // + // Write the new data. + // + psDevice->sMediaFunctions.BlockWrite(psInst->pvMedia, + (unsigned char *)psInst->pulBuffer, + psInst->ulCurrentLBA, 1); + + // + // Move on to the next Logical Block. + // + psInst->ulCurrentLBA++; + + // + // Check if all bytes have been received. + // + if(psInst->ulBytesToTransfer == 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->ulFlags &= ~USBD_FLAG_DMA_OUT; + + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psDevice); + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucOUTEndpoint, + USB_EP_DEV_OUT); + + // + // If there is an event callback then call it to notify + // that last operation has completed. + // + if(psDevice->pfnEventCallback) + { + psDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, 0, + 0); + } + } + else + { + // + // Configure and enable DMA for the OUT transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, + UDMA_MODE_BASIC, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucOUTEndpoint), + psInst->pulBuffer, + (MAX_TRANSFER_SIZE >> 2)); + + // + // Start the DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucOUTDMA); + } + + 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. + // + ulSize = COMMAND_BUFFER_SIZE; + MAP_USBEndpointDataGet(psInst->ulUSBBase, psInst->ucOUTEndpoint, + g_pucCommand, &ulSize); + pSCSICBW = (tMSCCBW *)g_pucCommand; + + // + // Acknowledge the OUT data packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + false); + + // + // If this is a valid CBW then handle it. + // + if(pSCSICBW->dCBWSignature == CBW_SIGNATURE) + { + g_sSCSICSW.dCSWSignature = CSW_SIGNATURE; + g_sSCSICSW.dCSWTag = pSCSICBW->dCBWTag; + g_sSCSICSW.dCSWDataResidue = 0; + g_sSCSICSW.bCSWStatus = 0; + + USBDSCSICommand(psDevice, pSCSICBW); + } + 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->ucSCSIState = STATE_SCSI_IDLE; + } + + break; + } + default: + { + break; + } + } + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, + ulEPStatus); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tMSCInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDMSCDevice *)pvInstance)->psPrivateData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ucInterface = pucData[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(pucData[0] & USB_EP_DESC_IN) + { + psInst->ucINEndpoint = INDEX_TO_USB_EP((pucData[1] & 0x7f)); + + psInst->ucINDMA = UDMA_CHANNEL_USBEP1TX + + (((pucData[1] & 0x7f) - 1) * 2); + + // + // Basic configuration for DMA on the IN endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucINDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_32| + UDMA_DST_INC_NONE | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects + // devices that have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucINEndpoint, + psInst->ucINDMA); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + psInst->ucOUTDMA = UDMA_CHANNEL_USBEP1RX + + (((pucData[1] & 0x7f) - 1) * 2); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects + // devices that have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, + psInst->ucOUTDMA); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDMSCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDMSCDevice *)pvInstance; + + // + // Close the drive requested. + // + if(psDevice->psPrivateData->pvMedia != 0) + { + psDevice->psPrivateData->pvMedia = 0; + psDevice->sMediaFunctions.Close(0); + } + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnEventCallback(pvInstance, USB_EVENT_DISCONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvInstance, unsigned long ulValue) +{ + tMSCInstance *psInst; + const tUSBDMSCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDMSCDevice *)pvInstance; + + // + // Create the serial instance data. + // + psInst = psDevice->psPrivateData; + + // + // Insure that DMA is disable whenever the configuration is set. + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN); + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucOUTEndpoint, USB_EP_DEV_OUT); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucOUTDMA, UDMA_SIZE_32 | + UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | + UDMA_ARB_16); + + // + // Select this channel for this endpoint, this only affects devices that + // have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, psInst->ucOUTDMA); + + // + // Basic configuration for DMA on the IN endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucINDMA, UDMA_SIZE_32 | UDMA_SRC_INC_32| + UDMA_DST_INC_NONE | UDMA_ARB_16); + + // + // Select this channel for this endpoint, this only affects devices that + // have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucINEndpoint, psInst->ucINDMA); + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnEventCallback(pvInstance, 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 ulIndex is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psDevice 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 psDevice 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(unsigned long ulIndex, const tUSBDMSCDevice *psDevice) +{ + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + USBDMSCCompositeInit(ulIndex, psDevice); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateData->psDevInfo); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the mass storage class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psDevice 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 psDevice 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 * +USBDMSCCompositeInit(unsigned long ulIndex, const tUSBDMSCDevice *psDevice) +{ + tMSCInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pMSCDescriptor; + psInst->psDevInfo = &g_sMSCDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->bConnected = false; + psInst->eMediaStatus = USBDMSC_MEDIA_UNKNOWN; + + // + // Set the initial interface and endpoints. + // + psInst->ucInterface = 0; + psInst->ucOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucOUTDMA = DATA_OUT_DMA_CHANNEL; + psInst->ucINEndpoint = DATA_IN_ENDPOINT; + psInst->ucINDMA = DATA_IN_DMA_CHANNEL; + + // + // Set the initial SCSI state to idle. + // + psInst->ucSCSIState = STATE_SCSI_IDLE; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psDevice->ulNumStringDescriptors; + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // If DMA is in use then clear all DMA attributes. + // + MAP_uDMAChannelAttributeDisable(psInst->ucINDMA, UDMA_ATTR_ALL); + MAP_uDMAChannelAttributeDisable(psInst->ucOUTDMA, UDMA_ATTR_ALL); + + // + // Open the drive requested. + // + psInst->pvMedia = psDevice->sMediaFunctions.Open(0); + + if(psInst->pvMedia == 0) + { + // + // There is no media currently present. + // + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + else + { + // + // Media is now ready for use. + // + psInst->ucSenseKey = SCSI_RS_KEY_UNIT_ATTN; + psInst->usAddSenseCode = SCSI_RS_MED_NOTRDY2RDY; + } + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the mass storage device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDMSCInit() or USBDMSCInitComposite(). +//! +//! This function terminates mass storage operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e psDevice instance may not me used in any other call to the mass storage +//! device other than USBDMSCInit() or USBDMSCInitComposite(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDMSCTerm(void *pvInstance) +{ + const tUSBDMSCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); + + // + // Create a device instance pointer. + // + psDevice = pvInstance; + + // + // If the media was opened the close it out. + // + if(psDevice->psPrivateData->pvMedia != 0) + { + psDevice->psPrivateData->pvMedia = 0; + psDevice->sMediaFunctions.Close(0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance 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 *pvInstance, tUSBRequest *pUSBRequest) +{ + // + // This class only support a single LUN. + // + const static unsigned char ucMaxLun = 0; + + ASSERT(pvInstance != 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: + { + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&ucMaxLun, 1); + + 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(const tUSBDMSCDevice *psDevice) +{ + long lIdx; + tMSCInstance *psInst; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)g_pucCommand; + + // + // Create the serial instance data. + // + psInst = psDevice->psPrivateData; + + // + // Direct Access device, Removable storage and SCSI 1 responses. + // + pulData[0] = SCSI_INQ_PDT_SBC | (SCSI_INQ_RMB << 8); + + // + // Additional Length is fixed at 31 bytes. + // + pulData[1] = 31; + + // + // Copy the Vendor string. + // + for(lIdx = 0; lIdx < 8; lIdx++) + { + g_pucCommand[lIdx + 8] = psDevice->pucVendor[lIdx]; + } + + // + // Copy the Product string. + // + for(lIdx = 0; lIdx < 16; lIdx++) + { + g_pucCommand[lIdx + 16] = psDevice->pucProduct[lIdx]; + } + + // + // Copy the Version string. + // + for(lIdx = 0; lIdx < 4; lIdx++) + { + g_pucCommand[lIdx + 32] = psDevice->pucVersion[lIdx]; + } + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, 36); + + // + // Send the data to the host. + // + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, 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->ucSCSIState = 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(const tUSBDMSCDevice *psDevice) +{ + unsigned long ulBlocks; + tMSCInstance *psInst; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)g_pucCommand; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + if(psInst->pvMedia != 0) + { + ulBlocks = psDevice->sMediaFunctions.NumBlocks(psInst->pvMedia); + + pulData[0] = 0x08000000; + + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pucCommand[4] = ulBlocks >> 24; + g_pucCommand[5] = 0xff & (ulBlocks >> 16); + g_pucCommand[6] = 0xff & (ulBlocks >> 8); + g_pucCommand[7] = 0xff & (ulBlocks); + + // + // Current media capacity + // + g_pucCommand[8] = 0x2; + + // + // Fill in the block size, which is fixed at DEVICE_BLOCK_SIZE. + // + g_pucCommand[9] = 0xff & (DEVICE_BLOCK_SIZE >> 16); + g_pucCommand[10] = 0xff & (DEVICE_BLOCK_SIZE >> 8); + g_pucCommand[11] = 0xff & DEVICE_BLOCK_SIZE; + + // + // Send out the 12 bytes that are in this response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, + 12); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, 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->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ucSCSIState = 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(const tUSBDMSCDevice *psDevice) +{ + unsigned long ulBlocks; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + ulBlocks = psDevice->sMediaFunctions.NumBlocks(psInst->pvMedia); + + // + // Only decrement if any blocks were found. + // + if(ulBlocks != 0) + { + // + // One less than the maximum number is the last addressable + // block. + // + ulBlocks--; + } + + if(psInst->pvMedia != 0) + { + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pucCommand[0] = 0xff & (ulBlocks >> 24); + g_pucCommand[1] = 0xff & (ulBlocks >> 16); + g_pucCommand[2] = 0xff & (ulBlocks >> 8); + g_pucCommand[3] = 0xff & (ulBlocks); + + g_pucCommand[4] = 0; + + // + // Fill in the block size, which is fixed at DEVICE_BLOCK_SIZE. + // + g_pucCommand[5] = 0xff & (DEVICE_BLOCK_SIZE >> 16); + g_pucCommand[6] = 0xff & (DEVICE_BLOCK_SIZE >> 8); + g_pucCommand[7] = 0xff & DEVICE_BLOCK_SIZE; + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, + 8); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, 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->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ucSCSIState = 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(const tUSBDMSCDevice *psDevice) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // The request sense response. + // + g_pucCommand[0] = psInst->ucErrorCode; + g_pucCommand[1] = 0; + g_pucCommand[2] = psInst->ucSenseKey; + *(unsigned long *)&g_pucCommand[3] = 0; + + // + // There are 10 more bytes of data. + // + g_pucCommand[7] = 10; + + *(unsigned long *)&g_pucCommand[8] = 0; + + // + // Transition from not ready to ready. + // + *(unsigned short *)&g_pucCommand[12] = psInst->usAddSenseCode; + *(unsigned long *)&g_pucCommand[14] = 0; + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, 18); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Reset the valid flag on errors. + // + psInst->ucErrorCode = 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->ucSCSIState = 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(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + unsigned short usNumBlocks; + tMSCInstance *psInst; + + // + // Default the number of blocks. + // + usNumBlocks = 0; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + if(psInst->pvMedia != 0) + { + // + // Get the logical block from the CBW structure. This switching + // is required to convert from big to little endian. + // + psInst->ulCurrentLBA = (pSCSICBW->CBWCB[2] << 24) | + (pSCSICBW->CBWCB[3] << 16) | + (pSCSICBW->CBWCB[4] << 8) | + (pSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + usNumBlocks = (pSCSICBW->CBWCB[7] << 8) | pSCSICBW->CBWCB[8]; + + // + // Read the next logical block from the storage device. + // + if(psDevice->sMediaFunctions.BlockRead(psInst->pvMedia, + (unsigned char *)psInst->pulBuffer, + psInst->ulCurrentLBA, 1) == 0) + { + psInst->pvMedia = 0; + psDevice->sMediaFunctions.Close(0); + } + } + + // + // If there is media present then start transferring the data. + // + if(psInst->pvMedia != 0) + { + // + // Enable DMA on the endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, psInst->ucINEndpoint, + USB_EP_DEV_IN); + + // + // Configure and DMA for the IN transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucINDMA, + UDMA_MODE_BASIC, + psInst->pulBuffer, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucINEndpoint), + (MAX_TRANSFER_SIZE >> 2)); + + // + // Remember that a DMA is in progress. + // + psInst->ulFlags |= USBD_FLAG_DMA_IN; + + // + // Schedule the remaining bytes to send. + // + psInst->ulBytesToTransfer = (DEVICE_BLOCK_SIZE * usNumBlocks); + + // + // Start the DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucINDMA); + + // + // Move on and start sending blocks. + // + psInst->ucSCSIState = STATE_SCSI_SEND_BLOCKS; + + if(psDevice->pfnEventCallback) + { + psDevice->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->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ucSCSIState = 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(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + unsigned short usNumBlocks; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // 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->ulCurrentLBA = (pSCSICBW->CBWCB[2] << 24) | + (pSCSICBW->CBWCB[3] << 16) | + (pSCSICBW->CBWCB[4] << 8) | + (pSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + usNumBlocks = (pSCSICBW->CBWCB[7] << 8) | pSCSICBW->CBWCB[8]; + + psInst->ulBytesToTransfer = DEVICE_BLOCK_SIZE * usNumBlocks; + + // + // Start sending logical blocks, these are always multiples of + // DEVICE_BLOCK_SIZE bytes. + // + psInst->ucSCSIState = STATE_SCSI_RECEIVE_BLOCKS; + + // + // Enable uDMA on the endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, psInst->ucOUTEndpoint, + USB_EP_DEV_OUT); + + // + // Configure the DMA for the OUT transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, + UDMA_MODE_BASIC, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucOUTEndpoint), + psInst->pulBuffer, + (MAX_TRANSFER_SIZE >> 2)); + + // + // Remember that a DMA is in progress. + // + psInst->ulFlags |= USBD_FLAG_DMA_OUT; + + // + // Enable the OUT DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucOUTDMA); + + // + // Notify the application of the write event. + // + if(psDevice->pfnEventCallback) + { + psDevice->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->ucOUTEndpoint, + USB_EP_DEV_OUT); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ucSCSIState = 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(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + // + // Three extra bytes in this response. + // + g_pucCommand[0] = 3; + g_pucCommand[1] = 0; + g_pucCommand[2] = 0; + g_pucCommand[3] = 0; + + // + // Manually send the response back to the host. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, + 4); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, 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 = pSCSICBW->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->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ucSCSIState = 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(const tUSBDMSCDevice *psDevice) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // Respond with the requested status. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, + (unsigned char *)&g_sSCSICSW, 13); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Move the state to status sent so that the next interrupt will move the + // statue to idle. + // + psInst->ucSCSIState = STATE_SCSI_SENT_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle all SCSI commands. +// +//***************************************************************************** +unsigned long +USBDSCSICommand(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + unsigned long ulRetCode; + unsigned long ulTransferLength; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // Initialize the return code. + // + ulRetCode = 1; + + // + // Save the transfer length because it may be overwritten by some calls. + // + ulTransferLength = pSCSICBW->dCBWDataTransferLength; + + switch(pSCSICBW->CBWCB[0]) + { + // + // Respond to the SCSI Inquiry command. + // + case SCSI_INQUIRY_CMD: + { + USBDSCSIInquiry(psDevice); + + 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 + { + // + // Since there was no media, check for media here. + // + psInst->pvMedia = psDevice->sMediaFunctions.Open(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(psDevice); + + break; + } + + // + // Handle the Read Capacity command. + // + case SCSI_READ_CAPACITY: + { + USBDSCSIReadCapacity(psDevice); + + break; + } + + // + // Handle the Request Sense command. + // + case SCSI_REQUEST_SENSE: + { + USBDSCSIRequestSense(psDevice); + + break; + } + + // + // Handle the Read 10 command. + // + case SCSI_READ_10: + { + USBDSCSIRead10(psDevice, pSCSICBW); + + break; + } + + // + // Handle the Write 10 command. + // + case SCSI_WRITE_10: + { + USBDSCSIWrite10(psDevice, pSCSICBW); + + break; + } + + // + // Handle the Mode Sense 6 command. + // + case SCSI_MODE_SENSE_6: + { + USBDSCSIModeSense6(psDevice, pSCSICBW); + + 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 = pSCSICBW->dCBWDataTransferLength; + + // + // If there is data then there is more work to do. + // + if(pSCSICBW->dCBWDataTransferLength != 0) + { + if(pSCSICBW->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, + USB_EP_DEV_IN); + } + else + { + // + // Stall the OUT endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucOUTEndpoint, + USB_EP_DEV_OUT); + + } + // + // Send the status once the stall occurs. + // + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; + } + + // + // Set the sense codes. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_ILGL_RQST; + psInst->usAddSenseCode = SCSI_RS_PV_INVALID; + + break; + } + } + + // + // If there is no data then send out the current status. + // + if(ulTransferLength == 0) + { + USBDSCSISendStatus(psDevice); + } + return(ulRetCode); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdmsc.h b/usblib/device/usbdmsc.h new file mode 100644 index 0000000..6719827 --- /dev/null +++ b/usblib/device/usbdmsc.h @@ -0,0 +1,391 @@ +//***************************************************************************** +// +// usbdmsc.h - USB mass storage device class driver. +// +// Copyright (c) 2009-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// USBDMSCMediaChange() ulStatus values. +// +//***************************************************************************** +typedef enum +{ + USBDMSC_MEDIA_PRESENT, + USBDMSC_MEDIA_NOTPRESENT, + USBDMSC_MEDIA_UNKNOWN +} +tUSBDMSCMediaStatus; + +//***************************************************************************** +// +//! Media Access functions. +// +//***************************************************************************** +typedef struct +{ + // + //! This function is used to initialize and open the physical drive number + //! associated with the parameter ulDrive. The function will return zero if + //! the drive could not be opened for some reason. In the case of removable + //! device like an SD card this function should return zero if the SD card + //! is not present. + //! The function returns a pointer to data that should be passed to other + //! APIs or it will return 0 if no drive was found. + // + void *(* Open)(unsigned long ulDrive); + + //***************************************************************************** + // + // This function close the drive number in use by the mass storage class device. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // + // This function is used to close the physical drive number associated with the + // parameter /e pvDrive. This function will return 0 if the drive was closed + // successfully and any other value will indicate a failure. + // + // /return Returns 0 if the drive was successfully closed or non-zero for a + // failure. + // + //***************************************************************************** + void (* Close)(void * pvDrive); + + //***************************************************************************** + // + // This function will read a block from a device opened by the + // USBDMSCStorageOpen() call. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // /param pucData is the buffer that data will be written into. + // /param ulSector is the block address to read. + // /param ulNumBlocks is the number of blocks to read. + // + // This function is use to read blocks from a physical device and return them + // in the /e pucData buffer. The data area pointed to by /e pucData should be + // at least /e ulNumBlocks * Block Size bytes to prevent overwriting data. + // + // /return Returns the number of bytes that were read from the device. + // + //***************************************************************************** + unsigned long (* BlockRead)(void * pvDrive, unsigned char *pucData, + unsigned long ulSector, + unsigned long ulNumBlocks); + + //***************************************************************************** + // + // This function will write a block to a device opened by the + // USBDMSCStorageOpen() call. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // /param pucData is the buffer that data will be used for writing. + // /param ulNumBlocks is the number of blocks to write. + // + // This function is use to write blocks to a physical device from the buffer + // pointed to by the /e pucData buffer. If the number of blocks is greater than + // one then the block address will increment and write to the next block until + // /e ulNumBlocks * Block Size bytes have been written. + // + // /return Returns the number of bytes that were written to the device. + // + //***************************************************************************** + unsigned long (* BlockWrite)(void * pvDrive, unsigned char *pucData, + unsigned long ulSector, + unsigned long ulNumBlocks); + + //***************************************************************************** + // + // This function will return the number of blocks present on a device. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // + // This function is used to return the total number of blocks on a physical + // device based on the /e pvDrive parameter. + // + // /return Returns the number of blocks that are present in a device. + // + //***************************************************************************** + unsigned long (* NumBlocks)(void * pvDrive); + +} +tMSCDMedia; + +//***************************************************************************** +// +// These defines control the sizes of USB transfers for data and commands. +// +//***************************************************************************** +#define DEVICE_BLOCK_SIZE 512 + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// mass storage class. The memory for this structure is pointed to by +// the psPrivateData 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 +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + + // + // These three values are used to return the current sense data for an + // instance of the mass storage class. + // + unsigned char ucErrorCode; + unsigned char ucSenseKey; + unsigned short usAddSenseCode; + + // + // The pointer to the instance returned from the Open call to the media. + // + void *pvMedia; + + volatile tBoolean bConnected; + + // + // Holds the flag settings for this instance. + // + unsigned long ulFlags; + + tUSBDMSCMediaStatus eMediaStatus; + + unsigned long pulBuffer[DEVICE_BLOCK_SIZE>>2]; + unsigned long ulBytesToTransfer; + unsigned long ulCurrentLBA; + + unsigned char ucINEndpoint; + unsigned char ucINDMA; + unsigned char ucOUTEndpoint; + unsigned char ucOUTDMA; + unsigned char ucInterface; + unsigned char ucSCSIState; +} +tMSCInstance; + +//***************************************************************************** +// +// +//***************************************************************************** +#ifdef DEPRECATED +//***************************************************************************** +// +// This is the size in bytes of the private data for the mass storage class. +// +// This value is deprecated and should not be used, any new code should just +// pass in a tMSCInstance structure in the psPrivateData field. +// +//***************************************************************************** +#define USB_MSC_WORKSPACE_SIZE sizeof(tMSCInstance); +#endif + +//***************************************************************************** +// +//! 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. +// +// For reference this is sizeof(g_pAudioControlInterface) + +// sizeof(g_pAudioStreamInterface +// +//***************************************************************************** +#define COMPOSITE_DMSC_SIZE (23) + +//***************************************************************************** +// +//! 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. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! 8 byte vendor string. + // + unsigned char pucVendor[8]; + + // + //! 16 byte vendor string. + // + unsigned char pucProduct[16]; + + // + //! 4 byte vendor string. + // + unsigned char pucVersion[4]; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! 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. + // + unsigned char ucPwrAttributes; + + // + //! 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 unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! 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. + // + tMSCDMedia sMediaFunctions; + + // + //! This is the callback function for various events that occur during + //! mass storage class operation. + // + tUSBCallback pfnEventCallback; + + // + //! A pointer to 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 *psPrivateData; +} +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) + +extern tDeviceInfo g_sMSCDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDMSCInit(unsigned long ulIndex, + const tUSBDMSCDevice *psMSCDevice); +extern void *USBDMSCCompositeInit(unsigned long ulIndex, + const tUSBDMSCDevice *psMSCDevice); +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-cm3/Exe/usblib-cm3.a b/usblib/ewarm-cm3/Exe/usblib-cm3.a new file mode 100644 index 0000000..fca0847 Binary files /dev/null and b/usblib/ewarm-cm3/Exe/usblib-cm3.a differ diff --git a/usblib/ewarm-cm4f/Exe/usblib-cm4f.a b/usblib/ewarm-cm4f/Exe/usblib-cm4f.a new file mode 100644 index 0000000..eaf795c Binary files /dev/null and b/usblib/ewarm-cm4f/Exe/usblib-cm4f.a differ diff --git a/usblib/gcc-cm3/libusb-cm3.a b/usblib/gcc-cm3/libusb-cm3.a new file mode 100644 index 0000000..c863738 Binary files /dev/null and b/usblib/gcc-cm3/libusb-cm3.a differ diff --git a/usblib/gcc-cm4f/libusb-cm4f.a b/usblib/gcc-cm4f/libusb-cm4f.a new file mode 100644 index 0000000..4789abd Binary files /dev/null and b/usblib/gcc-cm4f/libusb-cm4f.a differ diff --git a/usblib/host/usbhaudio.c b/usblib/host/usbhaudio.c new file mode 100644 index 0000000..fcaa9c3 --- /dev/null +++ b/usblib/host/usbhaudio.c @@ -0,0 +1,1528 @@ +//***************************************************************************** +// +// usbhaudio.c - USB host audio driver. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usbaudio.h" +#include "usblib/host/usbhost.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 *pDevice); +static void USBAudioClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB host audio driver. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the call back. + // + tUSBHostAudioCallback pfnCallback; + + // + // This is the control interface. + // + unsigned char ucIControl; + + // + // This is the output streaming interface. + // + unsigned char ucOutInterface; + + // + // This is the currently selected active output interface used with + // ucOutInterface interface. + // + unsigned char ucOutAltSetting; + + // + // This is the streaming interface. + // + unsigned char ucInInterface; + + // + // This is the currently selected active input interface used with + // ucInInterface interface. + // + unsigned char ucInAltSetting; + + // + // The Isochronous endpoint addresses. + // + unsigned char ucIsochInAddress; + unsigned char ucIsochOutAddress; + + tACInputTerminal *pInTerminal; + tACOutputTerminal *pOutTerminal; + + // + // Holds the identifier for the Feature Unit for controlling volume. + // + unsigned char ucVolumeID; + + tACFeatureUnit *pFeatureUnit; + + // + // Holds what types of controls are enabled on the device. + // + unsigned short pusControls[3]; + + // + // Isochronous IN pipe. + // + unsigned long ulIsochInPipe; + unsigned short usPipeSizeIn; + tUSBHostAudioCallback pfnInCallback; + void *pvInBuffer; + + // + // Isochronous OUT pipe. + // + unsigned long ulIsochOutPipe; + unsigned short usPipeSizeOut; + tUSBHostAudioCallback pfnOutCallback; + void *pvOutBuffer; + + // + // State flags for this audio instance. + // + unsigned long ulFlags; +} +tUSBHostAudioInstance; + +//***************************************************************************** +// +// 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_AudioDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Audio Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHostAudioClassDriver = +{ + USB_CLASS_AUDIO, + USBAudioOpen, + USBAudioClose, + 0 +}; + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB IN pipe. +// +//***************************************************************************** +static void +PipeCallbackIN(unsigned long ulPipe, unsigned long ulEvent) +{ + // + // Only handle the data available callback and pass it on to the + // application. + // + if(ulEvent == USB_EVENT_RX_AVAILABLE) + { + if(g_AudioDevice.pfnInCallback) + { + g_AudioDevice.pfnInCallback( + g_AudioDevice.pvInBuffer, 0, USB_EVENT_RX_AVAILABLE); + } + } +} + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB OUT pipe. +// +//***************************************************************************** +static void +PipeCallbackOUT(unsigned long ulPipe, unsigned long ulEvent) +{ + // + // Only handle the transmit complete callback and pass it on to the + // application. + // + if(ulEvent == USB_EVENT_TX_COMPLETE) + { + if(g_AudioDevice.pfnOutCallback) + { + g_AudioDevice.pfnOutCallback( + g_AudioDevice.pvOutBuffer, 0, USB_EVENT_TX_COMPLETE); + } + } +} + +//***************************************************************************** +// +// Finds a given terminal and type in an audio configuration descriptor. +// +//***************************************************************************** +static tDescriptorHeader * +AudioTerminalGet(tConfigDescriptor *pConfigDesc, unsigned long ulTerminal, + unsigned long ulTerminalType) +{ + tACOutputTerminal *pOutput; + tDescriptorHeader *pHeader; + long lBytesRemaining; + + pHeader = (tDescriptorHeader *)pConfigDesc; + lBytesRemaining = pConfigDesc->wTotalLength; + + while(lBytesRemaining > 0) + { + // + // Output and input terminals are the same past the bDescriptorSubtype + // and wTerminalType that are being searched for. + // + pOutput = (tACOutputTerminal *)pHeader; + + // + // Only CS_INTERFACE descriptors can be a terminal. + // + if((pHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE) && + (ulTerminal == pOutput->bDescriptorSubtype)) + { + if((pOutput->bDescriptorSubtype == USB_AI_OUTPUT_TERMINAL) || + (pOutput->bDescriptorSubtype == USB_AI_INPUT_TERMINAL)) + + { + // + // If this was the terminal type that was requested, the + // return it. + // + if(pOutput->wTerminalType == ulTerminalType) + { + return(pHeader); + } + } + else if(pOutput->bDescriptorSubtype == USB_AI_FEATURE_UNIT) + { + return(pHeader); + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + lBytesRemaining -= pHeader->bLength; + + // + // Move the pointer to the next header. + // + pHeader = (tDescriptorHeader*)((unsigned long)pHeader + + pHeader->bLength); + } + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +// This function returns the interface number for the control interface +// in the structure passed in the pConfigDesc. +// +// \param pConfigDescriptor 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 unsigned long +AudioControlGet(tConfigDescriptor *pConfigDesc) +{ + tDescriptorHeader *pHeader; + tInterfaceDescriptor *pInterface; + unsigned long ulInterface; + long lBytes; + + pHeader = (tDescriptorHeader *)pConfigDesc; + lBytes = pConfigDesc->wTotalLength; + + // + // Initialize the interface number to an invalid value. + // + ulInterface = INVALID_INTERFACE; + + // + // Search the whole configuration descriptor. + // + while(lBytes > 0) + { + // + // Find an interface descriptor and see if it is a control interface. + // + if(pHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + pInterface = (tInterfaceDescriptor *)pHeader; + + // + // If this is the control interface then return the value to the + // caller. + // + if(pInterface->bInterfaceSubClass == USB_ASC_AUDIO_CONTROL) + { + ulInterface = pInterface->bInterfaceNumber; + + break; + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + lBytes -= pHeader->bLength; + + // + // Move the pointer to the next header. + // + pHeader = (tDescriptorHeader*)((unsigned long)pHeader + + pHeader->bLength); + } + return(ulInterface); +} + +//***************************************************************************** +// +// If it exists, finds the correct audio interface for a given audio format. +// +//***************************************************************************** +static unsigned long +AudioGetInterface(tUSBHostAudioInstance *pAudioDevice, + unsigned short usFormat, unsigned long ulSampleRate, + unsigned long ulBytes, unsigned long ulChannels, + unsigned long ulFlags) +{ + tDescriptorHeader *pHeader; + tInterfaceDescriptor *pInterface; + tEndpointDescriptor *pINEndpoint, *pOUTEndpoint; + tACHeader *pACHeader; + tACGeneral *pGeneral; + tASFormat *pFormat; + tEndpointDescriptor *pEndpoint; + unsigned char *pucValue; + unsigned long ulValue; + long lBytes, lIdx; + + // + // Initialize the Interface pointer to null. + // + pInterface = 0; + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // Start at the top of the configuration descriptor. + // + pHeader = (tDescriptorHeader *)pAudioDevice->pDevice->pConfigDescriptor; + + lBytes = pAudioDevice->pDevice->pConfigDescriptor->wTotalLength; + + while(lBytes > 0) + { + if(pHeader->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(pInterface) + { + break; + } + + // + // Get the new interface pointer. + // + pInterface = (tInterfaceDescriptor *)pHeader; + + // + // 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((pInterface->bNumEndpoints == 0) || + (pInterface->bInterfaceClass != USB_CLASS_AUDIO) || + (pInterface->bInterfaceSubClass != USB_ASC_AUDIO_STREAMING)) + { + pInterface = 0; + } + } + if((pInterface) && (pHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE)) + { + pACHeader = (tACHeader *)pHeader; + + // + // 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 *)pHeader; + + // + // If this interface has the wrong format then set it to null + // so that the rest of this interface is ignored. + // + if(pGeneral->wFormatTag != usFormat) + { + pInterface = 0; + } + } + else if(pACHeader->bDescriptorSubtype == USB_AS_FORMAT_TYPE) + { + pFormat = (tASFormat *)pHeader; + + // + // 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 != ulChannels) || + (pFormat->bSubFrameSize != ulBytes)) + { + pInterface = 0; + } + else + { + pucValue = &pFormat->tSamFreq; + + // + // Attempt to find the sample rate in the sample rate + // table for this interface. + // + for(lIdx = 0; lIdx < pFormat->bSamFreqType; lIdx++) + { + ulValue = (*((unsigned long *)&pucValue[lIdx * 3]) & + 0xffffff); + + if(ulValue == ulSampleRate) + { + 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(lIdx == pFormat->bSamFreqType) + { + pInterface = 0; + } + } + } + } + else if((pInterface) && + (pHeader->bDescriptorType == USB_DTYPE_ENDPOINT)) + { + pEndpoint = (tEndpointDescriptor *)pHeader; + + // + // See what direction is being requested. + // + if(ulFlags & 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) + { + pInterface = 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) + { + pInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint; + // + pOUTEndpoint = pEndpoint; + } + } + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + lBytes -= pHeader->bLength; + + // + // Move the pointer to the next header. + // + pHeader = (tDescriptorHeader*)((unsigned long)pHeader + + pHeader->bLength); + } + + // + // If there is still a valid interface then return the values. + // + if(pInterface) + { + // + // Check a valid IN endpoint descriptor. + // + if(pINEndpoint) + { + // + // Save the endpoint address. + // + g_AudioDevice.ucIsochInAddress = 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_AudioDevice.ulIsochInPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_AudioDevice.ulIsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_AudioDevice.pDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + } + else if(g_AudioDevice.usPipeSizeIn < pINEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_AudioDevice.ulIsochInPipe); + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_AudioDevice.ulIsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_AudioDevice.pDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_AudioDevice.usPipeSizeIn = pINEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous IN end point. + // + USBHCDPipeConfig(g_AudioDevice.ulIsochInPipe, + pINEndpoint->wMaxPacketSize, + 0, + g_AudioDevice.ucIsochInAddress); + } + + // + // Check a valid OUT endpoint descriptor. + // + if(pOUTEndpoint) + { + // + // Save the endpoint address. + // + g_AudioDevice.ucIsochOutAddress = 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_AudioDevice.ulIsochOutPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_AudioDevice.ulIsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_AudioDevice.pDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + } + else if(g_AudioDevice.usPipeSizeOut < pOUTEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_AudioDevice.ulIsochOutPipe); + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_AudioDevice.ulIsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_AudioDevice.pDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_AudioDevice.usPipeSizeOut = pOUTEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous OUT end point. + // + USBHCDPipeConfig(g_AudioDevice.ulIsochOutPipe, + pOUTEndpoint->wMaxPacketSize, 0, + g_AudioDevice.ucIsochOutAddress); + } + + return(pInterface->bInterfaceNumber | + (pInterface->bAlternateSetting << INTERFACE_ALTSETTING_S)); + } + return(INVALID_INTERFACE); +} + +//***************************************************************************** +// +// This function is used to open an instance of the USB host audio driver. +// +// \param pDevice 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 pDevice 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 *pDevice) +{ + unsigned long ulTemp; + tConfigDescriptor *pConfigDesc; + + // + // Don't allow the device to be opened without closing first. + // + if(g_AudioDevice.pDevice) + { + return(0); + } + + g_AudioDevice.pDevice = pDevice; + + // + // Save a shorter name for the configuration descriptor. + // + pConfigDesc = pDevice->pConfigDescriptor; + + // + // Find the input terminal. + // + g_AudioDevice.pInTerminal = + (tACInputTerminal *)AudioTerminalGet(pConfigDesc, + USB_AI_INPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the output terminal. + // + g_AudioDevice.pOutTerminal = + (tACOutputTerminal *)AudioTerminalGet(pConfigDesc, + USB_AI_OUTPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the feature unit. + g_AudioDevice.pFeatureUnit = + (tACFeatureUnit *)AudioTerminalGet(pConfigDesc, + USB_AI_FEATURE_UNIT, + 0); + + // + // Need some kind of terminal to send or receive audio from. + // + if((g_AudioDevice.pOutTerminal == 0) && + (g_AudioDevice.pInTerminal == 0)) + { + return(0); + } + + // + // Find the Audio control interface. + // + ulTemp = AudioControlGet(pConfigDesc); + + if(ulTemp == INVALID_INTERFACE) + { + return(0); + } + + // + // Save the control interface index and increment the number + // of interfaces that have been found. + // + g_AudioDevice.ucIControl = (unsigned char)ulTemp; + + // + // If the call back exists, call it with an Open event. + // + if(g_AudioDevice.pfnCallback != 0) + { + g_AudioDevice.pfnCallback((void *)&g_AudioDevice, + 0, USBH_AUDIO_EVENT_OPEN); + } + + // + // If a feature unit was found, save the ID + // + if(g_AudioDevice.pFeatureUnit != 0) + { + g_AudioDevice.ucVolumeID = g_AudioDevice.pFeatureUnit->bUnitID; + } + + // + // Save the device pointer. + // + g_AudioDevice.pDevice = pDevice; + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_AudioDevice.ulIsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_AudioDevice.pDevice, 256, + PipeCallbackIN); + g_AudioDevice.usPipeSizeIn = 256; + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_AudioDevice.ulIsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_AudioDevice.pDevice, 256, + PipeCallbackOUT); + g_AudioDevice.usPipeSizeOut = 256; + + // + // Clear the flags. + // + g_AudioDevice.ulFlags = 0; + + // + // Return the only instance of this device. + // + return(&g_AudioDevice); +} + +//***************************************************************************** +// +// This function is used to release an instance of the USB host audio driver. +// +// \param pvInstance 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 pvInstance pointer should be a +// valid value that was returned from a call to USBHostAudioOpen(). +// +// \return None. +// +//***************************************************************************** +static void +USBAudioClose(void *pvInstance) +{ + // + // Do nothing if there is not a driver open. + // + if(g_AudioDevice.pDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + g_AudioDevice.pDevice = 0; + + // + // Free the Isochronous IN pipe. + // + if(g_AudioDevice.ulIsochInPipe != 0) + { + USBHCDPipeFree(g_AudioDevice.ulIsochInPipe); + } + + // + // Free the Isochronous OUT pipe. + // + if(g_AudioDevice.ulIsochOutPipe != 0) + { + USBHCDPipeFree(g_AudioDevice.ulIsochOutPipe); + } + + // + // If the call back exists then call it. + // + if(g_AudioDevice.pfnCallback != 0) + { + g_AudioDevice.pfnCallback((void *)&g_AudioDevice, + 0, USBH_AUDIO_EVENT_CLOSE); + } +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the host audio class driver. +//! +//! \param ulIndex 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. +// +//***************************************************************************** +unsigned long +USBHostAudioOpen(unsigned long ulIndex, 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((ulIndex != 0) || (g_AudioDevice.pfnCallback)) + { + return(0); + } + + // + // Save the call back. + // + g_AudioDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return((unsigned long)&g_AudioDevice); +} + +//***************************************************************************** +// +//! This function should be called to release an audio device instance. +//! +//! \param ulInstance 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(unsigned long ulInstance) +{ + tUSBHostAudioInstance *pAudioDevice; + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // Close the audio device. + // + USBAudioClose((void *)pAudioDevice); + + // + // Clear the call back indicating that the device is now closed. + // + pAudioDevice->pfnCallback = 0; +} + +//***************************************************************************** +// +// This function is used to request settings from a given audio interface. +// +// \param ulInstance is an instance value for the audio device to access. +// \param ulInterface is the interface to access. +// \param ulChannel is the channel number to access. +// \param ulRequest 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 ulInterface is the interface to +// make the request specified by \e ulChannel and \e ulRequest. The +// \e ulRequest parameter must be one of the USB_AC_GET_* values. +// +// \return This function returns the requested value. +// +//***************************************************************************** +static unsigned long +VolumeSettingGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel, unsigned long ulRequest) +{ + unsigned long ulValue; + tUSBHostAudioInstance *pAudioDevice; + tUSBRequest SetupPacket; + + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + ulValue = 0; + + // + // This is a Class specific Interface IN request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = (ulRequest & 0xff); + + // + // Request for a string descriptor. + // + SetupPacket.wValue = VOLUME_CONTROL | (ulChannel & 0xff); + + // + // Set the language ID. + // + SetupPacket.wIndex = (pAudioDevice->ucVolumeID << 8) | + (ulInterface & 0xff); + + // + // Only request the space available. + // + SetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, pAudioDevice->pDevice, + (unsigned char *)&ulValue, 4, + pAudioDevice->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(ulValue); +} + +//***************************************************************************** +// +//! This function is used to get the current volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the current +//! volume setting. +//! \param ulChannel 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 ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel 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. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_CUR)); +} + +//***************************************************************************** +// +//! This function is used to get the maximum volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the maximum +//! volume control value. +//! \param ulChannel 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 ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel 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. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeMaxGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_MAX)); +} + +//***************************************************************************** +// +//! This function is used to get the minimum volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the minimum +//! volume control value. +//! \param ulChannel 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 ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel 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. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeMinGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_MIN)); +} + +//***************************************************************************** +// +//! This function is used to get the volume control resolution for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the resolution +//! for the volume control. +//! \param ulChannel 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 ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel 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. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeResGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_RES)); +} + +//***************************************************************************** +// +//! This function is used to set the current volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to set the current +//! volume setting. +//! \param ulChannel is the 0 based channel number to query. +//! \param ulValue 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 ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel 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(unsigned long ulInstance, unsigned ulInterface, + unsigned long ulChannel, unsigned long ulValue) +{ + tUSBHostAudioInstance *pAudioDevice; + tUSBRequest SetupPacket; + + // + // Create an audio instance pointer. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // This is a Class specific Interface OUT request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; + + // + // Request is to set the current value. + // + SetupPacket.bRequest = USB_AC_SET_CUR; + + // + // Request the volume control. + // + SetupPacket.wValue = VOLUME_CONTROL | (ulChannel & 0xff); + + // + // Set Volume control ID and interface to 0. + // + SetupPacket.wIndex = pAudioDevice->ucVolumeID << 8; + + // + // Only request the space available. + // + SetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, pAudioDevice->pDevice, + (unsigned char *)&ulValue, 2, + pAudioDevice->pDevice->DeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +//! This function is called to determine if an audio format is supported by the +//! connected USB Audio device. +//! +//! \param ulInstance is the device instance for this call. +//! \param ulSampleRate is the sample rate of the audio stream. +//! \param ulBits is the number of bits per sample in the audio stream. +//! \param ulChannels is the number of channels in the audio stream. +//! \param ulFlags 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 ulInstance 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 ulSampleRate, \e ulBits, and \e ulChannels values. The \e ulFlags +//! 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. +// +//***************************************************************************** +unsigned long +USBHostAudioFormatGet(unsigned long ulInstance, unsigned long ulSampleRate, + unsigned long ulBits, unsigned long ulChannels, + unsigned long ulFlags) +{ + tUSBHostAudioInstance *pAudioDevice; + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // Look for the requested format. + // + if(AudioGetInterface(pAudioDevice, USB_ADF_PCM, ulSampleRate, ulBits>>3, + ulChannels, ulFlags) != INVALID_INTERFACE) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is called to set the current sample rate on an audio +//! interface. +//! +//! \param ulInstance specifies the device instance for this call. +//! \param ulSampleRate is the sample rate in Hz. +//! \param ulBits is the number of bits per sample. +//! \param ulChannels is then number of audio channels. +//! \param ulFlags 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 ulInstance 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. +// +//***************************************************************************** +unsigned long +USBHostAudioFormatSet(unsigned long ulInstance, unsigned long ulSampleRate, + unsigned long ulBits, unsigned long ulChannels, + unsigned long ulFlags) +{ + tUSBHostAudioInstance *pAudioDevice; + unsigned long ulInterface; + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // Look for the requested format. + // + ulInterface = AudioGetInterface(pAudioDevice, USB_ADF_PCM, ulSampleRate, + ulBits>>3, ulChannels, ulFlags); + + if(ulInterface == INVALID_INTERFACE) + { + return(1); + } + + // + // Determine if this is an input or output request. + // + if(ulFlags & USBH_AUDIO_FORMAT_IN) + { + // + // Get the active interface number and alternate setting for this + // format. + // + pAudioDevice->ucInInterface = + (unsigned char)(ulInterface & INTERFACE_NUM_M); + pAudioDevice->ucInAltSetting = + (unsigned char)((ulInterface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + else + { + // + // Get the active interface number and alternate setting for this + // format. + // + pAudioDevice->ucOutInterface = + (unsigned char)(ulInterface & INTERFACE_NUM_M); + pAudioDevice->ucOutAltSetting = + (unsigned char)((ulInterface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + return(0); +} + +//***************************************************************************** +// +//! This function is called to send an audio buffer to the USB audio device. +//! +//! \param ulInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ulSize 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 ulParam can be ignored and the \e ulEvent 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. +// +//***************************************************************************** +long +USBHostAudioPlay(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, tUSBHostAudioCallback pfnCallback) +{ + tUSBHostAudioInstance *pAudioDevice; + unsigned long ulBytes; + + // + // Make sure that there is a device present. + // + if(g_AudioDevice.pDevice == 0) + { + return(0); + } + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // If the audio output interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_OUT_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_OUT_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (unsigned long)pAudioDevice->pDevice, + pAudioDevice->ucOutInterface, + pAudioDevice->ucOutAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + pAudioDevice->pfnOutCallback = pfnCallback; + pAudioDevice->pvOutBuffer = (void *)pvBuffer; + + // + // Schedule the data to be written out to the FIFO. + // + ulBytes = USBHCDPipeSchedule(pAudioDevice->ulIsochOutPipe, pvBuffer, + ulSize); + + // + // Return the number of bytes scheduled to be sent. + // + return(ulBytes); +} + +//***************************************************************************** +// +//! This function is called to provide an audio buffer to the USB audio device +//! for audio input. +//! +//! \param ulInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ulSize 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 ulParam is +//! the number of valid bytes in the pvBuffer and the \e ulEvent 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. +// +//***************************************************************************** +long +USBHostAudioRecord(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, tUSBHostAudioCallback pfnCallback) +{ + tUSBHostAudioInstance *pAudioDevice; + unsigned long ulBytes; + + // + // Make sure that there is a device present. + // + if(g_AudioDevice.pDevice == 0) + { + return(0); + } + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // If the audio input interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_IN_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_IN_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (unsigned long)pAudioDevice->pDevice, + pAudioDevice->ucInInterface, + pAudioDevice->ucInAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + pAudioDevice->pfnInCallback = pfnCallback; + pAudioDevice->pvInBuffer = (void *)pvBuffer; + + // + // Schedule the data to be written out to the FIFO. + // + ulBytes = USBHCDPipeSchedule(pAudioDevice->ulIsochInPipe, pvBuffer, ulSize); + + // + // Return the number of bytes scheduled to be sent. + // + return(ulBytes); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/host/usbhaudio.h b/usblib/host/usbhaudio.h new file mode 100644 index 0000000..a12e97e --- /dev/null +++ b/usblib/host/usbhaudio.h @@ -0,0 +1,162 @@ +//***************************************************************************** +// +// usbhaudio.h - USB host audio class driver. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +typedef void (* tUSBHostAudioCallback)(void *pvBuffer, + unsigned long ulParam, + unsigned long ulEvent); + +//***************************************************************************** +// +//! This is the size in bytes of the private data for the host audio class. +// +//***************************************************************************** +#define USB_HOST_AUDIO_INSTANCE_SIZE sizeof(tHostAudioInstance); + +//***************************************************************************** +// +// 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 ulParam +//! 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 ulParam 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 +{ + unsigned char ucChannels; + unsigned char ucBits; + unsigned long ulSampleRate; +} tUSBAudioFormat; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern unsigned long USBHostAudioOpen(unsigned long ulIndex, + tUSBHostAudioCallback pfnCallback); +extern void USBHostAudioClose(unsigned long ulInstance); +extern long USBHostAudioPlay(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, + tUSBHostAudioCallback pfnCallback); + +extern unsigned long USBHostAudioFormatGet(unsigned long ulInstance, + unsigned long ulSampleRate, + unsigned long ulBits, + unsigned long ulChannels, + unsigned long ulFlags); +extern unsigned long USBHostAudioFormatSet(unsigned long ulInstance, + unsigned long ulSampleRate, + unsigned long ulBits, + unsigned long ulChannels, + unsigned long ulFlags); + +extern long USBHostAudioRecord(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, + tUSBHostAudioCallback); + +extern unsigned long USBHostAudioVolumeGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +extern void USBHostAudioVolumeSet(unsigned long ulInstance, + unsigned ulInterface, + unsigned long ulChannel, + unsigned long ulValue); + +extern unsigned long USBHostAudioVolumeMaxGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +extern unsigned long USBHostAudioVolumeMinGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +extern unsigned long USBHostAudioVolumeResGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..5e3fa50 --- /dev/null +++ b/usblib/host/usbhhid.c @@ -0,0 +1,723 @@ +//***************************************************************************** +// +// usbhhid.c - This file contains the host HID driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhhid.h" + +static void * HIDDriverOpen(tUSBHostDevice *pDevice); +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. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the callback. + // + tUSBCallback pfnCallback; + + // + // Callback data provided by caller. + // + unsigned long ulCBData; + + // + // Used to remember what type of device was registered. + // + tHIDSubClassProtocol eDeviceType; + + // + // Interrupt IN pipe. + // + unsigned long ulIntInPipe; +} +tHIDInstance; + +//***************************************************************************** +// +// The instance data storage for attached hid devices. +// +//***************************************************************************** +static tHIDInstance g_pHIDDevice[MAX_HID_DEVICES]; + +//***************************************************************************** +// +//! This constant global structure defines the HID Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHIDClassDriver = +{ + USB_CLASS_HID, + HIDDriverOpen, + HIDDriverClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of a HID device. +//! +//! \param eDeviceType 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 ulCBData is the data that will be returned in when the pfnCallback +//! function is called. +//! +//! This function creates an instance of an specific type of HID device. The +//! \e eDeviceType 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. +// +//***************************************************************************** +unsigned long +USBHHIDOpen(tHIDSubClassProtocol eDeviceType, tUSBCallback pfnCallback, + unsigned long ulCBData) +{ + unsigned long ulLoop; + + // + // Find a free device instance structure. + // + for(ulLoop = 0; ulLoop < MAX_HID_DEVICES; ulLoop++) + { + if(g_pHIDDevice[ulLoop].eDeviceType == USBH_HID_DEV_NONE) + { + // + // Save the instance data for this device. + // + g_pHIDDevice[ulLoop].pfnCallback = pfnCallback; + g_pHIDDevice[ulLoop].eDeviceType = eDeviceType; + g_pHIDDevice[ulLoop].ulCBData = ulCBData; + + // + // Return the device instance pointer. + // + return((unsigned long)&g_pHIDDevice[ulLoop]); + } + } + + // + // 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 ulHIDInstance 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 ulHIDInstance parameter should hold the value that was returned from the +//! previous call to USBHHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +void +USBHHIDClose(unsigned long ulHIDInstance) +{ + tHIDInstance *pInst; + + // + // Get our instance pointer. + // + pInst = (tHIDInstance *)ulHIDInstance; + + // + // Disable any more notifications from the HID layer. + // + pInst->pfnCallback = 0; + + // + // Mark this device slot as free. + // + pInst->eDeviceType = USBH_HID_DEV_NONE; +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint. +// +//***************************************************************************** +static void +HIDIntINCallback(unsigned long ulPipe, unsigned long ulEvent) +{ + long lDev; + + switch (ulEvent) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + USBHCDPipeSchedule(ulPipe, 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(lDev = 0; lDev < MAX_HID_DEVICES; lDev++) + { + // + // Does this device own the pipe we've been passed? + // + if(g_pHIDDevice[lDev].ulIntInPipe == ulPipe) + { + // + // Yes - send the report data to the USB host HID device + // class driver. + // + g_pHIDDevice[lDev].pfnCallback( + (void *)g_pHIDDevice[lDev].ulCBData, + USB_EVENT_RX_AVAILABLE, + ulPipe, + 0); + } + } + + break; + } + } +} + +//***************************************************************************** +// +//! This function is used to open an instance of the HID driver. +//! +//! \param pDevice 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 pDevice 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 *pDevice) +{ + long lIdx, lDev; + tEndpointDescriptor *pEndpointDescriptor; + tInterfaceDescriptor *pInterface; + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface(pDevice->pConfigDescriptor, 0, 0); + + // + // Search the currently open instances for one that supports the protocol + // of this device. + // + for(lDev = 0; lDev < MAX_HID_DEVICES; lDev++) + { + if(g_pHIDDevice[lDev].eDeviceType == pInterface->bInterfaceProtocol) + { + // + // Save the device pointer. + // + g_pHIDDevice[lDev].pDevice = pDevice; + + for(lIdx = 0; lIdx < 3; lIdx++) + { + // + // Get the first endpoint descriptor. + // + pEndpointDescriptor = USBDescGetInterfaceEndpoint(pInterface, + lIdx, 256); + + // + // If no more endpoints then break out. + // + if(pEndpointDescriptor == 0) + { + break; + } + + // + // Interrupt + // + if((pEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // Interrupt IN. + // + if(pEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + g_pHIDDevice[lDev].ulIntInPipe = USBHCDPipeAlloc(0, + USBHCD_PIPE_INTR_IN, + pDevice, + HIDIntINCallback); + USBHCDPipeConfig(g_pHIDDevice[lDev].ulIntInPipe, + pEndpointDescriptor->wMaxPacketSize, + pEndpointDescriptor->bInterval, + (pEndpointDescriptor->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_pHIDDevice[lDev].pfnCallback != 0) + { + g_pHIDDevice[lDev].pfnCallback( + (void *)g_pHIDDevice[lDev].ulCBData, + USB_EVENT_CONNECTED, + (unsigned long)&g_pHIDDevice[lDev], 0); + } + + // + // Save the device pointer. + // + g_pHIDDevice[lDev].pDevice = pDevice; + + return (&g_pHIDDevice[lDev]); + } + } + + // + // 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 *pInst; + + // + // Get our instance pointer. + // + pInst = (tHIDInstance *)pvInstance; + + // + // Reset the device pointer. + // + pInst->pDevice = 0; + + // + // Free the Interrupt IN pipe. + // + if(pInst->ulIntInPipe != 0) + { + USBHCDPipeFree(pInst->ulIntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(pInst->pfnCallback != 0) + { + pInst->pfnCallback((void *)pInst->ulCBData, + USB_EVENT_DISCONNECTED, + (unsigned long)pvInstance, 0); + } +} + +//***************************************************************************** +// +//! This function is used to set the idle timeout for a HID device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ucDuration is the duration of the timeout in milliseconds. +//! \param ucReportID 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 ucDuration parameter and the report the timeout for is in the +//! \e ucReportID value. +//! +//! \return Always returns 0. +// +//***************************************************************************** +unsigned long +USBHHIDSetIdle(unsigned long ulInstance, unsigned char ucDuration, + unsigned char ucReportID) +{ + tUSBRequest SetupPacket; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Class specific interface OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS + | USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_IDLE; + SetupPacket.wValue = (ucDuration << 8) | ucReportID; + + // + // Set this on interface 1. + // + SetupPacket.wIndex = 0; + + // + // This is always 0 for this request. + // + SetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + return(USBHCDControlTransfer(0, &SetupPacket, pHIDInstance->pDevice, + 0, 0, MAX_PACKET_SIZE_EP0)); +} + +//***************************************************************************** +// +//! This function can be used to retrieve the report descriptor for a given +//! device instance. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param pucBuffer is the memory buffer to use to store the report +//! descriptor. +//! \param ulSize is the size in bytes of the buffer pointed to by +//! \e pucBuffer. +//! +//! 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 ulInstance parameter. +//! This call is blocking and will return the number of bytes read into the +//! \e pucBuffer. +//! +//! \return Returns the number of bytes read into the \e pucBuffer. +// +//***************************************************************************** +unsigned long +USBHHIDGetReportDescriptor(unsigned long ulInstance, unsigned char *pucBuffer, + unsigned long ulSize) +{ + tUSBRequest SetupPacket; + unsigned long ulBytes; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD + | USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + SetupPacket.wValue = USB_HID_DTYPE_REPORT << 8; + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = 0; + + // + // All devices must have at least an 8 byte max packet size so just ask + // for 8 bytes to start with. + // + SetupPacket.wLength = ulSize; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + ulBytes = USBHCDControlTransfer( + 0, + &SetupPacket, + pHIDInstance->pDevice, + pucBuffer, + ulSize, + pHIDInstance->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(ulBytes); +} + +//***************************************************************************** +// +//! This function is used to set or clear the boot protocol state of a device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ulBootProtocol 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. +// +//***************************************************************************** +unsigned long +USBHHIDSetProtocol(unsigned long ulInstance, unsigned long ulBootProtocol) +{ + tUSBRequest SetupPacket; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS + | USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_PROTOCOL; + + if(ulBootProtocol) + { + // + // Boot Protocol. + // + SetupPacket.wValue = 0; + } + else + { + // + // Report Protocol. + // + SetupPacket.wValue = 1; + } + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = 0; + + // + // Always 0. + // + SetupPacket.wLength = 0; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer( + 0, + &SetupPacket, + pHIDInstance->pDevice, + 0, + 0, + pHIDInstance->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to retrieve a report from a HID device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ulInterface is the interface to retrieve the report from. +//! \param pucData is the memory buffer to use to store the report. +//! \param ulSize is the size in bytes of the buffer pointed to by +//! \e pucBuffer. +//! +//! 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. +// +//***************************************************************************** +unsigned long +USBHHIDGetReport(unsigned long ulInstance, + unsigned long ulInterface, + unsigned char *pucData, + unsigned long ulSize) +{ + tHIDInstance *pHIDInstance; + + // + // Cast the instance pointer to the correct type for ease of use. + // + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // Read the Data out. + // + ulSize = USBHCDPipeReadNonBlocking(pHIDInstance->ulIntInPipe, pucData, + ulSize); + + // + // Return the number of bytes read from the interrupt in pipe. + // + return(ulSize); +} + +//***************************************************************************** +// +//! This function is used to send a report to a HID device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ulInterface is the interface to send the report to. +//! \param pucData is the memory buffer to use to store the report. +//! \param ulSize is the size in bytes of the buffer pointed to by +//! \e pucBuffer. +//! +//! 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. +// +//***************************************************************************** +unsigned long +USBHHIDSetReport(unsigned long ulInstance, unsigned long ulInterface, + unsigned char *pucData, unsigned long ulSize) +{ + tUSBRequest SetupPacket; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS + | USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_REPORT; + SetupPacket.wValue = USB_HID_REPORT_OUTPUT << 8; + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = (unsigned short)ulInterface; + + // + // Always 0. + // + SetupPacket.wLength = ulSize; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &SetupPacket, pHIDInstance->pDevice, + pucData, ulSize, + pHIDInstance->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(ulSize); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhid.h b/usblib/host/usbhhid.h new file mode 100644 index 0000000..2088c1e --- /dev/null +++ b/usblib/host/usbhhid.h @@ -0,0 +1,164 @@ +//***************************************************************************** +// +// usbhhid.h - This hold the host driver for hid class. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are the the events that will be passed in the ulEvent +// 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. + // + USBH_HID_DEV_NONE = 0, + + // + //! This is a keyboard device. + // + USBH_HID_DEV_KEYBOARD, + + // + //! This is a mouse device. + // + USBH_HID_DEV_MOUSE, + + // + //! This is a vendor specific device. + // + USBH_HID_DEV_VENDOR +} +tHIDSubClassProtocol; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern unsigned long USBHHIDOpen(tHIDSubClassProtocol eDeviceType, + tUSBCallback pfnCallback, + unsigned long ulCBData); +extern void USBHHIDClose(unsigned long ulInstance); +extern unsigned long USBHHIDGetReportDescriptor(unsigned long ulInstance, + unsigned char *pucBuffer, + unsigned long ulSize); +extern unsigned long USBHHIDSetIdle(unsigned long ulInstance, + unsigned char ucDuration, + unsigned char ucReportID); +extern unsigned long USBHHIDSetProtocol(unsigned long ulInstance, + unsigned long ulBootProtocol); +extern unsigned long USBHHIDSetReport(unsigned long ulInstance, + unsigned long ulInterface, + unsigned char *pucData, + unsigned long ulSize); +extern unsigned long USBHHIDGetReport(unsigned long ulInstance, + unsigned long ulInterface, + unsigned char *pucData, + unsigned long ulSize); +extern const tUSBHostClassDriver g_USBHIDClassDriver; + +//***************************************************************************** +// +// 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..7d356a3 --- /dev/null +++ b/usblib/host/usbhhidkeyboard.c @@ -0,0 +1,719 @@ +//***************************************************************************** +// +// usbhhidkeyboard.c - This file holds the application interfaces for USB +// keyboard devices. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 unsigned long USBHKeyboardCallback(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB keyboard report. +// +//***************************************************************************** +#define USBHKEYB_REPORT_SIZE 8 + +//***************************************************************************** +// +// These are the flags for the tUSBHKeyboard.ulHIDFlags member variable. +// +//***************************************************************************** +#define USBHKEYB_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a keyboard device instance. +// +//***************************************************************************** +typedef struct +{ + // + // Global flags for an instance of a keyboard. + // + unsigned long ulHIDFlags; + + // + // The applications registered callback. + // + tUSBCallback pfnCallback; + + // + // The HID instance pointer for this keyboard instance. + // + unsigned long ulHIDInstance; + + // + // NUM_LOCK, CAPS_LOCK, SCROLL_LOCK, COMPOSE or KANA keys. + // + unsigned char ucKeyModSticky; + + // + // This is the current state of the keyboard modifier keys. + // + unsigned char ucKeyModState; + + // + // This holds the keyboard usage codes for keys that are being held down. + // + unsigned char pucKeyState[6]; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + unsigned char pucBuffer[USBHKEYB_REPORT_SIZE]; +} +tUSBHKeyboard; + +//***************************************************************************** +// +// 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 pucBuffer is the memory used by the keyboard to interact with the +//! USB keyboard. +//! \param ulSize is the size of the buffer provided by \e pucBuffer. +//! +//! 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 pucBuffer 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. +// +//***************************************************************************** +unsigned long +USBHKeyboardOpen(tUSBCallback pfnCallback, unsigned char *pucBuffer, + unsigned long ulSize) +{ + // + // Save the callback and data pointers. + // + g_sUSBHKeyboard.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHKeyboard.ulHIDInstance = + USBHHIDOpen(USBH_HID_DEV_KEYBOARD, USBHKeyboardCallback, + (unsigned long)&g_sUSBHKeyboard); + + return((unsigned long)&g_sUSBHKeyboard); +} + +//***************************************************************************** +// +//! This function is used close an instance of a keyboard. +//! +//! \param ulInstance 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 ulInstance 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. +// +//***************************************************************************** +unsigned long +USBHKeyboardClose(unsigned long ulInstance) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Reset the callback to null. + // + pUSBHKeyboard->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(pUSBHKeyboard->ulHIDInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to map a USB usage ID to a printable character. +//! +//! \param ulInstance is the instance value for this keyboard. +//! \param pTable is the table to use to map the usage ID to characters. +//! \param ucUsageID 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 pTable 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 +//! pTable structure has values indicating which keys are modified by Caps Lock +//! and alternate values for shifted cases. The number of bytes returned from +//! this function depends on the \e pTable 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. +// +//***************************************************************************** +unsigned long +USBHKeyboardUsageToChar(unsigned long ulInstance, + const tHIDKeyboardUsageTable *pTable, + unsigned char ucUsageID) +{ + unsigned long ulValue; + const unsigned char *pucKeyBoardMap; + const unsigned short *pusKeyBoardMap; + unsigned long ulOffset; + unsigned long ulShift; + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // The added offset for the shifted character value. + // + ulShift = 0; + + // + // Offset in the table for the character. + // + ulOffset = (ucUsageID * pTable->ucBytesPerChar * 2); + + // + // Handle the case where CAPS lock has been set. + // + if(pUSBHKeyboard->ucKeyModSticky &= HID_KEYB_CAPS_LOCK) + { + // + // See if this usage ID is modified by Caps Lock by checking the packed + // bit array in the pulShiftState member of the pTable array. + // + if((pTable->pulCapsLock[ucUsageID >> 5]) >> (ucUsageID & 0x1f) & 1) + { + ulShift = pTable->ucBytesPerChar; + } + } + + // + // Now handle if a shift key is being held. + // + if((pUSBHKeyboard->ucKeyModState & 0x22) != 0) + { + // + // Not shifted yet so we need to shift. + // + if(ulShift == 0) + { + ulShift = pTable->ucBytesPerChar; + } + else + { + // + // Unshift because CAPS LOCK and shift were presed. + // + ulShift = 0; + } + } + + // + // One byte per character. + // + if(pTable->ucBytesPerChar == 1) + { + // + // Get the base address of the table. + // + pucKeyBoardMap = pTable->pCharMapping; + + ulValue = pucKeyBoardMap[ulOffset + ulShift]; + } + // + // Two bytes per character. + // + else if(pTable->ucBytesPerChar == 2) + { + // + // Get the base address of the table. + // + pusKeyBoardMap = (unsigned short *)pTable->pCharMapping; + + ulValue = pusKeyBoardMap[ulOffset + ulShift]; + } + // + // All other sizes are unsupported for now. + // + else + { + ulValue = 0; + } + + return(ulValue); +} + +//***************************************************************************** +// +//! This function is used to set one of the fixed modifier keys on a keyboard. +//! +//! \param ulInstance is the instance value for this keyboard. +//! \param ulModifiers 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 ulModifiers value is a bitmask of the following set of values: +//! - HID_KEYB_NUM_LOCK +//! - HID_KEYB_CAPS_LOCK +//! - HID_KEYB_SCROLL_LOCK +//! - HID_KEYB_COMPOSE +//! - 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 ulInstance 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. +// +//***************************************************************************** +unsigned long +USBHKeyboardModifierSet(unsigned long ulInstance, unsigned long ulModifiers) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Remeber the fact that this is set. + // + pUSBHKeyboard->ucKeyModSticky = (unsigned char)ulModifiers; + + // + // Set the LEDs on the keyboard. + // + USBHHIDSetReport(pUSBHKeyboard->ulHIDInstance, 0, + (unsigned char *)&ulModifiers, 1); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a keyboard interface after a keyboard +//! has been detected. +//! +//! \param ulInstance 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 ulInstance 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. +// +//***************************************************************************** +unsigned long +USBHKeyboardInit(unsigned long ulInstance) +{ + unsigned char ucModData; + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Set the initial rate to only update on keyboard state changes. + // + USBHHIDSetIdle(pUSBHKeyboard->ulHIDInstance, 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(pUSBHKeyboard->ulHIDInstance, + pUSBHKeyboard->pucBuffer, + USBHKEYB_REPORT_SIZE); + + // + // Set the keyboard to boot protocol. + // + USBHHIDSetProtocol(pUSBHKeyboard->ulHIDInstance, 1); + + // + // Used to clear the initial state of all on keyboard modifiers. + // + ucModData = 0; + + // + // Update the keyboard LED state. + // + USBHHIDSetReport(pUSBHKeyboard->ulHIDInstance, 0, &ucModData, 1); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to set the automatic poll rate of the keyboard. +//! +//! \param ulInstance is the instance value for this keyboard. +//! \param ulPollRate 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 ulInstance value is the value that was returned +//! when the application called USBHKeyboardOpen(). The \e ulPollRate 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. +// +//***************************************************************************** +unsigned long +USBHKeyboardPollRateSet(unsigned long ulInstance, unsigned long ulPollRate) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Send the Set Idle command to the USB keyboard. + // + USBHHIDSetIdle(pUSBHKeyboard->ulHIDInstance, ulPollRate, 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 *pUSBHKeyboard) +{ + long lNewKey, lOldKey; + + // + // rollover code so ignore this buffer. + // + if(pUSBHKeyboard->pucBuffer[2] == 0x01) + { + return; + } + + // + // Handle the keyboard modifier states. + // + if(pUSBHKeyboard->ucKeyModState != pUSBHKeyboard->pucBuffer[0]) + { + // + // Notify the application of the event. + // + pUSBHKeyboard->pfnCallback(0, USBH_EVENT_HID_KB_MOD, + pUSBHKeyboard->pucBuffer[0], 0); + + // + // Save the new state of the modifier keys. + // + pUSBHKeyboard->ucKeyModState = pUSBHKeyboard->pucBuffer[0]; + } + + // + // This loop checks for keys that have been released to make room for new + // ones that may have been pressed. + // + for(lOldKey = 2; lOldKey < 8; lOldKey++) + { + // + // If there is no old key pressed in this entry go to the next one. + // + if(pUSBHKeyboard->pucKeyState[lOldKey] == 0) + { + continue; + } + + // + // Check if this old key is still in the list of currently pressed + // keys. + // + for(lNewKey = 2; lNewKey < 8; lNewKey++) + { + // + // Break out if the key is still present. + // + if(pUSBHKeyboard->pucBuffer[lNewKey] + == pUSBHKeyboard->pucKeyState[lOldKey]) + { + break; + } + } + // + // If the old key was no longer in the list of pressed keys then + // notify the application of the key release. + // + if(lNewKey == 8) + { + // + // Send the key release notification to the application. + // + pUSBHKeyboard->pfnCallback(0, + USBH_EVENT_HID_KB_REL, + pUSBHKeyboard->pucKeyState[lOldKey], + 0); + // + // Remove the old key from the currently held key list. + // + pUSBHKeyboard->pucKeyState[lOldKey] = 0; + + } + } + + // + // This loop checks for new keys that have been pressed. + // + for(lNewKey = 2; lNewKey < 8; lNewKey++) + { + // + // The new list is empty so no new keys are pressed. + // + if(pUSBHKeyboard->pucBuffer[lNewKey] == 0) + { + break; + } + + // + // This loop checks if the current key was already pressed. + // + for(lOldKey = 2; lOldKey < 8; lOldKey++) + { + // + // If it is in both lists then it was already pressed so ignore it. + // + if(pUSBHKeyboard->pucBuffer[lNewKey] + == pUSBHKeyboard->pucKeyState[lOldKey]) + { + break; + } + } + // + // The key in the new list was not found so it is new. + // + if(lOldKey == 8) + { + // + // Look for a free location to store this key usage code. + // + for(lOldKey = 2; lOldKey < 8; lOldKey++) + { + // + // If an empty location is found, store it and notify the + // application. + // + if(pUSBHKeyboard->pucKeyState[lOldKey] == 0) + { + // + // Save the newly pressed key. + // + pUSBHKeyboard->pucKeyState[lOldKey] + = pUSBHKeyboard->pucBuffer[lNewKey]; + + // + // Notify the application of the new key that has been + // pressed. + // + pUSBHKeyboard->pfnCallback( + 0, + USBH_EVENT_HID_KB_PRESS, + pUSBHKeyboard->pucBuffer[lNewKey], + 0); + + break; + } + } + } + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvCBData is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ulEvent is the event that has been passed up from the HID driver. +//! \param ulMsgParam has meaning related to the \e ulEvent that occurred. +//! \param pvMsgData has meaning related to the \e ulEvent 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. +// +//***************************************************************************** +unsigned long +USBHKeyboardCallback(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgParam, void *pvMsgData) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)pvCBData; + + switch (ulEvent) + { + // + // New keyboard has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a keyboard is present. + // + pUSBHKeyboard->ulHIDFlags |= USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that a new keyboard was connected. + // + pUSBHKeyboard->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No keyboard is present. + // + pUSBHKeyboard->ulHIDFlags &= ~USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that the keyboard was disconnected. + // + pUSBHKeyboard->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New keyboard report structure was received. + // + USBHHIDGetReport(pUSBHKeyboard->ulHIDInstance, 0, + pUSBHKeyboard->pucBuffer, + USBHKEYB_REPORT_SIZE); + + // + // Update the application on the changes in the keyboard state. + // + UpdateKeyboardState(pUSBHKeyboard); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidkeyboard.h b/usblib/host/usbhhidkeyboard.h new file mode 100644 index 0000000..a84ff28 --- /dev/null +++ b/usblib/host/usbhhidkeyboard.h @@ -0,0 +1,77 @@ +//***************************************************************************** +// +// usbhhidkeyboard.h - This file holds the application interfaces for USB +// keyboard devices. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +extern unsigned long USBHKeyboardOpen(tUSBCallback pfnCallback, + unsigned char *pucBuffer, + unsigned long ulBufferSize); +extern unsigned long USBHKeyboardClose(unsigned long ulInstance); +extern unsigned long USBHKeyboardInit(unsigned long ulInstance); +extern unsigned long USBHKeyboardModifierSet(unsigned long ulInstance, + unsigned long ulModifiers); +extern unsigned long USBHKeyboardPollRateSet(unsigned long ulInstance, + unsigned long ulPollRate); + +extern unsigned long USBHKeyboardUsageToChar( + unsigned long ulInstance, + const tHIDKeyboardUsageTable *pTable, + unsigned char ucUsageID); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..d337965 --- /dev/null +++ b/usblib/host/usbhhidmouse.c @@ -0,0 +1,415 @@ +//***************************************************************************** +// +// usbhhidmouse.c - This file holds the application interfaces for USB +// mouse devices. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 unsigned long USBHMouseCallback(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB mouse report. +// +//***************************************************************************** +#define USBHMS_REPORT_SIZE 4 + +//***************************************************************************** +// +// These are the flags for the tUSBHMouse.ulHIDFlags member variable. +// +//***************************************************************************** +#define USBHMS_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a mouse device instance. +// +//***************************************************************************** +typedef struct +{ + // + // Global flags for an instance of a mouse. + // + unsigned long ulHIDFlags; + + // + // The applications registered callback. + // + tUSBCallback pfnCallback; + + // + // The current state of the buttons. + // + unsigned char ucButtons; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + unsigned char pucBuffer[USBHMS_REPORT_SIZE]; + + // + // Heap data for the mouse currently used to read the HID Report + // Descriptor. + // + unsigned char *pucHeap; + + // + // Size of the heap in bytes. + // + unsigned long ulHeapSize; + + // + // This is the instance value for the HID device that will be used for the + // mouse. + // + unsigned long ulMouseInstance; +} +tUSBHMouse; + +//***************************************************************************** +// +// 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 pucBuffer is the memory used by the driver to interact with the +//! USB mouse. +//! \param ulSize is the size of the buffer provided by \e pucBuffer. +//! +//! 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 pucBuffer 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. +// +//***************************************************************************** +unsigned long +USBHMouseOpen(tUSBCallback pfnCallback, unsigned char *pucBuffer, + unsigned long ulSize) +{ + // + // Save the callback and data pointers. + // + g_sUSBHMouse.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHMouse.ulMouseInstance = USBHHIDOpen(USBH_HID_DEV_MOUSE, + USBHMouseCallback, + (unsigned long)&g_sUSBHMouse); + + // + // Save the heap buffer and size. + // + g_sUSBHMouse.pucHeap = pucBuffer; + g_sUSBHMouse.ulHeapSize = ulSize; + + return((unsigned long)&g_sUSBHMouse); +} + +//***************************************************************************** +// +//! This function is used close an instance of a mouse. +//! +//! \param ulInstance 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 ulInstance value is the value +//! that was returned when the application called USBHMouseOpen(). +//! +//! \return Returns 0. +// +//***************************************************************************** +unsigned long +USBHMouseClose(unsigned long ulInstance) +{ + tUSBHMouse *pUSBHMouse; + + // + // Recover the pointer to the instance data. + // + pUSBHMouse = (tUSBHMouse *)ulInstance; + + // + // Reset the callback to null. + // + pUSBHMouse->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(pUSBHMouse->ulMouseInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a mouse interface after a mouse has +//! been detected. +//! +//! \param ulInstance 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 ulInstance 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. +// +//***************************************************************************** +unsigned long +USBHMouseInit(unsigned long ulInstance) +{ + tUSBHMouse *pUSBHMouse; + + // + // Recover the pointer to the instance data. + // + pUSBHMouse = (tUSBHMouse *)ulInstance; + + // + // Set the initial rate to only update on mouse state changes. + // + USBHHIDSetIdle(pUSBHMouse->ulMouseInstance, 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(pUSBHMouse->ulMouseInstance, + pUSBHMouse->pucHeap, + pUSBHMouse->ulHeapSize); + + // + // Set the mouse to boot protocol. + // + USBHHIDSetProtocol(pUSBHMouse->ulMouseInstance, 1); + + return(0); +} + +//***************************************************************************** +// +// This function handles updating the state of the mouse buttons and axis. +// +// \param pUSBHMouse 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 *pUSBHMouse) +{ + unsigned long ulButton; + + if(pUSBHMouse->pucBuffer[0] != pUSBHMouse->ucButtons) + { + for(ulButton = 1; ulButton <= 0x4; ulButton <<= 1) + { + if(((pUSBHMouse->pucBuffer[0] & ulButton) != 0) && + ((pUSBHMouse->ucButtons & ulButton) == 0)) + { + // + // Send the mouse button press notification to the application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_PRESS, + ulButton, + 0); + } + if(((pUSBHMouse->pucBuffer[0] & ulButton) == 0) && + ((pUSBHMouse->ucButtons & ulButton) != 0)) + { + // + // Send the mouse button release notification to the + // application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_REL, + ulButton, + 0); + } + } + + // + // Save the new state. + // + pUSBHMouse->ucButtons = pUSBHMouse->pucBuffer[0]; + } + if(pUSBHMouse->pucBuffer[1] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_X, + (unsigned long)pUSBHMouse->pucBuffer[1], + 0); + } + if(pUSBHMouse->pucBuffer[2] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_Y, + (unsigned long)pUSBHMouse->pucBuffer[2], + 0); + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvCBData is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ulEvent is the event that has been passed up from the HID driver. +//! \param ulMsgParam has meaning related to the \e ulEvent that occurred. +//! \param pvMsgData has meaning related to the \e ulEvent 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. +// +//***************************************************************************** +unsigned long +USBHMouseCallback(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgParam, void *pvMsgData) +{ + tUSBHMouse *pUSBHMouse; + + // + // Recover the pointer to the instance data. + // + pUSBHMouse = (tUSBHMouse *)pvCBData; + + switch(ulEvent) + { + // + // New mouse has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a mouse is present. + // + pUSBHMouse->ulHIDFlags |= USBHMS_DEVICE_PRESENT; + + // + // Notify the application that a new mouse was connected. + // + pUSBHMouse->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No mouse is present. + // + pUSBHMouse->ulHIDFlags &= ~USBHMS_DEVICE_PRESENT; + + // + // Notify the application that the mouse was disconnected. + // + pUSBHMouse->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New mouse report structure was received. + // + USBHHIDGetReport(pUSBHMouse->ulMouseInstance, 0, + pUSBHMouse->pucBuffer, + USBHMS_REPORT_SIZE); + + // + // Update the current state of the mouse and notify the application + // of any changes. + // + UpdateMouseState(pUSBHMouse); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidmouse.h b/usblib/host/usbhhidmouse.h new file mode 100644 index 0000000..4c4fb12 --- /dev/null +++ b/usblib/host/usbhhidmouse.h @@ -0,0 +1,68 @@ +//***************************************************************************** +// +// usbhhidmouse.h - This file holds the application interfaces for USB +// mouse devices. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +extern unsigned long USBHMouseOpen(tUSBCallback pfnCallback, + unsigned char *pucBuffer, + unsigned long ulBufferSize); +extern unsigned long USBHMouseClose(unsigned long ulInstance); +extern unsigned long USBHMouseInit(unsigned long ulInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..62dcd80 --- /dev/null +++ b/usblib/host/usbhhub.c @@ -0,0 +1,1251 @@ +//***************************************************************************** +// +// usbhhub.c - This file contains the host HID driver. +// +// Copyright (c) 2011-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "inc/hw_ints.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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Forward references to the hub class driver functions. +// +//***************************************************************************** +static void *HubDriverOpen(tUSBHostDevice *pDevice); +static void HubDriverClose(void *pvInstance); + +//***************************************************************************** +// +//! This constant global structure defines the Hub Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHubClassDriver = +{ + USB_CLASS_HUB, + HubDriverOpen, + HubDriverClose, + 0 +}; + +//***************************************************************************** +// +// The instance data storage for attached hub. +// +//***************************************************************************** +static tHubInstance *g_pRootHub; + +//***************************************************************************** +// +// Hub and port state change flags as reported via the hub's IN endpoint. +// +//***************************************************************************** +static volatile unsigned long g_ulChangeFlags; + +// +// Note: The following assumes ROOT_HUB_MAX_PORTS is less than 32! +// +static unsigned long g_ulHubChanges; + +//***************************************************************************** +// +// This function is called to send a request to the hub to set a feature on +// a given port. +// +// \param ulInstance is the hub device instance. +// \param ucPort is the port number for this request. +// \param usFeature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the set feature request to the hub indicated by the +// \e ulInstance parameter. The \e ucPort 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 ucPort value of 0 is an access to the hub itself and +// not one of the hub ports. The \e usFeature is the feature request to set +// on the given port. For example, a \e usFeature value of +// \e HUB_FEATURE_PORT_RESET and \e ucPort value of 1 will cause reset +// signaling to hub port 1. +// +// \return None. +// +//***************************************************************************** +static void +HubSetPortFeature(unsigned long ulInstance, unsigned char ucPort, + unsigned short usFeature) +{ + tUSBRequest SetupPacket; + tHubInstance *pHubInstance; + tUSBHostDevice *pDevice; + + // + // Retrieve the hub instance and device pointer. + // + pHubInstance = (tHubInstance *)ulInstance; + pDevice = pHubInstance->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + SetupPacket.bRequest = USBREQ_SET_FEATURE; + SetupPacket.wValue = usFeature; + SetupPacket.wIndex = ucPort; + SetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, + &SetupPacket, + pDevice, + 0, + 0, + pDevice->DeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to clear a feature on +// a given port. +// +// \param ulInstance is the hub device instance. +// \param ucPort is the port number for this request. +// \param usFeature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the clear feature request to the hub indicated by +// the \e ulInstance parameter. The \e ucPort 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 ucPort value of 0 is an access to the hub +// itself and not one of the hub ports. The \e usFeature is the feature +// request to clear on the given port. For example, a \e usFeature value of +// \e HUB_FEATURE_C_PORT_RESET and \e ucPort 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(unsigned long ulInstance, unsigned char ucPort, + unsigned short usFeature) +{ + tUSBRequest SetupPacket; + tHubInstance *pHubInstance; + tUSBHostDevice *pDevice; + + // + // Retrieve the hub instance and device pointer. + // + pHubInstance = (tHubInstance *)ulInstance; + pDevice = pHubInstance->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + SetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + SetupPacket.wValue = usFeature; + SetupPacket.wIndex = ucPort; + SetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, + &SetupPacket, + pDevice, + 0, + 0, + pDevice->DeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is used to retrieve the current status of a port on the +// hub. +// +// \param ulInstance is the hub device instance. +// \param ucPort is the port number for this request. +// \param pusPortStatus is a pointer to the memory to store the current status +// of the port. +// \param pusPortChange 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 ucPort parameter. The \e ucPort 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 ucPort value of 0 is an +// access to the hub itself and not one of the hub ports. +// +// \return None. +// +//***************************************************************************** +static tBoolean +HubGetPortStatus(unsigned long ulInstance, unsigned char ucPort, + unsigned short *pusPortStatus, unsigned short *pusPortChange) +{ + unsigned long ulData, ulRead; + tUSBRequest SetupPacket; + tHubInstance *pHubInstance; + tUSBHostDevice *pDevice; + + // + // Retrieve the hub instance and device pointer. + // + pHubInstance = (tHubInstance *)ulInstance; + pDevice = pHubInstance->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the fields to get the hub status. + // + SetupPacket.bRequest = USBREQ_GET_STATUS; + SetupPacket.wValue = 0; + SetupPacket.wIndex = (unsigned short)ucPort; + SetupPacket.wLength = 4; + + // + // Send the request. + // + ulRead = USBHCDControlTransfer(0, &SetupPacket, + pDevice, + (unsigned char *)&ulData, 4, + pDevice->DeviceDescriptor.bMaxPacketSize0); + + // + // Check that we received the correct number of bytes. + // + if(ulRead != 4) + { + return(false); + } + else + { + // + // We got 4 bytes from the device. Now translate these into the 2 + // unsigned shorts we pass back to the caller. + // + *pusPortStatus = (unsigned short)(ulData & 0xFFFF); + *pusPortChange = (unsigned short)(ulData >> 16); + + DEBUG_OUTPUT("Port %d, status 0x%04x, change 0x%04x\n", ucPort, + *pusPortStatus, *pusPortChange); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint for the hub +// device. +// +//***************************************************************************** +static void +HubIntINCallback(unsigned long ulPipe, unsigned long ulEvent) +{ + switch (ulEvent) + { + // + // 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(ulPipe, (unsigned char *)&g_ulHubChanges, + (unsigned long)g_pRootHub->ucReportSize); + 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(ulPipe); + + // + // Update our global "ports needing service" flags with the latest + // information we've just received. + // + g_ulChangeFlags |= g_ulHubChanges; + + // + // Send the report data to the USB host hub device class driver if + // we have been given a callback function. + // + if(g_pRootHub->pfnCallback) + { + g_pRootHub->pfnCallback((void *)g_pRootHub->ulCBData, + USB_EVENT_RX_AVAILABLE, + ulPipe, + &g_ulHubChanges); + } + + break; + } + case USB_EVENT_ERROR: + { + break; + } + } +} + +//***************************************************************************** +// +// Query the class-specific hub descriptor. +// +//***************************************************************************** +static tBoolean +GetHubDescriptor(tUsbHubDescriptor *psDesc) +{ + unsigned long ulRead; + tUSBRequest SetupPacket; + tUSBHostDevice *pDevice; + + // + // Retrieve the device pointer. + // + pDevice = g_pRootHub->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.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. + // + SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + SetupPacket.wValue = (USB_DTYPE_HUB << 8); + SetupPacket.wIndex = 0; + SetupPacket.wLength = sizeof(tUsbHubDescriptor); + + // + // Send the request. + // + ulRead = USBHCDControlTransfer(0, &SetupPacket, + pDevice, + (void *)psDesc, sizeof(tUsbHubDescriptor), + pDevice->DeviceDescriptor.bMaxPacketSize0); + + // + // Make sure we got at least some data. + // + if(ulRead == 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 *pDevice) +{ + tEndpointDescriptor *pEndpointDescriptor; + tInterfaceDescriptor *pInterface; + tUsbHubDescriptor sHubDesc; + tBoolean bRetcode; + unsigned long ulLoop; + + // + // If we are already talking to a hub, fail the call. We only support + // a single hub. + // + if(g_pRootHub->bHubActive) + { + return(0); + } + + // + // Get pointers to the device descriptors we need to look at. + // + pInterface = USBDescGetInterface(pDevice->pConfigDescriptor, 0, 0); + pEndpointDescriptor = USBDescGetInterfaceEndpoint(pInterface, 0, + pDevice->ulConfigDescriptorSize); + + // + // If there are no endpoints, something is wrong since a hub must have + // a single INT endpoint for signaling. + // + if(pEndpointDescriptor == 0) + { + return 0; + } + + // + // Make sure we really are talking to a hub. + // + if((pInterface->bInterfaceClass != USB_CLASS_HUB) || + pInterface -> bInterfaceSubClass || pInterface -> bInterfaceProtocol) + { + // + // Something is wrong - this isn't a hub or, if it is, we don't + // understand the protocol it is using. + // + return(0); + } + + // + // Remember the device information for later. + // + g_pRootHub->pDevice = pDevice; + + // + // A hub must support an interrupt endpoint so check this. + // + if((pEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // The endpoint is the correct type. Is it an IN endpoint? + // + if(pEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Yes - all is well with the hub endpoint so allocate a pipe to + // handle traffic from the hub. + // + g_pRootHub->ulIntInPipe = USBHCDPipeAlloc(0,USBHCD_PIPE_INTR_IN, + pDevice, + HubIntINCallback); + USBHCDPipeConfig(g_pRootHub->ulIntInPipe, + pEndpointDescriptor->wMaxPacketSize, + pEndpointDescriptor->bInterval, + pEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M); + } + } + + // + // Did we allocate the endpoint successfully? + // + if(!g_pRootHub->ulIntInPipe) + { + // + // 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_pRootHub->pfnCallback != 0) + { + g_pRootHub->pfnCallback((void *)g_pRootHub->ulCBData, + USB_EVENT_CONNECTED, + (unsigned long)g_pRootHub, 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_pRootHub->ucNumPorts = sHubDesc.bNbrPorts; + g_pRootHub->usHubCharacteristics = sHubDesc.wHubCharacteristics; + g_pRootHub->ucNumPortsInUse = (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_pRootHub->ucReportSize = ((sHubDesc.bNbrPorts + 1) + 7) / 8; + + // + // Enable power to all ports on the hub. + // + for(ulLoop = 1; ulLoop <= sHubDesc.bNbrPorts; ulLoop++) + { + // + // Turn on power to this port. + // + HubSetPortFeature((unsigned long )g_pRootHub, ulLoop, + HUB_FEATURE_PORT_POWER); + } + + // + // Clear out our port state structures. + // + for(ulLoop = 0; ulLoop < MAX_USB_DEVICES; ulLoop++) + { + g_pRootHub->psPorts[ulLoop].bChanged = false; + g_pRootHub->psPorts[ulLoop].sState = PORT_IDLE; + } + } + else + { + // + // Oops - we can't read the hub descriptor! Tidy up and return + // an error. + // + USBHCDPipeFree(g_pRootHub->ulIntInPipe); + g_pRootHub->pfnCallback = 0; + g_pRootHub->bHubActive = false; + return(0); + } + + // + // If we get here, all is well so remember that the hub is connected and + // active. + // + g_pRootHub->bHubActive = true; + + // + // Return our instance data pointer to the caller to use as a handle. + // + return((void *)g_pRootHub); +} + +//***************************************************************************** +// +// Close an instance of the hub driver. +// +//***************************************************************************** +static void +HubDriverClose(void *pvInstance) +{ + unsigned long ulLoop; + + // + // No device so just exit. + // + if(g_pRootHub->pDevice == 0) + { + return; + } + + // + // Disconnect any devices that are currently connected to the hub. + // + for(ulLoop = 0; ulLoop < MAX_USB_DEVICES; ulLoop++) + { + // + // Does this port have a device connected to it that we have previously + // reported to the host control layer? + // + if((g_pRootHub->psPorts[ulLoop].sState == PORT_ACTIVE) || + (g_pRootHub->psPorts[ulLoop].sState == PORT_RESET_WAIT) || + (g_pRootHub->psPorts[ulLoop].sState == PORT_ENUMERATED) || + (g_pRootHub->psPorts[ulLoop].sState == PORT_ERROR)) + { + // + // Yes - tell the host controller to disconnect the device. + // + USBHCDHubDeviceDisconnected(0, + g_pRootHub->psPorts[ulLoop].ulDevHandle); + + } + + // + // Make sure that the state returns to idle. + // + g_pRootHub->psPorts[ulLoop].sState = PORT_IDLE; + + } + + // + // Reset the device pointer. + // + g_pRootHub->pDevice = 0; + + // + // Mark the hub as absent. + // + g_pRootHub->bHubActive = false; + + // + // Note that we are not in the middle of enumerating anything. + // + g_pRootHub->bEnumerationBusy = false; + + // + // Free the Interrupt IN pipe. + // + if(g_pRootHub->ulIntInPipe != 0) + { + USBHCDPipeFree(g_pRootHub->ulIntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(g_pRootHub->pfnCallback != 0) + { + g_pRootHub->pfnCallback((void *)g_pRootHub->ulCBData, + USB_EVENT_DISCONNECTED, + (unsigned long)g_pRootHub, 0); + } +} + +//***************************************************************************** +// +// Perform any processing required as a result of a change in the reset +// signaling for a given port. +// +//***************************************************************************** +static void +HubDriverReset(unsigned char ucPort, tBoolean 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_pRootHub->psPorts[ucPort].sState = PORT_RESET_WAIT; + + // + // Set the wait to 10ms (10 frames) from now. + // + g_pRootHub->psPorts[ucPort].ulCount = 10; + } + else + { + // + // Was this device previously active? + // + if(g_pRootHub->psPorts[ucPort].sState == PORT_ACTIVE) + { + USBHCDHubDeviceDisconnected(0, + g_pRootHub->psPorts[ucPort].ulDevHandle); + } + + // + // The reset is active so mark our port as in reset. + // + g_pRootHub->psPorts[ucPort].sState = PORT_RESET_ACTIVE; + } +} + +//***************************************************************************** +// +// Start the process of enumerating a new device by issuing a reset to the +// appropriate downstream port. +// +//***************************************************************************** +static void +HubDriverDeviceReset(unsigned char ucPort) +{ + DEBUG_OUTPUT("Starting enumeration for port %d\n", ucPort); + + // + // Record the fact that we are in the process of enumerating a device. + // + g_pRootHub->bEnumerationBusy = true; + + // + // Save the port that is being enumerated. + // + g_pRootHub->ucEnumIdx = ucPort; + + // + // Mark the port as being reset. + // + g_pRootHub->psPorts[ucPort].sState = PORT_RESET_ACTIVE; + + // + // Initiate a reset on the relevant port to start the enumeration process. + // + HubSetPortFeature((unsigned long)g_pRootHub, + ucPort, + 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(unsigned char ucPort, tBoolean bLowSpeed) +{ + DEBUG_OUTPUT("HubDriverDeviceConnect\n"); + + // + // We've allocated a port table entry so fill it in then initiate a reset + // on the device. + // + g_pRootHub->psPorts[ucPort].bChanged = false; + g_pRootHub->psPorts[ucPort].bLowSpeed = bLowSpeed; + + // + // Mark the port as having a device present but not enumerated. + // + DEBUG_OUTPUT("Deferring enumeration for port %d\n", ucPort); + g_pRootHub->psPorts[ucPort].sState = PORT_CONNECTED; + + // + // Wait 100ms to reset the device. + // + g_pRootHub->psPorts[ucPort].ulCount = 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(unsigned char ucPort) +{ + // + // This is a device we are currently managing. Have we already informed + // the host controller that it is present? + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_ACTIVE) || + (g_pRootHub->psPorts[ucPort].sState == PORT_RESET_WAIT) || + (g_pRootHub->psPorts[ucPort].sState == PORT_ENUMERATED) || + (g_pRootHub->psPorts[ucPort].sState == PORT_ERROR)) + { + // + // Yes - tell the host controller that the device is not longer + // connected. + // + USBHCDHubDeviceDisconnected(0, g_pRootHub->psPorts[ucPort].ulDevHandle); + } + + // + // If the device was being enumerated, make sure we clear the flag + // indicating that an enumeration is still ongoing. + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_RESET_ACTIVE) || + (g_pRootHub->psPorts[ucPort].sState == PORT_RESET_WAIT) || + (g_pRootHub->psPorts[ucPort].sState == PORT_ACTIVE)) + { + g_pRootHub->bEnumerationBusy = false; + } + + // + // Free up the port state structure. + // + g_pRootHub->psPorts[ucPort].sState = PORT_IDLE; +} + +//***************************************************************************** +// +// This function is called periodically by USBHCDMain(). We use it to handle +// the hub port state machine. +// +//***************************************************************************** +void +USBHHubMain(void) +{ + unsigned short usStatus, usChanged; + unsigned char ucPort; + tBoolean bRetcode; + + // + // If the hub isn't present, just return. + // + if((g_pRootHub == 0) || (!g_pRootHub->bHubActive)) + { + return; + } + + // + // Initialize the status variables. + // + usStatus = 0; + usChanged = 0; + + // + // The hub is active and something changed. Check to see which port changed + // state and handle as necessary. + // + for(ucPort = 0; ucPort <= g_pRootHub->ucNumPortsInUse; ucPort++) + { + // + // Decrement any wait counter if there is one present. + // + if(g_pRootHub->psPorts[ucPort].ulCount != 0) + { + g_pRootHub->psPorts[ucPort].ulCount--; + } + + // + // Is this port waiting to be enumerated and is the last device + // enumeration finished? + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_CONNECTED) && + (!g_pRootHub->bEnumerationBusy) && + (g_pRootHub->psPorts[ucPort].ulCount == 0)) + { + // + // Yes - start the enumeration processing for this device. + // + HubDriverDeviceReset(ucPort); + } + + // + // If the state is PORT_RESET_WAIT then the hub is waiting before + // accessing device as the USB 2.0 specification requires. + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_RESET_WAIT) && + (g_pRootHub->psPorts[ucPort].ulCount == 0)) + { + // + // Start the enumeration process if the timeout has passed and + // the hub is waiting to start enumerating the device. + // + g_pRootHub->psPorts[ucPort].sState = PORT_ACTIVE; + + // + // Call the main host controller layer to have it enumerate the newly + // connected device. + // + g_pRootHub->psPorts[ucPort].ulDevHandle = + USBHCDHubDeviceConnected(0, 1, ucPort, + g_pRootHub->psPorts[ucPort].bLowSpeed, + g_pRootHub->psPorts[ucPort].pucConfigDesc, + g_pRootHub->psPorts[ucPort].ulConfigSize); + } + + // + // If an enumeration is in progress and the loop is not on the port + // being enumerated then skip the port. + // + if(g_pRootHub->bEnumerationBusy && (g_pRootHub->ucEnumIdx != ucPort)) + { + continue; + } + + // + // Did something change for this particular port? + // + if(g_ulChangeFlags & (1 << ucPort)) + { + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus((unsigned long)g_pRootHub, ucPort, + &usStatus, &usChanged); + + // + // 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(INT_USB0); + g_ulChangeFlags &= ~(1 << ucPort); + OS_INT_ENABLE(INT_USB0); + + // + // 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(usChanged & HUB_PORT_CHANGE_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connection change on port %d\n", ucPort); + + // + // Clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_CONNECTION); + + // + // Was a device connected or disconnected? + // + if(usStatus & HUB_PORT_STATUS_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connected\n"); + + // + // A device was connected. + // + HubDriverDeviceConnect(ucPort, + ((usStatus & HUB_PORT_STATUS_LOW_SPEED) ? + true : false)); + } + else + { + DEBUG_OUTPUT("Disconnected\n"); + + // + // A device was disconnected. + // + HubDriverDeviceDisconnect(ucPort); + } + } + + // + // Did a reset on the port complete? + // + if(usChanged & HUB_PORT_CHANGE_RESET) + { + // + // Clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_RESET); + + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus((unsigned long)g_pRootHub, ucPort, + &usStatus, &usChanged); + + DEBUG_OUTPUT("Reset %s for port %d\n", + ((usStatus & HUB_PORT_STATUS_RESET) ? "asserted" : + "deasserted"), ucPort); + + // + // Handle the reset case. + // + HubDriverReset(ucPort, (usStatus & HUB_PORT_STATUS_RESET) ? + true : false); + } + + // + // Did an over-current reset on the port complete? + // + if(usChanged & HUB_PORT_CHANGE_OVER_CURRENT) + { + DEBUG_OUTPUT("Port %d over current.\n", ucPort); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_OVER_CURRENT); + } + + // + // Has the port been enabled or disabled? + // + if(usChanged & HUB_PORT_CHANGE_ENABLED) + { + DEBUG_OUTPUT("Enable change for port %d.\n", ucPort); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_ENABLE); + } + + // + // Has the port been suspended or resumed? + // + if(usChanged & HUB_PORT_CHANGE_SUSPENDED) + { + DEBUG_OUTPUT("Suspend change for port %d.\n", ucPort); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_SUSPEND); + } + } + } +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device has been enumerated. +//! +//! \param ucHub is the address of the hub to which the downstream device +//! is attached. +//! \param ucPort 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(unsigned char ucHub, unsigned char ucPort) +{ + DEBUG_OUTPUT("Enumeration complete for hub %d, port %d\n", ucHub, ucPort); + + // + // Record the fact that the device is up and running. + // + g_pRootHub->psPorts[ucPort].sState = PORT_ENUMERATED; + + // + // 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_pRootHub->bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device failed to enumerate. +//! +//! \param ucHub is the address of the hub to which the downstream device +//! is attached. +//! \param ucPort 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(unsigned char ucHub, unsigned char ucPort) +{ + DEBUG_OUTPUT("Enumeration error for hub %d, port %d\n", ucHub, ucPort); + + // + // Record the fact that the device is not working correctly. + // + g_pRootHub->psPorts[ucPort].sState = PORT_ERROR; + + // + // 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_pRootHub->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. +//! \param pucHubPool is the memory pool allocated to the USB hub class. +//! \param ulPoolSize is the size in bytes of the memory pool provided by the +//! \e pucHubPool parameter. +//! \param psHubInstance is a pointer to an instance of the private hub data. +//! \param ulNumHubs is the number of hubs to support. +//! +//! 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 pucHubPool is memory provided to the hub class to +//! manage the devices that are connected to the hub. The \e ulPoolSize 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 ulNumHubs 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. +// +//***************************************************************************** +unsigned long +USBHHubOpen(tUSBCallback pfnCallback, unsigned char *pucHubPool, + unsigned long ulPoolSize, tHubInstance *psHubInstance, + unsigned long ulNumHubs) +{ + unsigned long ulLoop, ulBlockSize; + + // + // Only one hub is supported. + // + if(g_pRootHub) + { + DEBUG_OUTPUT("USBHHubOpen failed - already connected.\n"); + return(0); + } + + // + // Save this instance. + // + g_pRootHub = psHubInstance; + + // + // Save the instance data for this device. + // + g_pRootHub->pfnCallback = pfnCallback; + + // + // Divide the pool up into blocks, one for each supported port. We make + // sure that each block is a multiple of 4 bytes. + // + ulBlockSize = (ulPoolSize / MAX_USB_DEVICES) & ~3; + for(ulLoop = 0; ulLoop < MAX_USB_DEVICES; ulLoop++) + { + g_pRootHub->psPorts[ulLoop].pucConfigDesc = (pucHubPool + + (ulLoop * ulBlockSize)); + g_pRootHub->psPorts[ulLoop].ulConfigSize = ulBlockSize; + } + + DEBUG_OUTPUT("USBHHubOpen completed.\n"); + + // + // Return the device instance pointer. + // + return((unsigned long)g_pRootHub); +} + +//***************************************************************************** +// +//! This function is used to release a hub device instance. +//! +//! \param ulInstance 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(unsigned long ulInstance) +{ + // + // Forget the instance pointer. + // + g_pRootHub = 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_pRootHub = 0; + g_ulChangeFlags = 0; + g_ulHubChanges = 0; +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhub.h b/usblib/host/usbhhub.h new file mode 100644 index 0000000..e79965e --- /dev/null +++ b/usblib/host/usbhhub.h @@ -0,0 +1,206 @@ +//***************************************************************************** +// +// usbhhub.h - This hold the host driver for hid class. +// +// Copyright (c) 2011-2012 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 9453 of the Stellaris 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 + +extern const tUSBHostClassDriver g_USBHubClassDriver; + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// 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 + +typedef struct +{ + // + //! The total number of bytes in the descriptor (including this field). + // + unsigned char bLength; + + // + //! The descriptor type. For a hub descriptor, this will be USB_DTYPE_HUB + //! (0x29 or 41 decimal). + // + unsigned char bDescType; + + // + //! The number of downstream-facing ports that the hub supports. + // + unsigned char bNbrPorts; + + // + //! Characteristics of the hub device including its power switching + //! capabilities and overcurrent protection mode. + // + unsigned short 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. + // + unsigned char bPwrOn2PwrGood; + + // + //! The maximum current requirement for the hub circuitry in mA. + // + unsigned char 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. + // + unsigned char PortInfo[((ROOT_HUB_MAX_PORTS + 7) / 8) * 2]; +} +PACKED tUsbHubDescriptor; + +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// Values used as the usFeature parameter to USBHHubClearHubFeature. +// +//***************************************************************************** +#define HUB_FEATURE_C_HUB_LOCAL_POWER 0 +#define HUB_FEATURE_C_HUB_OVER_CURRENT 1 + +//***************************************************************************** +// +// Values used as the usFeature 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 *pusHubStatus and *pusHubChange 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 *pusPortStatus 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 *pusPortChange 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 + +//***************************************************************************** +// +// Public function prototypes for the HUB class driver. +// +//***************************************************************************** +extern unsigned long USBHHubOpen(tUSBCallback pfnCallback, + unsigned char *pucHubPool, + unsigned long ulPoolSize, + tHubInstance *psHubInstance, + unsigned long ulNumHubs); +extern void USBHHubClose(unsigned long ulInstance); + +#ifdef __cplusplus +} +#endif + +#endif // __USBHHUB_H__ diff --git a/usblib/host/usbhmsc.c b/usblib/host/usbhmsc.c new file mode 100644 index 0000000..7a2756e --- /dev/null +++ b/usblib/host/usbhmsc.c @@ -0,0 +1,713 @@ +//***************************************************************************** +// +// usbhmsc.c - USB MSC host driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/usb.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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Forward declarations for the driver open and close calls. +// +//***************************************************************************** +static void *USBHMSCOpen(tUSBHostDevice *pDevice); +static void USBHMSCClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB MSC host driver. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the callback. + // + tUSBHMSCCallback pfnCallback; + + // + // The Maximum LUNs + // + unsigned long ulMaxLUN; + + // + // The total number of blocks associated with this device. + // + unsigned long ulNumBlocks; + + // + // The size of the blocks associated with this device. + // + unsigned long ulBlockSize; + + // + // Bulk IN pipe. + // + unsigned long ulBulkInPipe; + + // + // Bulk OUT pipe. + // + unsigned long ulBulkOutPipe; +} +tUSBHMSCInstance; + +//***************************************************************************** +// +// The array of USB MSC host drivers. +// +//***************************************************************************** +static tUSBHMSCInstance g_USBHMSCDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Mass Storage Class Driver that +//! is provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHostMSCClassDriver = +{ + USB_CLASS_MASS_STORAGE, + USBHMSCOpen, + USBHMSCClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of the MSC driver. +//! +//! \param pDevice 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 pDevice 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 USBMSCClose() when the +//! driver is no longer needed. +//! +//! \return The function will return a pointer to a MSC driver instance. +// +//***************************************************************************** +static void * +USBHMSCOpen(tUSBHostDevice *pDevice) +{ + long lIdx; + tEndpointDescriptor *pEndpointDescriptor; + tInterfaceDescriptor *pInterface; + + // + // Don't allow the device to be opened without closing first. + // + if(g_USBHMSCDevice.pDevice) + { + return(0); + } + + // + // Save the device pointer. + // + g_USBHMSCDevice.pDevice = pDevice; + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface(pDevice->pConfigDescriptor, 0, 0); + + // + // Loop through the endpoints of the device. + // + for(lIdx = 0; lIdx < 3; lIdx++) + { + // + // Get the first endpoint descriptor. + // + pEndpointDescriptor = + USBDescGetInterfaceEndpoint(pInterface, lIdx, + pDevice->ulConfigDescriptorSize); + + // + // If no more endpoints then break out. + // + if(pEndpointDescriptor == 0) + { + break; + } + + // + // See if this is a bulk endpoint. + // + if((pEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_BULK) + { + // + // See if this is bulk IN or bulk OUT. + // + if(pEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Allocate the USB Pipe for this Bulk IN endpoint. + // + g_USBHMSCDevice.ulBulkInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_IN_DMA, + pDevice, + pEndpointDescriptor->wMaxPacketSize, + 0); + // + // Configure the USB pipe as a Bulk IN endpoint. + // + USBHCDPipeConfig(g_USBHMSCDevice.ulBulkInPipe, + pEndpointDescriptor->wMaxPacketSize, + 0, + (pEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + else + { + // + // Allocate the USB Pipe for this Bulk OUT endpoint. + // + g_USBHMSCDevice.ulBulkOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_OUT_DMA, + pDevice, + pEndpointDescriptor->wMaxPacketSize, + 0); + // + // Configure the USB pipe as a Bulk OUT endpoint. + // + USBHCDPipeConfig(g_USBHMSCDevice.ulBulkOutPipe, + pEndpointDescriptor->wMaxPacketSize, + 0, + (pEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + } + } + + // + // If the callback exists, call it with an Open event. + // + if(g_USBHMSCDevice.pfnCallback != 0) + { + g_USBHMSCDevice.pfnCallback((unsigned long)&g_USBHMSCDevice, + MSC_EVENT_OPEN, 0); + } + + + g_USBHMSCDevice.ulMaxLUN = 0xffffffff; + + // + // Return the only instance of this device. + // + return(&g_USBHMSCDevice); +} + +//***************************************************************************** +// +//! 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_USBHMSCDevice.pDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + g_USBHMSCDevice.pDevice = 0; + + // + // Free the Bulk IN pipe. + // + if(g_USBHMSCDevice.ulBulkInPipe != 0) + { + USBHCDPipeFree(g_USBHMSCDevice.ulBulkInPipe); + } + + // + // Free the Bulk OUT pipe. + // + if(g_USBHMSCDevice.ulBulkOutPipe != 0) + { + USBHCDPipeFree(g_USBHMSCDevice.ulBulkOutPipe); + } + + // + // If the callback exists then call it. + // + if(g_USBHMSCDevice.pfnCallback != 0) + { + g_USBHMSCDevice.pfnCallback((unsigned long)&g_USBHMSCDevice, + MSC_EVENT_CLOSE, 0); + } +} + +//***************************************************************************** +// +//! This function retrieves the maximum number of the logical units on a +//! mass storage device. +//! +//! \param pDevice is the device instance pointer for this request. +//! \param ulInterface is the interface number on the device specified by the +//! \e ulAddress parameter. +//! \param pucMaxLUN 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 *pDevice, unsigned long ulInterface, + unsigned char *pucMaxLUN) +{ + tUSBRequest SetupPacket; + + // + // This is a Class specific interface IN request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; + + // + // Request a the Max LUN for this interface. + // + SetupPacket.bRequest = USBREQ_GET_MAX_LUN; + SetupPacket.wValue = 0; + + // + // Indicate the interface to use. + // + SetupPacket.wIndex = (unsigned short)ulInterface; + + // + // Only request a single byte of data. + // + SetupPacket.wLength = 1; + + // + // Put the setup packet in the buffer and send the command. + // + if(USBHCDControlTransfer(0, &SetupPacket, pDevice, pucMaxLUN, 1, + MAX_PACKET_SIZE_EP0) != 1) + { + *pucMaxLUN = 0; + } +} + +//***************************************************************************** +// +//! This function checks if a drive is ready to be accessed. +//! +//! \param ulInstance 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 ulInstance parameter to determine which device to check and +//! will return zero when the device is ready. Any non-zero return code +//! indicates that the device was not ready. +//! +//! \return This function will return zero if the device is ready and it will +//! return a other value if the device is not ready or if an error occurred. +// +//***************************************************************************** +long +USBHMSCDriveReady(unsigned long ulInstance) +{ + unsigned char ucMaxLUN, pBuffer[SCSI_INQUIRY_DATA_SZ]; + unsigned long ulSize; + tUSBHMSCInstance *pMSCDevice; + + // + // Get the instance pointer in a more usable form. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // If there is no device present then return an error. + // + if(pMSCDevice->pDevice == 0) + { + return(-1); + } + + // + // Only request the maximum number of LUNs once. + // + if(g_USBHMSCDevice.ulMaxLUN == 0xffffffff) + { + // + // Get the Maximum LUNs on this device. + // + USBHMSCGetMaxLUN(g_USBHMSCDevice.pDevice, + g_USBHMSCDevice.pDevice->ulInterface, &ucMaxLUN); + + // + // Save the Maximum number of LUNs on this device. + // + g_USBHMSCDevice.ulMaxLUN = ucMaxLUN; + } + + // + // Just return if the device is returning not present. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + if(USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + pBuffer, &ulSize) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + if((pBuffer[SCSI_RS_SKEY] == SCSI_RS_KEY_UNIT_ATTN) && + (pBuffer[SCSI_RS_SKEY_AD_SKEY] == SCSI_RS_KEY_NOTPRSNT)) + { + return(-1); + } + + // + // Issue a SCSI Inquiry to get basic information on the device + // + ulSize = SCSI_INQUIRY_DATA_SZ; + if((USBHSCSIInquiry(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + pBuffer, &ulSize) != SCSI_CMD_STATUS_PASS)) + { + return(-1); + } + + // + // Get the size of the drive. + // + ulSize = SCSI_INQUIRY_DATA_SZ; + if(USBHSCSIReadCapacity(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + pBuffer, &ulSize) != SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Read Capacity command. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe, pBuffer, &ulSize); + + // + // If the read capacity failed then check if the drive is ready. + // + if(USBHSCSITestUnitReady(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe) != SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Test Unit Ready command. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe, pBuffer, &ulSize); + } + + return(-1); + } + else + { + // + // Read the block size out, value is stored big endian. + // + pMSCDevice->ulBlockSize = + (pBuffer[7] | (pBuffer[6] << 8) | pBuffer[5] << 16 | + (pBuffer[4] << 24)); + + // + // Read the block size out. + // + pMSCDevice->ulNumBlocks = + (pBuffer[3] | (pBuffer[2] << 8) | pBuffer[1] << 16 | + (pBuffer[0] << 24)); + } + + // + // See if the drive is ready to use. + // + if(USBHSCSITestUnitReady(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe) != SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Test Unit Ready command. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe, pBuffer, &ulSize); + + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the mass storage device class driver. +//! +//! \param ulDrive 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 ulDrive +//! 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. +// +//***************************************************************************** +unsigned long +USBHMSCDriveOpen(unsigned long ulDrive, tUSBHMSCCallback pfnCallback) +{ + // + // Only the first drive is supported and only one callback is supported. + // + if((ulDrive != 0) || (g_USBHMSCDevice.pfnCallback)) + { + return(0); + } + + // + // Save the callback. + // + g_USBHMSCDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return((unsigned long)&g_USBHMSCDevice); +} + +//***************************************************************************** +// +//! This function should be called to release a drive instance. +//! +//! \param ulInstance 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(unsigned long ulInstance) +{ + tUSBHMSCInstance *pMSCDevice; + + // + // Get a pointer to the device instance data from the handle. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // Close the drive (if it is already open) + // + USBHMSCClose((void *)pMSCDevice); + + // + // Clear the callback indicating that the device is now closed. + // + pMSCDevice->pfnCallback = 0; +} + +//***************************************************************************** +// +//! This function performs a block read to an MSC device. +//! +//! \param ulInstance is the device instance to use for this read. +//! \param ulLBA is the logical block address to read on the device. +//! \param pucData is a pointer to the returned data buffer. +//! \param ulNumBlocks 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 ulInstance parameter. The \e ulLBA parameter specifies the +//! logical block address to read on the device. This function will only +//! perform \e ulNumBlocks block sized reads. In most cases this is a read +//! of 512 bytes of data. The \e *pucData buffer should be at least +//! \e ulNumBlocks * 512 bytes in size. +//! +//! \return The function returns zero for success and any negative value +//! indicates a failure. +// +//***************************************************************************** +long +USBHMSCBlockRead(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, unsigned long ulNumBlocks) +{ + tUSBHMSCInstance *pMSCDevice; + unsigned long ulSize; + + // + // Get the instance pointer in a more usable form. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // If there is no device present then return an error. + // + if(pMSCDevice->pDevice == 0) + { + return(-1); + } + + // + // Calculate the actual byte size of the read. + // + ulSize = pMSCDevice->ulBlockSize * ulNumBlocks; + + // + // Perform the SCSI read command. + // + if(USBHSCSIRead10(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + ulLBA, pucData, &ulSize, + ulNumBlocks) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +//! This function performs a block write to an MSC device. +//! +//! \param ulInstance is the device instance to use for this write. +//! \param ulLBA is the logical block address to write on the device. +//! \param pucData is a pointer to the data to write out. +//! \param ulNumBlocks 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 ulInstance parameter. The \e ulLBA parameter specifies the +//! logical block address to write on the device. This function will only +//! perform \e ulNumBlocks block sized writes. In most cases this is a write +//! of 512 bytes of data. The \e *pucData buffer should contain at least +//! \e ulNumBlocks * 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. +// +//***************************************************************************** +long +USBHMSCBlockWrite(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, unsigned long ulNumBlocks) +{ + tUSBHMSCInstance *pMSCDevice; + unsigned long ulSize; + + // + // Get the instance pointer in a more usable form. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // If there is no device present then return an error. + // + if(pMSCDevice->pDevice == 0) + { + return(-1); + } + + // + // Calculate the actual byte size of the write. + // + ulSize = pMSCDevice->ulBlockSize * ulNumBlocks; + + // + // Perform the SCSI write command. + // + if(USBHSCSIWrite10(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + ulLBA, pucData, &ulSize, + ulNumBlocks) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhmsc.h b/usblib/host/usbhmsc.h new file mode 100644 index 0000000..f15ffc6 --- /dev/null +++ b/usblib/host/usbhmsc.h @@ -0,0 +1,95 @@ +//***************************************************************************** +// +// usbhmsc.h - Definitions for the USB MSC host driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are the the events that will be passed in the \e ulEvent +// 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)(unsigned long ulInstance, + unsigned long ulEvent, + void *pvEventData); + +//***************************************************************************** +// +// Prototypes for the USB MSC host driver APIs. +// +//***************************************************************************** +extern unsigned long USBHMSCDriveOpen(unsigned long ulDrive, + tUSBHMSCCallback pfnCallback); +extern void USBHMSCDriveClose(unsigned long ulInstance); +extern long USBHMSCDriveReady(unsigned long ulInstance); +extern long USBHMSCBlockRead(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, + unsigned long ulNumBlocks); +extern long USBHMSCBlockWrite(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, + unsigned long ulNumBlocks); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..072cf9f --- /dev/null +++ b/usblib/host/usbhost.h @@ -0,0 +1,348 @@ +//***************************************************************************** +// +// usbhost.h - Host specific definitions for the USB host library. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 + +//***************************************************************************** +// +//! 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: +//! +//! void (*pfnEvent)(void *pvData); +//! +//! 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 call back for events on USB Pipes allocated +// by USBHCDPipeAlloc(). +// +// \param ulPipe is well the pipe +// \param ulEvent is well the event +// +// longer def thand may need more text in order to be recogized what should +// this really say about ourselves. +// +// \return None. +// +//***************************************************************************** +typedef void (* tHCDPipeCallback)(unsigned long ulPipe, + unsigned long ulEvent); + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +typedef struct +{ + // + //! The current device address for this device. + // + unsigned long ulAddress; + + // + //! The current interface for this device. + // + unsigned long ulInterface; + + // + //! A flag used to determine whether we need to pass an interrupt + //! notification on to this device as a result of endpoint activity. + // + tBoolean bNotifyInt; + + // + //! A flag used to record whether this is a low-speed or a full-speed + //! device. + // + tBoolean bLowSpeed; + + // + //! A flag indicating whether or not we have read the device's + //! configuration descriptor yet. + // + tBoolean bConfigRead; + + // + //! The hub number to which this device is attached. + // + unsigned char ucHub; + + // + //! The hub port number to which the device is attached. + // + unsigned char ucHubPort; + + // + //! The device descriptor for this device. + // + tDeviceDescriptor DeviceDescriptor; + + // + //! A pointer to the configuration descriptor for this device. + // + tConfigDescriptor *pConfigDescriptor; + + // + //! The size of the buffer allocated to pConfigDescriptor. + // + unsigned long ulConfigDescriptorSize; +} +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. + // + unsigned long ulInterfaceClass; + + // + //! The function is called when this class of device has been detected. + // + void * (*pfnOpen)(tUSBHostDevice *pDevice); + + // + //! 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. +// +//***************************************************************************** +void USBHCDEvents(void *pvData); + +//***************************************************************************** +// +// Prototypes for the USB Host controller APIs. +// +//***************************************************************************** +extern void USBHCDMain(void); +extern long USBHCDEventEnable(unsigned long ulIndex, void *pvEventDriver, + unsigned long ulEvent); +extern long USBHCDEventDisable(unsigned long ulIndex, void *pvEventDriver, + unsigned long ulEvent); +extern void USBHCDInit(unsigned long ulIndex, void *pData, + unsigned long ulSize); +extern void USBHCDPowerConfigInit(unsigned long ulIndex, + unsigned long ulFlags); +extern unsigned long USBHCDPowerConfigGet(unsigned long ulIndex); +extern unsigned long USBHCDPowerConfigSet(unsigned long ulIndex, + unsigned long ulConfig); +extern unsigned long USBHCDPowerAutomatic(unsigned long ulIndex); +extern void + USBHCDRegisterDrivers(unsigned long ulIndex, + const tUSBHostClassDriver * const *ppHClassDrvrs, + unsigned long ulNumDrivers); +extern void USBHCDTerm(unsigned long ulIndex); +extern void USBHCDSetConfig(unsigned long ulIndex, unsigned long ulDevice, + unsigned long ulConfiguration); +extern void USBHCDSetInterface(unsigned long ulIndex, unsigned long ulDevice, + unsigned long ulInterface, + unsigned ulAltSetting); +extern unsigned long USBHCDHubDeviceConnected(unsigned long ulIndex, + unsigned char ucHub, + unsigned char ucPort, + tBoolean bLowSpeed, + unsigned char *pucConfigPool, + unsigned long ulConfigSize); +extern void USBHCDHubDeviceDisconnected(unsigned long ulIndex, + unsigned long ulDevIndex); +extern void USBHCDSuspend(unsigned long ulIndex); +extern void USBHCDResume(unsigned long ulIndex); +extern void USBHCDReset(unsigned long ulIndex); +extern void USBHCDPipeFree(unsigned long ulPipe); +extern unsigned long USBHCDPipeAlloc(unsigned long ulIndex, + unsigned long ulEndpointType, + tUSBHostDevice *psDevice, + tHCDPipeCallback pCallback); +extern unsigned long USBHCDPipeAllocSize(unsigned long ulIndex, + unsigned long ulEndpointType, + tUSBHostDevice *psDevice, + unsigned long ulFIFOSize, + tHCDPipeCallback pCallback); +extern unsigned long USBHCDPipeConfig(unsigned long ulPipe, + unsigned long ulMaxPayload, + unsigned long ulInterval, + unsigned long ulTargetEndpoint); +extern unsigned long USBHCDPipeStatus(unsigned long ulPipe); +extern unsigned long USBHCDPipeWrite(unsigned long ulPipe, + unsigned char *pData, + unsigned long ulSize); +extern unsigned long USBHCDPipeRead(unsigned long ulPipe, unsigned char *pData, + unsigned long ulSize); +extern unsigned long USBHCDPipeSchedule(unsigned long ulPipe, + unsigned char *pucData, + unsigned long ulSize); +extern void USBHCDPipeDataAck(unsigned long ulPipe); +extern unsigned long USBHCDPipeReadNonBlocking(unsigned long ulPipe, + unsigned char *pucData, + unsigned long ulSize); +extern unsigned long USBHCDControlTransfer(unsigned long ulIndex, + tUSBRequest *pSetupPacket, + tUSBHostDevice *pDevice, + unsigned char *pData, + unsigned long ulSize, + unsigned long ulMaxPacketSize); +extern void USB0HostIntHandler(void); + +extern unsigned char USBHCDDevHubPort(unsigned long ulInstance); +extern unsigned char USBHCDDevAddress(unsigned long ulInstance); +extern unsigned char USBHCDDevClass(unsigned long ulInstance, + unsigned long ulInterface); +extern unsigned char USBHCDDevSubClass(unsigned long ulInstance, + unsigned long ulInterface); +extern unsigned char USBHCDDevProtocol(unsigned long ulInstance, + unsigned long ulInterface); + + +//***************************************************************************** +// +// The host class drivers supported by the USB library. +// +//***************************************************************************** +extern const tUSBHostClassDriver g_USBHostMSCClassDriver; +extern const tUSBHostClassDriver g_USBHIDClassDriver; +extern const tUSBHostClassDriver g_USBHostAudioClassDriver; + +#include "usbhostpriv.h" + +//***************************************************************************** +// +// 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..f3c8e81 --- /dev/null +++ b/usblib/host/usbhostenum.c @@ -0,0 +1,5694 @@ +//***************************************************************************** +// +// usbhostenum.c - Device enumeration code for the USB host library. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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/udma.h" +#include "driverlib/usb.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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_hcd +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_eUSBMode; + +extern void OTGDeviceDisconnect(unsigned long ulIndex); + +//***************************************************************************** +// +// Internal function prototypes. +// +//***************************************************************************** +static void USBHCDEP0StateTx(void); +static void USBHCDEnumHandler(void); +static void USBHCDClearFeature(unsigned long ulDevAddress, + unsigned long ulEndpoint, + unsigned long ulFeature); + +//***************************************************************************** +// +// 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 + +volatile unsigned long g_ulUSBHIntEvents; + +//***************************************************************************** +// +// Flags used to indicate that a uDMA transfer is pending on a pipe. +// +//***************************************************************************** +#define DMA_PEND_TRANSMIT_FLAG 0x10000 +#define DMA_PEND_RECEIVE_FLAG 0x1 + +volatile unsigned long g_ulDMAPending = 0; + +//***************************************************************************** +// +// Flag used to indicate that a workaround should be applied when using +// uDMA with USB. The uDMA transfers must match the USB FIFO size when +// with Rev A0 silicon. +// +//***************************************************************************** +static unsigned long g_bUseDMAWA = 0; + +//***************************************************************************** +// +// This holds the current power configuration that is used when USBHCDInit() +// is called. +// +//***************************************************************************** +static unsigned long g_ulPowerConfig = 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. + // + EP0_STATE_IDLE, + + // + // Setup packet is expecting data IN. + // + EP0_STATE_SETUP_IN, + + // + // Setup packet is sending data OUT. + // + EP0_STATE_SETUP_OUT, + + // + // The USB device is receiving data from the device due to an SETUP IN + // request. + // + EP0_STATE_RX, + + // + // 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. + // + EP0_STATE_STATUS, + + // + // This state is for when a response only has a status phase and no + // data phase. + // + EP0_STATE_STATUS_IN, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + EP0_STATE_STALL, + + // + // An error has occurred on endpoint 0. + // + EP0_STATE_ERROR +} +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. + // + unsigned char *pData; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_DeviceState.pEP0Data data buffer. + // + volatile unsigned long ulBytesRemaining; + + // + // The amount of data being sent/received due to a request. + // + unsigned long ulDataSize; + + // + // This is the current device address in use by endpoint 0. + // + unsigned long ulDevAddress; + + // + // The maximum packet size for the device responding to the setup packet. + // + unsigned long ulMaxPacketSize; + + // + // The host controller's state. + // + tEP0State eState; +} +tHostState; + +//***************************************************************************** +// +// This variable holds the current state of endpoint 0. +// +//***************************************************************************** +static volatile tHostState g_sUSBHEP0State = +{ + 0, // pData + 0, // ulBytesRemaining + 0, // ulDataSize + 0, // ulDevAddress + 0, // ulMaxPacketSize + EP0_STATE_IDLE // eState +}; + +//***************************************************************************** +// +// 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 unsigned long g_ulTickms = (50000000 / 3000); +static volatile unsigned long g_ulCurrentTick = 0; + +//***************************************************************************** +// +// The current active drivers. +// +//***************************************************************************** +static long g_lUSBHActiveDriver[MAX_USB_DEVICES + 1]; +static void *g_pvDriverInstance[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. + // + unsigned char ucEPNumber; + + // + // The DMA channel assigned to this endpoint. + // + unsigned char ucDMAChannel; + + // + // The current type for this pipe. + // + unsigned long ulType; + + // + // The millisecond interval for this pipe. + // + unsigned long ulInterval; + + // + // The next tick value to trigger and event on this pipe. + // + unsigned long ulNextEventTick; + + // + // The current call back for this pipe. + // + tHCDPipeCallback pfnCallback; + + // + // The pointer to which IN data must be copied. + // + unsigned char *pucReadPtr; + + // + // The number of bytes of read data to copy. + // + unsigned long ulReadSize; + + // + // The state of a given USB pipe. + // + volatile enum + { + PIPE_READING, + PIPE_DATA_READY, + PIPE_DATA_SENT, + PIPE_WRITING, + PIPE_STALLED, + PIPE_ERROR, + PIPE_IDLE, + PIPE_DISABLED + } + eState; + + // + // The actual FIFO offset allocated to this endpoint. + // + unsigned short usFIFOAddr; + + // + // The size of the FIFO entry based on the size parameter. These are + // equivalent to the USB_FIFO_SZ_* values in usb.h. + // + unsigned char ucFIFOSize; + + // + // The bit offset in the allocation structure. + // + unsigned char ucFIFOBitOffset; +} +tUSBHCDPipe; + +//***************************************************************************** +// +// The internal state of the device. +// +//***************************************************************************** +typedef enum +{ + HCD_DEV_DISCONNECTED, + HCD_DEV_CONNECTED, + HCD_DEV_REQUEST, + HCD_DEV_RESET, + HCD_DEV_ADDRESSED, + HCD_DEV_CONFIGURED, + HCD_DEV_GETSTRINGS, + HCD_DEV_ERROR, + HCD_VBUS_ERROR, + HCD_POWER_FAULT, + HCD_IDLE +} +tUSBHDeviceState; + +static void ProcessUSBDeviceStateMachine(tUSBHDeviceState eOldState, + unsigned long ulDevIndex); + +//***************************************************************************** +// +// 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(ulIdx) (g_sUSBHCD.USBOUTPipes[ulIdx].ulType | ulIdx) +#define IN_PIPE_HANDLE(ulIdx) (g_sUSBHCD.USBINPipes[ulIdx].ulType | ulIdx) + +//***************************************************************************** +// +// 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 +{ + unsigned long ulUSBBase; + + tUSBHCDPipe USBControlPipe; + tUSBHCDPipe USBOUTPipes[MAX_NUM_PIPES]; + tUSBHCDPipe USBINPipes[MAX_NUM_PIPES]; + unsigned char ucDMAChannels[MAX_NUM_DMA_CHANNELS]; + + // + // 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 USBDevice[MAX_USB_DEVICES + 1]; + + // + // Holds the current state of the device. + // + volatile tUSBHDeviceState eDeviceState[MAX_USB_DEVICES + 1]; + + // + // Pointer to the memory pool for this controller. + // + void *pvPool; + + // + // The pool size for this controller. + // + unsigned long ulPoolSize; + + // + // The number of endpoint pairs supported by the controller. + // + unsigned long ulNumEndpoints; + + // + // The class drivers for this controller. + // + const tUSBHostClassDriver * const *pClassDrivers; + + // + // The number of class drivers. + // + unsigned long ulNumClassDrivers; + + // + // This is the index in the driver list of the event driver. + // + long lEventDriver; + + // + // These are the generic event information used by the event driver. + // + unsigned long ulEventEnables; + + unsigned long ulClass; +} +tUSBHCD; + +//***************************************************************************** +// +// The global to hold all of the state information for a given host controller. +// +//***************************************************************************** +static tUSBHCD g_sUSBHCD; + +//***************************************************************************** +// +// Return the device index from a ulInstance value passed from an external +// source. +// +//***************************************************************************** +static unsigned char +HCDInstanceToDevIndex(unsigned long ulInstance) +{ + unsigned long ulDevIndex; + + // + // Get the device instance from the instance value. + // + ulDevIndex = (ulInstance & 0xff); + + // + // If the above math went negative or is too large just return 0xff. + // + if(ulDevIndex > MAX_USB_DEVICES) + { + ulDevIndex = 0xff; + } + + return(ulDevIndex); +} + +//============================================================================= +// +// This is the internal function that will map an event to a valid event flag. +// +// \param ulEvent 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 ulEvent parameter. +// +//============================================================================= +static unsigned long +GetEventFlag(unsigned long ulEvent) +{ + unsigned long ulEventFlag; + + ulEventFlag = 0; + + // + // Search for a valid event flag for the requested event. + // + switch(ulEvent) + { + case USB_EVENT_SOF: + { + ulEventFlag |= USBHCD_EVFLAG_SOF; + break; + } + case USB_EVENT_CONNECTED: + { + ulEventFlag |= USBHCD_EVFLAG_CONNECT; + break; + } + case USB_EVENT_DISCONNECTED: + { + ulEventFlag |= USBHCD_EVFLAG_DISCNCT; + break; + } + case USB_EVENT_UNKNOWN_CONNECTED: + { + ulEventFlag |= USBHCD_EVFLAG_UNKCNCT; + break; + } + case USB_EVENT_POWER_FAULT: + { + ulEventFlag |= USBHCD_EVFLAG_PWRFAULT; + break; + } + case USB_EVENT_POWER_DISABLE: + { + ulEventFlag |= USBHCD_EVFLAG_PWRDIS; + break; + } + case USB_EVENT_POWER_ENABLE: + { + ulEventFlag |= USBHCD_EVFLAG_PWREN; + break; + } + default: + { + break; + } + } + return(ulEventFlag); +} + +//============================================================================= +// +//! This function is called to enable a specific USB HCD event notification. +//! +//! \param ulIndex 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 ulEvent 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 ulEvent 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. +// +//============================================================================= +long +USBHCDEventEnable(unsigned long ulIndex, void *pvEventDriver, + unsigned long ulEvent) +{ + long lRet; + unsigned long ulEventFlag; + + ASSERT(ulIndex == 0); + + // + // Default the return to fail the call unless a valid event is found. + // + lRet = 0; + + // + // Get the event flag for this event. + // + ulEventFlag = GetEventFlag(ulEvent); + + // + // Check if there was an event flag for the corresponding event. + // + if(ulEventFlag) + { + // + // Set the enable for this event. + // + g_sUSBHCD.ulEventEnables |= ulEventFlag; + + // + // Indicate that the event was valid and is now enabled. + // + lRet = 1; + } + + return(lRet); +} + +//============================================================================= +// +//! This function is called to disable a specific USB HCD event notification. +//! +//! \param ulIndex 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 ulEvent 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 ulEvent 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. +// +//============================================================================= +long +USBHCDEventDisable(unsigned long ulIndex, void *pvEventDriver, + unsigned long ulEvent) +{ + long lRet; + unsigned long ulEventFlag; + + ASSERT(ulIndex == 0); + + // + // Default the return to fail the call unless a valid event is found. + // + lRet = 0; + + // + // Get the event flag for this event. + // + ulEventFlag = GetEventFlag(ulEvent); + + // + // Check if there was an event flag for the corresponding event. + // + if(ulEventFlag) + { + // + // Clear the enable for this event. + // + g_sUSBHCD.ulEventEnables &= ~ulEventFlag; + + // + // Indicate that the event was valid and is now disabled. + // + lRet = 1; + } + + return(lRet); +} + +//***************************************************************************** +// +// 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(unsigned long ulIndex, unsigned long ulClass) +{ + 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.ulEvent = USB_EVENT_UNKNOWN_CONNECTED; + sEvent.ulInstance = ulClass; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_UNKCNCT); +} + +//***************************************************************************** +// +// Internal memory allocation space is two unsigned long values where each +// bit represents a 64 byte block in the FIFO. This requires 64 bits for +// the 4096 bytes of FIFO available. +// +//***************************************************************************** +static unsigned long g_ulAlloc[2]; + +//***************************************************************************** +// +// This function handles freeing FIFO memory that has been allocated using the +// FIFOAlloc() function. +// +//***************************************************************************** +static void +FIFOFree(tUSBHCDPipe *pUSBPipe) +{ + unsigned long ulMask; + + // + // Calculate the mask value to use to clear off the allocated blocks used + // by the USB pipe specified by pUSBPipe. + // + ulMask = (1 << (pUSBPipe->ucFIFOSize - 2)) - 1; + ulMask = ulMask << pUSBPipe->ucFIFOBitOffset; + + // + // Determine which 32 bit word to access based on the size. + // + if(pUSBPipe->ucFIFOSize > USB_FIFO_SZ_64) + { + // + // If the FIFO size is greater than 64 then use the upper 32 bits. + // + g_ulAlloc[1] &= ~ulMask; + } + else + { + // + // If the FIFO size is less than or equal to 64 then use the lower + // 32 bits. + // + g_ulAlloc[0] &= ~ulMask; + } +} + +//***************************************************************************** +// +// This function is used to allocate FIFO memory to a given USB pipe. +// +// \param pUSBPipe is the USB pipe that needs FIFO memory allocated. +// \param ulSize is the minimum size in bytes of the FIFO to allocate. +// +// This function will allocate \e ulSize bytes to the USB pipe in the +// \e pUSBPipe parameter. The function will fill the pUSBPipe structure +// members ucFIFOSize and ucFIFOAddr 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 unsigned long +FIFOAlloc(tUSBHCDPipe *pUSBPipe, unsigned long ulSize) +{ + unsigned long ulBlocks, ulStart, ulBlockSize; + unsigned short usFIFOAddr; + unsigned long ulTemp, ulIndex; + + // + // 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(ulSize > 64) + { + ulIndex = 1; + } + else + { + ulIndex = 0; + } + + // + // Initial FIFO address is 0. + // + usFIFOAddr = 0; + + // + // Initialize the bit pattern and bit location. + // + ulBlocks = 1; + ulStart = 0; + + // + // The initial block size is always the minimum size of 64 bytes. + // + ulBlockSize = 64; + + // + // The initial size and offset are 64 and 0. + // + pUSBPipe->ucFIFOBitOffset = 0; + pUSBPipe->ucFIFOSize = 3; + + // + // Scan through 32 bits looking for a memory block large enough to fill + // the request. + // + while(usFIFOAddr <= 32) + { + // + // If the pattern is zero then it is a possible match. + // + if((g_ulAlloc[ulIndex] & ulBlocks) == 0) + { + // + // If the size is large enough then save it and break out of the + // loop. + // + if(ulBlockSize >= ulSize) + { + // + // Mark the memory as allocated. + // + g_ulAlloc[ulIndex] |= ulBlocks; + + break; + } + + // + // Increment the size of the FIFO block. + // + pUSBPipe->ucFIFOSize++; + + // + // Add in a new bit to the size of the allocation. + // + ulBlocks = ulBlocks | (ulBlocks << 1) ; + + // + // Double the current size. + // + ulBlockSize <<= 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. + // + pUSBPipe->ucFIFOBitOffset = usFIFOAddr; + pUSBPipe->ucFIFOSize = 3; + + // + // Reset the block size to the minimum (64 bytes). + // + ulBlockSize = 64; + + // + // Store the current starting bit location and set the block mask + // to this value. + // + ulStart = 1 << usFIFOAddr; + ulBlocks = ulStart; + } + + // + // Increase the address of the FIFO offset. + // + usFIFOAddr++; + } + + // + // If there was no block large enough then fail this call. + // + if(usFIFOAddr > 32) + { + ulBlockSize = 0; + pUSBPipe->usFIFOAddr = 0; + pUSBPipe->ucFIFOBitOffset = 0; + pUSBPipe->ucFIFOSize = 0; + } + else + { + // + // Calculate the offset in the FIFO. + // + ulTemp = pUSBPipe->ucFIFOBitOffset * 64; + + // + // Sizes greater than 64 are allocated in the second half of the FIFO + // memory space. + // + if(ulSize > 64) + { + ulTemp += 2048; + } + + // + // Convert this to the value that can be set in the USB controller. + // + pUSBPipe->usFIFOAddr = (unsigned short)ulTemp; + } + return(ulBlockSize); +} + +//***************************************************************************** +// +//! This function is used to allocate a USB HCD pipe. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param ulEndpointType is the type of endpoint that this pipe will be +//! communicating with. +//! \param psDevice is the device instance associated with this endpoint. +//! \param ulSize 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 ulSize 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. +// +//***************************************************************************** +unsigned long +USBHCDPipeAllocSize(unsigned long ulIndex, unsigned long ulEndpointType, + tUSBHostDevice *psDevice, unsigned long ulSize, + tHCDPipeCallback pfnCallback) +{ + long lIdx, lDMAIdx; + unsigned long ulHubAddr; + + ASSERT(ulIndex == 0); + + // + // Find a USB pipe that is free. + // + for(lIdx = 0; lIdx < MAX_NUM_PIPES; lIdx++) + { + // + // Handle OUT Pipes. + // + if(ulEndpointType & EP_PIPE_TYPE_OUT) + { + // + // A zero address indicates free. + // + if(g_sUSBHCD.USBOUTPipes[lIdx].psDevice == 0) + { + // + // Set up uDMA for the pipe. + // + if(ulEndpointType & EP_PIPE_USE_UDMA) + { + // + // First three endpoints have fixed channels on some + // parts so bias the pipes to match this as best is + // possible. + // + if(lIdx < 3) + { + // + // Check if the fixed channel is available for this + // USB pipe. + // + if((g_sUSBHCD.ucDMAChannels[1 + (lIdx * 2)] == + USBHCD_DMA_UNUSED)) + { + // + // The default channel was available so use it. + // + g_sUSBHCD.ucDMAChannels[1 + (lIdx * 2)] = lIdx; + g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel = + 1 + (lIdx * 2); + } + else + { + // + // Go to the next USB pipe if the fixed one was not + // available. + // + continue; + } + } + else + { + // + // Either the fixed channel was not available or the + // pipe index was more than the first 3 pipes that are + // available on all parts. + // + for(lDMAIdx = 1; lDMAIdx < MAX_NUM_DMA_CHANNELS; + lDMAIdx += 2) + { + // + // Find any available channel. + // + if(g_sUSBHCD.ucDMAChannels[lDMAIdx] == + USBHCD_DMA_UNUSED) + { + // + // Save the index and the DMA channel + // information. + // + g_sUSBHCD.ucDMAChannels[lDMAIdx] = lIdx; + g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel = + lDMAIdx; + } + } + } + + // + // If no DMA channel was available then just disable DMA + // on this pipe. + // + if(g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel == + USBHCD_DMA_UNUSED) + { + ulEndpointType &= ~EP_PIPE_USE_UDMA; + } + else + { + // + // Set the DMA channel for this endpoint, this has no + // effect on parts without configurable DMA. + // + MAP_USBEndpointDMAChannel( + USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel); + + // + // Clear all the attributes for the channel + // + MAP_uDMAChannelAttributeDisable( + g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel, + UDMA_ATTR_ALL); + + // + // Configure the uDMA channel for the pipe + // + MAP_uDMAChannelControlSet( + g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel, + (UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | + UDMA_ARB_64)); + } + } + + // + // Save the endpoint type and device address and callback + // function. + // + g_sUSBHCD.USBOUTPipes[lIdx].ulType = ulEndpointType; + g_sUSBHCD.USBOUTPipes[lIdx].psDevice = psDevice; + g_sUSBHCD.USBOUTPipes[lIdx].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, + INDEX_TO_USB_EP(lIdx + 1), + USB_HOST_OUT_STATUS); + + // + // Initialize the endpoint as idle. + // + g_sUSBHCD.USBOUTPipes[lIdx].eState = PIPE_IDLE; + + // + // Allocate space in the FIFO for this endpoint. + // + if(FIFOAlloc(&g_sUSBHCD.USBOUTPipes[lIdx], ulSize) != 0) + { + // + // Configure the FIFO. + // + MAP_USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + g_sUSBHCD.USBOUTPipes[lIdx].usFIFOAddr, + g_sUSBHCD.USBOUTPipes[lIdx].ucFIFOSize, + USB_EP_HOST_OUT); + } + + // + // Set the function address for this endpoint. + // + MAP_USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + psDevice->ulAddress, USB_EP_HOST_OUT); + + // + // Set the hub and port address for the endpoint. + // + ulHubAddr = (psDevice->ucHub << 8) | + psDevice->ucHubPort; + USBHostHubAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + ulHubAddr, (USB_EP_HOST_OUT | + (psDevice->bLowSpeed ? + USB_EP_SPEED_LOW : USB_EP_SPEED_FULL))); + + break; + } + } + // + // Handle IN Pipes. + // + else if(ulEndpointType & EP_PIPE_TYPE_IN) + { + // + // A zero address indicates free. + // + if(g_sUSBHCD.USBINPipes[lIdx].psDevice == 0) + { + // + // Set up uDMA for the pipe. + // + if(ulEndpointType & EP_PIPE_USE_UDMA) + { + // + // First three endpoints have fixed channels on some + // parts so bias the pipes to match this as best is + // possible. + // + if(lIdx < 3) + { + // + // Check if the fixed channel is available for this + // USB pipe. + // + if(g_sUSBHCD.ucDMAChannels[lIdx * 2] == + USBHCD_DMA_UNUSED) + { + // + // The default channel was available so use it. + // + g_sUSBHCD.ucDMAChannels[lIdx * 2] = lIdx; + g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel = lIdx * 2; + } + else + { + // + // Go to the next USB pipe if the fixed one was not + // available. + // + continue; + } + } + else + { + // + // Either the fixed channel was not available or the + // pipe index was more than the first 3 pipes that are + // available on all parts. + // + for(lDMAIdx = 0; lDMAIdx < MAX_NUM_DMA_CHANNELS; + lDMAIdx += 2) + { + // + // Find any available channel. + // + if(g_sUSBHCD.ucDMAChannels[lDMAIdx] == + USBHCD_DMA_UNUSED) + { + // + // Save the index and the DMA channel + // information. + // + g_sUSBHCD.ucDMAChannels[lDMAIdx] = lIdx; + g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel = + lDMAIdx; + } + } + } + + // + // If no DMA channel was available then just disable DMA + // on this pipe. + // + if(g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel == + USBHCD_DMA_UNUSED) + { + ulEndpointType &= ~EP_PIPE_USE_UDMA; + } + else + { + // + // Set the DMA channel for this endpoint, this has no + // effect on parts without configurable DMA. + // + // + MAP_USBEndpointDMAChannel( + USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel); + + // + // Clear all the attributes for the channel + // + MAP_uDMAChannelAttributeDisable( + g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel, + UDMA_ATTR_ALL); + + // + // Configure the uDMA channel for the pipe + // + MAP_uDMAChannelControlSet( + g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel, + (UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | + UDMA_ARB_64)); + } + } + + // + // Save the endpoint type and device address and callback + // function. + // + g_sUSBHCD.USBINPipes[lIdx].ulType = ulEndpointType; + g_sUSBHCD.USBINPipes[lIdx].psDevice = psDevice; + g_sUSBHCD.USBINPipes[lIdx].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, + INDEX_TO_USB_EP(lIdx + 1), + USB_HOST_IN_STATUS); + + // + // Allocate space in the FIFO for this endpoint. + // + if(FIFOAlloc(&g_sUSBHCD.USBINPipes[lIdx], ulSize) != 0) + { + // + // Configure the FIFO. + // + MAP_USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + g_sUSBHCD.USBINPipes[lIdx].usFIFOAddr, + g_sUSBHCD.USBINPipes[lIdx].ucFIFOSize, + USB_EP_HOST_IN); + } + + // + // Set the function address for this endpoint. + // + MAP_USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + psDevice->ulAddress, USB_EP_HOST_IN); + + // + // Set the hub and port address for the endpoint. + // + ulHubAddr = (psDevice->ucHub << 8) | + psDevice->ucHubPort; + USBHostHubAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1), + ulHubAddr, (USB_EP_HOST_IN | + (psDevice->bLowSpeed ? + USB_EP_SPEED_LOW : USB_EP_SPEED_FULL))); + + // + // Reset the state of the pipe to idle. + // + g_sUSBHCD.USBINPipes[lIdx].eState = PIPE_IDLE; + + break; + } + } + } + + // + // Did not find a free pipe. + // + if(lIdx == MAX_NUM_PIPES) + { + return(0); + } + + // + // Return the pipe index and type that was allocated. + // + return(ulEndpointType | lIdx); +} + +//***************************************************************************** +// +//! This function is used to allocate a USB HCD pipe. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param ulEndpointType 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. +// +//***************************************************************************** +unsigned long +USBHCDPipeAlloc(unsigned long ulIndex, unsigned long ulEndpointType, + tUSBHostDevice *psDevice, tHCDPipeCallback pfnCallback) +{ + // + // The old API allocated only 64 bytes to each endpoint. + // + return(USBHCDPipeAllocSize(ulIndex, ulEndpointType, 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 ulMaxPayload +//! parameter is typically read directly from the devices endpoint descriptor +//! and is expressed in bytes. +//! +//! Setting the \e ulInterval parameter depends on the type of endpoint being +//! configured. For endpoints that do not need to use the \e ulInterval +//! parameter \e ulInterval should be set to 0. For Bulk \e ulInterval is a +//! value from 2-16 and will set the NAK timeout value as 2^(\e ulInterval-1) +//! frames. For interrupt endpoints \e ulInterval is a value from 1-255 and +//! is the count in frames between polling the endpoint. For isochronous +//! endpoints \e ulInterval ranges from 1-16 and is the polling interval in +//! frames represented as 2^(\e ulInterval-1) frames. +//! +//! \param ulPipe is the allocated endpoint to modify. +//! \param ulMaxPayload is maximum data that can be handled per transaction. +//! \param ulInterval is the polling interval for data transfers expressed in +//! frames. +//! \param ulTargetEndpoint 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. +// +//***************************************************************************** +unsigned long +USBHCDPipeConfig(unsigned long ulPipe, unsigned long ulMaxPayload, + unsigned long ulInterval, unsigned long ulTargetEndpoint) +{ + unsigned long ulFlags; + unsigned long ulIndex; + + // + // Get the index number from the allocated pipe. + // + ulIndex = (ulPipe & EP_PIPE_IDX_M); + + // + // Set the direction. + // + if(ulPipe & EP_PIPE_TYPE_OUT) + { + // + // Set the mode for this endpoint. + // + if(g_sUSBHCD.USBOUTPipes[ulIndex].ulType & EP_PIPE_TYPE_BULK) + { + ulFlags = USB_EP_MODE_BULK; + } + else if(g_sUSBHCD.USBOUTPipes[ulIndex].ulType & EP_PIPE_TYPE_INTR) + { + ulFlags = USB_EP_MODE_INT; + } + else if(g_sUSBHCD.USBOUTPipes[ulIndex].ulType & EP_PIPE_TYPE_ISOC) + { + ulFlags = USB_EP_MODE_ISOC; + } + else + { + ulFlags = USB_EP_MODE_CTRL; + } + + ulFlags |= USB_EP_HOST_OUT; + + g_sUSBHCD.USBOUTPipes[ulIndex].ucEPNumber = + (unsigned char)ulTargetEndpoint; + + // + // Save the interval and the next tick to trigger a scheduler event. + // + g_sUSBHCD.USBOUTPipes[ulIndex].ulInterval = ulInterval; + g_sUSBHCD.USBOUTPipes[ulIndex].ulNextEventTick = + ulInterval + g_ulCurrentTick; + + // + // Set the device speed. + // + ulFlags |= (g_sUSBHCD.USBOUTPipes[ulIndex].psDevice->bLowSpeed ? + USB_EP_SPEED_LOW : USB_EP_SPEED_FULL); + } + else + { + // + // Set the mode for this endpoint. + // + if(g_sUSBHCD.USBINPipes[ulIndex].ulType & EP_PIPE_TYPE_BULK) + { + ulFlags = USB_EP_MODE_BULK; + } + else if(g_sUSBHCD.USBINPipes[ulIndex].ulType & EP_PIPE_TYPE_INTR) + { + ulFlags = USB_EP_MODE_INT; + } + else if(g_sUSBHCD.USBINPipes[ulIndex].ulType & EP_PIPE_TYPE_ISOC) + { + ulFlags = USB_EP_MODE_ISOC; + } + else + { + ulFlags = USB_EP_MODE_CTRL; + } + ulFlags |= USB_EP_HOST_IN; + + g_sUSBHCD.USBINPipes[ulIndex].ucEPNumber = + (unsigned char)ulTargetEndpoint; + + // + // Save the interval and the next tick to trigger a scheduler event. + // + g_sUSBHCD.USBINPipes[ulIndex].ulInterval = ulInterval; + g_sUSBHCD.USBINPipes[ulIndex].ulNextEventTick = + ulInterval + g_ulCurrentTick; + + // + // Set the device speed. + // + ulFlags |= (g_sUSBHCD.USBINPipes[ulIndex].psDevice->bLowSpeed ? + USB_EP_SPEED_LOW : USB_EP_SPEED_FULL); + } + + // + // Set up the appropriate flags if uDMA is used. + // + if(ulPipe & EP_PIPE_USE_UDMA) + { + ulFlags |= USB_EP_DMA_MODE_0; + } + + // + // Configure the endpoint according to the flags determined above. + // + USBHostEndpointConfig(USB0_BASE, + INDEX_TO_USB_EP((ulPipe & EP_PIPE_IDX_M) + 1), + ulMaxPayload, ulInterval, ulTargetEndpoint, + ulFlags); + + 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 ulPipe 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. +// +//***************************************************************************** +unsigned long +USBHCDPipeStatus(unsigned long ulPipe) +{ + return(0); +} + +//***************************************************************************** +// +//! This function is used to write data to a USB HCD pipe. +//! +//! \param ulPipe is the USB pipe to put data into. +//! \param pucData is a pointer to the data to send. +//! \param ulSize 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 ulSize 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. +// +//***************************************************************************** +unsigned long +USBHCDPipeWrite(unsigned long ulPipe, unsigned char *pucData, + unsigned long ulSize) +{ + unsigned long ulEndpoint; + unsigned long ulRemainingBytes; + unsigned long ulByteToSend; + unsigned long ulPipeIdx; + unsigned long ulEPStatus; + + // + // Determine which endpoint interface that this pipe is using. + // + ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1); + + // + // Get index used for looking up pipe data + // + ulPipeIdx = ulPipe & EP_PIPE_IDX_M; + + // + // Set the total number of bytes to send out. + // + ulRemainingBytes = ulSize; + + if(ulSize > 64) + { + // + // Only send 64 bytes at a time. + // + ulByteToSend = 64; + } + else + { + // + // Send the requested number of bytes. + // + ulByteToSend = ulSize; + } + + // + // Send all of the requested data. + // + while(ulRemainingBytes != 0) + { + // + // Start a write request. + // + g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState = PIPE_WRITING; + + // + // If uDMA is not enabled for this pipe, or if the uDMA workaround + // is applied, then don't use uDMA for this transfer. + // + if(!(ulPipe & EP_PIPE_USE_UDMA) || + (g_bUseDMAWA && (ulByteToSend != 64))) + { + // + // Disable uDMA on the USB endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ulEndpoint, USB_EP_HOST_OUT); + + // + // Put the data in the buffer. + // + MAP_USBEndpointDataPut(USB0_BASE, ulEndpoint, pucData, + ulByteToSend); + + // + // Schedule the data to be sent. + // + MAP_USBEndpointDataSend(USB0_BASE, ulEndpoint, USB_TRANS_OUT); + } + + // + // Otherwise, uDMA should be used for this transfer + // + else + { + // + // Set up the uDMA transfer. + // + MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2), + UDMA_MODE_AUTO, pucData, + (void *)USBFIFOAddrGet(USB0_BASE, + ulEndpoint), + ulByteToSend); + + // + // Enable uDMA on the USB endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_OUT); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(INT_USB0); + + // + // Set pending transmit DMA flag + // + g_ulDMAPending |= DMA_PEND_TRANSMIT_FLAG << ulPipeIdx; + + // + // Enable the uDMA channel to start the transfer + // + MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2)); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } + + // + // Wait for a status change. + // + while(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_WRITING) + { + // + // Read the status of the endpoint connected to this pipe. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, + INDEX_TO_USB_EP(ulPipeIdx + 1)); + + // + // Check if the device stalled the request. + // + if(ulEPStatus & USB_HOST_OUT_STALL) + { + // + // If uDMA is being used, then disable the channel. + // + if(ulPipe & EP_PIPE_USE_UDMA) + { + MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1TX + + (ulPipeIdx * 2)); + } + } + + // + // If a disconnect event occurs the exit out of the loop. + // + if(g_ulUSBHIntEvents & INT_EVENT_DISCONNECT) + { + // + // Set the pipe state to error. + // + g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState = PIPE_ERROR; + } + } + + // + // If the data was successfully sent then decrement the count and + // continue. + // + if(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_DATA_SENT) + { + // + // Decrement the remaining data and advance the pointer. + // + ulRemainingBytes -= ulByteToSend; + pucData += ulByteToSend; + } + else if(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_STALLED) + { + // + // Zero out the size so that the caller knows that no data was + // written. + // + ulSize = 0; + + // + // If uDMA is being used, then disable the channel. + // + if(ulPipe & EP_PIPE_USE_UDMA) + { + // + // Disable the DMA channel. + // + MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2)); + } + + // + // This is the actual endpoint number. + // + USBHCDClearFeature( + g_sUSBHCD.USBOUTPipes[ulPipeIdx].psDevice->ulAddress, + ulPipe, USB_FEATURE_EP_HALT); + + // + // If there was a stall, then no more data is coming so break out. + // + break; + } + + // + // If there are less than 64 bytes to send then this is the last + // of the data to go out. + // + if(ulRemainingBytes < 64) + { + ulByteToSend = ulRemainingBytes; + } + else if(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_ERROR) + { + // + // An error occurred so stop this transaction and set the number + // of bytes to zero. + // + ulSize = 0; + break; + } + } + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState = PIPE_IDLE; + + return(ulSize); +} + +//***************************************************************************** +// +//! This function is used to schedule and IN transaction on a USB HCD pipe. +//! +//! \param ulPipe is the USB pipe to read data from. +//! \param pucData is a pointer to store the data that is received. +//! \param ulSize is the size in bytes of the buffer pointed to by pucData. +//! +//! 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. +// +//***************************************************************************** +unsigned long +USBHCDPipeSchedule(unsigned long ulPipe, unsigned char *pucData, + unsigned long ulSize) +{ + unsigned long ulEndpoint; + unsigned long ulPipeIdx; + + // + // Get index used for looking up pipe data + // + ulPipeIdx = ulPipe & EP_PIPE_IDX_M; + + // + // Determine which endpoint interface that this pipe is using. + // + ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1); + + if(ulPipe & EP_PIPE_TYPE_OUT) + { + // + // Start a write request. + // + g_sUSBHCD.USBOUTPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_WRITING; + + // + // Check if uDMA is enabled on this pipe. + // + if(ulPipe & EP_PIPE_USE_UDMA) + { + // + // Set up the uDMA transfer. + // + MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2), + UDMA_MODE_AUTO, pucData, + (void *)USBFIFOAddrGet(USB0_BASE, + ulEndpoint), + ulSize); + + // + // Enable uDMA on the USB endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_OUT); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(INT_USB0); + + // + // Set pending transmit DMA flag + // + g_ulDMAPending |= DMA_PEND_TRANSMIT_FLAG << ulPipeIdx; + + // + // Enable the uDMA channel to start the transfer + // + MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2)); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } + else + { + // + // Put the data in the buffer. + // + MAP_USBEndpointDataPut(USB0_BASE, ulEndpoint, pucData, ulSize); + + // + // Schedule the data to be sent. + // + MAP_USBEndpointDataSend(USB0_BASE, ulEndpoint, USB_TRANS_OUT); + } + } + else + { + // + // Start a read request. + // + g_sUSBHCD.USBINPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_READING; + + // + // If uDMA is not enabled for this pipe, or if the uDMA workaround + // is applied, then do not use uDMA for this transfer. + // + if((ulPipe & EP_PIPE_USE_UDMA) == 0) + { + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ulEndpoint, 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 + { + // + // Compute bytes to transfer and set up transfer + // + MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2), + UDMA_MODE_AUTO, + (void *)USBFIFOAddrGet(USB0_BASE, + ulEndpoint), + pucData, ulSize); + + // + // Enable uDMA on the endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_IN); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(INT_USB0); + + // + // Set pending DMA flag + // + g_ulDMAPending |= DMA_PEND_RECEIVE_FLAG << ulPipeIdx; + + // + // Enable the uDMA channel to start the transfer + // + MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2)); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } + + // + // Remember details of the buffer into which the data will be read. + // + g_sUSBHCD.USBINPipes[ulPipeIdx].pucReadPtr = pucData; + g_sUSBHCD.USBINPipes[ulPipeIdx].ulReadSize = ulSize; + + // + // Trigger a request for data from the device. + // + MAP_USBHostRequestIN(USB0_BASE, ulEndpoint); + + // + // No data was put into or read from the buffer. + // + ulSize = 0; + } + return(ulSize); +} + +//***************************************************************************** +// +//! This function is used to read data from a USB HCD pipe. +//! +//! \param ulPipe is the USB pipe to read data from. +//! \param pucData is a pointer to store the data that is received. +//! \param ulSize is the size in bytes of the buffer pointed to by pucData. +//! +//! 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 ulSize 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 pucData buffer. +// +//***************************************************************************** +unsigned long +USBHCDPipeReadNonBlocking(unsigned long ulPipe, unsigned char *pucData, + unsigned long ulSize) +{ + unsigned long ulEndpoint; + + // + // Determine which endpoint interface that this pipe is using. + // + ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1); + + // + // Read the data out of the USB endpoint interface. + // + MAP_USBEndpointDataGet(USB0_BASE, ulEndpoint, pucData, &ulSize); + + // + // Acknowledge that the data was read from the endpoint. + // + MAP_USBHostEndpointDataAck(USB0_BASE, ulEndpoint); + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.USBINPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_IDLE; + + return(ulSize); +} + +//***************************************************************************** +// +//! This function acknowledges data received via an interrupt IN pipe. +//! +//! \param ulPipe 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 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(unsigned long ulPipe) +{ + unsigned long ulEndpoint; + + // + // Determine which endpoint interface that this pipe is using. + // + ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1); + + // + // Acknowledge that the data was read from the endpoint. + // + USBHostEndpointDataAck(USB0_BASE, ulEndpoint); + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.USBINPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_IDLE; +} + +//***************************************************************************** +// +//! This function is used to read data from a USB HCD pipe. +//! +//! \param ulPipe is the USB pipe to read data from. +//! \param pucData is a pointer to store the data that is received. +//! \param ulSize is the size in bytes of the buffer pointed to by pucData. +//! +//! This function will block and will only return when it has read as much data +//! as requested 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 ulSize 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 pucData buffer. +// +//***************************************************************************** +unsigned long +USBHCDPipeRead(unsigned long ulPipe, unsigned char *pucData, + unsigned long ulSize) +{ + unsigned long ulEndpoint; + unsigned long ulRemainingBytes; + unsigned long ulBytesRead; + unsigned long ulPipeIdx; + unsigned long ulEPStatus; + + // + // Get index used for looking up pipe data + // + ulPipeIdx = ulPipe & EP_PIPE_IDX_M; + + // + // Initialized the number of bytes read. + // + ulBytesRead = 0; + + // + // Determine which endpoint interface that this pipe is using. + // + ulEndpoint = INDEX_TO_USB_EP(ulPipeIdx + 1); + + // + // Set the remaining bytes to received. + // + ulRemainingBytes = ulSize; + + // + // Continue until all data requested has been received. + // + while(ulRemainingBytes != 0) + { + // + // Start a read request. + // + g_sUSBHCD.USBINPipes[ulPipeIdx].eState = PIPE_READING; + + // + // If uDMA is not enabled for this pipe, or if the uDMA workaround + // is applied, then do not use uDMA for this transfer. + // + if(!(ulPipe & EP_PIPE_USE_UDMA) || + (g_bUseDMAWA && (ulRemainingBytes < 64))) + { + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ulEndpoint, 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 + { + // + // Compute bytes to transfer and set up transfer + // + ulBytesRead = ulRemainingBytes > 64 ? 64 : ulRemainingBytes; + + MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2), + UDMA_MODE_AUTO, + (void *)USBFIFOAddrGet(USB0_BASE, + ulEndpoint), + pucData, ulBytesRead); + + // + // Enable uDMA on the endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_IN); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(INT_USB0); + + // + // Set pending DMA flag + // + g_ulDMAPending |= DMA_PEND_RECEIVE_FLAG << ulPipeIdx; + + // + // Enable the uDMA channel to start the transfer + // + MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2)); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } + + // + // Set up for the next transaction. + // + g_sUSBHCD.USBINPipes[ulPipeIdx].pucReadPtr = pucData; + g_sUSBHCD.USBINPipes[ulPipeIdx].ulReadSize = (ulRemainingBytes < 64) ? + ulRemainingBytes : 64; + + // + // Trigger a request for data from the device. + // + MAP_USBHostRequestIN(USB0_BASE, ulEndpoint); + + // + // Wait for a status change. + // + while(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_READING) + { + // + // Read the status of the endpoint connected to this pipe. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, + INDEX_TO_USB_EP(ulPipeIdx + 1)); + + // + // Check if the device stalled the request. + // + if(ulEPStatus & USB_HOST_IN_STALL) + { + // + // If uDMA is being used, then disable the channel. + // + if(ulPipe & EP_PIPE_USE_UDMA) + { + // + // Disable the DMA channel. + // + MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1RX + + (ulPipeIdx * 2)); + } + } + + // + // If a disconnect event occurs the exit out of the loop. + // + if(g_ulUSBHIntEvents & INT_EVENT_DISCONNECT) + { + // + // Set the pipe state to error. + // + g_sUSBHCD.USBINPipes[ulPipeIdx].eState = PIPE_ERROR; + } + } + + // + // If data is ready then return it. + // + if(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_DATA_READY) + { + // + // If not using uDMA then read the data from the USB. Otherwise + // the data will already be in the buffer. + // + if(!(ulPipe & EP_PIPE_USE_UDMA) || + (g_bUseDMAWA && (ulRemainingBytes < 64))) + { + // + // Compute bytes to transfer and set up transfer + // + ulBytesRead = ulRemainingBytes > 64 ? 64 : ulRemainingBytes; + + // + // Acknowledge that the data was read from the endpoint. + // + MAP_USBHostEndpointDataAck(USB0_BASE, ulEndpoint); + } + + // + // Subtract the number of bytes read from the bytes remaining. + // + ulRemainingBytes -= ulBytesRead; + + // + // If there were less than 64 bytes read, then this was a short + // packet and no more data will be returned. + // + if(ulBytesRead < 64) + { + // + // Subtract off the bytes that were not received and exit the + // loop. + // + ulSize = ulSize - ulRemainingBytes; + break; + } + else + { + // + // Move the buffer ahead to receive more data into the buffer. + // + pucData += 64; + } + } + else if(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_STALLED) + { + // + // Zero out the size so that the caller knows that no data was read. + // + ulSize = 0; + + // + // If uDMA is being used, then disable the channel. + // + if(ulPipe & EP_PIPE_USE_UDMA) + { + MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2)); + } + + // + // This is the actual endpoint number. + // + USBHCDClearFeature( + g_sUSBHCD.USBINPipes[ulPipeIdx].psDevice->ulAddress, + ulPipe, USB_FEATURE_EP_HALT); + + // + // If there was a stall, then no more data is coming so break out. + // + break; + } + else if(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_ERROR) + { + // + // An error occurred so stop this transaction and set the number + // of bytes to zero. + // + ulSize = 0; + break; + } + } + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.USBINPipes[ulPipeIdx].eState = PIPE_IDLE; + + return(ulSize); +} + +//***************************************************************************** +// +//! This function is used to release a USB pipe. +//! +//! \param ulPipe 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(unsigned long ulPipe) +{ + unsigned long ulDMAIdx; + unsigned long ulIndex; + + // + // Get the index number from the allocated pipe. + // + ulIndex = (ulPipe & EP_PIPE_IDX_M); + + if(ulPipe & EP_PIPE_TYPE_OUT) + { + // + // Clear the address and type for this endpoint to free it up. + // + g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].psDevice = 0; + g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ulType = 0; + g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].pfnCallback = 0; + + // + // Check if this pipe has allocated a DMA channel. + // + if(g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel != + USBHCD_DMA_UNUSED) + { + // + // Get the DMA channel used by this pipe. + // + ulDMAIdx = + g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel; + + // + // This is a debug check that will prevent accessing beyond the + // buffer allocated to the DMA channels. + // + ASSERT(ulDMAIdx < MAX_NUM_DMA_CHANNELS); + + // + // Mark the channel as free for use. + // + g_sUSBHCD.ucDMAChannels[ulDMAIdx] = USBHCD_DMA_UNUSED; + + // + // Clear out the current channel in use by this pipe. + // + g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel = + USBHCD_DMA_UNUSED; + } + + // + // Free up the FIFO memory used by this endpoint. + // + if(g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucFIFOSize) + { + FIFOFree(&g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M]); + } + + // + // Set the function address for this endpoint back to zero. + // + USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 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, INDEX_TO_USB_EP(ulIndex + 1), + 0, (USB_EP_HOST_OUT | USB_EP_SPEED_LOW)); + } + else if(ulPipe & EP_PIPE_TYPE_IN) + { + // + // Clear the address and type for this endpoint to free it up. + // + g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].psDevice = 0; + g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ulType = 0; + g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].pfnCallback = 0; + + // + // Check if this pipe has allocated a DMA channel. + // + if(g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel != + USBHCD_DMA_UNUSED) + { + // + // Get the DMA channel used by this pipe. + // + ulDMAIdx = g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel; + + // + // This is a debug check that will prevent accessing beyond the + // buffer allocated to the DMA channels. + // + ASSERT(ulDMAIdx < MAX_NUM_DMA_CHANNELS); + + // + // Mark the channel as free for use. + // + g_sUSBHCD.ucDMAChannels[ulDMAIdx] = USBHCD_DMA_UNUSED; + + // + // Clear out the current channel in use by this pipe. + // + g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel = + USBHCD_DMA_UNUSED; + } + + // + // Free up the FIFO memory used by this endpoint. + // + if(g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucFIFOSize) + { + FIFOFree(&g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M]); + } + + // + // Set the function address for this endpoint back to zero. + // + USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 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, INDEX_TO_USB_EP(ulIndex + 1), + 0, (USB_EP_HOST_IN | USB_EP_SPEED_LOW)); + + // + // Clear any pending IN transactions. + // + USBHostRequestINClear(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1)); + } +} + +//***************************************************************************** +// +// This internal function initializes the HCD code. +// +// \param ulIndex specifies which USB controller to use. +// \param pvPool is a pointer to the data to use as a memory pool for this +// controller. +// \param ulPoolSize 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(unsigned long ulIndex, void *pvPool, + unsigned long ulPoolSize) +{ + long lIdx; + + ASSERT(ulIndex == 0); + + // + // Get the number of endpoints supported by this device. + // + g_sUSBHCD.ulNumEndpoints = USBNumEndpointsGet(USB0_BASE); + + // + // The first 64 Bytes are allocated to endpoint 0. + // + g_ulAlloc[0] = 1; + g_ulAlloc[1] = 0; + + // + // Save the base address for this controller. + // + g_sUSBHCD.ulUSBBase = USB0_BASE; + + // + // All Pipes are unused at start. + // + for(lIdx = 0; lIdx < MAX_NUM_PIPES; lIdx++) + { + g_sUSBHCD.USBINPipes[lIdx].psDevice = 0; + g_sUSBHCD.USBINPipes[lIdx].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel = USBHCD_DMA_UNUSED; + g_sUSBHCD.USBOUTPipes[lIdx].psDevice = 0; + g_sUSBHCD.USBOUTPipes[lIdx].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel = 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(); + + // + // All DMA channels are unused at start. + // + for(lIdx = 0; lIdx < MAX_NUM_DMA_CHANNELS; lIdx++) + { + g_sUSBHCD.ucDMAChannels[lIdx] = USBHCD_DMA_UNUSED; + } + + // + // Initialized the device structures. + // + for(lIdx = 0; lIdx <= MAX_USB_DEVICES; lIdx++) + { + // + // Clear the config descriptor and state. + // + g_sUSBHCD.eDeviceState[lIdx] = HCD_IDLE; + g_sUSBHCD.USBDevice[lIdx].pConfigDescriptor = 0; + g_sUSBHCD.USBDevice[lIdx].bConfigRead = false; + + // + // Initialize the device descriptor. + // + g_sUSBHCD.USBDevice[lIdx].DeviceDescriptor.bLength = 0; + g_sUSBHCD.USBDevice[lIdx].DeviceDescriptor.bMaxPacketSize0 = 0; + + // + // Initialize the device address. + // + g_sUSBHCD.USBDevice[lIdx].ulAddress = 0; + + // + // Set the current interface to 0. + // + g_sUSBHCD.USBDevice[lIdx].ulInterface = 0; + + // + // Clear the active driver for the device. + // + g_lUSBHActiveDriver[lIdx] = -1; + } + + // + // Allocate the memory needed for reading descriptors. + // + g_sUSBHCD.pvPool = pvPool; + g_sUSBHCD.ulPoolSize = ulPoolSize; + + // + // Initialize the device class. + // + g_sUSBHCD.ulClass = USB_CLASS_EVENTS; + + // + // Default enable connect, disconnect, unknown device and power fault + // event notifications. + // + g_sUSBHCD.ulEventEnables = 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_eUSBMode == USB_MODE_HOST) || (g_eUSBMode == USB_MODE_FORCE_HOST)) + { + // + // 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_MODE_DETECT | + USB_INTCTRL_POWER_FAULT); + + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + + // + // There is no automatic power in pure host mode. + // + USBHCDPowerConfigSet(ulIndex, g_ulPowerConfig & ~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); + } +} + +//***************************************************************************** +// +//! This function is used to set the power pin and power fault configuration. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param ulPwrConfig 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 ulPwrConfig +//! 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 USBHCD_VBUS_MANUAL is used then the application must provide an +//! event driver to receive the USB_EVENT_POWER_ENABLE and +//! 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: +//! USB_HOST_PWRFLT_LOW, USB_HOST_PWRFLT_HIGH, USB_HOST_PWRFLT_EP_NONE, +//! USB_HOST_PWRFLT_EP_TRI, USB_HOST_PWRFLT_EP_LOW, USB_HOST_PWRFLT_EP_HIGH, +//! USB_HOST_PWREN_LOW, USB_HOST_PWREN_HIGH, USB_HOST_PWREN_VBLOW, and +//! USB_HOST_PWREN_VBHIGH. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDPowerConfigInit(unsigned long ulIndex, unsigned long ulPwrConfig) +{ + ASSERT(ulIndex == 0); + + // + // Save the value as it will be used later. + // + g_ulPowerConfig = ulPwrConfig; +} + +//***************************************************************************** +// +//! This function is used to get the power pin and power fault configuration. +//! +//! \param ulIndex 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. +//! +//***************************************************************************** +unsigned long +USBHCDPowerConfigGet(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // Save the value as it will be used later. + // + return(g_ulPowerConfig); +} + +//***************************************************************************** +// +//! This function is used to set the power pin and power fault configuration. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param ulConfig 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. +//! +//***************************************************************************** +unsigned long +USBHCDPowerConfigSet(unsigned long ulIndex, unsigned long ulConfig) +{ + ASSERT(ulIndex == 0); + + // + // Remember the current setting. + // + g_ulPowerConfig = ulConfig; + + // + // Clear out the two flag bits. + // + ulConfig = g_ulPowerConfig & ~(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_ulPowerConfig & 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_ulPowerConfig & 1) + { + g_ulPowerConfig |= USB_HOST_PWRFLT_EP_LOW; + ulConfig |= USB_HOST_PWRFLT_EP_LOW; + } + else + { + g_ulPowerConfig |= USB_HOST_PWRFLT_EP_HIGH; + ulConfig |= USB_HOST_PWRFLT_EP_HIGH; + } + } + + // + // Initialize the power configuration. + // + MAP_USBHostPwrConfig(USB0_BASE, ulConfig); + + // + // If not in manual mode then just turn on power. + // + if((g_ulPowerConfig & 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 ulIndex 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. +//! +//***************************************************************************** +unsigned long +USBHCDPowerAutomatic(unsigned long ulIndex) +{ + // + // Check if the controller is automatically applying power or not. + // + if(g_ulPowerConfig & USBHCD_VBUS_MANUAL) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is used to initialize the HCD code. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param pvPool is a pointer to the data to use as a memory pool for this +//! controller. +//! \param ulPoolSize 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 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 USB_MODE_HOST 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. +//! +//! \note Forcing of the USB controller mode feature is not available on all +//! Stellaris microcontrollers. Consult the data sheet for the microcontroller +//! that the application is using to determine if this feature is available. +//! +//! 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(unsigned long ulIndex, void *pvPool, unsigned long ulPoolSize) +{ + long lDriver; + + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + + // + // Make sure there is at least enough to read the configuration descriptor. + // + ASSERT(ulPoolSize >= sizeof(tConfigDescriptor)); + + // + // Should not call this if the stack is in device mode. + // + ASSERT(g_eUSBMode != USB_MODE_DEVICE); + ASSERT(g_eUSBMode != USB_MODE_FORCE_DEVICE); + + // + // If the mode was not set then default to USB_MODE_HOST. + // + if(g_eUSBMode == USB_MODE_NONE) + { + g_eUSBMode = USB_MODE_HOST; + } + + // + // 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(); + + // + // If the application not requesting OTG mode then set the mode to forced + // host mode. If the mode is actually USB_MODE_HOST, this will be switched + // off when ID pin detection is complete and the ID is no longer in use. + // + if(g_eUSBMode != USB_MODE_OTG) + { + // + // Force Host mode on devices that support force host mode. + // + MAP_USBHostMode(USB0_BASE); + } + + // + // Call our internal function to perform the initialization. + // + USBHCDInitInternal(ulIndex, pvPool, ulPoolSize); + + // + // No event driver is present by default. + // + g_sUSBHCD.lEventDriver = -1; + + // + // Search through the Host Class driver list for the devices class. + // + for(lDriver = 0; lDriver < g_sUSBHCD.ulNumClassDrivers; lDriver++) + { + if(g_sUSBHCD.pClassDrivers[lDriver]->ulInterfaceClass == + USB_CLASS_EVENTS) + { + // + // Event driver was found so remember it. + // + g_sUSBHCD.lEventDriver = lDriver; + } + } + + // + // Get the number of ticks per millisecond, this is only used by blocking + // delays using the SysCtlDelay() function. + // + g_ulTickms = MAP_SysCtlClockGet() / 3000; + + // + // Check to see if uDMA workaround is needed. + // + if(CLASS_IS_DUSTDEVIL && REVISION_IS_A0) + { + g_bUseDMAWA = 1; + } +} + +//***************************************************************************** +// +//! This function is used to initialize the HCD class driver list. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param ppHClassDrvs is an array of host class drivers that are +//! supported on this controller. +//! \param ulNumDrivers 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 ulIndex parameter. This function should be called +//! before enabling the host controller driver with the USBHCDInit() function. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDRegisterDrivers(unsigned long ulIndex, + const tUSBHostClassDriver * const *ppHClassDrvs, + unsigned long ulNumDrivers) +{ + ASSERT(ulIndex == 0); + + // + // Save the class drivers. + // + g_sUSBHCD.pClassDrivers = ppHClassDrvs; + + // + // Save the number of class drivers. + // + g_sUSBHCD.ulNumClassDrivers = ulNumDrivers; +} + +//***************************************************************************** +// +//! This function is used to terminate the HCD code. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // End the session. + // + USBOTGSessionRequest(USB0_BASE, false); + + // + // Remove power from the USB bus. + // + MAP_USBHostPwrDisable(USB0_BASE); + + // + // Disable USB interrupts. + // + OS_INT_DISABLE(INT_USB0); + + 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. + // + g_sUSBHCD.USBINPipes[0].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.USBINPipes[1].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.USBINPipes[2].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.USBOUTPipes[0].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.USBOUTPipes[1].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.USBOUTPipes[2].ulType = USBHCD_PIPE_UNUSED; + g_sUSBHCD.eDeviceState[0] = HCD_IDLE; + g_sUSBHCD.USBDevice[0].pConfigDescriptor = 0; + g_sUSBHCD.USBDevice[0].bConfigRead = false; + g_sUSBHCD.USBDevice[0].DeviceDescriptor.bLength = 0; + g_sUSBHCD.USBDevice[0].DeviceDescriptor.bMaxPacketSize0 = 0; + g_sUSBHCD.USBDevice[0].ulAddress = 0; + g_sUSBHCD.USBDevice[0].ulInterface = 0; + g_sUSBHCD.pvPool = 0; + g_sUSBHCD.ulPoolSize = 0; +} + +//***************************************************************************** +// +//! This function generates reset signaling on the USB bus. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // Start the reset signaling. + // + MAP_USBHostReset(USB0_BASE, 1); + + // + // Wait 20ms + // + OS_DELAY(g_ulTickms * 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_ulTickms * 20); +} + +//***************************************************************************** +// +//! This function will generate suspend signaling on the USB bus. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // Start the suspend signaling. + // + MAP_USBHostSuspend(USB0_BASE); +} + +//***************************************************************************** +// +//! This function will generate resume signaling on the USB bus. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // Start the resume signaling. + // + MAP_USBHostResume(USB0_BASE, 1); + + // + // Wait 100ms + // + OS_DELAY(g_ulTickms * 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 ulIndex specifies which USB controller to use. +//! \param pDevice 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 pDevice->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 pDevice->DeviceDescriptor.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 unsigned long +USBHCDGetConfigDescriptor(unsigned long ulIndex, tUSBHostDevice *pDevice) +{ + tUSBRequest SetupPacket; + unsigned long ulBytes; + + ASSERT(ulIndex == 0); + + ulBytes = 0; + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + SetupPacket.wValue = USB_DTYPE_CONFIGURATION << 8; + + // + // Index is always 0 for device configurations requests. + // + SetupPacket.wIndex = 0; + + // + // Only ask for the configuration header first to see how big the + // whole thing is. + // + if(!pDevice->bConfigRead) + { + // + // Only request the space available. + // + SetupPacket.wLength = sizeof(tConfigDescriptor); + + // + // Put the setup packet in the buffer. + // + ulBytes = + USBHCDControlTransfer(0, &SetupPacket, pDevice, + (unsigned char *)pDevice->pConfigDescriptor, + sizeof(tConfigDescriptor), + pDevice->DeviceDescriptor.bMaxPacketSize0); + } + + // + // If the Configuration header was successfully returned then get the + // full configuration descriptor. + // + if(ulBytes == sizeof(tConfigDescriptor)) + { + // + // Save the total size and request the full configuration descriptor. + // + SetupPacket.wLength = + pDevice->pConfigDescriptor->wTotalLength; + + // + // Don't allow the buffer to be larger than was allocated. + // + if(SetupPacket.wLength > g_sUSBHCD.ulPoolSize) + { + SetupPacket.wLength = g_sUSBHCD.ulPoolSize; + } + + // + // Put the setup packet in the buffer. + // + ulBytes = + USBHCDControlTransfer(0, &SetupPacket, pDevice, + (unsigned char *)pDevice->pConfigDescriptor, + SetupPacket.wLength, + pDevice->DeviceDescriptor.bMaxPacketSize0); + + // + // If we read the descriptor, remember the fact. + // + if(ulBytes) + { + pDevice->bConfigRead = true; + } + } + + return(ulBytes); +} + +//***************************************************************************** +// +//! This function issues a request for a device descriptor from a device. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param pDevice 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 pDevice->DeviceDescriptor descriptor is used to hold the data for this +//! request. \e pDevice->DeviceDescriptor.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 unsigned long +USBHCDGetDeviceDescriptor(unsigned long ulIndex, tUSBHostDevice *pDevice) +{ + tUSBRequest SetupPacket; + unsigned long ulBytes; + + ASSERT(ulIndex == 0); + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + SetupPacket.wValue = USB_DTYPE_DEVICE << 8; + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = 0; + + // + // All devices must have at least an 8 byte max packet size so just ask + // for 8 bytes to start with. + // + SetupPacket.wLength = sizeof(tDeviceDescriptor); + + ulBytes = 0; + + // + // Discover the max packet size for endpoint 0. + // + if(pDevice->DeviceDescriptor.bMaxPacketSize0 == 0) + { + // + // Put the setup packet in the buffer. + // + ulBytes = + USBHCDControlTransfer(ulIndex, &SetupPacket, pDevice, + (unsigned char *)&(pDevice->DeviceDescriptor), + sizeof(tDeviceDescriptor), + 8); + } + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + if(ulBytes < sizeof(tDeviceDescriptor)) + { + SetupPacket.wLength = (unsigned short)sizeof(tDeviceDescriptor); + + ulBytes = + USBHCDControlTransfer(ulIndex, &SetupPacket, pDevice, + (unsigned char *)&(pDevice->DeviceDescriptor), + sizeof(tDeviceDescriptor), + pDevice->DeviceDescriptor.bMaxPacketSize0); + } + + return(ulBytes); +} + +//***************************************************************************** +// +//! This function is used to send the set address command to a device. +//! +//! \param ulDevIndex 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 ulDevAddress 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 ulDevAddress parameter is used +//! for all further communications with the device after this function +//! returns. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSetAddress(unsigned long ulDevIndex, unsigned long ulDevAddress) +{ + tUSBRequest SetupPacket; + + // + // This is a Standard Device OUT request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_ADDRESS; + SetupPacket.wValue = ulDevAddress; + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = 0; + + // + // Only request the space available. + // + SetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, &g_sUSBHCD.USBDevice[ulDevIndex], 0, + 0, MAX_PACKET_SIZE_EP0); + + // + // Must delay 2ms after setting the address. + // + OS_DELAY(g_ulTickms * 2); +} + +//***************************************************************************** +// +//! This function is used to send a Clear Feature request to a device. +//! +//! \param ulDevAddress is the USB bus address of the device that will receive +//! this request. +//! \param ulPipe is the pipe that will be used to send the request. +//! \param ulFeature is one of the USB_FEATURE_* definitions. +//! +//! This function will issue a Clear Feature request to the device indicated +//! by the \e ulDevAddress parameter. The \e ulPipe parameter is the USB pipe +//! that should be used to send this request. The \e ulFeature 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(unsigned long ulDevAddress, unsigned long ulPipe, + unsigned long ulFeature) +{ + tUSBRequest SetupPacket; + unsigned long ulIndex; + + // + // Get the index number from the allocated pipe. + // + ulIndex = (ulPipe & EP_PIPE_IDX_M); + + // + // This is a Standard Device OUT request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_ENDPOINT; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + SetupPacket.wValue = ulFeature; + + // + // Set the endpoint to access. + // + if(ulPipe & EP_PIPE_TYPE_IN) + { + SetupPacket.wIndex = g_sUSBHCD.USBINPipes[ulIndex].ucEPNumber | 0x80; + } + else + { + SetupPacket.wIndex = g_sUSBHCD.USBOUTPipes[ulIndex].ucEPNumber; + } + + // + // This is always 0. + // + SetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, + &g_sUSBHCD.USBDevice[ulDevAddress - 1], 0, 0, + MAX_PACKET_SIZE_EP0); + + // + // Set the endpoint to access. + // + if(ulPipe & EP_PIPE_TYPE_IN) + { + MAP_USBEndpointDataToggleClear(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1), + USB_EP_HOST_IN); + } + else + { + MAP_USBEndpointDataToggleClear(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1), + USB_EP_HOST_OUT); + } + + // + // Must delay 2ms after clearing the feature. + // + OS_DELAY(g_ulTickms * 2); +} + +//***************************************************************************** +// +//! This function is used to set the current configuration for a device. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param ulDevice is the USB device for this function. +//! \param ulConfiguration is one of the devices valid configurations. +//! +//! This function is used to set the current device configuration for a USB +//! device. The \e ulConfiguration 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(unsigned long ulIndex, unsigned long ulDevice, + unsigned long ulConfiguration) +{ + tUSBRequest SetupPacket; + tUSBHostDevice *pDevice; + + ASSERT(ulIndex == 0); + + pDevice = (tUSBHostDevice *)ulDevice; + + // + // This is a Standard Device OUT request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_CONFIG; + SetupPacket.wValue = ulConfiguration; + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = 0; + + // + // Only request the space available. + // + SetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, pDevice, 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 ulIndex specifies which USB controller to use. +//! \param ulDevice is the USB device for this function. +//! \param ulInterface is one of the valid interface numbers for a device. +//! \param ulAltSetting is one of the valid alternate interfaces for the +//! ulInterface number. +//! +//! This function is used to change the alternate setting for one of the valid +//! interfaces on a USB device. The \e ulDevice 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 ulInterface and \e ulAltSetting. +//! +//! \b Example: Set the USB device interface 2 to alternate setting 1. +//! +//! \verbatim +//! USBHCDSetInterface(0, ulDevice, 2, 1); +//! \endverbatim +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSetInterface(unsigned long ulIndex, unsigned long ulDevice, + unsigned long ulInterface, unsigned ulAltSetting) +{ + tUSBRequest SetupPacket; + tUSBHostDevice *pDevice; + + ASSERT(ulIndex == 0); + + pDevice = (tUSBHostDevice *)ulDevice; + + // + // This is a Standard Device OUT request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_INTERFACE; + + // + // Index is the interface to access. + // + SetupPacket.wIndex = ulInterface; + + // + // wValue is the alternate setting. + // + SetupPacket.wValue = ulAltSetting; + + + // + // Only request the space available. + // + SetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, pDevice, 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) +{ + long lIdx; + + g_ulCurrentTick++; + + for(lIdx = 0; lIdx < g_sUSBHCD.ulNumEndpoints; lIdx++) + { + // + // Skip unused pipes. + // + if(g_sUSBHCD.USBINPipes[lIdx].ulType == USBHCD_PIPE_UNUSED) + { + continue; + } + + // + // If the tick has expired and it has an interval then update it. + // + if((g_sUSBHCD.USBINPipes[lIdx].ulInterval != 0) && + (g_sUSBHCD.USBINPipes[lIdx].ulNextEventTick == g_ulCurrentTick)) + { + // + // Schedule the next event. + // + g_sUSBHCD.USBINPipes[lIdx].ulNextEventTick += + g_sUSBHCD.USBINPipes[lIdx].ulInterval; + + // + // 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.USBINPipes[lIdx].eState == PIPE_IDLE) && + (g_sUSBHCD.USBINPipes[lIdx].pfnCallback)) + { + g_sUSBHCD.USBINPipes[lIdx].pfnCallback(IN_PIPE_HANDLE(lIdx), + USB_EVENT_SCHEDULER); + } + } + } +} + +//***************************************************************************** +// +// The internal USB host mode interrupt handler. +// +// \param ulIndex is the USB controller associated with this interrupt. +// \param ulStatus 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(unsigned long ulIndex, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + static unsigned long ulSOFDivide = 0; + unsigned long ulEvent; + unsigned long ulIdx; + unsigned long ulDevIndex; + long lClassDrvr; + + // + // 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.USBDevice[0].bNotifyInt = true; + + if(ulStatus & USB_INTCTRL_SOF) + { + // + // Indicate that a start of frame has occurred. + // + g_ulUSBHIntEvents |= INT_EVENT_SOF; + } + + // + // A power fault has occurred so notify the application. + // + if(ulStatus & USB_INTCTRL_POWER_FAULT) + { + // + // Indicate that a power fault has occurred. + // + g_ulUSBHIntEvents |= INT_EVENT_POWER_FAULT; + + // + // Turn off power to the bus. + // + MAP_USBHostPwrDisable(USB0_BASE); + + // + // Disable USB interrupts. + // + OS_INT_DISABLE(INT_USB0); + + return; + } + + // + // In the event of a USB VBUS error, end the session and remove power to + // the device. + // + if(ulStatus & 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_ulUSBHIntEvents = INT_EVENT_VBUS_ERR; + return; + } + + // + // Received a reset from the host. + // + if(ulStatus & USB_INTCTRL_BABBLE) + { + } + + // + // Suspend was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_SUSPEND) + { + } + + // + // Start the session. + // + if(ulStatus & USB_INTCTRL_SESSION) + { + // + // Power the USB bus. + // + MAP_USBHostPwrEnable(USB0_BASE); + + USBOTGSessionRequest(USB0_BASE, true); + } + + // + // Resume was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_RESUME) + { + } + + // + // Device connected so tell the main routine to issue a reset. + // + if(ulStatus & USB_INTCTRL_CONNECT) + { + // + // Set the connect flag and clear disconnect if it happens to be set. + // + g_ulUSBHIntEvents |= INT_EVENT_CONNECT; + g_ulUSBHIntEvents &= ~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 USB_MODE_HOST. + // + if(ulStatus & USB_INTCTRL_MODE_DETECT) + { + // + // If in USB_MODE_HOST mode then switch back to OTG detection + // so that VBUS can be monitored but free up the ID pin. + // + if(g_eUSBMode == USB_MODE_HOST) + { + USBOTGMode(USB0_BASE); + } + } + + // + // Device was unplugged. + // + if(ulStatus & USB_INTCTRL_DISCONNECT) + { + // + // Set the disconnect flag and clear connect if it happens to be set. + // + g_ulUSBHIntEvents |= INT_EVENT_DISCONNECT; + g_ulUSBHIntEvents &= ~INT_EVENT_CONNECT; + } + + // + // Start of Frame was received. + // + if(ulStatus & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ulUSBSOFCount++; + + // + // Increment our SOF divider. + // + ulSOFDivide++; + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ulSOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ulSOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Get the current endpoint interrupt status. + // + ulStatus = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ulStatus & USB_INTEP_0) + { + // + // Indicate that a start of frame has occurred. + // + g_ulUSBHIntEvents |= INT_EVENT_ENUM; + } + + // + // Check to see if any uDMA transfers are pending + // + for(ulIdx = 0; ulIdx < MAX_NUM_PIPES; ulIdx++) + { + if((g_ulDMAPending == 0) && (ulStatus == 0)) + { + break; + } + + // + // Check each pipe to see if uDMA is pending + // + if(g_ulDMAPending & (DMA_PEND_RECEIVE_FLAG << ulIdx)) + { + // + // Handle the case where the pipe is reading + // + if(g_sUSBHCD.USBINPipes[ulIdx].eState == PIPE_READING) + { + // + // If the DMA channel transfer is complete, send an ack. + // + if(uDMAChannelModeGet(UDMA_CHANNEL_USBEP1RX + (ulIdx * 2)) + == UDMA_MODE_STOP) + { + MAP_USBHostEndpointDataAck(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1)); + g_ulDMAPending &= ~(DMA_PEND_RECEIVE_FLAG << ulIdx); + + // + // If using uDMA then the endpoint status int will not + // occur. So process the data ready event here. + // + g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_DATA_READY; + ulEvent = USB_EVENT_RX_AVAILABLE; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.USBINPipes[ulIdx].pfnCallback) + { + g_sUSBHCD.USBINPipes[ulIdx].pfnCallback( + IN_PIPE_HANDLE(ulIdx), ulEvent); + } + + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + g_sUSBHCD.USBINPipes[ulIdx].psDevice->bNotifyInt = true; + } + } + } + + // + // Check for a pending DMA transmit transaction. + // + if(g_ulDMAPending & (DMA_PEND_TRANSMIT_FLAG << ulIdx)) + { + // + // Handle the case where the pipe is writing + // + if(g_sUSBHCD.USBOUTPipes[ulIdx].eState == PIPE_WRITING) + { + // + // If the uDMA channel transfer is complete, then tell + // the USB controller to go ahead and send the data + // + if(uDMAChannelModeGet(UDMA_CHANNEL_USBEP1TX + (ulIdx * 2)) + == UDMA_MODE_STOP) + { + MAP_USBEndpointDataSend(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1), + USB_TRANS_OUT); + g_ulDMAPending &= ~(DMA_PEND_TRANSMIT_FLAG << ulIdx); + } + } + } + + // + // Check the next pipe, the first time through this will clear out + // any interrupts dealing with endpoint zero since it was handled above. + // + ulStatus >>= 1; + + // + // Check the status of the transmit(OUT) pipes. + // + if(ulStatus & 1) + { + // + // Read the status of the endpoint connected to this pipe. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1)); + + // + // Check if the device stalled the request. + // + if(ulEPStatus & USB_HOST_OUT_STALL) + { + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1), + USB_HOST_OUT_STALL); + + // + // Save the STALLED state. + // + g_sUSBHCD.USBOUTPipes[ulIdx].eState = PIPE_STALLED; + + // + // Notify the pipe that it was stalled. + // + ulEvent = USB_EVENT_STALL; + } + else if(ulEPStatus & USB_HOST_OUT_ERROR) + { + // + // Clear the error condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1), + USB_HOST_OUT_ERROR); + + // + // Save the Pipes error state. + // + g_sUSBHCD.USBOUTPipes[ulIdx].eState = PIPE_ERROR; + + // + // Notify the pipe that had an error. + // + ulEvent = USB_EVENT_ERROR; + } + else + { + // + // Data was transmitted successfully. + // + g_sUSBHCD.USBOUTPipes[ulIdx].eState = PIPE_DATA_SENT; + + // + // Notify the pipe that its last transaction was completed. + // + ulEvent = USB_EVENT_TX_COMPLETE; + } + + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1), + ulEPStatus); + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.USBOUTPipes[ulIdx].pfnCallback) + { + g_sUSBHCD.USBOUTPipes[ulIdx].pfnCallback(OUT_PIPE_HANDLE(ulIdx), + ulEvent); + } + + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + g_sUSBHCD.USBOUTPipes[ulIdx].psDevice->bNotifyInt = true; + } + + // + // Check the status of the receive(IN) pipes. + // + if(ulStatus & 0x10000) + { + // + // Clear the status flag for the IN Pipe. + // + ulStatus &= ~0x10000; + + // + // Read the status of the endpoint connected to this pipe. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1)); + + // + // Check if the device stalled the request. + // + if(ulEPStatus & USB_HOST_IN_STALL) + { + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + INDEX_TO_USB_EP(ulIdx + 1), + USB_HOST_IN_STALL); + + // + // Save the STALLED state. + // + g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_STALLED; + + // + // Notify the pipe that it was stalled. + // + ulEvent = USB_EVENT_STALL; + } + else if(ulEPStatus & 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, + INDEX_TO_USB_EP(ulIdx + 1), + USB_HOST_IN_ERROR); + + // + // Save the STALLED state. + // + g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_ERROR; + + // + // Notify the pipe that it was stalled. + // + ulEvent = USB_EVENT_ERROR; + } + else + { + // + // Data is available. + // + g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_DATA_READY; + + // + // 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.USBINPipes[ulIdx].pucReadPtr) + { + USBEndpointDataGet(USB0_BASE, INDEX_TO_USB_EP(ulIdx + 1), + g_sUSBHCD.USBINPipes[ulIdx].pucReadPtr, + &g_sUSBHCD.USBINPipes[ulIdx].ulReadSize); + } + + // + // Notify the pipe that its last transaction was completed. + // + ulEvent = USB_EVENT_RX_AVAILABLE; + } + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.USBINPipes[ulIdx].pfnCallback) + { + g_sUSBHCD.USBINPipes[ulIdx].pfnCallback(IN_PIPE_HANDLE(ulIdx), + ulEvent); + } + + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + g_sUSBHCD.USBINPipes[ulIdx].psDevice->bNotifyInt = true; + } + } + + // + // Send back notifications to any class driver whose endpoint required + // service during the handler. + // + for(ulDevIndex = 0; ulDevIndex <= MAX_USB_DEVICES; ulDevIndex++) + { + // + // Which class driver does this device use? + // + lClassDrvr = g_lUSBHActiveDriver[ulDevIndex]; + + // + // If a class driver is in use, and one of its endpoints was serviced + // and the class driver has an interrupt callback... + // + if((lClassDrvr >= 0) && g_sUSBHCD.USBDevice[ulDevIndex].bNotifyInt && + (g_sUSBHCD.pClassDrivers[lClassDrvr]->pfnIntHandler)) + { + // + // ...call the class driver's interrupt notification callback. + // + g_sUSBHCD.pClassDrivers[lClassDrvr]->pfnIntHandler( + g_pvDriverInstance[ulDevIndex]); + } + } +} + +//***************************************************************************** +// +//! 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) +{ + unsigned long ulStatus; + + // + // Get the control interrupt status. + // + ulStatus = MAP_USBIntStatusControl(USB0_BASE); + + // + // Call the internal handler to process the interrupts. + // + USBHostIntHandlerInternal(0, ulStatus); +} + +//***************************************************************************** +// +//! This function opens the class driver. +//! +//! \param ulIndex specifies which USB controller to use. +//! \param ulDeviceNum 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 long +USBHCDOpenDriver(unsigned long ulIndex, unsigned long ulDeviceNum) +{ + long lDriver; + unsigned long ulClass; + tInterfaceDescriptor *pInterface; + tEventInfo sEvent; + + ASSERT(ulIndex == 0); + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface( + g_sUSBHCD.USBDevice[ulDeviceNum].pConfigDescriptor, + g_sUSBHCD.USBDevice[ulDeviceNum].ulInterface, + USB_DESC_ANY); + + // + // Read the interface class. + // + ulClass = pInterface->bInterfaceClass; + + // + // Search through the Host Class driver list for the devices class. + // + for(lDriver = 0; lDriver < g_sUSBHCD.ulNumClassDrivers; lDriver++) + { + // + // If a driver was found call the open for this driver and save which + // driver is in use. + // + if(g_sUSBHCD.pClassDrivers[lDriver]->ulInterfaceClass == ulClass) + { + // + // Call the open function for the class driver. + // + g_pvDriverInstance[ulDeviceNum] = + g_sUSBHCD.pClassDrivers[lDriver]->pfnOpen( + &g_sUSBHCD.USBDevice[ulDeviceNum]); + + // + // If the driver was successfully loaded then break out of the + // loop. + // + if(g_pvDriverInstance[ulDeviceNum] != 0) + { + break; + } + } + } + + // + // If no drivers were found then return -1 to indicate an invalid + // driver instance. + // + if(lDriver == g_sUSBHCD.ulNumClassDrivers) + { + // + // Send an unknown connection event. + // + SendUnknownConnect(ulIndex, ulClass); + + // + // Indicate that no driver was found. + // + lDriver = -1; + } + + // + // If the connect event is enabled then send the event. + // + sEvent.ulEvent = USB_EVENT_CONNECTED; + sEvent.ulInstance = (ulIndex << 16) | ulDeviceNum; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_CONNECT); + + return(lDriver); +} + +//***************************************************************************** +// +// This function will send an event to a registered event driver. +// +// \param ulIndex 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(unsigned long ulIndex, tEventInfo *psEvent, + unsigned long ulEvFlag) +{ + // + // Make sure that an event driver has been registered. + // + if((g_sUSBHCD.lEventDriver != -1) && + (g_sUSBHCD.pClassDrivers[g_sUSBHCD.lEventDriver]->pfnIntHandler) && + (g_sUSBHCD.ulEventEnables & ulEvFlag)) + { + g_sUSBHCD.pClassDrivers[g_sUSBHCD.lEventDriver]->pfnIntHandler(psEvent); + } +} + +//***************************************************************************** +// +// This function handles the necessary clean up for device disconnect. +// +// \param ulIndex 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(unsigned long ulIndex, unsigned long ulDevIndex) +{ + tEventInfo sEvent; + + ASSERT(ulIndex == 0); + ASSERT(ulDevIndex <= 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.ulEvent = USB_EVENT_DISCONNECTED; + sEvent.ulInstance = (ulIndex << 16) | ulDevIndex; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_DISCNCT); + + // + // Reset the class. + // + g_sUSBHCD.ulClass = USB_CLASS_EVENTS; + + if(g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor) + { + // + // Invalidate the configuration descriptor. + // + g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor = 0; + g_sUSBHCD.USBDevice[ulDevIndex].bConfigRead = false; + + } + + // + // Reset the max packet size so that this will be re-read from new devices. + // + g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bMaxPacketSize0 = 0; + + // + // No longer have a device descriptor. + // + g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bLength = 0; + + // + // No longer addressed. + // + g_sUSBHCD.USBDevice[ulDevIndex].ulAddress = 0; + + // + // If this was an active driver then close it out. + // + if(g_lUSBHActiveDriver[ulDevIndex] >= 0) + { + // + // Call the driver Close entry point. + // + g_sUSBHCD.pClassDrivers[g_lUSBHActiveDriver[ulDevIndex]]-> + pfnClose(g_pvDriverInstance[ulDevIndex]); + + // + // No active driver now present. + // + g_lUSBHActiveDriver[ulDevIndex] = -1; + g_pvDriverInstance[ulDevIndex] = 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((ulDevIndex == 0) && (g_eUSBMode == USB_MODE_OTG)) + { + 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 eOldState; + unsigned long ulLoop; + tEventInfo sEvent; + + // + // Save the old state to detect changes properly. + // + eOldState = g_sUSBHCD.eDeviceState[0]; + + // + // Fix up the state if any important interrupt events occurred. + // + if(g_ulUSBHIntEvents) + { + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(INT_USB0); + + if(g_ulUSBHIntEvents & 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.ulEvent = USB_EVENT_POWER_FAULT; + sEvent.ulInstance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWRFAULT); + + g_sUSBHCD.eDeviceState[0] = HCD_POWER_FAULT; + } + else if(g_ulUSBHIntEvents & INT_EVENT_VBUS_ERR) + { + // + // A VBUS error has occurred. This event trumps connect and + // disconnect since it will cause a controller reset. + // + g_sUSBHCD.eDeviceState[0] = HCD_VBUS_ERROR; + } + else + { + // + // Has a device connected? + // + if(g_ulUSBHIntEvents & INT_EVENT_CONNECT) + { + g_sUSBHCD.eDeviceState[0] = HCD_DEV_RESET; + g_sUSBHCD.USBDevice[0].ucHub = 0; + g_sUSBHCD.USBDevice[0].ucHubPort = 0; + } + else + { + // + // Has a device disconnected? + // + if(g_ulUSBHIntEvents & INT_EVENT_DISCONNECT) + { + g_sUSBHCD.eDeviceState[0] = HCD_DEV_DISCONNECTED; + } + } + + // + // Handle the start of frame event + // + if(g_ulUSBHIntEvents & INT_EVENT_SOF) + { + // + // If the connect event is enabled then send the event. + // + sEvent.ulEvent = USB_EVENT_SOF; + sEvent.ulInstance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_SOF); + + USBHostCheckPipes(); + + // + // Call the hub driver to have it perform any necessary processing to + // handle downstream devices. + // + USBHHubMain(); + } + } + + // + // Clear the flags. + // + g_ulUSBHIntEvents = 0; + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } + + // + // 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(ulLoop = 0; ulLoop <= MAX_USB_DEVICES; ulLoop++) + { + // + // 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(ulLoop != 0) + { + eOldState = g_sUSBHCD.eDeviceState[ulLoop]; + } + + // + // Process the state machine for this device. + // + ProcessUSBDeviceStateMachine(eOldState, ulLoop); + } +} + +static void +ProcessUSBDeviceStateMachine(tUSBHDeviceState eOldState, + unsigned long ulDevIndex) +{ + switch(g_sUSBHCD.eDeviceState[ulDevIndex]) + { + // + // There was a power fault condition so shut down and wait for the + // application to re-initialized the system. + // + case HCD_POWER_FAULT: + { + break; + } + + // + // There was a VBUS error so handle it. + // + case HCD_VBUS_ERROR: + { + // + // Disable USB interrupts. + // + OS_INT_DISABLE(INT_USB0); + + // + // If there was a device in any state of connection then indicate + // that it has been disconnected. + // + if((eOldState != HCD_IDLE) && (eOldState != HCD_POWER_FAULT)) + { + // + // Handle device disconnect. + // + USBHCDDeviceDisconnected(0, ulDevIndex); + } + + // + // Reset the controller. + // + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + + // + // Wait for 100ms before trying to re-power the device. + // + OS_DELAY(g_ulTickms * 100); + + // + // Re-initialize the HCD. + // + USBHCDInitInternal(0, g_sUSBHCD.pvPool, g_sUSBHCD.ulPoolSize); + + break; + } + // + // Trigger a reset to the connected device. + // + case HCD_DEV_RESET: + { + if(!ulDevIndex) + { + // + // 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.eDeviceState[0] = HCD_DEV_CONNECTED; + + // + // Set the memory to use for the config descriptor and save the + // size. + // + g_sUSBHCD.USBDevice[0].pConfigDescriptor = g_sUSBHCD.pvPool; + g_sUSBHCD.USBDevice[0].ulConfigDescriptorSize = + g_sUSBHCD.ulPoolSize; + + // + // Remember that we don't have a valid configuration descriptor + // yet. + // + g_sUSBHCD.USBDevice[0].bConfigRead = false; + + break; + } + // + // Device connection has been established now start enumerating + // the device. + // + case HCD_DEV_CONNECTED: + { + // + // First check if we have read the device descriptor at all + // before proceeding. + // + if(g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bLength == 0) + { + // + // Initialize a request for the device descriptor. + // + DEBUG_OUTPUT("Connection %d - getting device descriptor\n", + ulDevIndex); + + if(USBHCDGetDeviceDescriptor(0, + &g_sUSBHCD.USBDevice[ulDevIndex]) == 0) + { + // + // If the device descriptor cannot be read then the device + // will be treated as unknown. + // + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_ERROR; + + DEBUG_OUTPUT("Connection %d - failed to get descriptor\n", + ulDevIndex); + + // + // Send an unknown connection event. + // + SendUnknownConnect(0, 1); + + // + // If the device is connected via a hub, tell the hub + // driver that we experienced an error enumerating the + // device. + // + if(g_sUSBHCD.USBDevice[ulDevIndex].ucHub) + { + USBHHubEnumerationError( + g_sUSBHCD.USBDevice[ulDevIndex].ucHub, + g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort); + } + } + } + // + // If we have the device descriptor then move on to setting + // the address of the device. + // + else if(g_sUSBHCD.USBDevice[ulDevIndex].ulAddress == 0) + { + DEBUG_OUTPUT("Connection %d - setting address %d\n", + ulDevIndex, ulDevIndex + 1); + + // + // Send the set address command. + // + USBHCDSetAddress(ulDevIndex, (ulDevIndex + 1)); + + // + // Save the address. + // + g_sUSBHCD.USBDevice[ulDevIndex].ulAddress = (ulDevIndex + 1); + + // + // Move on to the addressed state. + // + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_ADDRESSED; + } + break; + } + case HCD_DEV_ADDRESSED: + { + // + // First check if we have read the configuration descriptor. + // + if(!g_sUSBHCD.USBDevice[ulDevIndex].bConfigRead) + { + DEBUG_OUTPUT("Connection %d - getting config descriptor\n", + ulDevIndex); + + // + // Initialize a request for the config descriptor. + // + if(USBHCDGetConfigDescriptor(0, + &g_sUSBHCD.USBDevice[ulDevIndex]) == 0) + { + // + // If the device descriptor cannot be read then the device + // will be treated as unknown. + // + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_ERROR; + + DEBUG_OUTPUT("Connection %d - failed to get descriptor\n", + ulDevIndex); + + // + // Send an unknown connection event. + // + 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.USBDevice[ulDevIndex].ucHub) + { + USBHHubEnumerationError( + g_sUSBHCD.USBDevice[ulDevIndex].ucHub, + g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort); + } + } + } + // + // Now have addressed and received the device configuration, + // so get ready to set the device configuration. + // + else + { + DEBUG_OUTPUT("Connection %d - setting configuration.\n", + ulDevIndex); + + // + // Use the first configuration to set the device + // configuration. + // + USBHCDSetConfig(0, + (unsigned long)&g_sUSBHCD.USBDevice[ulDevIndex], 1); + + // + // Move on to the configured state. + // + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_CONFIGURED; + + // + // Open the driver for the device. + // + g_lUSBHActiveDriver[ulDevIndex] = USBHCDOpenDriver(0, + ulDevIndex); + + // + // If the device is connected via a hub, tell the hub + // driver that enumeration is complete. + // + if(g_sUSBHCD.USBDevice[ulDevIndex].ucHub) + { + USBHHubEnumerationComplete( + g_sUSBHCD.USBDevice[ulDevIndex].ucHub, + g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort); + } + } + break; + } + // + // The device was making a request and is now complete. + // + case HCD_DEV_REQUEST: + { + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_CONNECTED; + break; + } + // + // The strings are currently not accessed. + // + case HCD_DEV_GETSTRINGS: + { + break; + } + // + // Basically Idle at this point. + // + case HCD_DEV_DISCONNECTED: + { + DEBUG_OUTPUT("Connection %d - disconnected.\n", + ulDevIndex); + + // + // Handle device disconnect. + // + USBHCDDeviceDisconnected(0, ulDevIndex); + + // + // Return to the Idle state. + // + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_IDLE; + break; + } + + // + // Connection and enumeration is complete so allow this function + // to exit. + // + case HCD_DEV_CONFIGURED: + { + break; + } + + // + // Poorly behaving device are in limbo in this state until removed. + // + case HCD_DEV_ERROR: + { + DEBUG_OUTPUT("Connection %d - Error!\n", ulDevIndex); + + // + // 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(ulDevIndex == 0) + { + g_ulUSBHIntEvents |= INT_EVENT_DISCONNECT; + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_IDLE; + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +//! This function completes a control transaction to a device. +//! +//! \param ulIndex is the controller index to use for this transfer. +//! \param pSetupPacket is the setup request to be sent. +//! \param pDevice is the device instance pointer for this request. +//! \param pData is the data to send for OUT requests or the receive buffer +//! for IN requests. +//! \param ulSize is the size of the buffer in pData. +//! \param ulMaxPacketSize 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. +// +//***************************************************************************** +unsigned long +USBHCDControlTransfer(unsigned long ulIndex, tUSBRequest *pSetupPacket, + tUSBHostDevice *pDevice, unsigned char *pData, + unsigned long ulSize, unsigned long ulMaxPacketSize) +{ + unsigned long ulRemaining; + unsigned long ulDataSize; + + // + // Debug sanity check. + // + ASSERT(g_sUSBHEP0State.eState == EP0_STATE_IDLE); + ASSERT(ulIndex == 0); + + // + // Initialize the state of the data for this request. + // + g_sUSBHEP0State.pData = pData; + g_sUSBHEP0State.ulBytesRemaining = ulSize; + g_sUSBHEP0State.ulDataSize = ulSize; + + // + // Set the maximum packet size. + // + g_sUSBHEP0State.ulMaxPacketSize = ulMaxPacketSize; + + // + // Save the current address. + // + g_sUSBHEP0State.ulDevAddress = pDevice->ulAddress; + + // + // Set the address the host will used to communicate with the device. + // + MAP_USBHostAddrSet(USB0_BASE, USB_EP_0, g_sUSBHEP0State.ulDevAddress, + USB_EP_HOST_OUT); + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (unsigned char *)pSetupPacket, + sizeof(tUSBRequest)); + + // + // If this is an IN request, change to that state. + // + if(pSetupPacket->bmRequestType & USB_RTYPE_DIR_IN) + { + g_sUSBHEP0State.eState = EP0_STATE_SETUP_IN; + } + else + { + // + // If there is no data then this is not an OUT request. + // + if(ulSize != 0) + { + // + // Since there is data, this is an OUT request. + // + g_sUSBHEP0State.eState = EP0_STATE_SETUP_OUT; + } + else + { + // + // Otherwise this request has no data and just a status phase. + // + g_sUSBHEP0State.eState = EP0_STATE_STATUS_IN; + } + } + + // + // Make sure we are talking to the correct device. + // + USBHostHubAddrSet(USB0_BASE, USB_EP_0, + ((pDevice->ucHub << 8) | (pDevice->ucHubPort)), + USB_EP_HOST_OUT | (pDevice->bLowSpeed ? + USB_EP_SPEED_LOW : USB_EP_SPEED_FULL)); + + // + // 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.eState != EP0_STATE_IDLE) + { + OS_INT_DISABLE(INT_USB0); + + if((g_ulUSBHIntEvents & (INT_EVENT_ENUM | INT_EVENT_SOF)) == (INT_EVENT_ENUM | INT_EVENT_SOF)) + { + g_ulUSBHIntEvents &= ~(INT_EVENT_ENUM | INT_EVENT_SOF); + + USBHCDEnumHandler(); + } + + OS_INT_ENABLE(INT_USB0); + + if(g_sUSBHEP0State.eState == EP0_STATE_ERROR) + { + return(0xffffffff); + } + + // + // If we aborted the transfer due to an error, tell the caller + // that no bytes were transferred. + // + if(g_ulUSBHIntEvents & (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. + // + ulDataSize = g_sUSBHEP0State.ulDataSize; + ulRemaining = g_sUSBHEP0State.ulBytesRemaining; + + return(ulDataSize - ulRemaining); +} + +//***************************************************************************** +// +// Starts enumerating a new device connected via the hub. +// +// \param ulIndex is the index of the USB controller to use. +// \param ulHub is the hub address from which the connection is being made. +// \param ulPort is the hub port number that the new device is connected to. +// \param pucConfigPool is memory to be used to store the device's config +// descriptor. +// \param ulConfigSize is the number of bytes available in the buffer pointed +// to by pucConfigPool. +// +// 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. +// +//***************************************************************************** +unsigned long +USBHCDHubDeviceConnected(unsigned long ulIndex, unsigned char ucHub, + unsigned char ucPort, tBoolean bLowSpeed, + unsigned char *pucConfigPool, + unsigned long ulConfigSize) +{ + unsigned long ulDevIndex; + + // + // Debug sanity checks. + // + ASSERT(ulIndex == 0); + ASSERT(pucConfigPool); + ASSERT(ulConfigSize); + ASSERT(ucPort); + + DEBUG_OUTPUT("Connection from hub %d, port %d.\n", ucHub, ucPort); + + // + // Look for a free slot in the device table. + // + for(ulDevIndex = 1; ulDevIndex <= MAX_USB_DEVICES; ulDevIndex++) + { + if(g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor == 0) + { + // + // We found one. Set the state to ensure that it gets enumerated. + // + g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor = + (tConfigDescriptor *)pucConfigPool; + g_sUSBHCD.USBDevice[ulDevIndex].ulConfigDescriptorSize = + ulConfigSize; + g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor->bLength = 0; + g_sUSBHCD.USBDevice[ulDevIndex].ucHub = ucHub; + g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort = ucPort; + g_sUSBHCD.USBDevice[ulDevIndex].bLowSpeed = bLowSpeed; + g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bLength = 0; + + // + // Set the state to ensure enumeration begins. + // + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_CONNECTED; + + DEBUG_OUTPUT("Allocating device %d\n", ulDevIndex); + + // + // Pass the device index back to the hub driver. + // + return(ulDevIndex); + } + } + + // + // 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); +} + +//***************************************************************************** +// +// TODO: Documentation +// +//***************************************************************************** +void +USBHCDHubDeviceDisconnected(unsigned long ulIndex, unsigned long ulDevIndex) +{ + // + // Debug sanity checks. + // + ASSERT(ulIndex == 0); + ASSERT(ulDevIndex && (ulDevIndex <= MAX_USB_DEVICES)); + + DEBUG_OUTPUT("Disconnection from hub %d, port %d, device %d\n", + g_sUSBHCD.USBDevice[ulDevIndex].ucHub, + g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort, ulDevIndex); + + // + // Set the device state to ensure that USBHCDMain cleans it up. + // + g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_DISCONNECTED; +} + +//***************************************************************************** +// +// This is the endpoint 0 interrupt handler. +// +// \return None. +// +//***************************************************************************** +static void +USBHCDEnumHandler(void) +{ + unsigned long ulEPStatus; + unsigned long ulDataSize; + + // + // Get the end point 0 status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + // + // If there was an error then go to the error state. + // + if(ulEPStatus == 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.eState = EP0_STATE_ERROR; + + return; + } + + switch(g_sUSBHEP0State.eState) + { + // + // Handle the status state, this is a transitory state from + // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE. + // + case EP0_STATE_STATUS: + { + // + // Handle the case of a received status packet. + // + if(ulEPStatus & (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.eState = EP0_STATE_IDLE; + + break; + } + + // + // This state triggers a STATUS IN request from the device. + // + case EP0_STATE_STATUS_IN: + { + // + // Generate an IN request from the device. + // + MAP_USBHostRequestStatus(USB0_BASE); + + // + // Change to the status phase and wait for the response. + // + g_sUSBHEP0State.eState = EP0_STATE_STATUS; + + break; + } + + // + // In the IDLE state the code is waiting to receive data from the host. + // + case EP0_STATE_IDLE: + { + break; + } + + // + // Data is still being sent to the host so handle this in the + // EP0StateTx() function. + // + case EP0_STATE_SETUP_OUT: + { + // + // Send remaining data if necessary. + // + USBHCDEP0StateTx(); + + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint 0. + // + case EP0_STATE_SETUP_IN: + { + // + // 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.eState = EP0_STATE_RX; + + break; + } + + // + // The endpoint remains in this state until all requested data has + // been received. + // + case EP0_STATE_RX: + { + // + // There was a stall on endpoint 0 so go back to the idle state + // as this command has been terminated. + // + if(ulEPStatus & USB_HOST_EP0_RX_STALL) + { + g_sUSBHEP0State.eState = EP0_STATE_IDLE; + + // + // Clear the stalled state on endpoint 0. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0, ulEPStatus); + break; + } + + // + // Set the number of bytes to get out of this next packet. + // + if(g_sUSBHEP0State.ulBytesRemaining > + g_sUSBHEP0State.ulMaxPacketSize) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ulDataSize = MAX_PACKET_SIZE_EP0; + } + else + { + // + // There was space so send the remaining bytes. + // + ulDataSize = g_sUSBHEP0State.ulBytesRemaining; + } + + if(ulDataSize != 0) + { + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, + g_sUSBHEP0State.pData, + &ulDataSize); + } + + // + // Advance the pointer. + // + g_sUSBHEP0State.pData += ulDataSize; + + // + // Decrement the number of bytes that are being waited on. + // + g_sUSBHEP0State.ulBytesRemaining -= ulDataSize; + + // + // 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 short packet and indicates that this transfer is + // complete. If there were exactly g_sUSBHEP0State.ulMaxPacketSize + // remaining then there still needs to be null packet sent before + // this transfer is complete. + // + if((ulDataSize < g_sUSBHEP0State.ulMaxPacketSize) || + (g_sUSBHEP0State.ulBytesRemaining == 0)) + { + // + // Return to the idle state. + // + g_sUSBHEP0State.eState = EP0_STATE_STATUS; + + // + // No more data. + // + g_sUSBHEP0State.pData = 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 EP0_STATE_STALL: + { + // + // Reset the global end point 0 state to IDLE. + // + g_sUSBHEP0State.eState = EP0_STATE_IDLE; + + 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) +{ + unsigned long ulNumBytes; + unsigned char *pData; + + // + // In the TX state on endpoint 0. + // + g_sUSBHEP0State.eState = EP0_STATE_SETUP_OUT; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = g_sUSBHEP0State.ulBytesRemaining; + + // + // Limit individual transfers to 64 bytes. + // + if(ulNumBytes > 64) + { + ulNumBytes = 64; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pData = (unsigned char *)g_sUSBHEP0State.pData; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_sUSBHEP0State.ulBytesRemaining -= ulNumBytes; + g_sUSBHEP0State.pData += ulNumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulNumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ulNumBytes == 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.eState = EP0_STATE_STATUS_IN; + } +} + +//***************************************************************************** +// +//! This function returns the USB hub port for the requested device instance. +//! +//! \param ulInstance 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 ulInstance parameter. The caller must use the value for +//! \e ulInstance was passed to the application when it receives a +//! USB_EVENT_CONNECTED event. The function returns the USB hub port for +//! the interface number specified by the \e ulInterface parameter. +//! +//! \return The USB hub port for the requested interface. +// +//***************************************************************************** +unsigned char +USBHCDDevHubPort(unsigned long ulInstance) +{ + unsigned long ulDevIndex; + + ulDevIndex = HCDInstanceToDevIndex(ulInstance); + + if(ulDevIndex == 0xff) + { + return(ulDevIndex); + } + + return(g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort); +} + +//***************************************************************************** +// +//! This function will return the USB address for the requested device +//! instance. +//! +//! \param ulInstance is a unique value indicating which device to query. +//! +//! This function returns the USB address for the device that is associated +//! with the \e ulInstance parameter. The caller must use a value for +//! \e ulInstance have been passed to the application when it receives a +//! USB_EVENT_CONNECTED event. The function will return the USB address for +//! the interface number specified by the \e ulInterface parameter. +//! +//! \return The USB address for the requested interface. +// +//***************************************************************************** +unsigned char +USBHCDDevAddress(unsigned long ulInstance) +{ + unsigned long ulDevIndex; + + ulDevIndex = HCDInstanceToDevIndex(ulInstance); + + if(ulDevIndex == 0xff) + { + return(ulDevIndex); + } + + return(g_sUSBHCD.USBDevice[ulDevIndex].ulAddress); +} + +//***************************************************************************** +// +//! This function will return the USB class for the requested device +//! instance. +//! +//! \param ulInstance is a unique value indicating which device to query. +//! \param ulInterface 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 ulInstance parameter. The caller must use a value for +//! \e ulInstance have been passed to the application when it receives a +//! USB_EVENT_CONNECTED event. The function will return the USB class for +//! the interface number specified by the \e ulInterface parameter. If +//! \e ulInterface 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. +// +//***************************************************************************** +unsigned char +USBHCDDevClass(unsigned long ulInstance, unsigned long ulInterface) +{ + unsigned long ulDevIndex; + tInterfaceDescriptor *pInterface; + + ulDevIndex = HCDInstanceToDevIndex(ulInstance); + + // + // If the instance was not valid return an undefined class. + // + if(ulDevIndex == 0xff) + { + return(USB_CLASS_DEVICE); + } + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface( + g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor, + g_sUSBHCD.USBDevice[ulDevIndex].ulInterface, + ulInterface); + + // + // Make sure that the interface requested actually exists. + // + if(pInterface) + { + // + // Return the interface class. + // + return(pInterface->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 ulInstance is a unique value indicating which device to query. +//! \param ulInterface 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 ulInstance parameter. The caller must use a value for +//! \e ulInstance have been passed to the application when it receives a +//! USB_EVENT_CONNECTED event. The function will return the USB subclass for +//! the interface number specified by the \e ulInterface parameter. If +//! \e ulInterface 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. +// +//***************************************************************************** +unsigned char +USBHCDDevSubClass(unsigned long ulInstance, unsigned long ulInterface) +{ + unsigned long ulDevIndex; + tInterfaceDescriptor *pInterface; + + ulDevIndex = HCDInstanceToDevIndex(ulInstance); + + // + // If the instance was not valid return an undefined subclass. + // + if(ulDevIndex == 0xff) + { + return(USB_SUBCLASS_UNDEFINED); + } + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface( + g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor, + g_sUSBHCD.USBDevice[ulDevIndex].ulInterface, + ulInterface); + + // + // Make sure that the interface requested actually exists. + // + if(pInterface) + { + // + // Return the interface subclass. + // + return(pInterface->bInterfaceSubClass); + } + + // + // No valid interface so return an undefined subclass. + // + return(USB_SUBCLASS_UNDEFINED); +} + +//***************************************************************************** +// +//! This function will return the USB protocol for the requested device +//! instance. +//! +//! \param ulInstance is a unique value indicating which device to query. +//! \param ulInterface 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 ulInstance parameter. The caller must use a value for +//! \e ulInstance have been passed to the application when it receives a +//! USB_EVENT_CONNECTED event. The function will return the USB protocol for +//! the interface number specified by the \e ulInterface parameter. If +//! \e ulInterface 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. +// +//***************************************************************************** +unsigned char +USBHCDDevProtocol(unsigned long ulInstance, unsigned long ulInterface) +{ + unsigned long ulDevIndex; + tInterfaceDescriptor *pInterface; + + ulDevIndex = HCDInstanceToDevIndex(ulInstance); + + // + // If the instance was not valid return an undefined protocol. + // + if(ulDevIndex == 0xff) + { + return(USB_PROTOCOL_UNDEFINED); + } + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface( + g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor, + g_sUSBHCD.USBDevice[ulDevIndex].ulInterface, + ulInterface); + + // + // Make sure that the interface requested actually exists. + // + if(pInterface) + { + // + // Return the interface protocol. + // + return(pInterface->bInterfaceProtocol); + } + + // + // No valid interface so return an undefined protocol. + // + return(USB_PROTOCOL_UNDEFINED); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhostpriv.h b/usblib/host/usbhostpriv.h new file mode 100644 index 0000000..44c0a73 --- /dev/null +++ b/usblib/host/usbhostpriv.h @@ -0,0 +1,257 @@ +//***************************************************************************** +// +// usbhostpriv.h - Internal header file for USB host functions. +// +// Copyright (c) 2011-2012 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 9453 of the Stellaris 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. + // + PORT_IDLE, + + // + // The port has a device present and is waiting for the enumeration + // sequence to begin. + // + PORT_CONNECTED, + + // + // A device connection notification has been received and we have initiated + // a reset to the port. We are waiting for the reset to complete. + // + PORT_RESET_ACTIVE, + + // + // The Port reset has completed but now the hub is waiting the required + // 10ms before accessing the device. + // + PORT_RESET_WAIT, + + // + // 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. + // + PORT_ACTIVE, + + // + // A device has completed enumeration. + // + PORT_ENUMERATED, + + // + // A device is attached to the port but enumeration failed. + // + PORT_ERROR +} +tHubPortState; + +//***************************************************************************** +// +// The list of valid event flags in the g_sUSBHCD.ulEventEnables 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 structure holds all data specific to a single hub port. +// +//***************************************************************************** +typedef struct +{ + // + // A pointer to storage for the configuration descriptor of a device + // attached to this port. + // + unsigned char *pucConfigDesc; + + // + // The size of the storage pointed to by pucConfigDesc. + // + unsigned long ulConfigSize; + + // + // The handle used by the HCD layer to identify this device. + // + unsigned long ulDevHandle; + + // + // The current state of the port. + // + volatile tHubPortState sState; + + // + // General counter used in various states. + // + volatile unsigned long ulCount; + + // + // A flag used to indicate that the downstream device is a low speed + // device. + // + tBoolean bLowSpeed; + + // + // This flag is set if the hub reports that a change is pending on this + // port. + // + volatile tBoolean bChanged; +} +tHubPort; + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a Hub device. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the callback function pointer. + // + tUSBCallback pfnCallback; + + // + // Callback data provided by caller. + // + unsigned long ulCBData; + + // + // Interrupt IN pipe. + // + unsigned long ulIntInPipe; + + // + // Hub characteristics as reported in the class-specific hub descriptor. + // + unsigned short usHubCharacteristics; + + // + // The number of downstream-facing ports the hub supports. + // + unsigned char ucNumPorts; + + // + // 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. + // + unsigned char ucNumPortsInUse; + + // + // The size of a status change packet sent by the hub. This is determined + // from the number of ports supported by the hub. + // + unsigned char ucReportSize; + + // + // Flag indicating whether the hub is connected. + // + tBoolean bHubActive; + + // + // Flag indicating that a device is currently in process of being + // enumerated. + // + volatile tBoolean bEnumerationBusy; + + // + // This is valid if bEnumerationBusy is set and indicates the port + // that is in the process of enumeration. + // + unsigned char ucEnumIdx; + + // + // The state of each of the ports we support on the hub. + // + tHubPort psPorts[MAX_USB_DEVICES]; +} +tHubInstance; + +//***************************************************************************** +// +// Functions within the host controller that are called by the hub class driver +// +//***************************************************************************** +extern unsigned long USBHCDHubDeviceConnected(unsigned long ulIndex, + unsigned char ucHub, + unsigned char ucPort, + tBoolean bLowSpeed, + unsigned char *pucConfigPool, + unsigned long ulConfigSize); +extern void USBHCDHubDeviceDisconnected(unsigned long ulIndex, + unsigned long ulDevIndex); + +//***************************************************************************** +// +// Functions in the hub class driver that are called by the host controller. +// +//***************************************************************************** +extern void USBHHubMain(void); +extern void USBHHubInit(void); +extern void USBHHubEnumerationComplete(unsigned char ucHub, + unsigned char ucPort); +extern void USBHHubEnumerationError(unsigned char ucHub, unsigned char ucPort); + +//***************************************************************************** +// +// 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..9199514 --- /dev/null +++ b/usblib/host/usbhscsi.c @@ -0,0 +1,778 @@ +//***************************************************************************** +// +// usbhscsi.c - USB host SCSI layer used by the USB host MSC driver. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pSCSICmd is the SCSI command structure to send. +//! \param pucData is pointer to the command data to be sent. +//! \param pulSize 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 pucData is the buffer to store the data into and \e pulSize is used to +//! store the amount of data to request as well as used to indicate how many +//! bytes were filled into the \e pucData buffer on return. For commands that +//! are sending data, \e pucData is the data to be sent and \e pulSize 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 unsigned long +USBHSCSISendCommand(unsigned long ulInPipe, unsigned long ulOutPipe, + tMSCCBW *pSCSICmd, unsigned char *pucData, + unsigned long *pulSize) +{ + tMSCCSW CmdStatus; + unsigned long ulBytes; + + // + // Initialize the command status. + // + CmdStatus.dCSWSignature = 0; + CmdStatus.dCSWTag = 0; + CmdStatus.bCSWStatus = SCSI_CMD_STATUS_FAIL; + + // + // Set the CBW signature and tag. + // + pSCSICmd->dCBWSignature = CBW_SIGNATURE; + pSCSICmd->dCBWTag = CBW_TAG_VALUE; + + // + // Set the size of the data to be returned by the device. + // + pSCSICmd->dCBWDataTransferLength = *pulSize; + + // + // Send the command. + // + ulBytes = USBHCDPipeWrite(ulOutPipe, + (unsigned char*)pSCSICmd, sizeof(tMSCCBW)); + + // + // If no bytes went out then the command failed. + // + if(ulBytes == 0) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Only request data if there is data to request. + // + if(pSCSICmd->dCBWDataTransferLength != 0) + { + // + // See if this is a read or a write. + // + if(pSCSICmd->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Read the data back. + // + *pulSize = USBHCDPipeRead(ulInPipe, pucData, *pulSize); + } + else + { + // + // Write the data out. + // + *pulSize = USBHCDPipeWrite(ulOutPipe, pucData, *pulSize); + } + } + + // + // Get the status of the command. + // + ulBytes = USBHCDPipeRead(ulInPipe, (unsigned char *)&CmdStatus, + sizeof(tMSCCSW)); + + + // + // If the status was invalid or did not have the correct signature then + // indicate a failure. + // + if((ulBytes == 0) || (CmdStatus.dCSWSignature != CSW_SIGNATURE) || + (CmdStatus.dCSWTag != CBW_TAG_VALUE)) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Return the status. + // + return((unsigned long)CmdStatus.bCSWStatus); +} + +//***************************************************************************** +// +//! This will issue the SCSI inquiry command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize 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 ulInPipe and +//! \e ulOutPipe parameters indicate which USB pipes to use for this call. +//! +//! \note The \e pucData 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. +// +//***************************************************************************** +unsigned long +USBHSCSIInquiry(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.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. + // + *pulSize = SCSI_INQUIRY_DATA_SZ; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // This is the length of the command itself. + // + SCSICmd.bCBWCBLength = 6; + + // + // Send Inquiry command with no request for vital product data. + // + pulData[0] = SCSI_INQUIRY_CMD; + + // + // Allocation length. + // + pulData[1] = SCSI_INQUIRY_DATA_SZ; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacity command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize 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 ulInPipe and \e ulOutPipe parameters indicate which USB pipes to +//! use for this call. +//! +//! \note The \e pucData 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. +// +//***************************************************************************** +unsigned long +USBHSCSIReadCapacity(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // Set the size of the command data. + // + *pulSize = SCSI_READ_CAPACITY_SZ; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + SCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pulData[0] = SCSI_READ_CAPACITY; + pulData[1] = 0; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacities command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize 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 ulInPipe and \e ulOutPipe 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. +// +//***************************************************************************** +unsigned long +USBHSCSIReadCapacities(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + SCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pulData[0] = SCSI_READ_CAPACITIES; + pulData[1] = 0; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This will issue the SCSI Mode Sense(6) command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param ulFlags is a combination of flags defining the exact query that is +//! to be made. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize 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 ulInPipe +//! and \e ulOutPipe parameters indicate which USB pipes to use for this call. +//! The call will return at most the number of bytes in the \e pulSize +//! parameter, however it can return less and change the \e pulSize parameter +//! to the number of valid bytes in the \e *pulSize buffer. +//! +//! The \e ulFlags 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(ulInPipe, ulOutPipe, +//! SCSI_MS_PC_CURRENT | SCSI_MS_PC_ALL, +//! pucData, pulSize); +//! \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. +// +//***************************************************************************** +unsigned long +USBHSCSIModeSense6(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned long ulFlags, unsigned char *pucData, + unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 6; + + // + // Set the options for the Mode Sense Command (6). + // + pulData[0] = (SCSI_MODE_SENSE_6 | ulFlags); + pulData[1] = (unsigned char)*pulSize; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Test Unit Ready command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe 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. +// +//***************************************************************************** +unsigned long +USBHSCSITestUnitReady(unsigned long ulInPipe, unsigned long ulOutPipe) +{ + tMSCCBW SCSICmd; + unsigned long ulSize; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // No data in this command. + // + ulSize = 0; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 6; + + // + // Set the parameter options. + // + pulData[0] = SCSI_TEST_UNIT_READY; + pulData[1] = 0; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, 0, &ulSize)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Request Sense command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize 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 pucData. The +//! parameter \e pulSize should have the allocation size in bytes of the buffer +//! pointed to by pucData. +//! +//! \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. +// +//***************************************************************************** +unsigned long +USBHSCSIRequestSense(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 12; + + // + // Set the parameter options. + // + pulData[0] = SCSI_REQUEST_SENSE; + pulData[1] = 18; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Read(10) command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param ulLBA is the logical block address to read. +//! \param pucData is the data buffer to return the data. +//! \param pulSize is the size of the buffer on entry and number of bytes read +//! on exit. +//! \param ulNumBlocks 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 ulLBA 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 pucData. The parameter \e pulSize should indicate enough space to +//! hold a full block size, or only the first pulSize bytes of the LBA will +//! be 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. +// +//***************************************************************************** +unsigned long +USBHSCSIRead10(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned long ulLBA, unsigned char *pucData, + unsigned long *pulSize, unsigned long ulNumBlocks) +{ + tMSCCBW SCSICmd; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + SCSICmd.CBWCB[0] = SCSI_READ_10; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[1] = 0; + + // + // LBA starts at offset 2. + // + SCSICmd.CBWCB[2] = (unsigned char)(ulLBA >> 24); + SCSICmd.CBWCB[3] = (unsigned char)(ulLBA >> 16); + SCSICmd.CBWCB[4] = (unsigned char)(ulLBA >> 8); + SCSICmd.CBWCB[5] = (unsigned char)ulLBA; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[6] = 0; + + // + // Transfer length in blocks starts at offset 2. + // This also sets the Control value to 0 at offset 9. + // + SCSICmd.CBWCB[7] = (ulNumBlocks & 0xFF00) >> 8; + *((unsigned long *)&SCSICmd.CBWCB[8]) = (ulNumBlocks & 0xFF); + *((unsigned long *)&SCSICmd.CBWCB[12]) = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! 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 ulLBA 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 pucData parameter. The parameter \e pulSize should indicate the amount +//! of data to write to the specified LBA. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param ulLBA is the logical block address to read. +//! \param pucData is the data buffer to write out. +//! \param pulSize is the size of the buffer. +//! \param ulNumBlocks 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. +// +//***************************************************************************** +unsigned long +USBHSCSIWrite10(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned long ulLBA, unsigned char *pucData, + unsigned long *pulSize, unsigned long ulNumBlocks) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_OUT; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + SCSICmd.CBWCB[0] = SCSI_WRITE_10; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[1] = 0; + + // + // LBA starts at offset 2. + // + SCSICmd.CBWCB[2] = (unsigned char)(ulLBA >> 24); + SCSICmd.CBWCB[3] = (unsigned char)(ulLBA >> 16); + SCSICmd.CBWCB[4] = (unsigned char)(ulLBA >> 8); + SCSICmd.CBWCB[5] = (unsigned char)ulLBA; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[6] = 0; + + // + // Set the transfer length in blocks. + // This also sets the Control value to 0 at offset 9. + // + SCSICmd.CBWCB[7] = (ulNumBlocks & 0xFF00) >> 8; + + // + // The blocks go into is byte offset 8 or word address 2. + // + pulData[2] = (ulNumBlocks & 0xFF); + + // + // The blocks go into is byte offset 12 or word address 3. + // + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhscsi.h b/usblib/host/usbhscsi.h new file mode 100644 index 0000000..21897eb --- /dev/null +++ b/usblib/host/usbhscsi.h @@ -0,0 +1,102 @@ +//***************************************************************************** +// +// usbhscsi.h - Definitions for the USB host SCSI layer. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 unsigned long USBHSCSIInquiry(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pucBuffer, + unsigned long *pulSize); +extern unsigned long USBHSCSIReadCapacity(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pData, + unsigned long *pulSize); +extern unsigned long USBHSCSIReadCapacities(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pData, + unsigned long *pulSize); +extern unsigned long USBHSCSIModeSense6(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned long ulFlags, + unsigned char *pData, + unsigned long *pulSize); +extern unsigned long USBHSCSITestUnitReady(unsigned long ulInPipe, + unsigned long ulOutPipe); +extern unsigned long USBHSCSIRequestSense(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pucData, + unsigned long *pulSize); +extern unsigned long USBHSCSIRead10(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned long ulLBA, + unsigned char *pucData, + unsigned long *pulSize, + unsigned long ulNumBlocks); +extern unsigned long USBHSCSIWrite10(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned long ulLBA, + unsigned char *pucData, + unsigned long *pulSize, + unsigned long ulNumBlocks); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..d6ea1da --- /dev/null +++ b/usblib/readme.txt @@ -0,0 +1,21 @@ +This project will build the Stellaris USB Library. + +------------------------------------------------------------------------------- + +Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. diff --git a/usblib/rvmdk-cm3/usblib-cm3.lib b/usblib/rvmdk-cm3/usblib-cm3.lib new file mode 100644 index 0000000..9e7a259 Binary files /dev/null and b/usblib/rvmdk-cm3/usblib-cm3.lib differ diff --git a/usblib/rvmdk-cm4f/usblib-cm4f.lib b/usblib/rvmdk-cm4f/usblib-cm4f.lib new file mode 100644 index 0000000..c2aa670 Binary files /dev/null and b/usblib/rvmdk-cm4f/usblib-cm4f.lib differ diff --git a/usblib/usb-ids.h b/usblib/usb-ids.h new file mode 100644 index 0000000..e0eb0a9 --- /dev/null +++ b/usblib/usb-ids.h @@ -0,0 +1,63 @@ +//***************************************************************************** +// +// usb-ids.h - Definitions of VIDs and PIDs used by Stellaris USB examples. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBIDS_H__ +#define __USBIDS_H__ + +//***************************************************************************** +// +// TI Stellaris Vendor ID. +// +//***************************************************************************** +#define USB_VID_STELLARIS 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_DFU 0x00FF + +#ifndef DEPRECATED +//***************************************************************************** +// +// Deprecated definitions. +// +//***************************************************************************** +#define USB_VID_LUMINARY USB_VID_STELLARIS +#endif + +#endif /* __USBIDS_H__ */ diff --git a/usblib/usbaudio.h b/usblib/usbaudio.h new file mode 100644 index 0000000..83b1e21 --- /dev/null +++ b/usblib/usbaudio.h @@ -0,0 +1,717 @@ +//***************************************************************************** +// +// usbaudio.h - Definitions used by Audio Class devices. +// +// Copyright (c) 2009-2012 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 9453 of the Stellaris 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_DSUBTYPE_HEADER for the header. + // + unsigned char bDescriptorSubtype; + + // + //! Audio Device Class Specification Release Number in Binary-Coded + //! Decimal. + // + unsigned short 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. + // + unsigned short wTotalLength; + + // + //! The number of AudioStreaming and MIDIStreaming interfaces in the Audio + //! Interface Collection to which this AudioControl interface belongs. + // + unsigned char bInCollection; + + // + //! Interface number of the first AudioStreaming or MIDIStreaming interface + //! in the Collection. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_DSUBTYPE_IN_TERM for the header. + // + unsigned char bDescriptorSubtype; + + // + //! Constant uniquely identifying the Unit within the audio function. This + //! value is used in all requests to address this Unit. + // + unsigned char bUnitID; + + // + //! ID of the Unit or Terminal to which this Feature Unit is connected. + // + unsigned char bSourceID; + + // + //! ID of the Output Terminal to which this Input Terminal is associated. + // + unsigned char 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. + // + unsigned short 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_DSUBTYPE_OUT_TERM for the header. + // + unsigned char bDescriptorSubtype; + + // + //! Constant uniquely identifying the Terminal within the audio function. + //! This value is used in all requests to address this Terminal. + // + unsigned char bTerminalID; + + // + //! Constant characterizing the type of Terminal. See USB Audio Terminal + //! Types. + // + unsigned short wTerminalType; + + // + //! Constant, identifying the Input Terminal to which this Output Terminal + //! is associated. + // + unsigned char bAssocTerminal; + + // + //! ID of the Unit or Terminal to which this Terminal is connected. + // + unsigned char bSourceID; + + // + //! Index of a string descriptor, describing the Output Terminal. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_DSUBTYPE_OUT_TERM for the header. + // + unsigned char bDescriptorSubtype; + + // + //! Constant uniquely identifying the Terminal within the audio function. + //! This value is used in all requests to address this Terminal. + // + unsigned char bTerminalID; + + // + //! Constant characterizing the type of Terminal. See USB Audio Terminal + //! Types. + // + unsigned short wTerminalType; + + // + //! Constant, identifying the Input Terminal to which this Output Terminal + //! is associated. + // + unsigned char bAssocTerminal; + + // + //! Number of logical output channels in the Terminal's output audio + //! channel cluster. + // + unsigned char bNrChannels; + + // + //! Describes the spatial location of the logical channels. + // + unsigned short wChannelConfig; + + // + //! Index of a string descriptor, describing the name of the first logical + //! channel. + // + unsigned char iChannelNames; + + // + //! Index of a string descriptor, describing the Output Terminal. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_AI_MIXER_UNIT for the header. + // + unsigned char bDescriptorSubtype; + + // + //! Constant uniquely identifying the Unit within the audio function. This + //! value is used in all requests to address this Unit. + // + unsigned char bUnitID; + + // + //! Number of Input Pins of this Unit. + // + unsigned char bNrInPins; + + // + //! ID of the Unit or Terminal to which the first Input Pin of this Mixer Unit is + //! connected. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_AI_MIXER_UNIT for the header. + // + unsigned char bDescriptorSubtype; + + // + //! Constant uniquely identifying the Unit within the audio function. This + //! value is used in all requests to address this Unit. + // + unsigned char bUnitID; + + // + //! Number of Input Pins of this Unit. + // + unsigned char bNrInPins; + + // + //! ID of the Unit or Terminal to which the first Input Pin of this Mixer Unit is + //! connected. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_DSUBTYPE_GENERAL for the header. + // + unsigned char bDescriptorSubtype; + + // + //! The Terminal ID of the Terminal to which the endpoint of this + //! interface is connected. + // + unsigned char bTerminalLink; + + // + //! Delay introduced by the data path. Expressed in number of frames. + // + unsigned char bDelay; + + // + //! The Audio Data Format that has to be used to communicate with this + //! interface. + // + unsigned short 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_CS_INTERFACE (36). + // + unsigned char bDescriptorType; + + // + //! This will be USB_AS_FORMAT_TYPE. + // + unsigned char bDescriptorSubtype; + + // + //! This will be USB_AS_FORMAT_TYPE_I. + // + unsigned char bFormatType; + + // + //! Number of channels on this streaming interface. + // + unsigned char bNrChannels; + + // + //! Number of bytes per audio sub-frame or channel. + // + unsigned char bSubFrameSize; + + // + //! Number of bits per sample. + // + unsigned char bBitResolution; + + // + //! Number of sample rates that are supported. + // + unsigned char bSamFreqType; + + // + //! Number of bits per sample. + // + unsigned char 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..a2617fc --- /dev/null +++ b/usblib/usbbuffer.c @@ -0,0 +1,1184 @@ +//***************************************************************************** +// +// usbbuffer.c - USB buffer object. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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; + unsigned long ulLastSent; + unsigned long ulFlags; +} +tUSBBufferVars; + +//***************************************************************************** +// +// Flags which may be set in the tUSBBufferVars ulFlags 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 *psVars; + unsigned long ulPacket, ulSpace, ulTotal, ulSent; + + // + // Get a pointer to our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Ask the lower layer if it has space to accept another packet of data. + // + ulPacket = psBuffer->pfnAvailable(psBuffer->pvHandle); + + // + // If we were returned something other than zero, we can write that number + // of bytes to the lower layer. + // + if(ulPacket) + { + // + // How much contiguous data do we have in the buffer? + // + ulSpace = USBRingBufContigUsed(&psVars->sRingBuf); + + // + // How much total data do we have in the buffer? + // + ulTotal = USBRingBufUsed(&psVars->sRingBuf); + + // + // How much data will we be sending as a result of this call? + // + ulSent = (ulPacket < ulTotal) ? ulPacket : ulTotal; + + // + // Write the contiguous bytes to the lower layer assuming there is + // something to send. + // + if(ulSpace) + { + // + // There is data available to send. Update our state to indicate + // the amount we will be sending in this packet. + // + psVars->ulLastSent = ulSent; + + // + // Determine the maximum sized block we can send in this transfer. + // + ulSpace = (ulSpace < ulPacket) ? ulSpace : ulPacket; + // + // 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, + (psVars->sRingBuf.pucBuf + + psVars->sRingBuf.ulReadIndex), ulSpace, + (((ulSpace < ulPacket) && + (ulSpace < ulTotal)) ? 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((ulSpace < ulPacket) && (ulSpace < ulTotal)) + { + // + // The packet straddled the wrap. How much space remains in + // the packet? + // + ulPacket -= ulSpace; + + // + // How much data can we actually send? + // + ulSpace = ulTotal - ulSpace; + ulSpace = (ulSpace > ulPacket) ? ulPacket : ulSpace; + + psBuffer->pfnTransfer(psBuffer->pvHandle, + psVars->sRingBuf.pucBuf, ulSpace, true); + } + } + else + { + // + // There is no data to send. Did we last send a full packet? + // + if(psVars->ulLastSent == ulPacket) + { + // + // Yes - if necessary, send a zero-length packet back to the + // host to complete the last transaction. + // + if(psVars->ulFlags & USB_BUFFER_FLAG_SEND_ZLP) + { + psVars->ulLastSent = 0; + psBuffer->pfnTransfer(psBuffer->pvHandle, + psVars->sRingBuf.pucBuf, 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 ulSize is the size reported in the event. +// \param pucData 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 unsigned long +HandleRxAvailable(tUSBBuffer *psBuffer, unsigned long ulSize, + unsigned char *pucData) +{ + tUSBBufferVars *psVars; + unsigned long ulAvail, ulRead, ulPacket, ulRetCount; + + // + // Get a pointer to our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Has the data already been read into memory? + // + if(pucData) + { + // + // Yes - is it already in our ring buffer? + // + if((pucData >= psBuffer->pcBuffer) && + (pucData < psBuffer->pcBuffer + psBuffer->ulBufferSize)) + { + // + // The data is already in our ring buffer so merely update the + // write pointer to add the new data. + // + USBRingBufAdvanceWrite(&psVars->sRingBuf, ulSize); + + // + // In this case, we pass back 0 to indicate that the lower layer + // doesn't need to make any buffer pointer updates. + // + ulRetCount = 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? + // + ulAvail = USBRingBufFree(&psVars->sRingBuf); + + // + // How much should we copy? + // + ulRead = (ulAvail < ulSize) ? ulAvail : ulSize; + + // + // Copy the data into the buffer. + // + USBRingBufWrite(&psVars->sRingBuf, pucData, ulRead); + + // + // 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. + // + ulRetCount = ulRead; + } + } + 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? + // + ulPacket = psBuffer->pfnAvailable(psBuffer->pvHandle); + + // + // How much contiguous space do we have in the buffer? + // + ulAvail = USBRingBufContigFree(&psVars->sRingBuf); + + // + // Get as much of the packet as we can in the available space. + // + ulRead = psBuffer->pfnTransfer(psBuffer->pvHandle, + (psVars->sRingBuf.pucBuf + + psVars->sRingBuf.ulWriteIndex), + ulAvail, true); + + // + // Advance the ring buffer write pointer to add our new data. + // + if(ulRead) + { + USBRingBufAdvanceWrite(&psVars->sRingBuf, ulRead); + } + + // + // Did we get the whole packet? + // + if(ulRead < ulPacket) + { + // + // No - how much space do we have in the buffer? + // + ulAvail = USBRingBufContigFree(&psVars->sRingBuf); + + // + // If there is any space left, read as much of the remainder of + // the packet as we can. + // + if(ulAvail) + { + ulPacket = + psBuffer->pfnTransfer(psBuffer->pvHandle, + (psVars->sRingBuf.pucBuf + + psVars->sRingBuf.ulWriteIndex), + ulAvail, true); + + // + // Update the write pointer after we read more data into the + // buffer. + // + if(ulPacket) + { + USBRingBufAdvanceWrite(&psVars->sRingBuf, ulPacket); + } + } + } + + // + // 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. + // + ulRetCount = 0; + } + + // + // How much data do we have in the buffer? + // + ulAvail = USBRingBufUsed(&psVars->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. + // + ulRead = psBuffer->pfnCallback(psBuffer->pvCBData, + USB_EVENT_RX_AVAILABLE, + ulAvail, + (psVars->sRingBuf.pucBuf + + psVars->sRingBuf.ulReadIndex)); + + // + // If the client read anything from the buffer, update the read pointer. + // + USBRingBufAdvanceRead(&psVars->sRingBuf, ulRead); + + // + // Return the correct value to the low level driver. + // + return(ulRetCount); +} + +//***************************************************************************** +// +// 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 unsigned long +HandleDataRemaining(tUSBBuffer *psBuffer) +{ + unsigned long ulBufData, ulClientData; + tUSBBufferVars *psVars; + + // + // Get a pointer to our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // How much data does the client currently have buffered? + // + ulClientData = psBuffer->pfnCallback(psBuffer->pvCBData, + USB_EVENT_DATA_REMAINING, 0, + (void *)0); + + // + // How much data do we have in the buffer? + // + ulBufData = USBRingBufUsed(&psVars->sRingBuf); + + // + // Return the total number of bytes of unprocessed data to the lower layer. + // + return(ulBufData + ulClientData); +} + +//***************************************************************************** +// +// Handles USB_EVENT_TX_COMPLETE for a transmit buffer. +// +// \param psBuffer points to the buffer which is receiving the event. +// \param ulSize 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 unsigned long +HandleTxComplete(tUSBBuffer *psBuffer, unsigned long ulSize) +{ + tUSBBufferVars *psVars; + + // + // Get a pointer to our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Update the transmit buffer read pointer to remove the data that has + // now been transmitted. + // + USBRingBufAdvanceRead(&psVars->sRingBuf, ulSize); + + // + // 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 ulSize is the size of the buffer requested. +// \param ppucBuffer 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 ulSize +// 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 ulSize 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 unsigned long +HandleRequestBuffer(tUSBBuffer *psBuffer, unsigned long ulSize, + unsigned char **ppucBuffer) +{ + tUSBBufferVars *psVars; + unsigned long ulSpace; + + // + // Get a pointer to our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // How much contiguous space do we have available? + // + ulSpace = USBRingBufContigFree(&psVars->sRingBuf); + + // + // Is there enough space available to satisfy the request? + // + if(ulSpace >= ulSize) + { + // + // Yes - return the current write pointer + // + *ppucBuffer = psVars->sRingBuf.pucBuf + psVars->sRingBuf.ulWriteIndex; + return(ulSize); + } + else + { + // + // We do not have enough contiguous space following the current write + // pointer to satisfy the request so do not provide a buffer. + // + *ppucBuffer = (unsigned char *)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 *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer && psBuffer->pvWorkspace && psBuffer->pcBuffer && + psBuffer->ulBufferSize && psBuffer->pfnAvailable && + psBuffer->pfnTransfer && psBuffer->pfnCallback); + + // + // Get a pointer to the buffer workspace and initialize the variables it + // contains. + // + psVars = psBuffer->pvWorkspace; + psVars->ulFlags = 0; + USBRingBufInit(&psVars->sRingBuf, psBuffer->pcBuffer, + psBuffer->ulBufferSize); + + // + // 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, tBoolean bSendZLP) +{ + tUSBBufferVars *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psVars = 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. + // + psVars->ulFlags |= USB_BUFFER_FLAG_SEND_ZLP; + } + else + { + // + // Disable ZLP transmission. + // + psVars->ulFlags &= ~ 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 *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer && psRingBuf); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Copy the current ring buffer settings to the clients storage. + // + *psRingBuf = psVars->sRingBuf; +} + +//***************************************************************************** +// +//! 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 ulLength 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, unsigned long ulLength) +{ + tUSBBufferVars *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Advance the ring buffer write pointer to include the newly written + // data. + // + if(ulLength) + { + USBRingBufAdvanceWrite(&psVars->sRingBuf, ulLength); + } + + // + // 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 ulLength 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 indices. 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, unsigned long ulLength) +{ + tUSBBufferVars *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Advance the ring buffer write pointer to include the newly written + // data. + // + if(ulLength) + { + USBRingBufAdvanceRead(&psVars->sRingBuf, ulLength); + } +} + +//***************************************************************************** +// +//! 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 pucData points to the first byte of data which is to be written. +//! \param ulLength 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. +// +//***************************************************************************** +unsigned long +USBBufferWrite(const tUSBBuffer *psBuffer, const unsigned char *pucData, + unsigned long ulLength) +{ + unsigned long ulSpace; + tUSBBufferVars *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer && pucData); + ASSERT(psBuffer->bTransmitBuffer == true); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // How much space is left in the buffer? + // + ulSpace = USBRingBufFree(&psVars->sRingBuf); + + // + // How many bytes will we write? + // + ulLength = (ulLength > ulSpace) ? ulSpace : ulLength; + + // + // Write the data to the buffer. + // + if(ulLength) + { + USBRingBufWrite(&psVars->sRingBuf, pucData, ulLength); + } + + // + // Try to transmit the next packet to the host. + // + ScheduleNextTransmission(psBuffer); + + // + // Tell the caller how many bytes we wrote to the buffer. + // + return(ulLength); +} + +//***************************************************************************** +// +//! 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 *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Flush the ring buffer. + // + USBRingBufFlush(&psVars->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 pucData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pucData. +//! +//! This function reads up to ulLength bytes of data received from the USB +//! host into the supplied application buffer. If the receive buffer +//! contains fewer than \e ulLength 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 pucData. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +unsigned long +USBBufferRead(const tUSBBuffer *psBuffer, unsigned char *pucData, + unsigned long ulLength) +{ + tUSBBufferVars *psVars; + unsigned long ulAvail, ulRead; + + // + // Check parameter validity. + // + ASSERT(psBuffer && pucData && ulLength); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // How much data is in the buffer? + // + ulAvail = USBRingBufUsed(&psVars->sRingBuf); + + // + // Determine how many bytes we can actually read. + // + ulRead = (ulAvail < ulLength) ? ulAvail : ulLength; + + // + // Read the data from the buffer assuming there is some to read. + // + if(ulRead) + { + USBRingBufRead(&psVars->sRingBuf, pucData, ulRead); + } + + // + // Tell the caller how many bytes we wrote to their buffer. + // + return(ulRead); +} + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +unsigned long +USBBufferDataAvailable(const tUSBBuffer *psBuffer) +{ + tUSBBufferVars *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Return the amount of data in the buffer. + // + return(USBRingBufUsed(&psVars->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. +// +//***************************************************************************** +unsigned long +USBBufferSpaceAvailable(const tUSBBuffer *psBuffer) +{ + tUSBBufferVars *psVars; + + // + // Check parameter validity. + // + ASSERT(psBuffer); + + // + // Get our workspace variables. + // + psVars = psBuffer->pvWorkspace; + + // + // Return the amount of space available in the buffer. + // + return(USBRingBufFree(&psVars->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 ulEvent is the identifier of the event being sent. This will be +//! a general event identifier of the form USBD_EVENT_xxxx or a device +//! class-dependent event of the form USBD_CDC_EVENT_xxx or +//! USBD_HID_EVENT_xxx. +//! \param ulMsgValue 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. +// +//***************************************************************************** +unsigned long +USBBufferEventCallback(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgValue, 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(ulEvent) + { + // + // 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, ulMsgValue, 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, ulMsgValue); + } + 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, ulMsgValue, 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, ulEvent, ulMsgValue, + pvMsgData)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbcdc.h b/usblib/usbcdc.h new file mode 100644 index 0000000..b96fff7 --- /dev/null +++ b/usblib/usbcdc.h @@ -0,0 +1,789 @@ +//***************************************************************************** +// +// usbhid.h - Definitions used by Communication Device Class devices. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris 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 a byte, word or long from a character pointer. +// +//***************************************************************************** +#define BYTE(pucData) (*(unsigned char *)(pucData)) +#define SHORT(pucData) (*(unsigned short *)(pucData)) +#define LONG(pucData) (*(unsigned long *)(pucData)) + +//***************************************************************************** +// +// 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 +// +//***************************************************************************** +// 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 +// 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 0x00000008 +#define USB_CDC_ETHERNET_XMIT_LATE_COLLISIONS 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.ucRequest) +// +//***************************************************************************** +#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 unsigned 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(pcBuf, usData) \ + do \ + { \ + (*(unsigned short *)(pcBuf)) = (usData); \ + } \ + 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(pcBuf, ucRate, ucStop, ucParity, ucDatabits) \ + do \ + { \ + (*(unsigned long *)(pcBuf)) = (ucRate); \ + (*((unsigned char *)(pcBuf) + 4)) = (ucStop); \ + (*((unsigned char *)(pcBuf) + 5)) = (ucParity); \ + (*((unsigned char *)(pcBuf) + 6)) = (ucDatabits); \ + } \ + 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 (ulRingerBmp) +// +//***************************************************************************** +#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(pcBuf, ucPattern, ucVolume, ulExists) \ + do \ + { \ + *(unsigned long *)(pcBuf) = ((ucPattern) + \ + ((ucVolume & 0xFF) << 8) + \ + (ulExists & USB_CDC_RINGER_EXISTS)); \ + } \ + while(0) + +//***************************************************************************** +// +// Macros to extract fields from the USB_CDC_SET_RINGER_PARMS data +// +//***************************************************************************** +#define GetRingerVolume(pcData) (BYTE((pcData)+1)) +#define GetRingerPattern(pcData) (BYTE(pcData)) +#define GetRingerExists(pcData) ((LONG(pcData)) & 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(pBbuf, usData) \ + do \ + { \ + WORD(pcBuf) = (usData); \ + } \ + 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(pcData) (BYTE(pcData)) + +//***************************************************************************** +// +// Macro to populate the CallState entries in response to request +// USB_CDC_GET_LINE_PARMS. The ucIndex parameter is a zero based index +// indicating which call entry in the pcBuf response buffer to fill in. Note +// that pcBuf points to the first byte of the buffer (the wLength field). +// +//***************************************************************************** +#define SetResponseCallState(pcBuf, ucIndex, ulActive, ucStateChange, \ + ucState) \ + do \ + { \ + (LONG((unsigned char *)(pcBuf) + (10 + (4 * (ucIndex))))) = \ + (((ulActive) & USB_CDC_CALL_IS_ACTIVE) + \ + (((ucStateChange) & 0xFF) << 8) + \ + ((ucState) & 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(pcBuf, usLength, \ + ucRingPattern, ucRingVolume, ulRingExists, \ + ulLineActive, ucLineCallIndex, \ + ulCallActive, ucCallStateChange, ucCallState) \ + do \ + { \ + (WORD(pcBuf)) = (usLength); \ + SetResponseRingerParams(((unsigned char *)(pcBuf) + 2), \ + (ucRingPattern), (ucRingVolume), \ + (ulRingExists)); \ + (LONG((unsigned char *)(pcBuf) + 6)) = \ + (((ulLineActive) & USB_CDC_LINE_IS_ACTIVE) + \ + ((ucLineCallIndex) & 0xFF)) ; \ + SetResponseCallState((pcBuf), 0, (ulCallActive), \ + (ucCallStateChange), (ucCallState)); \ + } \ + 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(psResult, ucCallState, ucIndex) \ + do \ + { \ + (WORD(psResult)) = (((ucCallState) & 0xFF) + \ + (((ucIndex) & 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(pcBuf, ulUSBitRate, ulDSBitRate) \ + do \ + { \ + LONG(pcBuf) = ulUSBitRate; \ + LONG((unsigned char *)(pcBuf) + 4) = ulDSBitRate; \ + } \ + 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. + // + unsigned long ulRate; + + // + //! 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 + // + unsigned char ucStop; + + // + //! 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. + // + unsigned char ucParity; + + // + //! The number of data bits per character. Valid values are 5, 6, 7 and 8 + //! in this implementation. + // + unsigned char ucDatabits; +} +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..4e0d8d0 --- /dev/null +++ b/usblib/usbdesc.c @@ -0,0 +1,478 @@ +//***************************************************************************** +// +// usbdesc.c - USB descriptor parsing functions. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 ulSize is the number of bytes of descriptor data found at pointer +//! \e psDesc. +//! \param ulType 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. +// +//***************************************************************************** +unsigned long +USBDescGetNum(tDescriptorHeader *psDesc, unsigned long ulSize, + unsigned long ulType) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulTotLength; + unsigned long ulCount; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = psDesc; + ulTotLength = 0; + ulCount = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ulTotLength < ulSize) + { + // + // Does this descriptor match the type passed (if a specific type + // has been specified)? + // + if((ulType == USB_DESC_ANY) || + (psDescCheck->bDescriptorType == (unsigned char)(ulType & 0xFF))) + { + ulCount++; + } + + // + // Move on to the next descriptor. + // + ulTotLength += (unsigned long)psDescCheck->bLength; + psDescCheck = NEXT_USB_DESCRIPTOR(psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ulCount); +} + +//***************************************************************************** +// +//! 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 ulSize is the number of bytes of descriptor data found at pointer +//! \e psDesc. +//! \param ulType 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 ulIndex is the zero based index of the descriptor whose pointer is +//! to be returned. For example, passing value 1 in \e ulIndex returns the +//! second matching descriptor. +//! +//! Return a pointer to the n-th descriptor of a particular type found in the +//! block of \e ulSize 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, unsigned long ulSize, + unsigned long ulType, unsigned long ulIndex) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulTotLength; + unsigned long ulCount; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = psDesc; + ulTotLength = 0; + ulCount = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ulTotLength < ulSize) + { + // + // Does this descriptor match the type passed (if a specific type + // has been specified)? + // + if((ulType == USB_DESC_ANY) || + (psDescCheck->bDescriptorType == (unsigned char)(ulType & 0xFF))) + { + // + // We found a matching descriptor. If our count matches the + // supplied index, we are done so return the pointer. + // + if(ulCount == ulIndex) + { + return(psDescCheck); + } + + // + // We have not found enough descriptors yet to satisfy the supplied + // index so increment our count and continue. + // + ulCount++; + } + + // + // Move on to the next descriptor. + // + ulTotLength += (unsigned long)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 ucInterfaceNumber 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. +// +//***************************************************************************** +unsigned long +USBDescGetNumAlternateInterfaces(tConfigDescriptor *psConfig, + unsigned char ucInterfaceNumber) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulTotLength; + unsigned long ulCount; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig; + ulTotLength = 0; + ulCount = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ulTotLength < (unsigned long)psConfig->wTotalLength) + { + // + // Is this an interface descriptor with the required interface number? + // + if((psDescCheck->bDescriptorType == USB_DTYPE_INTERFACE) && + (((tInterfaceDescriptor *)psDescCheck)->bInterfaceNumber == + ucInterfaceNumber)) + { + // + // Yes - increment our count. + // + ulCount++; + } + + // + // Move on to the next descriptor. + // + ulTotLength += (unsigned long)psDescCheck->bLength; + psDescCheck = NEXT_USB_DESCRIPTOR(psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ulCount); +} + +//***************************************************************************** +// +//! 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 ucInterfaceNumber is the interface number of the descriptor that is +//! being queried. +//! \param ulIndex 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, + unsigned char ucInterfaceNumber, + unsigned long ulIndex) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulTotLength; + unsigned long ulCount; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig; + ulTotLength = 0; + ulCount = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(ulTotLength < (unsigned long)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 == + ucInterfaceNumber)) + { + // + // This is an interface descriptor for interface ucInterfaceNumber. + // Determine if this is the n-th one we have found and, if so, + // return its pointer. + // + if(ulCount == ulIndex) + { + // + // 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. + // + ulCount++; + } + + // + // Move on to the next descriptor. + // + ulTotLength += (unsigned long)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 ulIndex is the zero based index of the interface that is to be +//! found. If ulAlt is set to a value other than \b USB_DESC_ANY, this will be +//! equivalent to the interface number being searched for. +//! \param ulAlt 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 ulAlt 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 ulAlt +//! if ulIndex is set to the required interface number and ulAlt 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, unsigned long ulIndex, + unsigned long ulAlt) +{ + // + // If we are being told to ignore the alternate configuration, this boils + // down to a very simple query. + // + if(ulAlt == USB_DESC_ANY) + { + // + // Return the ulIndex-th interface descriptor we find in the + // configuration descriptor. + // + return((tInterfaceDescriptor *)USBDescGet( + (tDescriptorHeader *)psConfig, + (unsigned long)psConfig->wTotalLength, + USB_DTYPE_INTERFACE, ulIndex)); + } + else + { + // + // In this case, a specific alternate setting number is required. + // Given that interface numbers are zero based indices, we can + // pass the supplied ulIndex parameter directly as the interface + // number to USBDescGetAlternateInterface to retrieve the requested + // interface descriptor pointer. + // + return(USBDescGetAlternateInterface(psConfig, ulIndex, ulAlt)); + } +} + +//***************************************************************************** +// +//! 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 ulIndex is the zero based index of the endpoint that is to be +//! found. +//! \param ulSize 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 ulIndex parameter is invalid (greater +//! than or equal to the bNumEndpoints field of the interface descriptor) or +//! the endpoint cannot be found within \e ulSize 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 ulSize to +//! limit the search range. +//! +//! \return Returns a pointer to the requested endpoint descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tEndpointDescriptor * +USBDescGetInterfaceEndpoint(tInterfaceDescriptor *psInterface, + unsigned long ulIndex, unsigned long ulSize) +{ + // + // Is the index passed valid? + // + if(ulIndex >= 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, + ulSize, USB_DTYPE_ENDPOINT, ulIndex)); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbdfu.h b/usblib/usbdfu.h new file mode 100644 index 0000000..9581ea5 --- /dev/null +++ b/usblib/usbdfu.h @@ -0,0 +1,430 @@ +//***************************************************************************** +// +// usbdfu.h - Definitions related to the USB Device Firmware Upgrade class. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 +{ + STATE_APP_IDLE = 0, + STATE_APP_DETACH, + STATE_IDLE, + STATE_DNLOAD_SYNC, + STATE_DNBUSY, + STATE_DNLOAD_IDLE, + STATE_MANIFEST_SYNC, + STATE_MANIFEST, + STATE_MANIFEST_WAIT_RESET, + STATE_UPLOAD_IDLE, + STATE_ERROR +} +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 +{ + STATUS_OK = 0, + STATUS_ERR_TARGET, + STATUS_ERR_FILE, + STATUS_ERR_WRITE, + STATUS_ERR_ERASE, + STATUS_ERR_CHECK_ERASED, + STATUS_ERR_PROG, + STATUS_ERR_VERIFY, + STATUS_ERR_ADDRESS, + STATUS_ERR_NOTDONE, + STATUS_ERR_FIRMWARE, + STATUS_ERR_VENDOR, + STATUS_ERR_USBR, + STATUS_ERR_POR, + STATUS_ERR_UNKNOWN, + STATUS_ERR_STALLEDPKT +} +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 + +//***************************************************************************** +// +// Stellaris-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 STATE_IDLE. +// +// An IN request containing the following parameters will result in the device +// sending back a tDFUQueryStellarisProtocol structure indicating that Stellaris +// 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 choses to use request ID 0x42 for some other purpose. +// +// wValue - 0x23(REQUEST_STELLARIS_VALUE) +// wIndex - Interface number +// wLength - sizeof(tDFUQueryStellarisProtocol) +// +//***************************************************************************** +#define USBD_DFU_REQUEST_STELLARIS 0x42 + +#define REQUEST_STELLARIS_VALUE 0x23 + +#define LM_DFU_PROTOCOL_MARKER 0x4C4D +#define LM_DFU_PROTOCOL_VERSION_1 0x0001 + +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// The structure sent to the host when a valid USBD_DFU_REQUEST_STELLARIS is +// received while the DFU device is in idle state. +// +//***************************************************************************** +typedef struct +{ + unsigned short usMarker; // LM_DFU_PROTOCOL_MARKER + unsigned short usVersion; // LM_DFU_PROTOCOL_VERSION_1 +} +PACKED tDFUQueryStellarisProtocol; + +//***************************************************************************** +// +// Structure sent to the host in response to USBD_DFU_REQUEST_GETSTATUS. +// +//***************************************************************************** +typedef struct +{ + unsigned char bStatus; + unsigned char bwPollTimeout[3]; + unsigned char bState; + unsigned char 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 +{ + unsigned char ucCommand; // Command identifier. + unsigned char ucData[7]; // Command-specific data elements. +} +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 usLength 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 +{ + unsigned char ucCommand; // DFU_CMD_PROG + unsigned char ucReserved; // Reserved - set to 0x00. + unsigned short usStartAddr; // Block start address / 1024 + unsigned long ulLength; // Total length, in bytes, of following data + // for the complete download operation. +} +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 ucCommand DFU_CMD_READ, usStartAddr set to the +// 1KB block start address and ulLength 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 usStartAddr and ulLength 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 ulLength passed must be a +// a multiple of 4. If this is not the case, the value will be truncated before +// the check is performed. +// +//***************************************************************************** +typedef struct +{ + unsigned char ucCommand; // DFU_CMD_READ or DFU_CMD_CHECK + unsigned char ucReserved; // Reserved - write to 0 + unsigned short usStartAddr; // Block start address / 1024 + unsigned long ulLength; // The number of bytes of data to read back or + // check. +} +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 usStartAddr with usNumBlocks +// 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 +{ + unsigned char ucCommand; // DFU_CMD_ERASE + unsigned char ucReserved; // Reserved - set to 0. + unsigned short usStartAddr; // Block start address / 1024 + unsigned short usNumBlocks; // The number of blocks to erase. + unsigned char ucReserved2[2]; // Reserved - set to 0. +} +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 +{ + unsigned char ucCommand; // DFU_CMD_INFO + unsigned char ucReserved[7]; // Reserved - set to 0. +} +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 Stellaris-specific commands such +// as DFU_CMD_INFO. +// +//***************************************************************************** +typedef struct +{ + unsigned char ucCommand; // DFU_CMD_BIN + unsigned char bBinary; // Set to true to omit image header or false + // to include it (the default). + unsigned char ucReserved[6]; // Reserved - set to 0. +} +PACKED tDFUDownloadBinHeader; + +//***************************************************************************** +// +// The DFU_CMD_RESET command uses a tDFUDownloadHeader structure since +// only the ucCommand 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. + // + unsigned short usFlashBlockSize; + + // + //! The number of blocks of flash in the device. Total flash size is + //! usNumFlashBlocks * usFlashBlockSize. + // + unsigned short usNumFlashBlocks; + + // + //! Information on the part number, family, version and package as + //! read from SYSCTL register DID1. + // + unsigned long ulPartInfo; + + // + //! Information on the part class and revision as read from SYSCTL DID0. + // + unsigned long ulClassInfo; + + // + //! Address 1 byte above the highest location the boot loader can access. + // + unsigned long ulFlashTop; + + // + //! Lowest address the boot loader can write or erase. + // + unsigned long ulAppStartAddr; +} +PACKED tDFUDeviceInfo; + +#ifndef DEPRECATED +//***************************************************************************** +// +// Various deprecated definitions. These are included to ensure backwards +// compatibility. +// +//***************************************************************************** +#define USBD_DFU_REQUEST_LUMINARY USBD_DFU_REQUEST_STELLARIS +#define REQUEST_LUMINARY_VALUE REQUEST_STELLARIS_VALUE +#define tDFUQueryLuminaryProtocol uDFUQueryStellarisProtocol +#endif + +#ifdef ewarm +#pragma pack() +#endif + +#endif // __USBDFU_H__ diff --git a/usblib/usbhid.h b/usblib/usbhid.h new file mode 100644 index 0000000..ac89cd3 --- /dev/null +++ b/usblib/usbhid.h @@ -0,0 +1,657 @@ +//***************************************************************************** +// +// usbhid.h - Definitions used by HID class devices and hosts. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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_POINTER 0x01 +#define USB_HID_MOUSE 0x02 +#define USB_HID_KEYBOARD 0x06 + +#define USB_HID_PHYSICAL 0x00 +#define USB_HID_APPLICATION 0x01 + +#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_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(unsigned long)) + +//***************************************************************************** +// +// All structures defined in this section of the header require byte packing of +// fields. This is usually a ccomplished 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 USB_HID_DTYPE_REPORT or + //! USB_HID_DTYPE_PHYSICAL. + // + unsigned char bDescriptorType; + + // + //! The total length of the HID class descriptor. + // + unsigned short 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For a HID descriptor, this will be + //! USB_HID_DTYPE_HID (0x21). + // + unsigned char 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. + // + unsigned short 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 USB_HID_COUNTRY_xxx. + unsigned char 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). + // + unsigned char 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 ucBytesPerChar, indicates the number of bytes per character in +//! the table. The pulCapsLock 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. + // + unsigned char ucBytesPerChar; + + // + //! 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. + // + unsigned long pulCapsLock[USBH_HID_CAPS_ARRAY_SZ]; + + // + //! This is the indexed table of Usage ID to character value. It must be + //! at least ucBytesPerChar * 2 * USBH_HID_MAX_USAGE bytes in size as it + //! is treated as a double indexed array. + // + void *pCharMapping; +} +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..411dc54 --- /dev/null +++ b/usblib/usbkeyboardmap.c @@ -0,0 +1,143 @@ +//***************************************************************************** +// +// usbkeyboardmap.c - This file holds the table to enable USB keyboard usage +// identifiers to be mapped to printable characters. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 unsigned char g_pucKeyBoardMap[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_pucKeyBoardMap +}; diff --git a/usblib/usblib-cm3.ewp b/usblib/usblib-cm3.ewp new file mode 100644 index 0000000..c3ae268 --- /dev/null +++ b/usblib/usblib-cm3.ewp @@ -0,0 +1,851 @@ + + + + 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\usbdhidkeyb.c + + + $PROJ_DIR$\device\usbdhidmouse.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 + + + diff --git a/usblib/usblib-cm3.sgxx b/usblib/usblib-cm3.sgxx new file mode 100644 index 0000000..f13bae7 Binary files /dev/null and b/usblib/usblib-cm3.sgxx differ diff --git a/usblib/usblib-cm3.uvopt b/usblib/usblib-cm3.uvopt new file mode 100644 index 0000000..5f4ee33 --- /dev/null +++ b/usblib/usblib-cm3.uvopt @@ -0,0 +1,608 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj + *.lib + *.txt; *.h; *.inc + *.plm + *.cpp + + + + 0 + 0 + + + + usblib-cm3 + 0x4 + ARM-ADS + + 8000000 + + 1 + 1 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\rvmdk-cm3\ + + + 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\LM3S101.PDF + + + + SARMCM3.DLL + + DLM.DLL + -pLM3S101 + SARMCM3.DLL + + TLM.DLL + -pLM3S101 + + + 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\usbdhidkeyb.c + usbdhidkeyb.c + + + 1 + 14 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdhidmouse.c + usbdhidmouse.c + + + 1 + 15 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdmsc.c + usbdmsc.c + + + 1 + 16 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhaudio.c + usbhaudio.c + + + 1 + 17 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhid.c + usbhhid.c + + + 1 + 18 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhidkeyboard.c + usbhhidkeyboard.c + + + 1 + 19 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhidmouse.c + usbhhidmouse.c + + + 1 + 20 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhub.c + usbhhub.c + + + 1 + 21 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhmsc.c + usbhmsc.c + + + 1 + 22 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhostenum.c + usbhostenum.c + + + 1 + 23 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhscsi.c + usbhscsi.c + + + 1 + 24 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbkeyboardmap.c + usbkeyboardmap.c + + + 1 + 25 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbmode.c + usbmode.c + + + 1 + 26 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbringbuf.c + usbringbuf.c + + + 1 + 27 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbtick.c + usbtick.c + + + + + Documentation + 1 + 0 + 0 + + 2 + 28 + 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-cm3.uvproj b/usblib/usblib-cm3.uvproj new file mode 100644 index 0000000..a4991cd --- /dev/null +++ b/usblib/usblib-cm3.uvproj @@ -0,0 +1,540 @@ + + + + 1.1 + +
### uVision Project, (C) Keil Software
+ + + + usblib-cm3 + 0x4 + ARM-ADS + + + LM3S101 + Texas Instruments + IRAM(0x20000000-0x200007FF) IROM(0-0x1FFF) CLOCK(8000000) CPUTYPE("Cortex-M3") + + "STARTUP\Luminary\Startup.s" ("Luminary Startup Code") + UL2CM3(-UU0101L5E -O14 -S0 -C0 -N00("ARM Cortex-M3") -D00(1BA00477) -L00(4) -FO7 -FD20000000 -FC800 -FN1 -FF0LM3S_8 -FS00 -FL02000) + 4079 + LM3Sxxx.H + + + + + + + + + + SFD\Luminary\LM3S101.SFR + 0 + + + + Luminary\ + Luminary\ + + 0 + 0 + 0 + 0 + 1 + + .\rvmdk-cm3\ + usblib-cm3 + 0 + 1 + 0 + 1 + 1 + .\rvmdk-cm3\ + 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 + + DLM.DLL + -pLM3S101 + SARMCM3.DLL + + TLM.DLL + -pLM3S101 + + + + 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-M3" + + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 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 + 0x800 + + + 1 + 0x0 + 0x2000 + + + 0 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x2000 + + + 1 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x800 + + + 0 + 0x0 + 0x0 + + + + + + 0 + 3 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + + + 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 + + + usbdhidkeyb.c + 1 + .\device\usbdhidkeyb.c + + + usbdhidmouse.c + 1 + .\device\usbdhidmouse.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 + + + + + Documentation + + + readme.txt + 5 + .\readme.txt + + + + + + + +
diff --git a/usblib/usblib-cm4f.ewp b/usblib/usblib-cm4f.ewp new file mode 100644 index 0000000..09cc894 --- /dev/null +++ b/usblib/usblib-cm4f.ewp @@ -0,0 +1,851 @@ + + + + 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\usbdhidkeyb.c + + + $PROJ_DIR$\device\usbdhidmouse.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 + + + diff --git a/usblib/usblib-cm4f.sgxx b/usblib/usblib-cm4f.sgxx new file mode 100644 index 0000000..131d691 Binary files /dev/null and b/usblib/usblib-cm4f.sgxx differ diff --git a/usblib/usblib-cm4f.uvopt b/usblib/usblib-cm4f.uvopt new file mode 100644 index 0000000..65e2904 --- /dev/null +++ b/usblib/usblib-cm4f.uvopt @@ -0,0 +1,608 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj + *.lib + *.txt; *.h; *.inc + *.plm + *.cpp + + + + 0 + 0 + + + + usblib-cm4f + 0x4 + ARM-ADS + + 8000000 + + 1 + 1 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\rvmdk-cm4f\ + + + 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\LM4F110B2QR.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\usbdhidkeyb.c + usbdhidkeyb.c + + + 1 + 14 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdhidmouse.c + usbdhidmouse.c + + + 1 + 15 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\device\usbdmsc.c + usbdmsc.c + + + 1 + 16 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhaudio.c + usbhaudio.c + + + 1 + 17 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhid.c + usbhhid.c + + + 1 + 18 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhidkeyboard.c + usbhhidkeyboard.c + + + 1 + 19 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhidmouse.c + usbhhidmouse.c + + + 1 + 20 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhhub.c + usbhhub.c + + + 1 + 21 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhmsc.c + usbhmsc.c + + + 1 + 22 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhostenum.c + usbhostenum.c + + + 1 + 23 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\host\usbhscsi.c + usbhscsi.c + + + 1 + 24 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbkeyboardmap.c + usbkeyboardmap.c + + + 1 + 25 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbmode.c + usbmode.c + + + 1 + 26 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbringbuf.c + usbringbuf.c + + + 1 + 27 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\usbtick.c + usbtick.c + + + + + Documentation + 1 + 0 + 0 + + 2 + 28 + 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-cm4f.uvproj b/usblib/usblib-cm4f.uvproj new file mode 100644 index 0000000..2bfa748 --- /dev/null +++ b/usblib/usblib-cm4f.uvproj @@ -0,0 +1,540 @@ + + + + 1.1 + +
### uVision Project, (C) Keil Software
+ + + + usblib-cm4f + 0x4 + ARM-ADS + + + LM4F110B2QR + 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\LM4F110B2QR.SFR + 0 + + + + Luminary\ + Luminary\ + + 0 + 0 + 0 + 0 + 1 + + .\rvmdk-cm4f\ + usblib-cm4f + 0 + 1 + 0 + 1 + 1 + .\rvmdk-cm4f\ + 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 + + + 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 + + + usbdhidkeyb.c + 1 + .\device\usbdhidkeyb.c + + + usbdhidmouse.c + 1 + .\device\usbdhidmouse.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 + + + + + Documentation + + + readme.txt + 5 + .\readme.txt + + + + + + + +
diff --git a/usblib/usblib.h b/usblib/usblib.h new file mode 100644 index 0000000..c1b8755 --- /dev/null +++ b/usblib/usblib.h @@ -0,0 +1,1813 @@ +//***************************************************************************** +// +// usblib.h - Main header file for the USB Library. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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. +// +//***************************************************************************** +#define USBLIB_NUM_EP 16 // Number of supported endpoints. + +//***************************************************************************** +// +// 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 +// { +// unsigned long ulFirstField; +// char cCharMember; +// unsigned short usShort; +// } +// 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 Stellaris 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 Stellaris code, this is a 32 bit integer. +// +//***************************************************************************** + +//***************************************************************************** +// +// 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. + // + unsigned char bmRequestType; + + // + //! Identifies the specific request being made. + // + unsigned char bRequest; + + // + //! Word-sized field that varies according to the request. + // + unsigned short wValue; + + // + //! Word-sized field that varies according to the request; typically used + //! to pass an index or offset. + // + unsigned short wIndex; + + // + //! The number of bytes to transfer if there is a data stage to the + //! request. + // + unsigned short 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. + // + unsigned char 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. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For a device descriptor, this will be + //! USB_DTYPE_DEVICE (1). + // + unsigned char bDescriptorType; + + // + //! The USB Specification Release Number in BCD format. For USB 2.0, this + //! will be 0x0200. + // + unsigned short bcdUSB; + + // + //! The device class code. + // + unsigned char bDeviceClass; + + // + //! The device subclass code. This value qualifies the value found in the + //! bDeviceClass field. + // + unsigned char bDeviceSubClass; + + // + //! The device protocol code. This value is qualified by the values of + //! bDeviceClass and bDeviceSubClass. + // + unsigned char bDeviceProtocol; + + // + //! The maximum packet size for endpoint zero. Valid values are 8, 16, 32 + //! and 64. + // + unsigned char bMaxPacketSize0; + + // + //! The device Vendor ID (VID) as assigned by the USB-IF. + // + unsigned short idVendor; + + // + //! The device Product ID (PID) as assigned by the manufacturer. + // + unsigned short idProduct; + + // + //! The device release number in BCD format. + // + unsigned short bcdDevice; + + // + //! The index of a string descriptor describing the manufacturer. + // + unsigned char iManufacturer; + + // + //! The index of a string descriptor describing the product. + // + unsigned char iProduct; + + // + //! The index of a string descriptor describing the device's serial + //! number. + // + unsigned char iSerialNumber; + + // + //! The number of possible configurations offered by the device. This + //! field indicates the number of distinct configuration descriptors that + //! the device offers. + // + unsigned char 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 + +//***************************************************************************** +// +//! 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For a device descriptor, this will be + //! USB_DTYPE_DEVICE_QUAL (6). + // + unsigned char bDescriptorType; + + // + //! The USB Specification Release Number in BCD format. For USB 2.0, this + //! will be 0x0200. + // + unsigned short bcdUSB; + + // + //! The device class code. + // + unsigned char bDeviceClass; + + // + //! The device subclass code. This value qualifies the value found in the + //! bDeviceClass field. + // + unsigned char bDeviceSubClass; + + // + //! The device protocol code. This value is qualified by the values of + //! bDeviceClass and bDeviceSubClass. + // + unsigned char bDeviceProtocol; + + // + //! The maximum packet size for endpoint zero when operating at a speed + //! other than high speed. + // + unsigned char bMaxPacketSize0; + + // + //! The number of other-speed configurations supported. + // + unsigned char bNumConfigurations; + + // + //! Reserved for future use. Must be set to zero. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For a configuration descriptor, this will + //! be USB_DTYPE_CONFIGURATION (2). + // + unsigned char 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. + // + unsigned short wTotalLength; + + // + //! The number of interface supported by this configuration. + // + unsigned char bNumInterfaces; + + // + //! The value used as an argument to the SetConfiguration standard request + //! to select this configuration. + // + unsigned char bConfigurationValue; + + // + //! The index of a string descriptor describing this configuration. + // + unsigned char iConfiguration; + + // + //! Attributes of this configuration. + // + unsigned char 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. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an interface descriptor, this will + //! be USB_DTYPE_INTERFACE (4). + // + unsigned char bDescriptorType; + + // + //! The number of this interface. This is a zero based index into the + //! array of concurrent interfaces supported by this configuration. + // + unsigned char bInterfaceNumber; + + // + //! The value used to select this alternate setting for the interface + //! defined in bInterfaceNumber. + // + unsigned char bAlternateSetting; + + // + //! The number of endpoints used by this interface (excluding endpoint + //! zero). + // + unsigned char bNumEndpoints; + + // + //! The interface class code as assigned by the USB-IF. + // + unsigned char bInterfaceClass; + + // + //! The interface subclass code as assigned by the USB-IF. + // + unsigned char bInterfaceSubClass; + + // + //! The interface protocol code as assigned by the USB-IF. + // + unsigned char bInterfaceProtocol; + + // + //! The index of a string descriptor describing this interface. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For an endpoint descriptor, this will + //! be USB_DTYPE_ENDPOINT (5). + // + unsigned char 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. + // + unsigned char 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. + // + unsigned char 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. + // + unsigned short wMaxPacketSize; + + // + //! The polling interval for data transfers expressed in frames or + //! micro frames depending upon the operating speed. + // + unsigned char 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For a string descriptor, this will be + //! USB_DTYPE_STRING (3). + // + unsigned char 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. + // + unsigned short 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. + // + unsigned char bLength; + + // + //! The type of the descriptor. For a string descriptor, this will be + //! USB_DTYPE_STRING (3). + // + unsigned char 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. + // + unsigned char bString; +} +PACKED tStringDescriptor; + +//***************************************************************************** +// +//! Write a 2 byte unsigned short value to a USB descriptor block. +//! +//! \param usValue is the two byte unsigned short that is to be written 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(usValue) (usValue & 0xff), (usValue >> 8) + +//***************************************************************************** +// +//! Write a 3 byte unsigned long value to a USB descriptor block. +//! +//! \param ulValue is the three byte unsigned value that is to be written 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(ulValue) (ulValue & 0xff), \ + ((ulValue >> 8) & 0xff), \ + ((ulValue >> 16) & 0xff) + +//***************************************************************************** +// +//! Write a 4 byte unsigned long value to a USB descriptor block. +//! +//! \param ulValue is the four byte unsigned long that is to be written 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(ulValue) (ulValue & 0xff), \ + ((ulValue >> 8) & 0xff), \ + ((ulValue >> 16) & 0xff), \ + ((ulValue >> 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 *)(((unsigned char *)(ptr)) + \ + *((unsigned char *)(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, unsigned long ulInfo); + +//***************************************************************************** +// +// Callback made to indicate that an interface alternate setting change has +// occurred. +// +//***************************************************************************** +typedef void (* tInterfaceCallback)(void *pvInstance, + unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting); + +//***************************************************************************** +// +// Generic interrupt handler callbacks. +// +//***************************************************************************** +typedef void (* tUSBIntHandler)(void *pvInstance); + +//***************************************************************************** +// +// Interrupt handler callbacks that have status information. +// +//***************************************************************************** +typedef void (* tUSBEPIntHandler)(void *pvInstance, + unsigned long ulStatus); + +//***************************************************************************** +// +// Generic handler callbacks that are used when the callers needs to call into +// an instance of class. +// +//***************************************************************************** +typedef void (* tUSBDeviceHandler)(void *pvInstance, + unsigned long ulRequest, + 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 how a given endpoint's FIFO is configured in +//! relation to the maximum packet size for the endpoint as specified in the +//! endpoint descriptor. +// +//***************************************************************************** +typedef struct +{ + // + //! This field indicates whether to configure an endpoint's FIFO to be + //! double- or single-buffered. If true, a double-buffered FIFO is + //! created and the amount of required FIFO storage is multiplied by two. + // + tBoolean bDoubleBuffer; + + // + //! This field defines endpoint mode flags which cannot be deduced from + //! the configuration descriptor, namely any in the set USB_EP_AUTO_xxx or + //! USB_EP_DMA_MODE_x. USBDCDConfig adds these flags to the endpoint + //! mode and direction determined from the config descriptor before it + //! configures the endpoint using a call to USBDevEndpointConfigSet(). + // + unsigned short usEPFlags; +} +tFIFOEntry; + +//***************************************************************************** +// +//! This structure defines endpoint and FIFO configuration information for +//! all endpoints that the device wishes to use. This information cannot be +//! determined by examining the USB configuration descriptor and is +//! provided to USBDCDConfig by the application to allow the USB controller +//! endpoints to be correctly configured. +// +//***************************************************************************** +typedef struct +{ + // + //! An array containing one FIFO entry for each of the IN endpoints. + //! Note that endpoint 0 is configured and managed by the USB device stack + //! so is excluded from this array. The index 0 entry of the array + //! corresponds to endpoint 1, index 1 to endpoint 2, etc. + // + tFIFOEntry sIn[USBLIB_NUM_EP - 1]; + + // + //! An array containing one FIFO entry for each of the OUT endpoints. + //! Note that endpoint 0 is configured and managed by the USB device stack + //! so is excluded from this array. The index 0 entry of the array + //! corresponds to endpoint 1, index 1 to endpoint 2, etc. + // + tFIFOEntry sOut[USBLIB_NUM_EP - 1]; +} +tFIFOConfig; + +//***************************************************************************** +// +//! 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 pucData. + // + unsigned short usSize; + + // + //! A pointer to a block of data containing an integral number of + //! USB descriptors which form part of a larger configuration descriptor. + // + const unsigned char *pucData; +} +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. + // + unsigned char ucNumSections; + + // + //! A pointer to an array of ucNumSections section pointers which must + //! be concatenated to form the configuration descriptor. + // + const tConfigSection * const *psSections; +} +tConfigHeader; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup general_usblib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// 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 unsigned long USBDescGetNum(tDescriptorHeader *psDesc, + unsigned long ulSize, unsigned long ulType); +extern tDescriptorHeader *USBDescGet(tDescriptorHeader *psDesc, + unsigned long ulSize, + unsigned long ulType, + unsigned long ulIndex); +extern unsigned long + USBDescGetNumAlternateInterfaces(tConfigDescriptor *psConfig, + unsigned char ucInterfaceNumber); +extern tInterfaceDescriptor *USBDescGetInterface(tConfigDescriptor *psConfig, + unsigned long ulIndex, + unsigned long ulAltCfg); +extern tEndpointDescriptor * + USBDescGetInterfaceEndpoint(tInterfaceDescriptor *psInterface, + unsigned long ulIndex, + unsigned long ulSize); + +//***************************************************************************** +// +//! 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 +{ + // + //! The application wishes to operate as a USB device. + // + USB_MODE_DEVICE = 0, + + // + //! The application wishes to operate as a USB host. + // + USB_MODE_HOST, + + // + //! The application wishes to operate as both a host and device using + //! On-The-Go protocols to negotiate. + // + USB_MODE_OTG, + + // + //! A marker indicating that no USB mode has yet been set by the + //! application. + // + USB_MODE_NONE, + + // + //! The application is forcing host mode so that the VBUS and ID pins are + //! not used or seen by the USB controller. + // + USB_MODE_FORCE_HOST, + + // + //! The application is forcing device mode so that the VBUS and ID pins are + //! not used or seen by the USB controller. + // + USB_MODE_FORCE_DEVICE, +} 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)(unsigned long ulIndex, tUSBMode eMode); + +//***************************************************************************** +// +// Mode selection and dual mode interrupt steering functions. +// +//***************************************************************************** +extern void USBStackModeSet(unsigned long ulIndex, tUSBMode eUSBMode, + tUSBModeCallback pfnCallback); +extern void USBDualModeInit(unsigned long ulIndex); +extern void USBDualModeTerm(unsigned long ulIndex); +extern void USBOTGMain(unsigned long ulMsTicks); +extern void USBOTGPollRate(unsigned long ulIndex, unsigned long ulPollRate); +extern void USBOTGModeInit(unsigned long ulIndex, unsigned long ulPollRate, + void *pHostData, unsigned long ulHostDataSize); +extern void USBOTGModeTerm(unsigned long ulIndex); +extern void USB0OTGModeIntHandler(void); +extern void USB0DualModeIntHandler(void); + +//***************************************************************************** +// +//! 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 ulEvent is the identifier of the asynchronous event which is being +//! notified to the client. +//! \param ulMsgParam 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 unsigned long (* tUSBCallback)(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgParam, + void *pvMsgData); + +//***************************************************************************** +// +//! 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 +{ + unsigned long ulEvent; + + unsigned long ulInstance; +} +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. +// +#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 ulMsgValue 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 ulMsgValue 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 ulMsgValue 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 ulMsgValue +//! 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 ulRequest 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 ulRequest 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 ulRequest 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 ulRequest 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 ulInstance is actually the class of the unsupported +//! device that was connected. +// +#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) + +//***************************************************************************** +// +// 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 // RX FIFO full +// +//! 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 // RX FIFO full + +//***************************************************************************** +// +// Close the general_usblib_api 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 unsigned long (* tUSBPacketTransfer)(void *pvHandle, + unsigned char *pcData, + unsigned long ulLength, + tBoolean 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. +// +//***************************************************************************** +typedef unsigned long (* 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 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. + // + tBoolean 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. + // + unsigned char *pcBuffer; + + // + //! The size, in bytes, of the buffer pointed to by pcBuffer. + // + unsigned long ulBufferSize; + + // + //! 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. + // + unsigned long ulSize; + + // + //! The ring buffer write index. + // + volatile unsigned long ulWriteIndex; + + // + //! The ring buffer read index. + // + volatile unsigned long ulReadIndex; + + // + //! The ring buffer. + // + unsigned char *pucBuf; +} +tUSBRingBufObject; + +//***************************************************************************** +// +// USB buffer API function prototypes. +// +//***************************************************************************** +extern const tUSBBuffer *USBBufferInit(const tUSBBuffer *psBuffer); +extern void USBBufferZeroLengthPacketInsert(const tUSBBuffer *psBuffer, + tBoolean bSendZLP); +extern void USBBufferInfoGet(const tUSBBuffer *psBuffer, + tUSBRingBufObject *psRingBuf); +extern void *USBBufferCallbackDataSet(tUSBBuffer *psBuffer, void *pvCBData); +extern unsigned long USBBufferWrite(const tUSBBuffer *psBuffer, + const unsigned char *pucData, + unsigned long ulLength); +extern void USBBufferDataWritten(const tUSBBuffer *psBuffer, + unsigned long ulLength); +extern void USBBufferDataRemoved(const tUSBBuffer *psBuffer, + unsigned long ulLength); +extern void USBBufferFlush(const tUSBBuffer *psBuffer); +extern unsigned long USBBufferRead(const tUSBBuffer *psBuffer, + unsigned char *pucData, + unsigned long ulLength); +extern unsigned long USBBufferDataAvailable(const tUSBBuffer *psBuffer); +extern unsigned long USBBufferSpaceAvailable(const tUSBBuffer *psBuffer); +extern unsigned long USBBufferEventCallback(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgValue, + void *pvMsgData); +extern tBoolean USBRingBufFull(tUSBRingBufObject *ptUSBRingBuf); +extern tBoolean USBRingBufEmpty(tUSBRingBufObject *ptUSBRingBuf); +extern void USBRingBufFlush(tUSBRingBufObject *ptUSBRingBuf); +extern unsigned long USBRingBufUsed(tUSBRingBufObject *ptUSBRingBuf); +extern unsigned long USBRingBufFree(tUSBRingBufObject *ptUSBRingBuf); +extern unsigned long USBRingBufContigUsed(tUSBRingBufObject *ptUSBRingBuf); +extern unsigned long USBRingBufContigFree(tUSBRingBufObject *ptUSBRingBuf); +extern unsigned long USBRingBufSize(tUSBRingBufObject *ptUSBRingBuf); +extern unsigned char USBRingBufReadOne(tUSBRingBufObject *ptUSBRingBuf); +extern void USBRingBufRead(tUSBRingBufObject *ptUSBRingBuf, + unsigned char *pucData, unsigned long ulLength); +extern void USBRingBufWriteOne(tUSBRingBufObject *ptUSBRingBuf, + unsigned char ucData); +extern void USBRingBufWrite(tUSBRingBufObject *ptUSBRingBuf, + const unsigned char *pucData, + unsigned long ulLength); +extern void USBRingBufAdvanceWrite(tUSBRingBufObject *ptUSBRingBuf, + unsigned long ulNumBytes); +extern void USBRingBufAdvanceRead(tUSBRingBufObject *ptUSBRingBuf, + unsigned long ulNumBytes); +extern void USBRingBufInit(tUSBRingBufObject *ptUSBRingBuf, + unsigned char *pucBuf, unsigned long ulSize); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBLIB_H__ diff --git a/usblib/usblibpriv.h b/usblib/usblibpriv.h new file mode 100644 index 0000000..edb241d --- /dev/null +++ b/usblib/usblibpriv.h @@ -0,0 +1,130 @@ +//***************************************************************************** +// +// 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-2012 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 9453 of the Stellaris 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 + +//***************************************************************************** +// +// Internal interrupt handlers called from the main vectors in device and +// host mode. +// +//***************************************************************************** +extern void USBDeviceIntHandlerInternal(unsigned long ulIndex, + unsigned long ulStatus); +extern void USBHostIntHandlerInternal(unsigned long ulIndex, + unsigned long ulStatus); + +//***************************************************************************** +// +// 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, unsigned long ulTicksmS); + +//***************************************************************************** +// +// Internal functions use to initialize the tick handler and register tick +// callbacks. +// +//***************************************************************************** +extern void InternalUSBTickInit(void); +extern void InternalUSBTickReset(void); +extern long InternalUSBRegisterTickHandler(tUSBTickHandler pfHandler, + void *pvInstance); +extern void InternalUSBStartOfFrameTick(unsigned long ulTicksmS); +extern void InternalUSBHCDSendEvent(unsigned long ulIndex, + tEventInfo *psEvent, + unsigned long ulEvFlag); + +//***************************************************************************** +// +// g_ulCurrentUSBTick 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 unsigned long g_ulCurrentUSBTick; + +//***************************************************************************** +// +// g_ulUSBSOFCount is a global counter for Start of Frame interrupts. It is +// incremented by the low level device- or host-mode interrupt handlers. +// +//***************************************************************************** +extern unsigned long g_ulUSBSOFCount; + +//***************************************************************************** +// +// 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_ulCurrentUSBTick + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBLIBPRIV_H__ diff --git a/usblib/usbmode.c b/usblib/usbmode.c new file mode 100644 index 0000000..10ece23 --- /dev/null +++ b/usblib/usbmode.c @@ -0,0 +1,1115 @@ +//***************************************************************************** +// +// usbmode.c - Functions related to dual mode USB device/host operation. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.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/device/usbdevice.h" +#include "usblib/host/usbhost.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_eUSBMode = USB_MODE_NONE; + +//***************************************************************************** +// +// The default and the current polling rate for the USB OTG library. +// +//***************************************************************************** +volatile unsigned long g_ulPollRate; + +//***************************************************************************** +// +// The current time remaining in milliseconds before checking the cable +// connection. +// +//***************************************************************************** +volatile unsigned long g_ulWaitTicks = 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. + // + USB_OTG_MODE_IDLE, + + // + // Waiting on ID mode detection. + // + USB_OTG_MODE_WAITID, + + // + // Waiting for next poll interval. + // + USB_OTG_MODE_WAIT, + + // + // Now in B-side wait for connect. + // + USB_OTG_MODE_B_WAITCON, + + // + // Now in A-side device mode. + // + USB_OTG_MODE_B_DEVICE, + + // + // Now in A-side host mode. + // + USB_OTG_MODE_A_HOST, +} +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_eDualMode = USB_MODE_NONE; + +static void USBOTGRemovePower(unsigned long ulIndex); + +//***************************************************************************** +// +// 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 eUSBMode is one of USB_MODE_HOST, USB_MODE_DEVICE, or USB_MODE_NONE. +// +// Based on the current state held in g_eDualMode 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 eUSBMode) +{ + if((g_eDualMode != eUSBMode) || (g_eDualMode == USB_MODE_NONE)) + { + // + // If going from host mode to unconfigured mode then remove power. + // + if((g_eDualMode == USB_MODE_HOST) && (eUSBMode == USB_MODE_NONE)) + { + // + // 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_eDualMode == USB_MODE_DEVICE) && (eUSBMode == USB_MODE_NONE)) + { + // + // End the current session. + // + USBOTGSessionRequest(USB0_BASE, false); + } + + // + // Reset the delay whenever returning to USB_MODE_NONE. + // + if(eUSBMode == USB_MODE_NONE) + { + g_ulWaitTicks = g_ulPollRate; + } + + // + // Do we have a mode change callback installed? + // + if((g_pfnUSBModeCallback) && (g_eDualMode != eUSBMode)) + { + // + // Inform the callback of the new operating mode. + // + g_pfnUSBModeCallback(0, eUSBMode); + } + + // + // Save the new mode. + // + g_eDualMode = eUSBMode; + } +} + +//***************************************************************************** +// +//! 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 ulIndex specifies the USB controller whose mode of operation is to +//! be set. This parameter must be set to 0. +//! \param eUSBMode indicates the mode that the application wishes to operate +//! in. Valid values are \b USB_MODE_DEVICE to operate as a USB device and +//! \b USB_MODE_HOST 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 eUSBMode is set to either \b USB_MODE_DEVICE or +//! \b USB_MODE_HOST, 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. +//! +//! \note Forcing of the USB controller mode feature is not available on all +//! Stellaris microcontrollers. Consult the data sheet for the microcontroller +//! that the application is using to determine if this feature is available. +//! +//! \return None. +// +//***************************************************************************** +void +USBStackModeSet(unsigned long ulIndex, tUSBMode eUSBMode, + tUSBModeCallback pfnCallback) +{ + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + + // + // Remember the mode so that we can steer the interrupts appropriately. + // + g_eUSBMode = eUSBMode; + + // + // 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((eUSBMode == USB_MODE_DEVICE) || (eUSBMode == USB_MODE_HOST)) + { + // + // Make sure that a callback was provided. + // + if(g_pfnUSBModeCallback) + { + g_pfnUSBModeCallback(0, eUSBMode); + } + } +} + +//***************************************************************************** +// +//! 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) +{ + unsigned long ulStatus; + + // + // Read the USB interrupt status. + // + ulStatus = 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(ulStatus & USB_HOST_INTS) + { + // + // Call the host's interrupt handler. + // + USBHostIntHandlerInternal(0, ulStatus & USB_HOST_INTS); + + // + // We have already processed these interrupts so clear them + // from the status. + // + ulStatus &= ~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 ulStatus parameter since the USB interrupt register is + // clear-on-read. + // + switch(g_eUSBMode) + { + case USB_MODE_NONE: + { + // + // No mode is set yet so we have no idea what to do. Just ignore + // the interrupt. + // + break; + } + + // + // Operating in pure host mode. + // + case USB_MODE_HOST: + { + // + // Call the host interrupt handler if there is anything still to + // process. + // + USBHostIntHandlerInternal(0, ulStatus); + + break; + } + + // + // Operating in pure device mode. + // + case USB_MODE_DEVICE: + { + // + // Call the device interrupt handler. + // + USBDeviceIntHandlerInternal(0, ulStatus); + + break; + } + + default: + { + break; + } + } +} + +//***************************************************************************** +// +//! Initializes the USB controller for dual mode operation. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + // + // We only support a single USB controller. + // + ASSERT(ulIndex == 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(INT_USB0); + + // + // Turn on session request to enable ID pin checking. + // + USBOTGSessionRequest(USB0_BASE, true); + + // + // Initialize the power configuration. + // + USBHostPwrConfig(USB0_BASE, USBHCDPowerConfigGet(ulIndex)); + + // + // 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(ulIndex)) + { + // + // 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 ulIndex 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(unsigned long ulIndex) +{ + // + // We only support a single USB controller. + // + ASSERT(ulIndex == 0); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(INT_USB0); + + MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL); + + MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +//***************************************************************************** +// +//! \addtogroup usblib_otg +//! @{ +// +//***************************************************************************** + + +//***************************************************************************** +// +//! Returns the USB controller to and inactive state when in OTG mode operation. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + // + // We only support a single USB controller. + // + ASSERT(ulIndex == 0); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(INT_USB0); + + // + // 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(USB_MODE_NONE); +} + +//***************************************************************************** +// +//! Initializes the USB controller for OTG mode operation. +//! +//! \param ulIndex specifies the USB controller that is to be initialized for +//! OTG mode operation. +//! \param ulPollingRate 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 ulPoolSize is the size in bytes of the buffer passed in as 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 ulPollingRate 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 ulPoolSize 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(unsigned long ulIndex, unsigned long ulPollingRate, + void *pvPool, unsigned long ulPoolSize) +{ + // + // We only support a single USB controller. + // + ASSERT(ulIndex == 0); + + // + // This should never be called if not in OTG mode. + // + ASSERT(g_eUSBMode == USB_MODE_OTG); + + // + // 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_eUSBMode = USB_MODE_OTG; + + // + // Remember that we have not yet determined whether we are device or + // host. + // + g_eDualMode = USB_MODE_NONE; + + // + // Set the default polling rate. + // + g_ulPollRate = ulPollingRate; + + // + // Enable the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Initialize the host controller stack. + // + USBHCDInit(ulIndex, pvPool, ulPoolSize); + + // + // 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(ulIndex, USBHCDPowerConfigGet(ulIndex)); + + // + // 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(ulIndex)) + { + // + // 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. + // + OS_INT_ENABLE(INT_USB0); +} + +//***************************************************************************** +// +// This function handles the steps required to remove power in OTG mode. +// +// \param ulIndex 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(unsigned long ulIndex) +{ + 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(ulIndex) == 0) + { + // + // Call the registered event driver to allow it to disable power. + // + sEvent.ulEvent = USB_EVENT_POWER_DISABLE; + sEvent.ulInstance = 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 ulIndex specifies which USB controller to set the polling rate. +//! \param ulPollRate 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 ulPollRate 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 ulPollRate 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(unsigned long ulIndex, unsigned long ulPollRate) +{ + // + // Save the timeout. + // + g_ulPollRate = ulPollRate; +} + +//***************************************************************************** +// +//! 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) +{ + unsigned long ulStatus; + tEventInfo sEvent; + + // + // Read the USB interrupt status. + // + ulStatus = USBIntStatusControl(USB0_BASE); + + // + // Check if this was an mode detect interrupt and under manual power + // control. + // + if((ulStatus & USB_INTCTRL_MODE_DETECT) && + (USBHCDPowerAutomatic(0) == 0)) + { + unsigned long ulMode; + + ulMode = USBModeGet(USB0_BASE); + + switch(ulMode) + { + // + // 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.ulEvent = USB_EVENT_POWER_ENABLE; + sEvent.ulInstance = 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 = USB_OTG_MODE_B_WAITCON; + + break; + } + + // + // Any other mode detect indicates USB_MODE_NONE. + // + default: + { + // + // Just inform the application that the mode was not device + // or host. + // + USBOTGSetMode(USB_MODE_NONE); + + 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(ulStatus & USB_INTCTRL_VBUS_ERR) + { + // + // Just inform the application that the mode was not device + // or host. + // + USBOTGSetMode(USB_MODE_NONE); + + // + // Return to idle mode. + // + g_eOTGModeState = USB_OTG_MODE_WAIT; + } + + // + // 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((ulStatus & USB_INTCTRL_DISCONNECT) && + (g_eOTGModeState == USB_OTG_MODE_B_DEVICE)) + { + // + // No longer a device so switch to unconfigured mode. + // + USBOTGSetMode(USB_MODE_NONE); + + // + // Return to idle mode. + // + g_eOTGModeState = USB_OTG_MODE_WAIT; + + return; + } + + // + // Handle receiving a reset. + // + if((ulStatus & USB_INTCTRL_RESET)&& + (g_eOTGModeState != USB_OTG_MODE_B_DEVICE)) + { + // + // 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 = USB_OTG_MODE_B_DEVICE; + + // + // Save the new mode. + // + USBOTGSetMode(USB_MODE_DEVICE); + } + + // + // If there is a connect interrupt while the library is waiting for + // one then move to full host mode state. + // + if(ulStatus & USB_INTCTRL_CONNECT) + { + // + // Move to A side host state. + // + g_eOTGModeState = USB_OTG_MODE_A_HOST; + + // + // Inform the application that controller is in host mode. + // + USBOTGSetMode(USB_MODE_HOST); + } + + // + // Call the correct device or host interrupt handler based on the current + // mode of operation. + // + switch(g_eOTGModeState) + { + case USB_OTG_MODE_A_HOST: + { + // + // Call the host interrupt handler if there is anything still to + // process. + // + USBHostIntHandlerInternal(0, ulStatus); + + break; + } + + // + // Operating in pure device mode. + // + case USB_OTG_MODE_B_DEVICE: + { + // + // Call the device interrupt handler. + // + USBDeviceIntHandlerInternal(0, ulStatus); + + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB host stack code to indicated that it +// has compeleted handing the device disconnection. +// +// \param ulIndex 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(unsigned long ulIndex) +{ + // + // This function is only valid when called in host mode. + // + if(g_eOTGModeState == USB_OTG_MODE_A_HOST) + { + // + // No longer a host so switch to unconfigured mode. + // + USBOTGSetMode(USB_MODE_NONE); + + g_eOTGModeState = USB_OTG_MODE_WAIT; + } +} + +//***************************************************************************** +// +//! This function is the main routine for the OTG Controller Driver. +//! +//! \param ulMsTicks 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 ulMsTicks 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(unsigned long ulMsTicks) +{ + tEventInfo sEvent; + + if(ulMsTicks > g_ulWaitTicks) + { + g_ulWaitTicks = 0; + } + else + { + g_ulWaitTicks -= ulMsTicks; + } + + switch(g_eOTGModeState) + { + case USB_OTG_MODE_IDLE: + { + g_eOTGModeState = USB_OTG_MODE_WAITID; + + // + // Initiate a session request and check the ID pin. + // + USBOTGSessionRequest(USB0_BASE, true); + break; + } + case USB_OTG_MODE_WAIT: + case USB_OTG_MODE_WAITID: + { + // + // If reached the timeout and polling is enabled then look again. + // + if((g_ulWaitTicks == 0) && (g_ulPollRate != 0)) + { + // + // Remove the session request. + // + USBOTGSessionRequest(USB0_BASE, false); + + // + // Return to idle mode. + // + USBOTGSetMode(USB_MODE_NONE); + + // + // 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.ulEvent = USB_EVENT_POWER_DISABLE; + sEvent.ulInstance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWRDIS); + } + + // + // Go back to the idle state. + // + g_eOTGModeState = USB_OTG_MODE_IDLE; + } + break; + } + case USB_OTG_MODE_A_HOST: + { + // + // Call the host main routine when acting as a host. + // + USBHCDMain(); + break; + } + case USB_OTG_MODE_B_WAITCON: + case USB_OTG_MODE_B_DEVICE: + default: + { + break; + } + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbmsc.h b/usblib/usbmsc.h new file mode 100644 index 0000000..ef91ce8 --- /dev/null +++ b/usblib/usbmsc.h @@ -0,0 +1,393 @@ +//***************************************************************************** +// +// usbmsc.h - Generic types and defines use by the mass storage class. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris 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 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. + // + unsigned long 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. + // + unsigned long 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. + // + unsigned long 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. + // + unsigned char 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. + // + unsigned char 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. + // + unsigned char 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). + // + unsigned char 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. + // + unsigned long dCSWSignature; + + // + // The device will set this field to the value received in the dCBWTag of + // the associated CBW. + // + unsigned long 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. + // + unsigned long 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. + // + unsigned char 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_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) \ + (((unsigned char *)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 ulFlags 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. + +//***************************************************************************** +// +// 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..b134632 --- /dev/null +++ b/usblib/usbringbuf.c @@ -0,0 +1,712 @@ +//***************************************************************************** +// +// usbringbuf.c - USB library ring buffer management utilities. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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 pulVal points to the index whose value is to be modified. +// \param ulDelta is the number of bytes to increment the index by. +// \param ulSize 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 unsigned long *pulVal, unsigned long ulDelta, + unsigned long ulSize) +{ + tBoolean bIntsOff; + + // + // Turn interrupts off temporarily. + // + bIntsOff = IntMasterDisable(); + + // + // Update the variable value. + // + *pulVal += ulDelta; + + // + // 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 ulDelta is greater than ulSize (which is extremely unlikely but...) + // + while(*pulVal >= ulSize) + { + *pulVal -= ulSize; + } + + // + // Restore the interrupt state + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Determines whether a ring buffer is full or not. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +tBoolean +USBRingBufFull(tUSBRingBufObject *ptUSBRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptUSBRingBuf->ulWriteIndex; + ulRead = ptUSBRingBuf->ulReadIndex; + + // + // Return the full status of the buffer. + // + return((((ulWrite + 1) % ptUSBRingBuf->ulSize) == ulRead) ? true : false); +} + +//***************************************************************************** +// +//! Determines whether a ring buffer is empty or not. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +tBoolean +USBRingBufEmpty(tUSBRingBufObject *ptUSBRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptUSBRingBuf->ulWriteIndex; + ulRead = ptUSBRingBuf->ulReadIndex; + + // + // Return the empty status of the buffer. + // + return((ulWrite == ulRead) ? true : false); +} + +//***************************************************************************** +// +//! Empties the ring buffer. +//! +//! \param ptUSBRingBuf is the ring buffer object to empty. +//! +//! Discards all data from the ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufFlush(tUSBRingBufObject *ptUSBRingBuf) +{ + tBoolean bIntsOff; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != 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(); + ptUSBRingBuf->ulReadIndex = ptUSBRingBuf->ulWriteIndex; + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Returns number of bytes stored in ring buffer. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +unsigned long +USBRingBufUsed(tUSBRingBufObject *ptUSBRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptUSBRingBuf->ulWriteIndex; + ulRead = ptUSBRingBuf->ulReadIndex; + + // + // Return the number of bytes contained in the ring buffer. + // + return((ulWrite >= ulRead) ? (ulWrite - ulRead) : + (ptUSBRingBuf->ulSize - (ulRead - ulWrite))); +} + +//***************************************************************************** +// +//! Returns number of bytes available in a ring buffer. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +unsigned long +USBRingBufFree(tUSBRingBufObject *ptUSBRingBuf) +{ + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return((ptUSBRingBuf->ulSize - 1) - USBRingBufUsed(ptUSBRingBuf)); +} + +//***************************************************************************** +// +//! Returns number of contiguous bytes of data stored in ring buffer ahead of +//! the current read pointer. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +unsigned long +USBRingBufContigUsed(tUSBRingBufObject *ptUSBRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptUSBRingBuf->ulWriteIndex; + ulRead = ptUSBRingBuf->ulReadIndex; + + // + // Return the number of contiguous bytes available. + // + return((ulWrite >= ulRead) ? (ulWrite - ulRead) : + (ptUSBRingBuf->ulSize - ulRead)); +} + +//***************************************************************************** +// +//! Returns number of contiguous free bytes available in a ring buffer. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +unsigned long +USBRingBufContigFree(tUSBRingBufObject *ptUSBRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptUSBRingBuf->ulWriteIndex; + ulRead = ptUSBRingBuf->ulReadIndex; + + // + // Return the number of contiguous bytes available. + // + if(ulRead > ulWrite) + { + // + // 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((ulRead - ulWrite) - 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(ptUSBRingBuf->ulSize - ulWrite - ((ulRead == 0) ? 1 : 0)); + } +} + +//***************************************************************************** +// +//! Returns the size in bytes of a ring buffer. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +unsigned long +USBRingBufSize(tUSBRingBufObject *ptUSBRingBuf) +{ + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return(ptUSBRingBuf->ulSize); +} + +//***************************************************************************** +// +//! Reads a single byte of data from a ring buffer. +//! +//! \param ptUSBRingBuf 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. +// +//***************************************************************************** +unsigned char +USBRingBufReadOne(tUSBRingBufObject *ptUSBRingBuf) +{ + unsigned char ucTemp; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(USBRingBufUsed(ptUSBRingBuf) != 0); + + // + // Write the data byte. + // + ucTemp = ptUSBRingBuf->pucBuf[ptUSBRingBuf->ulReadIndex]; + + // + // Increment the read index. + // + UpdateIndexAtomic(&ptUSBRingBuf->ulReadIndex, 1, ptUSBRingBuf->ulSize); + + // + // Return the character read. + // + return(ucTemp); +} + +//***************************************************************************** +// +//! Reads data from a ring buffer. +//! +//! \param ptUSBRingBuf points to the ring buffer to be read from. +//! \param pucData points to where the data should be stored. +//! \param ulLength is the number of bytes to be read. +//! +//! This function reads a sequence of bytes from a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufRead(tUSBRingBufObject *ptUSBRingBuf, unsigned char *pucData, + unsigned long ulLength) +{ + unsigned long ulTemp; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + ASSERT(pucData != NULL); + ASSERT(ulLength != 0); + + // + // Verify that data is available in the buffer. + // + ASSERT(ulLength <= USBRingBufUsed(ptUSBRingBuf)); + + // + // Read the data from the ring buffer. + // + for(ulTemp = 0; ulTemp < ulLength; ulTemp++) + { + pucData[ulTemp] = USBRingBufReadOne(ptUSBRingBuf); + } +} + +//***************************************************************************** +// +//! Removes bytes from the ring buffer by advancing the read index. +//! +//! \param ptUSBRingBuf points to the ring buffer from which bytes are to be +//! removed. +//! \param ulNumBytes 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 +//! ulNumBytes is larger than the number of bytes currently in the buffer, the +//! buffer is emptied. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufAdvanceRead(tUSBRingBufObject *ptUSBRingBuf, + unsigned long ulNumBytes) +{ + unsigned long ulCount; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Make sure that we are not being asked to remove more data than is + // there to be removed. + // + ulCount = USBRingBufUsed(ptUSBRingBuf); + ulCount = (ulCount < ulNumBytes) ? ulCount : ulNumBytes; + + // + // Advance the buffer read index by the required number of bytes. + // + UpdateIndexAtomic(&ptUSBRingBuf->ulReadIndex, ulCount, + ptUSBRingBuf->ulSize); +} + +//***************************************************************************** +// +//! Adds bytes to the ring buffer by advancing the write index. +//! +//! \param ptUSBRingBuf points to the ring buffer to which bytes have been +//! added. +//! \param ulNumBytes 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 ulNumBytes 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 *ptUSBRingBuf, + unsigned long ulNumBytes) +{ + unsigned long ulCount; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Make sure we were not asked to add a silly number of bytes. + // + ASSERT(ulNumBytes <= ptUSBRingBuf->ulSize); + + // + // Determine how much free space we currently think the buffer has. + // + ulCount = USBRingBufFree(ptUSBRingBuf); + + // + // 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(ulCount >= ulNumBytes); + + // + // Update the write pointer. + // + ptUSBRingBuf->ulWriteIndex += ulNumBytes; + + // + // Check and correct for wrap. + // + if(ptUSBRingBuf->ulWriteIndex >= ptUSBRingBuf->ulSize) + { + ptUSBRingBuf->ulWriteIndex -= ptUSBRingBuf->ulSize; + } + + // + // 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(ulCount < ulNumBytes) + { + // + // Yes - we need to advance the read pointer to ahead of the write + // pointer to discard some of the oldest data. + // + ptUSBRingBuf->ulReadIndex = ptUSBRingBuf->ulWriteIndex + 1; + + // + // Correct for buffer wrap if necessary. + // + if(ptUSBRingBuf->ulReadIndex >= ptUSBRingBuf->ulSize) + { + ptUSBRingBuf->ulReadIndex -= ptUSBRingBuf->ulSize; + } + } + +} + +//***************************************************************************** +// +//! Writes a single byte of data to a ring buffer. +//! +//! \param ptUSBRingBuf points to the ring buffer to be written to. +//! \param ucData is the byte to be written. +//! +//! This function writes a single byte of data into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufWriteOne(tUSBRingBufObject *ptUSBRingBuf, unsigned char ucData) +{ + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(USBRingBufFree(ptUSBRingBuf) != 0); + + // + // Write the data byte. + // + ptUSBRingBuf->pucBuf[ptUSBRingBuf->ulWriteIndex] = ucData; + + // + // Increment the write index. + // + UpdateIndexAtomic(&ptUSBRingBuf->ulWriteIndex, 1, ptUSBRingBuf->ulSize); +} + +//***************************************************************************** +// +//! Writes data to a ring buffer. +//! +//! \param ptUSBRingBuf points to the ring buffer to be written to. +//! \param pucData points to the data to be written. +//! \param ulLength is the number of bytes to be written. +//! +//! This function write a sequence of bytes into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +USBRingBufWrite(tUSBRingBufObject *ptUSBRingBuf, const unsigned char *pucData, + unsigned long ulLength) +{ + unsigned long ulTemp; + + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + ASSERT(pucData != NULL); + ASSERT(ulLength != 0); + + // + // Verify that space is available in the buffer. + // + ASSERT(ulLength <= USBRingBufFree(ptUSBRingBuf)); + + // + // Write the data into the ring buffer. + // + for(ulTemp = 0; ulTemp < ulLength; ulTemp++) + { + USBRingBufWriteOne(ptUSBRingBuf, pucData[ulTemp]); + } +} + +//***************************************************************************** +// +//! Initializes a ring buffer object. +//! +//! \param ptUSBRingBuf points to the ring buffer to be initialized. +//! \param pucBuf points to the data buffer to be used for the ring buffer. +//! \param ulSize 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 *ptUSBRingBuf, unsigned char *pucBuf, + unsigned long ulSize) +{ + // + // Check the arguments. + // + ASSERT(ptUSBRingBuf != NULL); + ASSERT(pucBuf != NULL); + ASSERT(ulSize != 0); + + // + // Initialize the ring buffer object. + // + ptUSBRingBuf->ulSize = ulSize; + ptUSBRingBuf->pucBuf = pucBuf; + ptUSBRingBuf->ulWriteIndex = ptUSBRingBuf->ulReadIndex = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/usbtick.c b/usblib/usbtick.c new file mode 100644 index 0000000..2bca4c6 --- /dev/null +++ b/usblib/usbtick.c @@ -0,0 +1,216 @@ +//***************************************************************************** +// +// usbtick.c - Functions related to USB stack tick timer handling. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#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_pfTickHandlers are called in the context of the USB SOF interrupt +// every USB_SOF_TICK_DIVIDE milliseconds. +// +//***************************************************************************** +tUSBTickHandler g_pfTickHandlers[MAX_USB_TICK_HANDLERS]; +void *g_pvTickInstance[MAX_USB_TICK_HANDLERS]; + +//***************************************************************************** +// +// Flag to indicate whether or not we have been initialized. +// +//***************************************************************************** +tBoolean 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. +// +//***************************************************************************** +unsigned long g_ulCurrentUSBTick = 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. +// +//***************************************************************************** +unsigned long g_ulUSBSOFCount = 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) +{ + unsigned long ulLoop; + + if(!g_bUSBTimerInitialized) + { + for(ulLoop = 0; ulLoop < MAX_USB_TICK_HANDLERS; ulLoop++) + { + g_pfTickHandlers[ulLoop] = (tUSBTickHandler)0; + g_pvTickInstance[ulLoop] = 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. +// +//***************************************************************************** +long +InternalUSBRegisterTickHandler(tUSBTickHandler pfHandler, + void *pvInstance) +{ + long lIdx; + + for(lIdx = 0; lIdx < MAX_USB_TICK_HANDLERS; lIdx++) + { + if(g_pfTickHandlers[lIdx] == 0) + { + // + // Save the handler. + // + g_pfTickHandlers[lIdx] = pfHandler; + + // + // Save the instance data. + // + g_pvTickInstance[lIdx] = pvInstance; + } + } + + if(lIdx == 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 ulTicksmS 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(unsigned long ulTicksmS) +{ + long lIdx; + + // + // Advance time. + // + g_ulCurrentUSBTick += ulTicksmS; + + // + // Call any registered SOF tick handlers. + // + for(lIdx = 0; lIdx < MAX_USB_TICK_HANDLERS; lIdx++) + { + if(g_pfTickHandlers[lIdx]) + { + g_pfTickHandlers[lIdx](g_pvTickInstance[lIdx], ulTicksmS); + } + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1