diff options
Diffstat (limited to 'packages')
91 files changed, 32145 insertions, 160 deletions
diff --git a/packages/bluez/bluez-utils3.inc b/packages/bluez/bluez-utils3.inc index 0bbba2cd14..c4debd01b2 100644 --- a/packages/bluez/bluez-utils3.inc +++ b/packages/bluez/bluez-utils3.inc @@ -76,9 +76,14 @@ OE_LT_RPATH_ALLOW[export]="1" do_install_append() { install -d ${D}${base_sbindir} ${D}${base_bindir}/ ${D}${sysconfdir}/apm/event.d/ - mv ${D}${sbindir}/* ${D}${base_sbindir}/ - mv ${D}${bindir}/* ${D}${base_bindir}/ - rmdir ${D}${bindir} ${D}${sbindir} + if [ "${sbindir}" != "${base_sbindir}" ]; then + mv ${D}${sbindir}/* ${D}${base_sbindir}/ + rmdir ${D}${sbindir} + fi + if [ "${bindir}" != "${base_bindir}" ]; then + mv ${D}${bindir}/* ${D}${base_bindir}/ + rmdir ${D}${bindir} + fi chmod u+s ${D}${base_sbindir}/hciattach ${D}${base_sbindir}/hciconfig install -m 0644 ${WORKDIR}/hcid.conf ${D}${sysconfdir}/bluetooth/ install -m 0644 ${S}/rfcomm/rfcomm.conf ${D}${sysconfdir}/bluetooth/ diff --git a/packages/busybox/busybox-1.11.1/angstrom/defconfig b/packages/busybox/busybox-1.11.3/angstrom/defconfig index f3130caca8..f3130caca8 100644 --- a/packages/busybox/busybox-1.11.1/angstrom/defconfig +++ b/packages/busybox/busybox-1.11.3/angstrom/defconfig diff --git a/packages/busybox/busybox-1.11.1/defconfig b/packages/busybox/busybox-1.11.3/defconfig index a7b8ed53a0..a7b8ed53a0 100644 --- a/packages/busybox/busybox-1.11.1/defconfig +++ b/packages/busybox/busybox-1.11.3/defconfig diff --git a/packages/busybox/busybox-1.11.1/slugos/defconfig b/packages/busybox/busybox-1.11.3/slugos/defconfig index d922529cc2..d922529cc2 100644 --- a/packages/busybox/busybox-1.11.1/slugos/defconfig +++ b/packages/busybox/busybox-1.11.3/slugos/defconfig diff --git a/packages/busybox/busybox-1.11.1/udhcpscript.patch b/packages/busybox/busybox-1.11.3/udhcpscript.patch index fc21d440cd..fc21d440cd 100644 --- a/packages/busybox/busybox-1.11.1/udhcpscript.patch +++ b/packages/busybox/busybox-1.11.3/udhcpscript.patch diff --git a/packages/busybox/busybox.inc b/packages/busybox/busybox.inc index cc196ad767..63f839e246 100644 --- a/packages/busybox/busybox.inc +++ b/packages/busybox/busybox.inc @@ -58,6 +58,13 @@ do_compile() { } do_install () { + oe_runmake busybox.links + if [ "x${layout_prefix}" = "x" ]; then + sed 's:^/usr/:/:' < busybox.links >busybox.links.new + mv busybox.links.new busybox.links + elif [ "${layout_prefix}" != "/usr" ]; then + echo "warning, busybox.links will lose with this prefix" + fi unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS install -d ${D}${sysconfdir}/init.d oe_runmake "PREFIX=${D}" install @@ -67,7 +74,11 @@ do_install () { install -d ${D}/busybox ls ${D} -R - cp -dPr ${D}${base_bindir} ${D}${base_sbindir} ${D}${prefix} ${D}/busybox/ + for i in ${D}${base_bindir} ${D}${base_sbindir} ${D}${prefix} ; do + if [ -d $i ]; then + cp -dPr $i ${D}/busybox/ + fi + done # Move the busybox binary back to /bin install -d ${D}${base_bindir} mv ${D}/busybox${base_bindir}/busybox ${D}${base_bindir}/ diff --git a/packages/busybox/busybox_1.11.1.bb b/packages/busybox/busybox_1.11.3.bb index 4d488218e2..790bf518cc 100644 --- a/packages/busybox/busybox_1.11.1.bb +++ b/packages/busybox/busybox_1.11.3.bb @@ -1,10 +1,8 @@ require busybox.inc -PR = "r3" +PR = "r0" SRC_URI = "\ http://www.busybox.net/downloads/busybox-${PV}.tar.gz \ - http://busybox.net/downloads/fixes-1.11.1/busybox-1.11.1-basename.patch;patch=1 \ - http://busybox.net/downloads/fixes-1.11.1/busybox-1.11.1-tar.patch;patch=1 \ \ file://udhcpscript.patch;patch=1 \ file://busybox-cron \ diff --git a/packages/ccid/ccid_1.3.8.bb b/packages/ccid/ccid_1.3.8.bb new file mode 100644 index 0000000000..c2ade3119a --- /dev/null +++ b/packages/ccid/ccid_1.3.8.bb @@ -0,0 +1,21 @@ +DESCRIPTION = "Generic USB CCID smart card reader driver" +HOMEPAGE = "http://pcsclite.alioth.debian.org/ccid.html" +LICENSE = "GPL" +PR = "r0" + +DEPENDS = "libusb pcsc-lite" +RDEPENDS = "pcsc-lite" + +SRC_URI = "http://alioth.debian.org/download.php/2482/ccid-${PV}.tar.bz2" + +inherit autotools + +EXTRA_OECONF = "--enable-udev" + +do_install_append () { + install -d "${D}/etc/udev/rules.d" + install -m 644 "${S}/src/pcscd_ccid.rules" "${D}/etc/udev/rules.d/85-pcscd_ccid.rules" +} + +FILES_${PN} += "${libdir}/pcsc/" +FILES_${PN}-dbg += "${libdir}/pcsc/drivers/*/*/*/.debug" diff --git a/packages/classpath/classpath-native.inc b/packages/classpath/classpath-native.inc index d178ce6779..31015b3d71 100644 --- a/packages/classpath/classpath-native.inc +++ b/packages/classpath/classpath-native.inc @@ -8,10 +8,6 @@ DEPENDS = "ecj-initial fastjar-native zip-native gettext-native" SRC_URI = "${GNU_MIRROR}/classpath/classpath-${PV}.tar.gz" -S = "${WORKDIR}/classpath-${PV}" - -FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/classpath-${PV}" - do_configure_prepend () { cp ${STAGING_DATADIR_NATIVE}/gettext/config.rpath ${S} diff --git a/packages/dmidecode/dmidecode_2.9.bb b/packages/dmidecode/dmidecode_2.9.bb new file mode 100644 index 0000000000..2186120498 --- /dev/null +++ b/packages/dmidecode/dmidecode_2.9.bb @@ -0,0 +1,19 @@ +DESCRIPTION = "DMI (Desktop Management Interface) table related utilities" +HOMEPAGE = "http://www.nongnu.org/dmidecode/" +LICENSE = "GPLv2" +PR = "r0" + +SRC_URI = "http://savannah.nongnu.org/download/dmidecode/${P}.tar.bz2" + +COMPATIBLE_HOST = "i.86.*-linux" + +do_unpack_extra() { + sed -i \ + -e '/^prefix/s:/usr/local:/usr:' \ + Makefile +} +addtask unpack_extra after do_unpack before do_patch + +do_install() { + oe_runmake DESTDIR="${D}" install +} diff --git a/packages/dsplink/codec-engine_2.10.bb b/packages/dsplink/codec-engine_2.10.bb index 098d0054cd..abfe2f24e6 100644 --- a/packages/dsplink/codec-engine_2.10.bb +++ b/packages/dsplink/codec-engine_2.10.bb @@ -5,8 +5,9 @@ RDEPENDS = "update-modules" inherit module -PR = "r7" -PV = "2.10" +# tconf from xdctools dislikes '.' in pwd :/ +PR = "r0" +PV = "210" # Get CE tarball from TI website, place in sources and calculate # md5sum diff --git a/packages/dsplink/codec-engine_2.21.bb b/packages/dsplink/codec-engine_2.21.bb new file mode 100644 index 0000000000..4c75464a27 --- /dev/null +++ b/packages/dsplink/codec-engine_2.21.bb @@ -0,0 +1,160 @@ +DESCRIPTION = "Codec Engine for TI ARM/DSP processors" + +DEPENDS = "virtual/kernel perl-native" +RDEPENDS = "update-modules" + +inherit module + +# tconf from xdctools dislikes '.' in pwd :/ +PR = "r0" +PV = "221" + +# Get CE tarball from TI website, place in sources and calculate +# md5sum +# Look for tarball at https://www-a.ti.com/downloads/sds_support/targetcontent/CE/index.html + +SRC_URI = "http://install.tarball.in.source.dir/codec_engine_2_21_00_06.tar.gz \ + " + +S = "${WORKDIR}/codec_engine_2_21_00_06" + +require ti-paths.inc + +PARALLEL_MAKE = "" +do_compile() { + echo "MVTOOL_PREFIX=${TARGET_PREFIX}" > ${S}/Rules.make + echo "UCTOOL_PREFIX=${TARGET_PREFIX}" >> ${S}/Rules.make + echo "LINUXKERNEL_INSTALL_DIR=${STAGING_KERNEL_DIR}" >> ${S}/Rules.make + + unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS + cd ${S}/cetools/packages/ti/sdo/linuxutils/cmem + + oe_runmake clean + oe_runmake KERNEL_PATH=${STAGING_KERNEL_DIR} \ + KERNEL_SRC=${STAGING_KERNEL_DIR} \ + KERNEL_VERSION=${KERNEL_VERSION} \ + CC="${KERNEL_CC}" LD="${KERNEL_LD}" \ + AR="${KERNEL_AR}" \ + release + oe_runmake KERNEL_PATH=${STAGING_KERNEL_DIR} \ + KERNEL_SRC=${STAGING_KERNEL_DIR} \ + KERNEL_VERSION=${KERNEL_VERSION} \ + CC="${KERNEL_CC}" LD="${KERNEL_LD}" \ + AR="${KERNEL_AR}" \ + debug + oe_runmake KERNEL_PATH=${STAGING_KERNEL_DIR} \ + KERNEL_SRC=${STAGING_KERNEL_DIR} \ + KERNEL_VERSION=${KERNEL_VERSION} \ + CC="${KERNEL_CC}" LD="${KERNEL_LD}" \ + AR="${KERNEL_AR}" + + cd ${S}/cetools/packages/ti/bios/power + if ! [ -e omap3530 ] ; then tar xf ti_bios_power,omap3530_bld.tar ; fi + cd omap3530/lpm + + sed -i -e s:/db/toolsrc/library/tools/vendors/mvl/arm/omap3/OMAP35x_SDK_0.9.7/src/linux/kernel_org/2.6_kernel:${STAGING_KERNEL_DIR}:g \ + -e s:/db/toolsrc/library/tools/vendors/cs/arm/arm-2007q3/bin/arm-none-linux-gnueabi-:${TARGET_PREFIX}:g \ + -e s:/db/atree/library/trees/power/power-d02x/imports:${STAGING_DIR_TARGET}:g \ + Makefile + + oe_runmake KERNEL_PATH=${STAGING_KERNEL_DIR} \ + KERNEL_SRC=${STAGING_KERNEL_DIR} \ + KERNEL_VERSION=${KERNEL_VERSION} \ + CC="${KERNEL_CC}" LD="${KERNEL_LD}" \ + AR="${KERNEL_AR}" + + cd ${S}/examples + export CE_INSTALL_DIR=${S} + export XDC_INSTALL_DIR=${TIXDCTOOLSDIR} + export BIOS_INSTALL_DIR=${TITOOLSDIR}/${TIBIOSDIR} + + sed -i -e s:/db/toolsrc/library/tools/vendors/cs/arm/arm-2007q3:${CROSS_DIR}:g \ + -e s:/db/toolsrc/library/tools/vendors/ti/c6x/6.0.16/Linux:${TITOOLSDIR}/${TICGTOOLSDIR}:g \ + -e s:/db/toolsrc/library/tools/vendors/opensource/gcc/4.1.0/Linux/gcc-4.1.0-glibc-2.3.6/i686-unknown-linux-gnu:/usr:g \ + -e s:arm-none-linux-gnueabi-:${TARGET_PREFIX}:g \ + -e 's:true, // build for uC Linux:false,:g' \ + ${S}/examples/user.bld + + sed -i -e s:/db/toolsrc/library/tools/vendors/ti/c6x/6.0.16/Linux:${TITOOLSDIR}/${TICGTOOLSDIR}:g \ + xdcpaths.mak + + for i in codecs extensions servers apps ; do + make -e -C ${S}/examples/ti/sdo/ce/examples/$i clean + make -e -C ${S}/examples/ti/sdo/ce/examples/$i + done + +} + +export DSPLIBS = "${S}/packages/ti/sdo/ce/utils/trace/lib/*.a* \ + ${S}/packages/ti/sdo/ce/bioslog/lib/*.a* \ + ${S}/packages/ti/sdo/ce/video/lib/*.a* \ + ${S}/packages/ti/sdo/ce/audio/lib/*.a* \ + ${S}/packages/ti/sdo/ce/speech/lib/*.a* \ + ${S}/packages/ti/sdo/ce/lib/*.a* \ + ${S}/packages/ti/sdo/ce/alg/lib/*.a* \ + ${S}/cetools/packages/ti/sdo/fc/dman3/*.a* \ + ${S}/cetools/packages/ti/sdo/fc/acpy3/*.a* \ + ${S}/packages/ti/sdo/ce/osal/linux/lib/osal_linux_470.a* \ + ${S}/packages/ti/sdo/ce/utils/xdm/lib/*.a* \ + ${S}/cetools/packages/ti/sdo/utils/trace/lib/*.a* \ + " + +do_install() { + unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS + cd ${S}/cetools/packages/ti/sdo/linuxutils/cmem + oe_runmake install + install -d ${D}/lib/modules/${KERNEL_VERSION}/kernel/drivers/dsp + mv ${D}/cmemk.ko ${D}/lib/modules/${KERNEL_VERSION}/kernel/drivers/dsp + install -d ${D}/${base_sbindir} + cd ${D} ; mv apitest apitestd multi_process multi_processd translate translated ${D}/${base_sbindir} + + + install -d ${D}/${libdir} + for i in ${DSPLIBS}; do + install -m 0755 $i ${D}/${libdir}/ || true + done + install -m 0755 ${S}/cetools/packages/ti/sdo/linuxutils/cmem/lib/*.a ${D}/${libdir} +} + +do_stage() { + install -d ${STAGING_LIBDIR} + for i in ${DSPLIBS} ; do + install -m 0755 $i ${STAGING_LIBDIR}/ + ln -sf ${STAGING_LIBDIR}/$(basename $i | awk -F. '{print $1}').a470MV ${STAGING_LIBDIR}/$(basename $i | awk -F. '{print $1}').a || true + done + + install -m 0755 ${S}/cetools/packages/ti/sdo/linuxutils/cmem/lib/*.a ${STAGING_LIBDIR}/ + + install -d ${STAGING_INCDIR}/codec-engine} + + for header in $(find ${S}/cetools/packages/ -name "*.h") ; do + install -d ${STAGING_INCDIR}/codec-engine/$(dirname $header | sed s:${S}::g) + cp -pPr $header ${STAGING_INCDIR}/codec-engine/$(echo $header | sed s:${S}::g) + done + + for header in $(find ${S}/packages/ -name "*.h") ; do + install -d ${STAGING_INCDIR}/codec-engine/$(dirname $header | sed s:${S}::g) + cp -pPr $header ${STAGING_INCDIR}/codec-engine/$(echo $header | sed s:${S}::g) + done + +} + +pkg_postinst_${PN}-module () { + if [ -n "$D" ]; then + exit 1 + fi + depmod -a + update-modules || true +} + +pkg_postrm_${PN}-module () { + update-modules || true +} + +PACKAGES =+ "dsplink-cmemk-module" +FILES_dsplink-cmemk-module = "${sysconfdir} /lib/modules/${KERNEL_VERSION}/kernel/drivers/dsp/*ko" +INHIBIT_PACKAGE_STRIP = "1" + +FILES_${PN} = "${base_sbindir}" + +PACKAGE_ARCH = "${MACHINE_ARCH}" diff --git a/packages/dsplink/dsplink.inc b/packages/dsplink/dsplink.inc index f303b6e53f..3b18d490cd 100644 --- a/packages/dsplink/dsplink.inc +++ b/packages/dsplink/dsplink.inc @@ -16,7 +16,7 @@ require ti-paths.inc DSPLINKPLATFORM ?= "Davinci" DSPLINKPLATFORM_omap5912osk = "OMAP" -DSPLINKPLATFORM_beagleboard = "OMAP" +DSPLINKPLATFORM_beagleboard = "OMAP3530" DSPLINKPLATFORM_davinci-sffsdr = "Davinci" DSPLINKPLATFORM_davinci-dvevm = "Davinci" @@ -32,21 +32,21 @@ KERNELARMFLAGS = "-include linux/autoconf.h -c -iwithprefix include -Iinclude -W -fno-omit-frame-pointer -mapcs -mno-sched-prolog \ -mlittle-endian \ -D__LINUX_ARM_ARCH__=5 -march=armv5t -mtune=arm9tdmi \ - -msoft-float -Uarm -mapcs \ + -Uarm \ -Wdeclaration-after-statement -Os -marm -mabi=aapcs-linux" KERNELARMFLAGS_armv6 = "-include linux/autoconf.h -c -iwithprefix include -Iinclude -Wall -Wstrict-prototypes \ -Wno-trigraphs -fno-strict-aliasing -fno-common \ -fno-omit-frame-pointer -mapcs -mno-sched-prolog \ -mlittle-endian \ -D__LINUX_ARM_ARCH__=6 -march=armv6j -mtune=arm1136jf-s \ - -msoft-float -Uarm -mapcs \ + -Uarm \ -Wdeclaration-after-statement -Os -marm -mabi=aapcs-linux" KERNELARMFLAGS_armv7a = "-c -nostdinc -include $(BASE_OSINC)/linux/autoconf.h -isystem $(OSINC_PLATFORM) -iwithprefix include -Iinclude -Wall -Wstrict-prototypes \ -Wno-trigraphs -fno-strict-aliasing -fno-common \ -fno-omit-frame-pointer -mapcs -mno-sched-prolog \ -mlittle-endian \ -D__LINUX_ARM_ARCH__=7 -march=armv7-a -mtune=cortex-a8 \ - -msoft-float -Uarm -mapcs \ + -Uarm \ -Wdeclaration-after-statement -Os -marm -mabi=aapcs-linux \ -D__KERNEL__ -mno-thumb-interwork -msoft-float -fno-optimize-sibling-calls -g -fno-stack-protector -Wno-pointer-sign -g -DUSE_UDEV=1 -DOS_LINUX -DLINUX_KERNEL" @@ -54,56 +54,38 @@ DSPFLAGS = "-q -pdr -pdv -pden -ml3 -mv64+ " export DSPLINKPLATFORM export DSPLINKDSP +export DSPLINKSOC do_configure () { - cp ${WORKDIR}/CURRENTCFG.MK ${S}/config - cp ${WORKDIR}/openembedded.mk ${S}/make/Linux - cp ${WORKDIR}/c64xx_5.xx_linux.mk ${S}/make/DspBios - - sed -i -e s:SED_ME_SOURCEDIR:${S}:g \ - -e s:SED_ME_GPPDISTRO:openembedded:g \ - -e s:SED_ME_KERNELVERSION:${KERNEL_VERSION}:g \ - -e s:SED_ME_DSPDISTRO:c64xx_5.xx_linux:g \ - -e s:SED_ME_PLATFORM:${DSPLINKPLATFORM}:g \ - -e s:SED_ME_DSP:${DSPLINKDSP}:g \ - -e s:SED_ME_SOC:${DSPLINKSOC}:g \ - ${S}/config/CURRENTCFG.MK - - sed -i -e s:SED_ME_CROSS:${STAGING_INCDIR}:g \ - -e s:SED_ME_STAGINGDIR:${STAGING_DIR_TARGET}:g \ - -e s:SED_ME_TARGET_PREFIX:${TARGET_PREFIX}:g \ - -e s:SED_ME_KERNELDIR:${STAGING_KERNEL_DIR}:g \ - -e 's:SEDME_KERNEL_ARMFLAGS:${KERNELARMFLAGS}:g' \ - -e 's:SEDME_USER_ARMFLAGS:${USERARMFLAGS}:g' \ - ${S}/make/Linux/openembedded.mk - - sed -i -e s:SEDME_TITOOLS_BASEPATH:${TITOOLSDIR}:g \ - -e s:SEDME_BIOSUNPACKDIR:${TIBIOSDIR}:g \ - -e s:SEDME_CGTOOLSDIR:${TICGTOOLSDIR}:g \ - -e 's:SEDME_DSPFLAGS:${DSPFLAGS}:g' \ - ${S}/make/DspBios/c64xx_5.xx_linux.mk + +# Run perl script to create appropriate makefiles (v1.60 and up) +DSPLINK=${S} perl config/bin/dsplinkcfg.pl --platform=${DSPLINKPLATFORM} --nodsp=1 --dspcfg_0=OMAP3530SHMEM --dspos_0=DSPBIOS5XX --gppos=OMAPLSP --comps=ponslrmc + } PARALLEL_MAKE = "" do_compile () { - ln -sf ${S}/gpp/src/api/*h ${S}/gpp/inc/ - ln -sf ${S}/gpp/src/pmgr/Linux/2.6.18 ${S}/gpp/src/pmgr/Linux/${KERNEL_VERSION} - ln -sf ${S}/gpp/src/api/Linux/2.6.18 ${S}/gpp/src/api/Linux/${KERNEL_VERSION} - ln -sf ${S}/gpp/src/osal/Linux/2.6.18 ${S}/gpp/src/osal/Linux/${KERNEL_VERSION} - ln -sf ${S}/gpp/src/pmgr/Linux/drv_pmgr.h ${S}/gpp/inc/drv_pmgr.h - ln -sf ${S}/gpp/src/pmgr/pmgr_proc.h ${S}/gpp/inc/pmgr_proc.h - unset DISPLAY + sed -i -e s:armv7a:armv7-a:g make/Linux/omap3530_2.6.mk || true + + # export various settings to override the defaults in the makefiles + export DSP_BASE_CGTOOLS=${TITOOLSDIR}/${TICGTOOLSDIR} + export DSP_BASE_BIOS=${TITOOLSDIR}/${TIBIOSDIR} + export DSP_BASE_RTDX=${TITOOLSDIR}/${TIBIOSDIR}/packages/ti/rtdx + export GPPTOOL_DIR=${CROSS_DIR} + export LINUXKERNEL_INSTALL_DIR=${STAGING_KERNEL_DIR} + export LINK_INSTALL_DIR=${S} + export VARIANT=${DSPLINKSOC} + export PLATFORM=${DSPLINKPLATFORM} + export BASE_TOOLCHAIN=${CROSS_DIR} + export BASE_CGTOOLS=${BASE_TOOLCHAIN}/bin + export OSINC_PLATFORM1=${CROSS_DIR}/lib/gcc/${TARGET_SYS}/$(${TARGET_PREFIX}gcc -dumpversion)/include + export OSINC_TARGET=${BASE_TOOLCHAIN}/target/usr/include + export ARCHIVER_AR=${TARGET_PREFIX}ar + export BASE_SABIOS=${DSP_BASE_BIOS} - sed -i -e 's:gcc:gcc${KERNEL_CCSUFFIX}:' ${S}/make/Linux/openembedded.mk - oe_runmake CC="${KERNEL_CC}" LD="${KERNEL_LD}" -C ${S}/gpp/src all targets - - sed -i -e 's:gcc${KERNEL_CCSUFFIX}:gcc:' ${S}/make/Linux/openembedded.mk - oe_runmake -C ${S}/gpp/src/samples - - oe_runmake -C ${S}/dsp/src - oe_runmake -C ${S}/dsp/src/samples + make -e -f ${WORKDIR}/Makefile.dsplink } do_install () { @@ -121,14 +103,9 @@ do_install () { install ${S}/gpp/BUILD/EXPORT/RELEASE/scalegpp ${D}/${bindir} install -d ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/loop.out ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/messagemulti.out ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/message.out ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/mpcsxfer.out ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/mplist.out ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/readwrite.out ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/ringio.out ${D}/${datadir}/dsplink - install ${S}/dsp/BUILD/EXPORT/RELEASE/scale.out ${D}/${datadir}/dsplink + for i in $(find ${S}/dsp/BUILD/ -name "*.out") ; do + install ${i} ${D}/${datadir}/dsplink + done install -d ${D}/${libdir} install -m 0755 ${S}/gpp/BUILD/EXPORT/RELEASE/dsplink.lib ${D}/${libdir} @@ -136,8 +113,8 @@ do_install () { do_stage() { - install -d ${STAGING_LIBDIR} - install -m 0755 ${S}/gpp/BUILD/EXPORT/RELEASE/dsplink.lib ${STAGING_LIBDIR}/ + install -d ${STAGING_DIR_TARGET}/dsplink + cp -pPr ${S}/* ${STAGING_DIR_TARGET}/dsplink/ } pkg_postinst_${PN}-module () { diff --git a/packages/dsplink/dsplink_1.51.00.08.bb b/packages/dsplink/dsplink_1.51.00.08.bb index b110ab9fb6..3c6d39e6c9 100644 --- a/packages/dsplink/dsplink_1.51.00.08.bb +++ b/packages/dsplink/dsplink_1.51.00.08.bb @@ -17,10 +17,11 @@ SRC_URI = "http://install.tarball.in.source.dir/dsplink_1_51_00_08.tar.gz \ file://c64xx_5.xx_linux.mk \ file://openembedded.mk \ file://prcs-fix-include.patch;patch=1;pnum=2 \ + file://Makefile.dsplink \ " SRC_URI_append_beagleboard = " \ - file://dsplink-128M.patch;patch=1;pnum=2 \ +# file://dsplink-128M.patch;patch=1;pnum=2 \ " S = "${WORKDIR}/dsplink_1_51_00_08/dsplink" diff --git a/packages/dsplink/dsplink_1.60.00.04.bb b/packages/dsplink/dsplink_1.60.00.04.bb new file mode 100644 index 0000000000..2e0de85b58 --- /dev/null +++ b/packages/dsplink/dsplink_1.60.00.04.bb @@ -0,0 +1,21 @@ +require dsplink.inc + + +DEFAULT_PREFERENCE = "-1" +DEFAULT_PREFERENCE_armv7a = "1" + +# The tconf tool breaks if there is a '.' in your pwd +PR = "r0" +PE = "1" +PV = "160" + +# Get dsplink tarball from TI website, place in sources and calculate +# md5sum +# Look for tarball at https://www-a.ti.com/downloads/sds_support/targetcontent/link/index.html + +SRC_URI = "http://install.tarball.in.source.dir/dsplink_1_60_00_04.tar.gz \ + file://Makefile.dsplink \ +" + +S = "${WORKDIR}/dsplink_1_60_00_04/dsplink" + diff --git a/packages/dsplink/files/Makefile.dsplink b/packages/dsplink/files/Makefile.dsplink new file mode 100755 index 0000000000..7156bcbda3 --- /dev/null +++ b/packages/dsplink/files/Makefile.dsplink @@ -0,0 +1,106 @@ +# +# ======== makeunix ======== +# + +# Import Tools Path from Rules.make +#include Rules.make + +PROJECT_BASE_DIR = $(shell pwd) +LINUXKERNEL_INSTALL_DIR:=/home/rmonklocal/oe/angstrom-davinci-dvevm-tmp/staging/davinci-dvevm-angstrom-linux-gnueabi/kernel +LINK_INSTALL_DIR := /home/rmonklocal/dsplink_1_51/dsplink + +# The prefix to be added before the GNU compiler tools (optionally including +# path), i.e. "arm_v5t_le-" or "/opt/bin/arm_v5t_le-". +GPPTOOL_DIR:=/home/rmonklocal/oe/angstrom-davinci-dvevm-tmp/cross + +# ---- DSP tools ---- +DSP_BASE_CGTOOLS := /home/rmonklocal/opt/cg6x_6_0_19 +DSP_BASE_BIOS := /home/rmonklocal/opt/bios_5_32_03 +DSP_BASE_RTDX := /home/rmonklocal/opt/bios_5_32_03/packages/ti/rtdx +OSINC_PLATFORM1 := something +ARCHIVER_AR := something + +# ---- get build host OS ---- +UNAME=$(shell uname) +ifeq "$(UNAME)" "Linux" + BUILD_HOST_OS=Linux +else + BUILD_HOST_OS=Solaris +endif + +# ---- construct Link build make vars ---- +GPP_MAKE_OPTS := COMPILER=$(GPPTOOL_DIR)/bin/arm-angstrom-linux-gnueabi-gcc \ + LINKER=$(GPPTOOL_DIR)/bin/arm-angstrom-linux-gnueabi-gcc \ + LD=$(GPPTOOL_DIR)/bin/arm-angstrom-linux-gnueabi-ld \ + ARCHIVER1=$(GPPTOOL_DIR)/bin/arm-angstrom-linux-gnueabi-ld \ + ARCHIVER2=$(GPPTOOL_DIR)/bin/arm-angstrom-linux-gnueabi-ld \ + CROSS_COMPILE=arm-angstrom-linux-gnueabi- \ + DSPLINK=$(LINK_INSTALL_DIR) \ + BASE_TOOLCHAIN=$(GPPTOOL_DIR) \ + BASE_BUILDOS=$(LINUXKERNEL_INSTALL_DIR) \ + ARCHIVER=$(ARCHIVER_AR) OSINC_PLATFORM=$(OSINC_PLATFORM1) \ + #STD_KRNL_FLAGS=\ + -include linux/autoconf.h -c -iwithprefix include -Iinclude -Wall -Wstrict-prototypes \ + -Wno-trigraphs -fno-strict-aliasing -fno-common \ + -fno-omit-frame-pointer -mapcs -mno-sched-prolog \ + -mlittle-endian \ + -D__LINUX_ARM_ARCH__=5 -march=armv5t -mtune=arm9tdmi \ + -msoft-float -Uarm -mapcs \ + -Wdeclaration-after-statement -Os -marm -mabi=aapcs-linux + + #STD_KRNL_FLAGS=\ + -nostdinc \ + -isystem /home/rmonklocal/oe/angstrom-davinci-dvevm-tmp/cross/lib/gcc/arm-angstrom-linux-gnueabi/4.2.4/include \ + -D__KERNEL__ \ + -Iinclude -include include/linux/autoconf.h \ + -mlittle-endian \ + -Wall \ + -Wundef \ + -Wstrict-prototypes \ + -Wno-trigraphs \ + -fno-strict-aliasing \ + -fno-common \ + -Werror-implicit-function-declaration \ + -Os \ + -fno-stack-protector \ + -marm \ + -fno-omit-frame-pointer \ + -mapcs \ + -mno-sched-prolog \ + -mabi=aapcs-linux \ + -mno-thumb-interwork \ + -D__LINUX_ARM_ARCH__=5 \ + -march=armv5te \ + -mtune=arm9tdmi \ + -msoft-float \ + -Uarm \ + -fno-omit-frame-pointer \ + -fno-optimize-sibling-calls \ + -Wdeclaration-after-statement \ + -Wno-pointer-sign \ + -c + +DSP_MAKE_OPTS := DSPLINK=$(DSPLINK) \ + DPPROOT=$(DSPLINK)/dsp \ + BASE_SABIOS=$(DSP_BASE_BIOS) \ + BASE_CGTOOLS=$(DSP_BASE_CGTOOLS) \ + BASE_RTDX=$(DSP_BASE_RTDX) + + +# ======== all ======== +all: $(LINK_INSTALL_DIR)/packages/dsplink/gpp/export/BIN/Linux/Davinci/RELEASE/dsplinkk.ko + +$(LINK_INSTALL_DIR)/packages/dsplink/gpp/export/BIN/Linux/Davinci/RELEASE/dsplinkk.ko: + @echo Building DSPLINK GPP driver, libs + make -s -C $(LINK_INSTALL_DIR)/gpp/src $(GPP_MAKE_OPTS) + make -s -C $(DSPLINK)/gpp/src/samples $(GPP_MAKE_OPTS) + @echo Building DSPLINK DSP libs and message sample for DaVinci... + make -C $(DSPLINK)/dsp/src $(DSP_MAKE_OPTS) + make -C $(DSPLINK)/dsp/src/samples $(DSP_MAKE_OPTS) + +# clean rules +clean: + @echo Cleaning DSPLINK GPP driver, libs + make -s -C $(LINK_INSTALL_DIR)/gpp/src $(GPP_MAKE_OPTS) clean + @rm -rf $(LINK_INSTALL_DIR)/gpp/export/BIN/* + @rm -rf $(LINK_INSTALL_DIR)/gpp/export/INCLUDE/* diff --git a/packages/dsplink/files/c64xx_5.xx_linux.mk b/packages/dsplink/files/c64xx_5.xx_linux.mk index ea806fff4e..0a75c147ae 100644 --- a/packages/dsplink/files/c64xx_5.xx_linux.mk +++ b/packages/dsplink/files/c64xx_5.xx_linux.mk @@ -135,7 +135,7 @@ CC_SW_REL := -o3 # ---------------------------------------------------------------------------- # Standard flags for the compiler # ---------------------------------------------------------------------------- -STD_CC_FLAGS := SEDME_DSPFLAGS -d"CHIP_DM642" +STD_CC_FLAGS := SEDME_DSPFLAGS # ---------------------------------------------------------------------------- # Standard flags for the compiler when building an executable diff --git a/packages/dsplink/ti-paths.inc b/packages/dsplink/ti-paths.inc index 83cbe683c0..b54f4bc6f5 100644 --- a/packages/dsplink/ti-paths.inc +++ b/packages/dsplink/ti-paths.inc @@ -1,8 +1,8 @@ # Path to the dir where the TI tools are unpacked TITOOLSDIR ?= "/OE/TI" # Path under TITOOLSDIR where dspbios is unpacked -TIBIOSDIR ?= "bios_5_32_03" -TIXDCTOOLSDIR ?= "${TIBIOSDIR}/xdctools" +TIBIOSDIR ?= "bios_5_32_04" +TIXDCTOOLSDIR ?= "${TITOOLSDIR}/xdctools_3_10_02" # Path under TITOOLSDIR where the dsp toolchain is unpacked -TICGTOOLSDIR ?= "cg6x_6_1_2" +TICGTOOLSDIR ?= "cg6x_6_0_19" diff --git a/packages/fswebcam/fswebcam_20070108.bb b/packages/fswebcam/fswebcam_20070108.bb new file mode 100644 index 0000000000..e24dbf3533 --- /dev/null +++ b/packages/fswebcam/fswebcam_20070108.bb @@ -0,0 +1,10 @@ +DESCRIPTION = "Webcam imaage grabber and manipulation application." +SECTION = "graphics" +DEPENDS = "gd" +LICENSE = "GPL" + +PR = "r0" + +inherit autotools + +SRC_URI = "http://www.firestorm.cx/fswebcam/files/${P}.tar.gz" diff --git a/packages/gcc/gcc-configure-common.inc b/packages/gcc/gcc-configure-common.inc index 795911db74..e88cee5cb2 100644 --- a/packages/gcc/gcc-configure-common.inc +++ b/packages/gcc/gcc-configure-common.inc @@ -75,6 +75,13 @@ do_configure () { export LDFLAGS_FOR_BUILD="${BUILD_LDFLAGS}" export ARCH_FLAGS_FOR_TARGET="${ARCH_FLAGS_FOR_TARGET}" (cd ${S} && gnu-configize) || die "failure running gnu-configize" + + # splice our idea of where the headers live into gcc's world + echo "NATIVE_SYSTEM_HEADER_DIR = ${layout_includedir}" > ${T}/t-oe + sed 's%^tmake_file=.*$%& ${T}/t-oe%' < ${S}/gcc/Makefile.in >${S}/gcc/Makefile.in.new + mv ${S}/gcc/Makefile.in.new ${S}/gcc/Makefile.in + + echo "#define STANDARD_INCLUDE_DIR \"${layout_includedir}\"" >> ${S}/gcc/defaults.h + oe_runconf } - diff --git a/packages/gcc/gcc-cross-kernel.inc b/packages/gcc/gcc-cross-kernel.inc index a567c60119..c0a8de77cc 100644 --- a/packages/gcc/gcc-cross-kernel.inc +++ b/packages/gcc/gcc-cross-kernel.inc @@ -9,6 +9,13 @@ do_install () { : } +do_compile () { + # This compiler is only for the kernel. Don't bother running fixincludes. + mkdir -p gcc + touch gcc/stmp-fixinc + oe_runmake +} + do_stage () { cd gcc oe_runmake installdirs install-common install-headers install-libgcc diff --git a/packages/gdb/gdb.inc b/packages/gdb/gdb.inc index 8e7121bd49..77a9ca6a3e 100644 --- a/packages/gdb/gdb.inc +++ b/packages/gdb/gdb.inc @@ -29,6 +29,11 @@ do_configure () { # override this function to avoid the autoconf/automake/aclocal/autoheader # calls for now (cd ${S} && gnu-configize) || die "failure in running gnu-configize" + + # Remove duplicate spaces to work around configure complaining about + # changed LDFLAGS. + LDFLAGS=$(echo "${LDFLAGS}" | sed "s/ / /") + CPPFLAGS="" oe_runconf } diff --git a/packages/gdb/gdb_6.3.bb b/packages/gdb/gdb_6.3.bb index a558775ff4..988cfb4c44 100644 --- a/packages/gdb/gdb_6.3.bb +++ b/packages/gdb/gdb_6.3.bb @@ -1,3 +1,3 @@ require gdb.inc -PR = "r3" +PR = "r4" diff --git a/packages/gdb/gdb_6.4.bb b/packages/gdb/gdb_6.4.bb index de6bcf34ba..be40f3f12b 100644 --- a/packages/gdb/gdb_6.4.bb +++ b/packages/gdb/gdb_6.4.bb @@ -1,3 +1,3 @@ require gdb.inc -PR = "r1"
\ No newline at end of file +PR = "r2" diff --git a/packages/gdb/gdb_6.6.bb b/packages/gdb/gdb_6.6.bb index c507a48aa8..e4502a648e 100644 --- a/packages/gdb/gdb_6.6.bb +++ b/packages/gdb/gdb_6.6.bb @@ -1,5 +1,5 @@ require gdb.inc -PR = "r4" +PR = "r5" SRC_URI += "file://early_debug_in_nptl.patch;patch=1;pnum=0" diff --git a/packages/gdb/gdb_6.7.1.bb b/packages/gdb/gdb_6.7.1.bb index a411974b15..fedf2bf1f1 100644 --- a/packages/gdb/gdb_6.7.1.bb +++ b/packages/gdb/gdb_6.7.1.bb @@ -1,5 +1,7 @@ require gdb.inc +PR = "r1" + DEFAULT_PREFERENCE_avr32 = "99" SRC_URI_avr32 = " http://avr32linux.org/twiki/pub/Main/GDBPatches/gdb-6.7.1.atmel.1.0.3.tar.bz2" S_avr32 = "${WORKDIR}/gdb-6.7.1.atmel.1.0.3" diff --git a/packages/gdb/gdb_6.8.bb b/packages/gdb/gdb_6.8.bb index 5b8f3769bd..be40f3f12b 100644 --- a/packages/gdb/gdb_6.8.bb +++ b/packages/gdb/gdb_6.8.bb @@ -1,3 +1,3 @@ require gdb.inc -PR = "r1" +PR = "r2" diff --git a/packages/giflib/giflib_4.1.6.bb b/packages/giflib/giflib_4.1.6.bb new file mode 100644 index 0000000000..0f4223c76a --- /dev/null +++ b/packages/giflib/giflib_4.1.6.bb @@ -0,0 +1,18 @@ +SECTION = "libs" +DESCRIPTION = "shared library for GIF images" +SRC_URI = "${SOURCEFORGE_MIRROR}/giflib/${BP}.tar.bz2" +LICENSE = "MIT" +PR = "r1" + +PACKAGES += "${PN}-utils" + +FILES_${PN} = "${libdir}" +FILES_${PN}-utils = "${bindir}" + +inherit autotools + +do_stage() { + oe_libinstall -so -C lib/.libs libgif ${STAGING_LIBDIR} + + install -m 0644 lib/gif_lib.h ${STAGING_INCDIR}/ +} diff --git a/packages/glibc/files/etc/ld.so.conf b/packages/glibc/files/etc/ld.so.conf deleted file mode 100644 index dfa65edb85..0000000000 --- a/packages/glibc/files/etc/ld.so.conf +++ /dev/null @@ -1,2 +0,0 @@ -/usr/local/lib -/usr/X11R6/lib diff --git a/packages/glibc/glibc-package.bbclass b/packages/glibc/glibc-package.bbclass index 43a64d8af1..e982a24eb2 100644 --- a/packages/glibc/glibc-package.bbclass +++ b/packages/glibc/glibc-package.bbclass @@ -26,12 +26,15 @@ ENABLE_BINARY_LOCALE_GENERATION ?= "0" # BINARY_LOCALE_ARCHES is a space separated list of regular expressions BINARY_LOCALE_ARCHES ?= "arm.*" +# Set this to zero if you don't want ldconfig in the output package +USE_LDCONFIG ?= "1" + PACKAGES = "glibc-dbg glibc catchsegv sln nscd ldd localedef glibc-utils glibc-dev glibc-doc glibc-locale libsegfault glibc-extra-nss glibc-thread-db glibc-pcprofile" PACKAGES_DYNAMIC = "glibc-gconv-* glibc-charmap-* glibc-localedata-* locale-base-* glibc-binary-localedata-*" libc_baselibs = "/lib/libc* /lib/libm* /lib/ld* /lib/libpthread* /lib/libresolv* /lib/librt* /lib/libutil* /lib/libnsl* /lib/libnss_files* /lib/libnss_compat* /lib/libnss_dns* /lib/libdl* /lib/libanl* /lib/libBrokenLocale*" -FILES_${PN} = "${sysconfdir} ${libc_baselibs} /sbin/ldconfig ${libexecdir}/* ${datadir}/zoneinfo" +FILES_${PN} = "${libc_baselibs} ${libexecdir}/* ${datadir}/zoneinfo ${@base_conditional('USE_LDCONFIG', '1', '/sbin/ldconfig', '', d)}" FILES_ldd = "${bindir}/ldd" FILES_libsegfault = "/lib/libSegFault*" FILES_glibc-extra-nss = "/lib/libnss*" @@ -70,7 +73,6 @@ do_install() { h=`echo $r|sed -e's,\.x$,.h,'` install -m 0644 ${S}/sunrpc/rpcsvc/$h ${D}/${includedir}/rpcsvc/ done - install -m 0644 ${WORKDIR}/etc/ld.so.conf ${D}/${sysconfdir}/ install -d ${D}${libdir}/locale make -f ${WORKDIR}/generate-supported.mk IN="${S}/localedata/SUPPORTED" OUT="${WORKDIR}/SUPPORTED" # get rid of some broken files... diff --git a/packages/glibc/glibc_2.2.5.bb b/packages/glibc/glibc_2.2.5.bb index 3e57d857ae..c7d308b6a2 100644 --- a/packages/glibc/glibc_2.2.5.bb +++ b/packages/glibc/glibc_2.2.5.bb @@ -1,7 +1,7 @@ require glibc.inc FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/glibc-cvs" -PR = "r18" +PR = "r19" DEFAULT_PREFERENCE_sh3 = "-99" diff --git a/packages/glibc/glibc_2.3.2+cvs20040726.bb b/packages/glibc/glibc_2.3.2+cvs20040726.bb index c53c2dd620..f0521d41ab 100644 --- a/packages/glibc/glibc_2.3.2+cvs20040726.bb +++ b/packages/glibc/glibc_2.3.2+cvs20040726.bb @@ -3,7 +3,7 @@ require glibc.inc DEFAULT_PREFERENCE_sh3 = "-99" FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/glibc-cvs" -PR = "r29" +PR = "r30" GLIBC_ADDONS ?= "linuxthreads" diff --git a/packages/glibc/glibc_2.3.2.bb b/packages/glibc/glibc_2.3.2.bb index 05b46ba2e9..891d9da388 100644 --- a/packages/glibc/glibc_2.3.2.bb +++ b/packages/glibc/glibc_2.3.2.bb @@ -1,6 +1,6 @@ require glibc.inc -PR = "r18" +PR = "r19" DEFAULT_PREFERENCE_sh3 = "-99" diff --git a/packages/glibc/glibc_2.3.3+cvs20041128.bb b/packages/glibc/glibc_2.3.3+cvs20041128.bb index a2492f6801..560347d770 100644 --- a/packages/glibc/glibc_2.3.3+cvs20041128.bb +++ b/packages/glibc/glibc_2.3.3+cvs20041128.bb @@ -3,7 +3,7 @@ require glibc.inc DEFAULT_PREFERENCE_sh3 = "-99" FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/glibc-cvs" -PR = "r14" +PR = "r15" GLIBC_ADDONS ?= "linuxthreads" diff --git a/packages/glibc/glibc_2.3.3+cvs20050221.bb b/packages/glibc/glibc_2.3.3+cvs20050221.bb index 16525029da..e0becb8c3f 100644 --- a/packages/glibc/glibc_2.3.3+cvs20050221.bb +++ b/packages/glibc/glibc_2.3.3+cvs20050221.bb @@ -3,7 +3,7 @@ require glibc.inc DEFAULT_PREFERENCE_sh3 = "-99" FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/glibc-cvs" -PR = "r14" +PR = "r15" GLIBC_ADDONS ?= "linuxthreads" diff --git a/packages/glibc/glibc_2.3.3+cvs20050420.bb b/packages/glibc/glibc_2.3.3+cvs20050420.bb index bd394921b9..1ae351889c 100644 --- a/packages/glibc/glibc_2.3.3+cvs20050420.bb +++ b/packages/glibc/glibc_2.3.3+cvs20050420.bb @@ -5,7 +5,7 @@ DEFAULT_PREFERENCE_i586 = "0" DEFAULT_PREFERENCE_sh3 = "-99" FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/glibc-cvs" -PR = "r12" +PR = "r13" GLIBC_ADDONS ?= "linuxthreads" diff --git a/packages/glibc/glibc_2.3.3.bb b/packages/glibc/glibc_2.3.3.bb index 2908baf4cb..03711f7ae3 100644 --- a/packages/glibc/glibc_2.3.3.bb +++ b/packages/glibc/glibc_2.3.3.bb @@ -1,6 +1,6 @@ require glibc.inc -PR = "r15" +PR = "r16" DEFAULT_PREFERENCE_sh3 = "-99" diff --git a/packages/glibc/glibc_2.3.5+cvs20050627.bb b/packages/glibc/glibc_2.3.5+cvs20050627.bb index 54a1654404..c9d0b66daf 100644 --- a/packages/glibc/glibc_2.3.5+cvs20050627.bb +++ b/packages/glibc/glibc_2.3.5+cvs20050627.bb @@ -2,7 +2,7 @@ require glibc.inc FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/glibc-cvs-2.3.5" SRCDATE = "20050627" -PR = "r21" +PR = "r22" #Doesnt build for sh3 DEFAULT_PREFERENCE_sh3="-1" diff --git a/packages/glibc/glibc_2.3.6.bb b/packages/glibc/glibc_2.3.6.bb index edf15cab61..8442a15e69 100644 --- a/packages/glibc/glibc_2.3.6.bb +++ b/packages/glibc/glibc_2.3.6.bb @@ -1,6 +1,6 @@ require glibc.inc -PR = "r3" +PR = "r4" #FILESPATH = "${@base_set_filespath([ '${FILE_DIRNAME}/glibc-2.3.6', '${FILE_DIRNAME}/orig/glibc', '${FILE_DIRNAME}/orig/files', '${FILE_DIRNAME}/orig' ], d)}" diff --git a/packages/glibc/glibc_2.4.bb b/packages/glibc/glibc_2.4.bb index 17cebf8921..8ee10003df 100644 --- a/packages/glibc/glibc_2.4.bb +++ b/packages/glibc/glibc_2.4.bb @@ -1,6 +1,6 @@ require glibc.inc -PR = "r19" +PR = "r20" #add the hosts that are confirmed to be working to COMPATIBLE_HOSTi COMPATIBLE_HOST = '(i.86.*-linux|sh.*-linux)' diff --git a/packages/glibc/glibc_2.5.bb b/packages/glibc/glibc_2.5.bb index a5908762ef..6e2295904b 100644 --- a/packages/glibc/glibc_2.5.bb +++ b/packages/glibc/glibc_2.5.bb @@ -1,5 +1,5 @@ require glibc.inc -PR = "r17" +PR = "r18" ARM_INSTRUCTION_SET = "arm" diff --git a/packages/glibc/glibc_2.6.1.bb b/packages/glibc/glibc_2.6.1.bb index 5cd1d5b3e7..c563a1f389 100644 --- a/packages/glibc/glibc_2.6.1.bb +++ b/packages/glibc/glibc_2.6.1.bb @@ -1,5 +1,5 @@ require glibc.inc -PR = "r12" +PR = "r13" PACKAGES_DYNAMIC = "libc6*" RPROVIDES_${PN}-dev = "libc6-dev virtual-libc-dev" diff --git a/packages/glibc/glibc_2.7.bb b/packages/glibc/glibc_2.7.bb index b6c329091c..3349ce5eef 100644 --- a/packages/glibc/glibc_2.7.bb +++ b/packages/glibc/glibc_2.7.bb @@ -5,7 +5,7 @@ ARM_INSTRUCTION_SET = "arm" PACKAGES_DYNAMIC = "libc6*" RPROVIDES_${PN}-dev = "libc6-dev virtual-libc-dev" -PR = "r7" +PR = "r8" # the -isystem in bitbake.conf screws up glibc do_stage BUILD_CPPFLAGS = "-I${STAGING_INCDIR_NATIVE}" diff --git a/packages/gtk+/gdk-pixbuf-csource-native_2.12.11.bb b/packages/gtk+/gdk-pixbuf-csource-native_2.12.11.bb index 2af16a344b..c7abc18855 100644 --- a/packages/gtk+/gdk-pixbuf-csource-native_2.12.11.bb +++ b/packages/gtk+/gdk-pixbuf-csource-native_2.12.11.bb @@ -1,6 +1,6 @@ require gtk+_${PV}.bb inherit native -DEPENDS = "jpeg-native libpng-native gettext-native glib-2.0-native" +DEPENDS = "jpeg-native libpng-native gettext-native glib-2.0-native libx11-native" S = "${WORKDIR}/gtk+-${PV}" FILESPATH = "${FILE_DIRNAME}/gdk-pixbuf-csource:${FILE_DIRNAME}/gtk+-${PV}:${FILE_DIRNAME}/files" SRC_URI += "file://reduce-dependencies.patch;patch=1" diff --git a/packages/kexecboot/linux-kexecboot-2.6.26/collie/defconfig b/packages/kexecboot/linux-kexecboot-2.6.26/collie/defconfig index 4434962a64..2e7c8130f6 100644 --- a/packages/kexecboot/linux-kexecboot-2.6.26/collie/defconfig +++ b/packages/kexecboot/linux-kexecboot-2.6.26/collie/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.26 -# Thu Jul 24 23:13:04 2008 +# Sat Oct 18 23:52:11 2008 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -34,7 +34,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_LOCALVERSION="" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION_AUTO=y CONFIG_SWAP=y CONFIG_SYSVIPC=y CONFIG_SYSVIPC_SYSCTL=y @@ -49,6 +49,8 @@ CONFIG_LOG_BUF_SHIFT=14 # CONFIG_NAMESPACES is not set CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="initramfs.cpio.gz" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y CONFIG_EMBEDDED=y @@ -251,7 +253,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="console=ttySA0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 rootdelay=3 mem=64M fbcon=rotate:1 dyntick=enable" +CONFIG_CMDLINE="console=ttySA0,115200n8 console=tty1 mem=64M fbcon=rotate:1" # CONFIG_XIP_KERNEL is not set CONFIG_KEXEC=y CONFIG_ATAGS_PROC=y @@ -342,14 +344,12 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_RAM is not set # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set -CONFING_MTD_SHARP=y +CONFIG_MTD_SHARP=y # # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PHYSMAP is not set -# CONFIG_MTD_ARM_INTEGRATOR is not set CONFIG_MTD_SA1100=y # CONFIG_MTD_PLATRAM is not set diff --git a/packages/kexecboot/linux-kexecboot_2.6.26.bb b/packages/kexecboot/linux-kexecboot_2.6.26.bb index 2db6575a72..a0d00ab120 100644 --- a/packages/kexecboot/linux-kexecboot_2.6.26.bb +++ b/packages/kexecboot/linux-kexecboot_2.6.26.bb @@ -1,6 +1,6 @@ require linux-kexecboot.inc -PR = "r5" +PR = "r6" DEFAULT_PREFERENCE = "-1" DEFAULT_PREFERENCE_qemuarm = "-1" DEFAULT_PREFERENCE_qemux86 = "-1" diff --git a/packages/lesstif/files/000_bootstrap_script.diff b/packages/lesstif/files/000_bootstrap_script.diff new file mode 100644 index 0000000000..ca1a998700 --- /dev/null +++ b/packages/lesstif/files/000_bootstrap_script.diff @@ -0,0 +1,30 @@ +Index: lesstif1-1-0.93.94/bootstrap +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ lesstif1-1-0.93.94/bootstrap 2006-05-26 17:36:58.000000000 +0200 +@@ -0,0 +1,25 @@ ++#! /bin/sh ++ ++for x in aclocal.m4 configure config.guess config.log config.sub config.cache config.h.in config.h compile libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 ltmain.sh libtool ltconfig missing mkinstalldirs depcomp install-sh; do rm -f $x test/$x; done ++rm -Rf autom4te.cache ++ ++# Explain what we are doing from now ++set -x ++ ++libtoolize --copy --force ++aclocal-1.9 -I . ++autoconf ++autoheader ++automake-1.9 --foreign --add-missing --copy ++ ++cd test ++ ++libtoolize --copy --force ++aclocal-1.9 -I .. ++autoconf ++autoheader ++automake-1.9 --foreign --add-missing --copy ++ ++# Remove cruft that we no longer want ++rm -Rf autom4te.cache ++ diff --git a/packages/lesstif/files/000_libtool_linking.diff b/packages/lesstif/files/000_libtool_linking.diff new file mode 100644 index 0000000000..18a5bc57a4 --- /dev/null +++ b/packages/lesstif/files/000_libtool_linking.diff @@ -0,0 +1,11 @@ +--- lesstif2-0.94.4.orig/clients/Motif-2.1/mwm/Makefile.am ++++ lesstif2-0.94.4/clients/Motif-2.1/mwm/Makefile.am +@@ -37,7 +37,7 @@ + + appdir= $(libdir)/X11/app-defaults + +-mwmddir= $(libdir)/X11/mwm ++mwmddir= /etc/X11/mwm + mwmd_DATA= system.mwmrc alt.map README + + diff --git a/packages/lesstif/files/010_rebootstrap-small.diff b/packages/lesstif/files/010_rebootstrap-small.diff new file mode 100644 index 0000000000..8bf355fdb9 --- /dev/null +++ b/packages/lesstif/files/010_rebootstrap-small.diff @@ -0,0 +1,456 @@ +Index: lesstif2-0.95.0/ac_debug.m4 +=================================================================== +--- lesstif2-0.95.0.orig/ac_debug.m4 2004-02-01 16:49:40.000000000 +0100 ++++ lesstif2-0.95.0/ac_debug.m4 2006-07-11 11:11:36.000000000 +0200 +@@ -4,6 +4,90 @@ + dnl Source code which depends on this is mostly in + dnl DebugUtil.c/.h + dnl ++AC_DEFUN(LT_WITH_DMALLOC, ++[AC_MSG_CHECKING(if malloc debugging is wanted) ++AC_ARG_WITH(dmalloc, ++[ --with-dmalloc[=path] use dmalloc, see INSTALL(.html) for reference], ++[if test "$withval" = no; then ++ AC_MSG_RESULT(no) ++else ++dnl We overwrite the variables since we won't continue in ++dnl case of an error! ++dnl We modify CFLAGS, and also link libs (LDFLAGS) and programs (LIBS) ++ if test "$withval" != yes; then ++dnl a path was given ++ CFLAGS="$CFLAGS -I$withval/include -DDMALLOC_FUNC_CHECK" ++ ++ LDFLAGS="$LDFLAGS -L$withval/lib -ldmalloc" ++ LIBS="$LIBS -L$withval/lib -ldmalloc" ++ else ++dnl no path was given ++ CFLAGS="$CFLAGS -DDMALLOC_FUNC_CHECK" ++ LDFLAGS="$LDFLAGS -ldmalloc" ++ LIBS="$LIBS -ldmalloc" ++ fi ++ AC_TRY_LINK( ++ [#include <dmalloc.h>], ++ [char *ptr; ++ ptr=malloc(1); ++ free(ptr); ++ ], ++ [AC_DEFINE(WITH_DMALLOC,1, ++ [Define if using the dmalloc debugging malloc package]) ++ AC_MSG_RESULT(Using dmalloc)], ++ AC_MSG_ERROR(dmalloc not found) ++ ) ++fi], ++[AC_MSG_RESULT(no)]) ++]) ++ ++ ++dnl ++dnl Enable another malloc checker for debugging purposes ++dnl Source code which depends on this is mostly in ++dnl DebugUtil.c/.h ++dnl ++AC_DEFUN(LT_WITH_DBMALLOC, ++[AC_MSG_CHECKING(if malloc debugging is wanted) ++AC_ARG_WITH(dbmalloc, ++[ --with-dbmalloc[=path] use dbmalloc, see INSTALL(.html) for reference], ++[if test "$withval" = no; then ++ AC_MSG_RESULT(no) ++else ++dnl We overwrite the variables since we won't continue in ++dnl case of an error! ++dnl We modify CFLAGS, and also link libs (LDFLAGS) and programs (LIBS) ++ if test "$withval" != yes; then ++dnl a path was given ++ CFLAGS="$CFLAGS -I$withval/include" ++ ++ LDFLAGS="$LDFLAGS -L$withval/lib -ldbmalloc" ++ LIBS="$LIBS -L$withval/lib -ldbmalloc" ++ else ++dnl no path was given ++ LDFLAGS="$LDFLAGS -ldbmalloc" ++ LIBS="$LIBS -ldbmalloc" ++ fi ++ AC_TRY_LINK( ++ [#include <dbmalloc.h>], ++ [char *ptr; ++ ptr=malloc(1); ++ free(ptr); ++ ], ++ [AC_DEFINE(WITH_DBMALLOC,1, ++ [Define if using the dbmalloc debugging malloc package]) ++ AC_MSG_RESULT(Using dbmalloc)], ++ AC_MSG_ERROR(dbmalloc not found) ++ ) ++fi], ++[AC_MSG_RESULT(no)]) ++]) ++dnl ++dnl Enable malloc checker for debugging purposes ++dnl See http://dmalloc.com, INSTALL(.html) for references to this. ++dnl Source code which depends on this is mostly in ++dnl DebugUtil.c/.h ++dnl + AC_DEFUN([LT_WITH_DMALLOC], + [AC_MSG_CHECKING(if malloc debugging is wanted) + AC_ARG_WITH(dmalloc, +Index: lesstif2-0.95.0/ac_find_xft.m4 +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ lesstif2-0.95.0/ac_find_xft.m4 2006-07-11 11:11:44.000000000 +0200 +@@ -0,0 +1,299 @@ ++dnl ++dnl $Header: /home/kobras/cvsroot/debian/lesstif1-1/ac_find_xft.m4,v 1.1 2004/05/27 10:48:25 kobras Exp $ ++dnl ++dnl $XFree86: xc/lib/fontconfig/configure.in,v 1.7 2002/08/01 15:57:25 keithp Exp $ ++dnl ++dnl Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc. ++dnl Manipulated into AC_FIND_XFT macro by Danny Backx (also © 2002). ++dnl ++dnl Permission to use, copy, modify, distribute, and sell this software and its ++dnl documentation for any purpose is hereby granted without fee, provided that ++dnl the above copyright notice appear in all copies and that both that ++dnl copyright notice and this permission notice appear in supporting ++dnl documentation, and that the name of Keith Packard not be used in ++dnl advertising or publicity pertaining to distribution of the software without ++dnl specific, written prior permission. Keith Packard makes no ++dnl representations about the suitability of this software for any purpose. It ++dnl is provided "as is" without express or implied warranty. ++dnl ++dnl KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, ++dnl INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO ++dnl EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR ++dnl CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, ++dnl DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ++dnl TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ++dnl PERFORMANCE OF THIS SOFTWARE. ++dnl ++ ++AC_DEFUN(AC_FIND_XFT, ++[ ++AH_TEMPLATE([HAVE_FREETYPE], [We have the FreeType library]) ++AH_TEMPLATE([HAVE_FONTCONFIG], [We have the fontconfig library]) ++AH_TEMPLATE([HAVE_XRENDER], [We have the fontconfig library]) ++AH_TEMPLATE([FC_DEFAULT_FONTS], [We have the fontconfig library]) ++AH_TEMPLATE([X_FONT_DIR], [We have the fontconfig library]) ++AH_TEMPLATE([CONFDIR], [We have the fontconfig library]) ++AH_TEMPLATE([USE_XFT], [We have the fontconfig library]) ++ ++AC_ARG_WITH(freetype_includes, [ --with-freetype-includes=DIR Use FreeType includes in DIR], freetype_includes=$withval, freetype_includes=yes) ++AC_ARG_WITH(freetype_lib, [ --with-freetype-lib=DIR Use FreeType library in DIR], freetype_lib=$withval, freetype_lib=yes) ++AC_ARG_WITH(freetype_config, [ --with-freetype-config=PROG Use FreeType configuration program PROG], freetype_config=$withval, freetype_config=yes) ++dnl AC_ARG_WITH(expat, [ --with-expat=DIR Use Expat in DIR], expat=$withval, expat=yes) ++dnl AC_ARG_WITH(expat_includes, [ --with-expat-includes=DIR Use Expat includes in DIR], expat_includes=$withval, expat_includes=yes) ++dnl AC_ARG_WITH(expat_lib, [ --with-expat-lib=DIR Use Expat library in DIR], expat_lib=$withval, expat_lib=yes) ++AC_ARG_WITH(default_fonts, [ --with-default-fonts=DIR Use fonts from DIR when config is busted], defaultfonts="$withval", default_fonts=yes) ++dnl AC_ARG_WITH(confdir, [ --with-confdir=DIR Use DIR to store configuration files (default /etc/fonts)], confdir="$withval", confdir=yes) ++AC_ARG_WITH(fontconfig_includes, [ --with-fontconfig-includes=DIR Use Fontconfig includes in DIR], fontconfig_includes=$withval, fontconfig_includes=yes) ++AC_ARG_WITH(fontconfig_lib, [ --with-fontconfig-lib=DIR Use Fontconfig library in DIR], fontconfig_lib=$withval, fontconfig_lib=yes) ++AC_ARG_WITH(fontconfig_config, [ --with-fontconfig-config=PROG Use Fontconfig configuration program PROG], fontconfig_config=$withval, fontconfig_config=yes) ++AC_ARG_ENABLE(xrender, [ --enable-xrender Enable Xrender]) ++ ++# Using x libraries, set X font directory ++case "$no_x" in ++yes) ++ ;; ++*) ++ X_FONT_DIR="$x_libraries/X11/fonts" ++ AC_DEFINE_UNQUOTED(X_FONT_DIR,$X_FONT_DIR) ++ ;; ++esac ++AC_SUBST(X_FONT_DIR) ++ ++# ++# Check freetype configuration ++# ++case "$freetype_config" in ++no) ++ ;; ++yes) ++ AC_CHECK_PROG(ft_config,freetype-config,freetype-config,no) ++ ;; ++*) ++ ft_config="$freetype_config" ++ ;; ++esac ++ ++case "$freetype_includes" in ++no) ++ FREETYPE_CFLAGS="" ++ ;; ++yes) ++ case "$ft_config" in ++ no) ++ FREETYPE_CFLAGS="" ++ ;; ++ *) ++ FREETYPE_CFLAGS="`$ft_config --cflags`" ++ ;; ++ esac ++ ;; ++*) ++ FREETYPE_CFLAGS="-I$freetype_includes" ++ ;; ++esac ++ ++case "$freetype_lib" in ++no) ++ freetype_lib="" ++ ;; ++yes) ++ case "$ft_config" in ++ no) ++ freetype_lib="" ++ ;; ++ *) ++ freetype_lib="`$ft_config --libs`" ++ ;; ++ esac ++ ;; ++*) ++ freetype_lib="-L$freetype_lib -lfreetype" ++ ;; ++esac ++ ++saved_LIBS="$LIBS" ++LIBS="$LIBS $freetype_lib" ++saved_CPPFLAGS="$CPPFLAGS" ++CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" ++AC_CHECK_HEADERS(freetype/freetype.h) ++ ++HAVEFREETYPE="no" ++case "$ac_cv_header_freetype_freetype_h" in ++no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++yes) ++ AC_CHECK_FUNCS(FT_Init_FreeType) ++ case "$ac_cv_func_FT_Init_FreeType" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ HAVEFREETYPE="yes" ++ AC_DEFINE(HAVE_FREETYPE) ++ AC_SUBST(FREETYPE_CFLAGS) ++ ;; ++ esac ++ ;; ++esac ++ ++case "$default_fonts" in ++yes) ++ FC_DEFAULT_FONTS="/usr/share/fonts" ++ AC_DEFINE_UNQUOTED(FC_DEFAULT_FONTS, "/usr/share/fonts") ++ ;; ++*) ++ FC_DEFAULT_FONTS="$default_fonts" ++ AC_DEFINE_UNQUOTED(FC_DEFAULT_FONTS, "$default_fonts") ++ ;; ++esac ++ ++AC_SUBST(FC_DEFAULT_FONTS) ++ ++# ++# Set CONFDIR and FONTCONFIG_PATH ++# ++ ++case "$confdir" in ++no|yes) ++ confdir=/etc/fonts ++ ;; ++*) ++ ;; ++esac ++AC_SUBST(confdir) ++CONFDIR='${confdir}' ++AC_DEFINE_UNQUOTED(CONFDIR, "$CONFDIR") ++AC_SUBST(CONFDIR) ++ ++# ++# Check X configuration ++# ++HAVEXRENDER="no" ++case "$enable_xrender" in ++no) ++ ;; ++*) ++ XRENDER_CFLAGS="-I$x_includes" ++ XRENDER_LIBS="-L$x_libraries -lXft -lXrender" ++ ++ saved_LIBS="$LIBS" ++ LIBS="$LIBS $XRENDER_LIBS" ++ saved_CPPFLAGS="$CPPFLAGS" ++ CPPFLAGS="$CPPFLAGS $XRENDER_CFLAGS" ++ AC_CHECK_HEADERS(X11/extensions/Xrender.h) ++ ++ case "$ac_cv_header_X11_extensions_Xrender_h" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ AC_CHECK_FUNCS(XRenderParseColor) ++ case "$ac_cv_func_XRenderParseColor" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ HAVEXRENDER="yes" ++ AC_DEFINE(HAVE_XRENDER) ++ AC_SUBST(XRENDER_CFLAGS) ++ AC_SUBST(XRENDER_LIBS) ++ ;; ++ esac ++ ;; ++ esac ++ ++ ;; ++esac ++ ++# ++# Check fontconfig configuration ++# ++case "$fontconfig_config" in ++no) ++ ;; ++yes) ++ AC_CHECK_PROG(fc_config,fontconfig-config,fontconfig-config,no) ++ ;; ++*) ++ fc_config="$fontconfig_config" ++ ;; ++esac ++ ++case "$fontconfig_includes" in ++no) ++ FONTCONFIG_CFLAGS="" ++ ;; ++yes) ++ case "$fc_config" in ++ no) ++ FONTCONFIG_CFLAGS="" ++ ;; ++ *) ++ FONTCONFIG_CFLAGS="`$fc_config --cflags`" ++ ;; ++ esac ++ ;; ++*) ++ FONTCONFIG_CFLAGS="-I$fontconfig_includes" ++ ;; ++esac ++ ++case "$fontconfig_lib" in ++no) ++ fontconfig_lib="" ++ ;; ++yes) ++ case "$fc_config" in ++ no) ++ fontconfig_lib="" ++ ;; ++ *) ++ FONTCONFIG_LIBS="`$fc_config --libs`" ++ ;; ++ esac ++ ;; ++*) ++ FONTCONFIG_LIBS="-L$fontconfig_lib -lfontconfig" ++ ;; ++esac ++ ++saved_LIBS="$LIBS" ++LIBS="$LIBS $FONTCONFIG_LIBS" ++saved_CPPFLAGS="$CPPFLAGS" ++CPPFLAGS="$CPPFLAGS $FONTCONFIG_CFLAGS" ++AC_CHECK_HEADERS(fontconfig/fontconfig.h) ++ ++case "$ac_cv_header_fontconfig_fontconfig_h" in ++no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++yes) ++ AC_CHECK_FUNCS(FcInit) ++ case "$ac_cv_func_FcInit" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ AC_DEFINE(HAVE_FONTCONFIG) ++ AC_SUBST(FONTCONFIG_CFLAGS) ++ AC_SUBST(FONTCONFIG_LIBS) ++ ;; ++ esac ++ ;; ++esac ++ ++dnl ++dnl Should this be conditionally defined ? ++dnl ++if test "$HAVEXRENDER" = "yes" -a "$HAVEFREETYPE" = "yes" ++then ++ AC_DEFINE(USE_XFT) ++fi ++]) +Index: lesstif2-0.95.0/ac_have_libxp.m4 +=================================================================== +--- lesstif2-0.95.0.orig/ac_have_libxp.m4 2004-02-01 16:49:40.000000000 +0100 ++++ lesstif2-0.95.0/ac_have_libxp.m4 2006-07-11 11:11:36.000000000 +0200 +@@ -11,6 +11,52 @@ + dnl Makefiles. Perhaps one should immediately add those libs + dnl to link commands which include libXm version2.1?! + dnl ++AC_DEFUN(LT_HAVE_LIBXP, ++[AC_REQUIRE([AC_PATH_X]) ++AC_CACHE_CHECK(whether libXp is available, lt_cv_libxp, ++[lt_save_CFLAGS="$CFLAGS" ++lt_save_CPPFLAGS="$CPPFLAGS" ++lt_save_LIBS="$LIBS" ++LIBS="$X_LIBS -lXp -lXext -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS $LIBS" ++CFLAGS="$X_CFLAGS $CFLAGS" ++CPPFLAGS="$X_CFLAGS $CPPFLAGS" ++AC_TRY_LINK([ ++#include <X11/Intrinsic.h> ++#include <X11/extensions/Print.h> ++],[ ++Display *display=NULL; ++short major_version, minor_version; ++Status rc; ++rc=XpQueryVersion(display, &major_version, &minor_version); ++], ++lt_cv_libxp=yes, ++lt_cv_libxp=no) ++]) ++if test $lt_cv_libxp = yes; then ++ AC_DEFINE(HAVE_LIB_XP) ++ LT_HAVELIBXP=1 ++else ++ LT_HAVELIBXP=0 ++fi ++AM_CONDITIONAL(Have_Libxp, test "$lt_cv_libxp" = "yes") ++AC_SUBST(LT_HAVELIBXP) ++CFLAGS="$lt_save_CFLAGS" ++CPPFLAGS="$lt_save_CPPFLAGS" ++LIBS="$lt_save_LIBS" ++]) ++dnl ++dnl Check for libXp ++dnl In fact this check ensures that ++dnl - <X11/extensions/Print.h> and ++dnl - both libXp and libXext ++dnl are in place ++dnl Perhaps AC_CHECK_LIB() could be used as well, but ++dnl requires the same amount of work to get all linker ++dnl flags and additional libraries specified. ++dnl If the test succeeds 'Have_Libxp' will be defined within our ++dnl Makefiles. Perhaps one should immediately add those libs ++dnl to link commands which include libXm version2.1?! ++dnl + AC_DEFUN([LT_HAVE_LIBXP], + [AC_REQUIRE([AC_PATH_X]) + AC_CACHE_CHECK(whether libXp is available, lt_cv_libxp, diff --git a/packages/lesstif/files/020_bad_integer_cast.diff b/packages/lesstif/files/020_bad_integer_cast.diff new file mode 100644 index 0000000000..620d702f0f --- /dev/null +++ b/packages/lesstif/files/020_bad_integer_cast.diff @@ -0,0 +1,13 @@ +--- lesstif2-0.94.4.orig/include/Motif-2.1/XmI/XpmI.h ++++ lesstif2-0.94.4/include/Motif-2.1/XmI/XpmI.h +@@ -217,8 +217,8 @@ + FUNC(xpmHashSlot, xpmHashAtom *, (xpmHashTable *table, char *s)); + FUNC(xpmHashIntern, int, (xpmHashTable *table, char *tag, void *data)); + +-#define HashAtomData(i) ((void *)i) +-#define HashColorIndex(slot) ((unsigned int)((*slot)->data)) ++#define HashAtomData(i) ((void *)(uintptr_t)i) ++#define HashColorIndex(slot) ((uintptr_t)((*slot)->data)) + #define USE_HASHTABLE (cpp > 2 && ncolors > 4) + + /* I/O utility */ diff --git a/packages/lesstif/files/020_missing_xm_h.diff b/packages/lesstif/files/020_missing_xm_h.diff new file mode 100644 index 0000000000..2d298d5f4c --- /dev/null +++ b/packages/lesstif/files/020_missing_xm_h.diff @@ -0,0 +1,11 @@ +--- lesstif2-0.94.4.orig/include/Motif-2.1/Xm/XmStrDefs.h ++++ lesstif2-0.94.4/include/Motif-2.1/Xm/XmStrDefs.h +@@ -28,6 +28,8 @@ + + #include <X11/StringDefs.h> + ++#include <Xm/Xm.h> ++ + #ifdef __cplusplus + extern "C" { + #endif diff --git a/packages/lesstif/files/020_render_table_crash.diff b/packages/lesstif/files/020_render_table_crash.diff new file mode 100644 index 0000000000..1699dbe190 --- /dev/null +++ b/packages/lesstif/files/020_render_table_crash.diff @@ -0,0 +1,11 @@ +--- lesstif2-0.95.0.orig/lib/Xm-2.1/RenderTable.c ++++ lesstif2-0.95.0/lib/Xm-2.1/RenderTable.c +@@ -465,7 +465,7 @@ + DEBUGOUT(_LtDebug(__FILE__, w, "_XmRenderTableFinaliseTag(%s)\n", tag)); + #if 1 + /* Experimental start */ +- if (r->dpy == 0) ++ if (r->dpy == 0 && w) + r->dpy = XtDisplay(w); + /* Experimental end */ + #endif diff --git a/packages/lesstif/files/020_unsigned_int.diff b/packages/lesstif/files/020_unsigned_int.diff new file mode 100644 index 0000000000..a682d9704c --- /dev/null +++ b/packages/lesstif/files/020_unsigned_int.diff @@ -0,0 +1,38 @@ +--- lesstif2-0.94.4.orig/lib/Xm-2.1/Xpmcreate.c ++++ lesstif2-0.94.4/lib/Xm-2.1/Xpmcreate.c +@@ -1265,10 +1265,10 @@ + register char *src; + register char *dst; + register unsigned int *iptr; +- register unsigned int x, y, i; ++ register unsigned int x, y; + register char *data; + Pixel pixel, px; +- int nbytes, depth, ibu, ibpp; ++ int nbytes, depth, ibu, ibpp, i; + + data = image->data; + iptr = pixelindex; +--- lesstif2-0.94.4.orig/lib/Xm-2.1/Xpmscan.c ++++ lesstif2-0.94.4/lib/Xm-2.1/Xpmscan.c +@@ -672,8 +672,8 @@ + char *dst; + unsigned int *iptr; + char *data; +- unsigned int x, y, i; +- int bits, depth, ibu, ibpp, offset; ++ unsigned int x, y; ++ int bits, depth, ibu, ibpp, offset, i; + unsigned long lbt; + Pixel pixel, px; + +@@ -684,6 +684,9 @@ + ibpp = image->bits_per_pixel; + offset = image->xoffset; + ++ if (image->bitmap_unit < 0) ++ return (XpmNoMemory); ++ + if ((image->bits_per_pixel | image->depth) == 1) { + ibu = image->bitmap_unit; + for (y = 0; y < height; y++) diff --git a/packages/lesstif/files/020_xpmpipethrough.diff b/packages/lesstif/files/020_xpmpipethrough.diff new file mode 100644 index 0000000000..69f9a2464c --- /dev/null +++ b/packages/lesstif/files/020_xpmpipethrough.diff @@ -0,0 +1,381 @@ +Index: lesstif2-0.95.0/lib/Xm-2.1/XpmRdFToI.c +=================================================================== +--- lesstif2-0.95.0.orig/lib/Xm-2.1/XpmRdFToI.c 2004-11-18 22:00:58.000000000 +0100 ++++ lesstif2-0.95.0/lib/Xm-2.1/XpmRdFToI.c 2006-07-11 11:13:29.000000000 +0200 +@@ -44,11 +44,15 @@ + DebugUtil.h! */ + #include <stdio.h> + #include <string.h> ++#include <errno.h> + + #include <ctype.h> + #ifdef HAVE_SYS_TYPES_H + #include <sys/types.h> + #endif ++#ifdef HAVE_SYS_WAIT_H ++#include <sys/wait.h> ++#endif + #ifdef HAVE_SYS_STAT_H + #include <sys/stat.h> + #endif +@@ -87,16 +91,6 @@ + strcpy(dst, src); \ + else return (XpmFileInvalid); } + #endif +-#include <sys/stat.h> +-#if !defined(NO_ZPIPE) && defined(WIN32) +-# define popen _popen +-# define pclose _pclose +-# if defined(STAT_ZFILE) +-# include <io.h> +-# define stat _stat +-# define fstat _fstat +-# endif +-#endif + + LFUNC(OpenReadFile, int, (char *filename, xpmData *mdata)); + LFUNC(xpmDataClose, void, (xpmData *mdata)); +@@ -173,90 +167,131 @@ + } + #endif /* CXPMPROG */ + +-/* +- * open the given file to be read as an xpmData which is returned. +- */ + #ifndef NO_ZPIPE +- FILE *s_popen(char *cmd, const char *type); +-#else +-# define s_popen popen ++/* Do not depend on errno after read_through */ ++FILE* ++xpmPipeThrough(fd, cmd, arg1, mode) ++ int fd; ++ const char* cmd; ++ const char* arg1; ++ const char* mode; ++{ ++ FILE* fp; ++ int status, fds[2], in = 0, out = 1; ++ pid_t pid; ++ if ( 'w' == *mode ) ++ out = 0, in = 1; ++ if ( pipe(fds) < 0 ) ++ return NULL; ++ pid = fork(); ++ if ( pid < 0 ) ++ goto fail1; ++ if ( 0 == pid ) ++ { ++ close(fds[in]); ++ if ( dup2(fds[out], out) < 0 ) ++ goto err; ++ close(fds[out]); ++ if ( dup2(fd, in) < 0 ) ++ goto err; ++ close(fd); ++ pid = fork(); ++ if ( pid < 0 ) ++ goto err; ++ if ( 0 == pid ) ++ { ++ execlp(cmd, cmd, arg1, NULL); ++ perror(cmd); ++ goto err; ++ } ++ _exit(0); ++ err: ++ _exit(1); ++ } ++ close(fds[out]); ++ /* calling process: wait for first child */ ++ while ( waitpid(pid, &status, 0) < 0 && EINTR == errno ) ++ ; ++ if ( WIFSIGNALED(status) || ++ (WIFEXITED(status) && WEXITSTATUS(status) != 0) ) ++ goto fail2; ++ fp = fdopen(fds[in], mode); ++ if ( !fp ) ++ goto fail2; ++ close(fd); /* still open in 2nd child */ ++ return fp; ++fail1: ++ close(fds[out]); ++fail2: ++ close(fds[in]); ++ return NULL; ++} + #endif + ++/* ++ * open the given file to be read as an xpmData which is returned. ++ */ + static int + OpenReadFile(filename, mdata) + char *filename; + xpmData *mdata; + { +-#ifndef NO_ZPIPE +- char buf[BUFSIZ]; +-# ifdef STAT_ZFILE +- char *compressfile; +- struct stat status; +-# endif +-#endif +- + if (!filename) { + mdata->stream.file = (stdin); + mdata->type = XPMFILE; + } else { +-#ifndef NO_ZPIPE +- size_t len = strlen(filename); +- +- if(len == 0 || +- filename[len-1] == '/') +- return(XpmOpenFailed); +- if ((len > 2) && !strcmp(".Z", filename + (len - 2))) { +- mdata->type = XPMPIPE; +- snprintf(buf, sizeof(buf), "uncompress -c \"%s\"", filename); +- if (!(mdata->stream.file = s_popen(buf, "r"))) +- return (XpmOpenFailed); +- +- } else if ((len > 3) && !strcmp(".gz", filename + (len - 3))) { +- mdata->type = XPMPIPE; +- snprintf(buf, sizeof(buf), "gunzip -qc \"%s\"", filename); +- if (!(mdata->stream.file = s_popen(buf, "r"))) +- return (XpmOpenFailed); +- +- } else { +-# ifdef STAT_ZFILE +- if (!(compressfile = (char *) XpmMalloc(len + 4))) ++ int fd = open(filename, O_RDONLY); ++#if defined(NO_ZPIPE) ++ if ( fd < 0 ) ++ return XpmOpenFailed; ++#else ++ const char* ext = NULL; ++ if ( fd >= 0 ) ++ ext = strrchr(filename, '.'); ++#ifdef STAT_ZFILE /* searching for z-files if the given name not found */ ++ else ++ { ++ size_t len = strlen(filename); ++ char *compressfile = (char *) XpmMalloc(len + 4); ++ if ( !compressfile ) + return (XpmNoMemory); +- +- snprintf(compressfile, len+4, "%s.Z", filename); +- if (!stat(compressfile, &status)) { +- snprintf(buf, sizeof(buf), "uncompress -c \"%s\"", compressfile); +- if (!(mdata->stream.file = s_popen(buf, "r"))) { ++ strcpy(compressfile, filename); ++ strcpy(compressfile + len, ext = ".Z"); ++ fd = open(compressfile, O_RDONLY); ++ if ( fd < 0 ) ++ { ++ strcpy(compressfile + len, ext = ".gz"); ++ fd = open(compressfile, O_RDONLY); ++ if ( fd < 0 ) ++ { + XpmFree(compressfile); +- return (XpmOpenFailed); +- } +- mdata->type = XPMPIPE; +- } else { +- snprintf(compressfile, len+4, "%s.gz", filename); +- if (!stat(compressfile, &status)) { +- snprintf(buf, sizeof(buf), "gunzip -c \"%s\"", compressfile); +- if (!(mdata->stream.file = s_popen(buf, "r"))) { +- XpmFree(compressfile); +- return (XpmOpenFailed); +- } +- mdata->type = XPMPIPE; +- } else { +-# endif +-#endif +- if (!(mdata->stream.file = fopen(filename, "r"))) { +-#if !defined(NO_ZPIPE) && defined(STAT_ZFILE) +- XpmFree(compressfile); +-#endif +- return (XpmOpenFailed); +- } +- mdata->type = XPMFILE; +-#ifndef NO_ZPIPE +-# ifdef STAT_ZFILE ++ return XpmOpenFailed; + } + } + XpmFree(compressfile); +-# endif + } + #endif ++ if ( ext && !strcmp(ext, ".Z") ) ++ { ++ mdata->type = XPMPIPE; ++ mdata->stream.file = xpmPipeThrough(fd, "uncompress", "-c", "r"); ++ } ++ else if ( ext && !strcmp(ext, ".gz") ) ++ { ++ mdata->type = XPMPIPE; ++ mdata->stream.file = xpmPipeThrough(fd, "gunzip", "-qc", "r"); ++ } ++ else ++#endif /* z-files */ ++ { ++ mdata->type = XPMFILE; ++ mdata->stream.file = fdopen(fd, "r"); ++ } ++ if (!mdata->stream.file) ++ { ++ close(fd); ++ return (XpmOpenFailed); ++ } + } + mdata->CommentLength = 0; + #ifdef CXPMPROG +@@ -273,15 +308,6 @@ + xpmDataClose(mdata) + xpmData *mdata; + { +- switch (mdata->type) { +- case XPMFILE: +- if (mdata->stream.file != (stdin)) +- fclose(mdata->stream.file); +- break; +-#ifndef NO_ZPIPE +- case XPMPIPE: ++ if (mdata->stream.file != (stdin)) + fclose(mdata->stream.file); +- break; +-#endif +- } + } +Index: lesstif2-0.95.0/lib/Xm-2.1/XpmWrFFrI.c +=================================================================== +--- lesstif2-0.95.0.orig/lib/Xm-2.1/XpmWrFFrI.c 2005-04-13 20:03:27.000000000 +0200 ++++ lesstif2-0.95.0/lib/Xm-2.1/XpmWrFFrI.c 2006-07-11 11:13:29.000000000 +0200 +@@ -50,11 +50,15 @@ + DebugUtil.h! */ + #include <stdio.h> + #include <string.h> ++#include <errno.h> + + #include <ctype.h> + #ifdef HAVE_SYS_TYPES_H + #include <sys/types.h> + #endif ++#ifdef HAVE_SYS_WAIT_H ++#include <sys/wait.h> ++#endif + #ifdef HAVE_SYS_STAT_H + #include <sys/stat.h> + #endif +@@ -94,11 +98,6 @@ + else return (XpmFileInvalid); } + #endif + +-#if !defined(NO_ZPIPE) && defined(WIN32) +-# define popen _popen +-# define pclose _pclose +-#endif +- + /* MS Windows define a function called WriteFile @#%#&!!! */ + LFUNC(xpmWriteFile, int, (FILE *file, XpmImage *image, char *name, + XpmInfo *info)); +@@ -354,58 +353,48 @@ + fprintf(file, ",\n\"XPMENDEXT\""); + } + ++ ++#ifndef NO_ZPIPE ++FUNC(xpmPipeThrough, FILE*, (int fd, ++ const char* cmd, ++ const char* arg1, ++ const char* mode)); ++#endif ++ + /* + * open the given file to be written as an xpmData which is returned + */ +-#ifndef NO_ZPIPE +- FILE *s_popen(char *cmd, const char *type); +-#else +-# define s_popen popen +-#endif + static int + OpenWriteFile(filename, mdata) + char *filename; + xpmData *mdata; + { +-#ifndef NO_ZPIPE +- char buf[BUFSIZ]; +- +-#endif +- + if (!filename) { + mdata->stream.file = (stdout); + mdata->type = XPMFILE; + } else { + #ifndef NO_ZPIPE +- size_t len = strlen(filename); +- +- if(len == 0 || +- filename[0] == '/' || +- strstr(filename, "../") != NULL || +- filename[len-1] == '/') +- return(XpmOpenFailed); +- ++ size_t len; ++#endif ++ int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); ++ if ( fd < 0 ) ++ return(XpmOpenFailed); ++#ifndef NO_ZPIPE ++ len = strlen(filename); + if (len > 2 && !strcmp(".Z", filename + (len - 2))) { +- snprintf(buf, sizeof(buf), "compress > \"%s\"", filename); +- if (!(mdata->stream.file = s_popen(buf, "w"))) +- return (XpmOpenFailed); +- ++ mdata->stream.file = xpmPipeThrough(fd, "compress", NULL, "w"); + mdata->type = XPMPIPE; + } else if (len > 3 && !strcmp(".gz", filename + (len - 3))) { +- snprintf(buf, sizeof(buf), "gzip -q > \"%s\"", filename); +- if (!(mdata->stream.file = s_popen(buf, "w"))) +- return (XpmOpenFailed); +- ++ mdata->stream.file = xpmPipeThrough(fd, "gzip", "-q", "w"); + mdata->type = XPMPIPE; +- } else { ++ } else + #endif +- if (!(mdata->stream.file = fopen(filename, "w"))) +- return (XpmOpenFailed); +- ++ { ++ mdata->stream.file = fdopen(fd, "w"); + mdata->type = XPMFILE; +-#ifndef NO_ZPIPE + } +-#endif ++ if (!mdata->stream.file) ++ return (XpmOpenFailed); + } + return (XpmSuccess); + } +@@ -417,15 +406,6 @@ + xpmDataClose(mdata) + xpmData *mdata; + { +- switch (mdata->type) { +- case XPMFILE: +- if (mdata->stream.file != (stdout)) +- fclose(mdata->stream.file); +- break; +-#ifndef NO_ZPIPE +- case XPMPIPE: ++ if (mdata->stream.file != (stdout)) + fclose(mdata->stream.file); +- break; +-#endif +- } + } diff --git a/packages/lesstif/files/021_xim_chained_list_crash.diff b/packages/lesstif/files/021_xim_chained_list_crash.diff new file mode 100644 index 0000000000..10bdf8d0b9 --- /dev/null +++ b/packages/lesstif/files/021_xim_chained_list_crash.diff @@ -0,0 +1,24 @@ +diff -ru lesstif2-0.94.4-old/lib/Xm-2.1/XmIm.c lesstif2-0.94.4/lib/Xm-2.1/XmIm.c +--- lesstif2-0.94.4-old/lib/Xm-2.1/XmIm.c 2004-10-20 21:32:11.000000000 +0200 ++++ lesstif2-0.94.4/lib/Xm-2.1/XmIm.c 2007-03-28 14:39:27.000000000 +0200 +@@ -133,7 +133,10 @@ + p->next = q->next; + } + +- XtFree((char *)stuff); ++ /* if count!=0 then someone uses the stuff as orig_xim ++ so unlink it but not free it */ ++ if (!stuff->count) ++ XtFree((char *)stuff); + } + + /* +@@ -1060,6 +1063,8 @@ + XCloseIM(stuff->xim); + DEBUGOUT(_LtDebug(__FILE__, w, "XCloseIM(%p)\n", stuff->xim)); + stuff->orig_xim->xim = NULL; ++ /* stuff->orig_xim is now useless */ ++ XtFree(stuff->orig_xim); + } else { + DEBUGOUT(_LtDebug(__FILE__, w, "XmImCloseXIM(%p), count -> %d\n", + stuff->xim, stuff->orig_xim->count)); diff --git a/packages/lesstif/files/030_manpage.diff b/packages/lesstif/files/030_manpage.diff new file mode 100644 index 0000000000..da6363258c --- /dev/null +++ b/packages/lesstif/files/030_manpage.diff @@ -0,0 +1,11 @@ +--- lesstif2-0.94.4.orig/doc/lessdox/clients/mwm.1 ++++ lesstif2-0.94.4/doc/lessdox/clients/mwm.1 +@@ -88,7 +88,7 @@ + + The resources are documented in the app defaults file for + .BR mwm , +-.I usr/X11R6/lib/X11/app-defaults/Mwm . ++.IR /etc/X11/app-defaults/Mwm . + + .SH COPYING + See the file COPYING which accompanies this distribution of mwm. diff --git a/packages/lesstif/files/ac_debug.m4.diff b/packages/lesstif/files/ac_debug.m4.diff new file mode 100644 index 0000000000..3fe6e8357d --- /dev/null +++ b/packages/lesstif/files/ac_debug.m4.diff @@ -0,0 +1,95 @@ +Index: lesstif2-0.95.0/ac_debug.m4 +=================================================================== +--- lesstif2-0.95.0.orig/ac_debug.m4 2004-02-01 16:49:40.000000000 +0100 ++++ lesstif2-0.95.0/ac_debug.m4 2006-07-11 11:11:36.000000000 +0200 +@@ -4,6 +4,90 @@ + dnl Source code which depends on this is mostly in + dnl DebugUtil.c/.h + dnl ++AC_DEFUN(LT_WITH_DMALLOC, ++[AC_MSG_CHECKING(if malloc debugging is wanted) ++AC_ARG_WITH(dmalloc, ++[ --with-dmalloc[=path] use dmalloc, see INSTALL(.html) for reference], ++[if test "$withval" = no; then ++ AC_MSG_RESULT(no) ++else ++dnl We overwrite the variables since we won't continue in ++dnl case of an error! ++dnl We modify CFLAGS, and also link libs (LDFLAGS) and programs (LIBS) ++ if test "$withval" != yes; then ++dnl a path was given ++ CFLAGS="$CFLAGS -I$withval/include -DDMALLOC_FUNC_CHECK" ++ ++ LDFLAGS="$LDFLAGS -L$withval/lib -ldmalloc" ++ LIBS="$LIBS -L$withval/lib -ldmalloc" ++ else ++dnl no path was given ++ CFLAGS="$CFLAGS -DDMALLOC_FUNC_CHECK" ++ LDFLAGS="$LDFLAGS -ldmalloc" ++ LIBS="$LIBS -ldmalloc" ++ fi ++ AC_TRY_LINK( ++ [#include <dmalloc.h>], ++ [char *ptr; ++ ptr=malloc(1); ++ free(ptr); ++ ], ++ [AC_DEFINE(WITH_DMALLOC,1, ++ [Define if using the dmalloc debugging malloc package]) ++ AC_MSG_RESULT(Using dmalloc)], ++ AC_MSG_ERROR(dmalloc not found) ++ ) ++fi], ++[AC_MSG_RESULT(no)]) ++]) ++ ++ ++dnl ++dnl Enable another malloc checker for debugging purposes ++dnl Source code which depends on this is mostly in ++dnl DebugUtil.c/.h ++dnl ++AC_DEFUN(LT_WITH_DBMALLOC, ++[AC_MSG_CHECKING(if malloc debugging is wanted) ++AC_ARG_WITH(dbmalloc, ++[ --with-dbmalloc[=path] use dbmalloc, see INSTALL(.html) for reference], ++[if test "$withval" = no; then ++ AC_MSG_RESULT(no) ++else ++dnl We overwrite the variables since we won't continue in ++dnl case of an error! ++dnl We modify CFLAGS, and also link libs (LDFLAGS) and programs (LIBS) ++ if test "$withval" != yes; then ++dnl a path was given ++ CFLAGS="$CFLAGS -I$withval/include" ++ ++ LDFLAGS="$LDFLAGS -L$withval/lib -ldbmalloc" ++ LIBS="$LIBS -L$withval/lib -ldbmalloc" ++ else ++dnl no path was given ++ LDFLAGS="$LDFLAGS -ldbmalloc" ++ LIBS="$LIBS -ldbmalloc" ++ fi ++ AC_TRY_LINK( ++ [#include <dbmalloc.h>], ++ [char *ptr; ++ ptr=malloc(1); ++ free(ptr); ++ ], ++ [AC_DEFINE(WITH_DBMALLOC,1, ++ [Define if using the dbmalloc debugging malloc package]) ++ AC_MSG_RESULT(Using dbmalloc)], ++ AC_MSG_ERROR(dbmalloc not found) ++ ) ++fi], ++[AC_MSG_RESULT(no)]) ++]) ++dnl ++dnl Enable malloc checker for debugging purposes ++dnl See http://dmalloc.com, INSTALL(.html) for references to this. ++dnl Source code which depends on this is mostly in ++dnl DebugUtil.c/.h ++dnl + AC_DEFUN([LT_WITH_DMALLOC], + [AC_MSG_CHECKING(if malloc debugging is wanted) + AC_ARG_WITH(dmalloc, diff --git a/packages/lesstif/files/ac_find_xft.m4.diff b/packages/lesstif/files/ac_find_xft.m4.diff new file mode 100644 index 0000000000..69529b868d --- /dev/null +++ b/packages/lesstif/files/ac_find_xft.m4.diff @@ -0,0 +1,304 @@ +Index: lesstif2-0.95.0/ac_find_xft.m4 +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ lesstif2-0.95.0/ac_find_xft.m4 2006-07-11 11:11:44.000000000 +0200 +@@ -0,0 +1,299 @@ ++dnl ++dnl $Header: /home/kobras/cvsroot/debian/lesstif1-1/ac_find_xft.m4,v 1.1 2004/05/27 10:48:25 kobras Exp $ ++dnl ++dnl $XFree86: xc/lib/fontconfig/configure.in,v 1.7 2002/08/01 15:57:25 keithp Exp $ ++dnl ++dnl Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc. ++dnl Manipulated into AC_FIND_XFT macro by Danny Backx (also © 2002). ++dnl ++dnl Permission to use, copy, modify, distribute, and sell this software and its ++dnl documentation for any purpose is hereby granted without fee, provided that ++dnl the above copyright notice appear in all copies and that both that ++dnl copyright notice and this permission notice appear in supporting ++dnl documentation, and that the name of Keith Packard not be used in ++dnl advertising or publicity pertaining to distribution of the software without ++dnl specific, written prior permission. Keith Packard makes no ++dnl representations about the suitability of this software for any purpose. It ++dnl is provided "as is" without express or implied warranty. ++dnl ++dnl KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, ++dnl INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO ++dnl EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR ++dnl CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, ++dnl DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ++dnl TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ++dnl PERFORMANCE OF THIS SOFTWARE. ++dnl ++ ++AC_DEFUN(AC_FIND_XFT, ++[ ++AH_TEMPLATE([HAVE_FREETYPE], [We have the FreeType library]) ++AH_TEMPLATE([HAVE_FONTCONFIG], [We have the fontconfig library]) ++AH_TEMPLATE([HAVE_XRENDER], [We have the fontconfig library]) ++AH_TEMPLATE([FC_DEFAULT_FONTS], [We have the fontconfig library]) ++AH_TEMPLATE([X_FONT_DIR], [We have the fontconfig library]) ++AH_TEMPLATE([CONFDIR], [We have the fontconfig library]) ++AH_TEMPLATE([USE_XFT], [We have the fontconfig library]) ++ ++AC_ARG_WITH(freetype_includes, [ --with-freetype-includes=DIR Use FreeType includes in DIR], freetype_includes=$withval, freetype_includes=yes) ++AC_ARG_WITH(freetype_lib, [ --with-freetype-lib=DIR Use FreeType library in DIR], freetype_lib=$withval, freetype_lib=yes) ++AC_ARG_WITH(freetype_config, [ --with-freetype-config=PROG Use FreeType configuration program PROG], freetype_config=$withval, freetype_config=yes) ++dnl AC_ARG_WITH(expat, [ --with-expat=DIR Use Expat in DIR], expat=$withval, expat=yes) ++dnl AC_ARG_WITH(expat_includes, [ --with-expat-includes=DIR Use Expat includes in DIR], expat_includes=$withval, expat_includes=yes) ++dnl AC_ARG_WITH(expat_lib, [ --with-expat-lib=DIR Use Expat library in DIR], expat_lib=$withval, expat_lib=yes) ++AC_ARG_WITH(default_fonts, [ --with-default-fonts=DIR Use fonts from DIR when config is busted], defaultfonts="$withval", default_fonts=yes) ++dnl AC_ARG_WITH(confdir, [ --with-confdir=DIR Use DIR to store configuration files (default /etc/fonts)], confdir="$withval", confdir=yes) ++AC_ARG_WITH(fontconfig_includes, [ --with-fontconfig-includes=DIR Use Fontconfig includes in DIR], fontconfig_includes=$withval, fontconfig_includes=yes) ++AC_ARG_WITH(fontconfig_lib, [ --with-fontconfig-lib=DIR Use Fontconfig library in DIR], fontconfig_lib=$withval, fontconfig_lib=yes) ++AC_ARG_WITH(fontconfig_config, [ --with-fontconfig-config=PROG Use Fontconfig configuration program PROG], fontconfig_config=$withval, fontconfig_config=yes) ++AC_ARG_ENABLE(xrender, [ --enable-xrender Enable Xrender]) ++ ++# Using x libraries, set X font directory ++case "$no_x" in ++yes) ++ ;; ++*) ++ X_FONT_DIR="$x_libraries/X11/fonts" ++ AC_DEFINE_UNQUOTED(X_FONT_DIR,$X_FONT_DIR) ++ ;; ++esac ++AC_SUBST(X_FONT_DIR) ++ ++# ++# Check freetype configuration ++# ++case "$freetype_config" in ++no) ++ ;; ++yes) ++ AC_CHECK_PROG(ft_config,freetype-config,freetype-config,no) ++ ;; ++*) ++ ft_config="$freetype_config" ++ ;; ++esac ++ ++case "$freetype_includes" in ++no) ++ FREETYPE_CFLAGS="" ++ ;; ++yes) ++ case "$ft_config" in ++ no) ++ FREETYPE_CFLAGS="" ++ ;; ++ *) ++ FREETYPE_CFLAGS="`$ft_config --cflags`" ++ ;; ++ esac ++ ;; ++*) ++ FREETYPE_CFLAGS="-I$freetype_includes" ++ ;; ++esac ++ ++case "$freetype_lib" in ++no) ++ freetype_lib="" ++ ;; ++yes) ++ case "$ft_config" in ++ no) ++ freetype_lib="" ++ ;; ++ *) ++ freetype_lib="`$ft_config --libs`" ++ ;; ++ esac ++ ;; ++*) ++ freetype_lib="-L$freetype_lib -lfreetype" ++ ;; ++esac ++ ++saved_LIBS="$LIBS" ++LIBS="$LIBS $freetype_lib" ++saved_CPPFLAGS="$CPPFLAGS" ++CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" ++AC_CHECK_HEADERS(freetype/freetype.h) ++ ++HAVEFREETYPE="no" ++case "$ac_cv_header_freetype_freetype_h" in ++no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++yes) ++ AC_CHECK_FUNCS(FT_Init_FreeType) ++ case "$ac_cv_func_FT_Init_FreeType" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ HAVEFREETYPE="yes" ++ AC_DEFINE(HAVE_FREETYPE) ++ AC_SUBST(FREETYPE_CFLAGS) ++ ;; ++ esac ++ ;; ++esac ++ ++case "$default_fonts" in ++yes) ++ FC_DEFAULT_FONTS="/usr/share/fonts" ++ AC_DEFINE_UNQUOTED(FC_DEFAULT_FONTS, "/usr/share/fonts") ++ ;; ++*) ++ FC_DEFAULT_FONTS="$default_fonts" ++ AC_DEFINE_UNQUOTED(FC_DEFAULT_FONTS, "$default_fonts") ++ ;; ++esac ++ ++AC_SUBST(FC_DEFAULT_FONTS) ++ ++# ++# Set CONFDIR and FONTCONFIG_PATH ++# ++ ++case "$confdir" in ++no|yes) ++ confdir=/etc/fonts ++ ;; ++*) ++ ;; ++esac ++AC_SUBST(confdir) ++CONFDIR='${confdir}' ++AC_DEFINE_UNQUOTED(CONFDIR, "$CONFDIR") ++AC_SUBST(CONFDIR) ++ ++# ++# Check X configuration ++# ++HAVEXRENDER="no" ++case "$enable_xrender" in ++no) ++ ;; ++*) ++ XRENDER_CFLAGS="-I$x_includes" ++ XRENDER_LIBS="-L$x_libraries -lXft -lXrender" ++ ++ saved_LIBS="$LIBS" ++ LIBS="$LIBS $XRENDER_LIBS" ++ saved_CPPFLAGS="$CPPFLAGS" ++ CPPFLAGS="$CPPFLAGS $XRENDER_CFLAGS" ++ AC_CHECK_HEADERS(X11/extensions/Xrender.h) ++ ++ case "$ac_cv_header_X11_extensions_Xrender_h" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ AC_CHECK_FUNCS(XRenderParseColor) ++ case "$ac_cv_func_XRenderParseColor" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ HAVEXRENDER="yes" ++ AC_DEFINE(HAVE_XRENDER) ++ AC_SUBST(XRENDER_CFLAGS) ++ AC_SUBST(XRENDER_LIBS) ++ ;; ++ esac ++ ;; ++ esac ++ ++ ;; ++esac ++ ++# ++# Check fontconfig configuration ++# ++case "$fontconfig_config" in ++no) ++ ;; ++yes) ++ AC_CHECK_PROG(fc_config,fontconfig-config,fontconfig-config,no) ++ ;; ++*) ++ fc_config="$fontconfig_config" ++ ;; ++esac ++ ++case "$fontconfig_includes" in ++no) ++ FONTCONFIG_CFLAGS="" ++ ;; ++yes) ++ case "$fc_config" in ++ no) ++ FONTCONFIG_CFLAGS="" ++ ;; ++ *) ++ FONTCONFIG_CFLAGS="`$fc_config --cflags`" ++ ;; ++ esac ++ ;; ++*) ++ FONTCONFIG_CFLAGS="-I$fontconfig_includes" ++ ;; ++esac ++ ++case "$fontconfig_lib" in ++no) ++ fontconfig_lib="" ++ ;; ++yes) ++ case "$fc_config" in ++ no) ++ fontconfig_lib="" ++ ;; ++ *) ++ FONTCONFIG_LIBS="`$fc_config --libs`" ++ ;; ++ esac ++ ;; ++*) ++ FONTCONFIG_LIBS="-L$fontconfig_lib -lfontconfig" ++ ;; ++esac ++ ++saved_LIBS="$LIBS" ++LIBS="$LIBS $FONTCONFIG_LIBS" ++saved_CPPFLAGS="$CPPFLAGS" ++CPPFLAGS="$CPPFLAGS $FONTCONFIG_CFLAGS" ++AC_CHECK_HEADERS(fontconfig/fontconfig.h) ++ ++case "$ac_cv_header_fontconfig_fontconfig_h" in ++no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++yes) ++ AC_CHECK_FUNCS(FcInit) ++ case "$ac_cv_func_FcInit" in ++ no) ++ CPPFLAGS="$saved_CPPFLAGS" ++ LIBS="$saved_LIBS" ++ ;; ++ yes) ++ AC_DEFINE(HAVE_FONTCONFIG) ++ AC_SUBST(FONTCONFIG_CFLAGS) ++ AC_SUBST(FONTCONFIG_LIBS) ++ ;; ++ esac ++ ;; ++esac ++ ++dnl ++dnl Should this be conditionally defined ? ++dnl ++if test "$HAVEXRENDER" = "yes" -a "$HAVEFREETYPE" = "yes" ++then ++ AC_DEFINE(USE_XFT) ++fi ++]) diff --git a/packages/lesstif/files/ac_have_libxp.m4.diff b/packages/lesstif/files/ac_have_libxp.m4.diff new file mode 100644 index 0000000000..577bd5de0a --- /dev/null +++ b/packages/lesstif/files/ac_have_libxp.m4.diff @@ -0,0 +1,57 @@ +Index: lesstif2-0.95.0/ac_have_libxp.m4 +=================================================================== +--- lesstif2-0.95.0.orig/ac_have_libxp.m4 2004-02-01 16:49:40.000000000 +0100 ++++ lesstif2-0.95.0/ac_have_libxp.m4 2006-07-11 11:11:36.000000000 +0200 +@@ -11,6 +11,52 @@ + dnl Makefiles. Perhaps one should immediately add those libs + dnl to link commands which include libXm version2.1?! + dnl ++AC_DEFUN(LT_HAVE_LIBXP, ++[AC_REQUIRE([AC_PATH_X]) ++AC_CACHE_CHECK(whether libXp is available, lt_cv_libxp, ++[lt_save_CFLAGS="$CFLAGS" ++lt_save_CPPFLAGS="$CPPFLAGS" ++lt_save_LIBS="$LIBS" ++LIBS="$X_LIBS -lXp -lXext -lXt $X_PRE_LIBS -lX11 $X_EXTRA_LIBS $LIBS" ++CFLAGS="$X_CFLAGS $CFLAGS" ++CPPFLAGS="$X_CFLAGS $CPPFLAGS" ++AC_TRY_LINK([ ++#include <X11/Intrinsic.h> ++#include <X11/extensions/Print.h> ++],[ ++Display *display=NULL; ++short major_version, minor_version; ++Status rc; ++rc=XpQueryVersion(display, &major_version, &minor_version); ++], ++lt_cv_libxp=yes, ++lt_cv_libxp=no) ++]) ++if test $lt_cv_libxp = yes; then ++ AC_DEFINE(HAVE_LIB_XP) ++ LT_HAVELIBXP=1 ++else ++ LT_HAVELIBXP=0 ++fi ++AM_CONDITIONAL(Have_Libxp, test "$lt_cv_libxp" = "yes") ++AC_SUBST(LT_HAVELIBXP) ++CFLAGS="$lt_save_CFLAGS" ++CPPFLAGS="$lt_save_CPPFLAGS" ++LIBS="$lt_save_LIBS" ++]) ++dnl ++dnl Check for libXp ++dnl In fact this check ensures that ++dnl - <X11/extensions/Print.h> and ++dnl - both libXp and libXext ++dnl are in place ++dnl Perhaps AC_CHECK_LIB() could be used as well, but ++dnl requires the same amount of work to get all linker ++dnl flags and additional libraries specified. ++dnl If the test succeeds 'Have_Libxp' will be defined within our ++dnl Makefiles. Perhaps one should immediately add those libs ++dnl to link commands which include libXm version2.1?! ++dnl + AC_DEFUN([LT_HAVE_LIBXP], + [AC_REQUIRE([AC_PATH_X]) + AC_CACHE_CHECK(whether libXp is available, lt_cv_libxp, diff --git a/packages/lesstif/files/aclocal.m4.diff b/packages/lesstif/files/aclocal.m4.diff new file mode 100644 index 0000000000..6ce3f8a939 --- /dev/null +++ b/packages/lesstif/files/aclocal.m4.diff @@ -0,0 +1,2746 @@ +Index: lesstif2-0.95.0/aclocal.m4 +=================================================================== +--- lesstif2-0.95.0.orig/aclocal.m4 2006-06-10 11:35:23.000000000 +0200 ++++ lesstif2-0.95.0/aclocal.m4 2006-07-11 11:11:59.000000000 +0200 +@@ -1,7 +1,7 @@ +-# generated automatically by aclocal 1.9.4 -*- Autoconf -*- ++# generated automatically by aclocal 1.9.6 -*- Autoconf -*- + +-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +-# Free Software Foundation, Inc. ++# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, ++# 2005 Free Software Foundation, Inc. + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. +@@ -13,7 +13,7 @@ + + # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- + +-# serial 47 AC_PROG_LIBTOOL ++# serial 48 Debian 1.5.22-4 AC_PROG_LIBTOOL + + + # AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) +@@ -143,7 +143,7 @@ + default_ofile=libtool + can_build_shared=yes + +-# All known linkers require a `.a' archive for static linking (except M$VC, ++# All known linkers require a `.a' archive for static linking (except MSVC, + # which needs '.lib'). + libext=a + ltmain="$ac_aux_dir/ltmain.sh" +@@ -163,6 +163,7 @@ + test -z "$AS" && AS=as + test -z "$CC" && CC=cc + test -z "$LTCC" && LTCC=$CC ++test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS + test -z "$DLLTOOL" && DLLTOOL=dlltool + test -z "$LD" && LD=ld + test -z "$LN_S" && LN_S="ln -s" +@@ -182,10 +183,10 @@ + if test -n "$RANLIB"; then + case $host_os in + openbsd*) +- old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds" ++ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" + ;; + *) +- old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds" ++ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +@@ -233,6 +234,9 @@ + # If no C compiler was specified, use CC. + LTCC=${LTCC-"$CC"} + ++# If no C compiler flags were specified, use CFLAGS. ++LTCFLAGS=${LTCFLAGS-"$CFLAGS"} ++ + # Allow CC to be a program name with arguments. + compiler=$CC + ])# _LT_AC_SYS_COMPILER +@@ -261,7 +265,7 @@ + AC_DEFUN([_LT_COMPILER_BOILERPLATE], + [ac_outfile=conftest.$ac_objext + printf "$lt_simple_compile_test_code" >conftest.$ac_ext +-eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d' >conftest.err ++eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err + _lt_compiler_boilerplate=`cat conftest.err` + $rm conftest* + ])# _LT_COMPILER_BOILERPLATE +@@ -274,7 +278,7 @@ + AC_DEFUN([_LT_LINKER_BOILERPLATE], + [ac_outfile=conftest.$ac_objext + printf "$lt_simple_link_test_code" >conftest.$ac_ext +-eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d' >conftest.err ++eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err + _lt_linker_boilerplate=`cat conftest.err` + $rm conftest* + ])# _LT_LINKER_BOILERPLATE +@@ -359,8 +363,8 @@ + # find a string as large as possible, as long as the shell can cope with it + for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do + # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... +- if (echo_test_string="`eval $cmd`") 2>/dev/null && +- echo_test_string="`eval $cmd`" && ++ if (echo_test_string=`eval $cmd`) 2>/dev/null && ++ echo_test_string=`eval $cmd` && + (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null + then + break +@@ -529,7 +533,7 @@ + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then +- case "`/usr/bin/file conftest.o`" in ++ case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*linux*) +@@ -580,6 +584,22 @@ + CFLAGS="$SAVE_CFLAGS" + fi + ;; ++sparc*-*solaris*) ++ # Find out which ABI we are using. ++ echo 'int i;' > conftest.$ac_ext ++ if AC_TRY_EVAL(ac_compile); then ++ case `/usr/bin/file conftest.o` in ++ *64-bit*) ++ case $lt_cv_prog_gnu_ld in ++ yes*) LD="${LD-ld} -m elf64_sparc" ;; ++ *) LD="${LD-ld} -64" ;; ++ esac ++ ;; ++ esac ++ fi ++ rm -rf conftest* ++ ;; ++ + AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], + [*-*-cygwin* | *-*-mingw* | *-*-pw32*) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) +@@ -611,7 +631,7 @@ + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ +- -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ ++ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) +@@ -622,9 +642,9 @@ + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. +- $echo "X$_lt_compiler_boilerplate" | $Xsed >conftest.exp +- $SED '/^$/d' conftest.err >conftest.er2 +- if test ! -s conftest.err || diff conftest.exp conftest.er2 >/dev/null; then ++ $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp ++ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 ++ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi +@@ -650,13 +670,13 @@ + LDFLAGS="$LDFLAGS $3" + printf "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then +- # The compiler can only warn and ignore the option if not recognized ++ # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD +- $echo "X$_lt_linker_boilerplate" | $Xsed > conftest.exp +- $SED '/^$/d' conftest.err >conftest.er2 ++ $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp ++ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi +@@ -725,25 +745,42 @@ + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else +- lt_cv_sys_max_cmd_len=65536 # usable default for *BSD ++ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; ++ ++ interix*) ++ # We know the value 262144 and hardcode it with a safety zone (like BSD) ++ lt_cv_sys_max_cmd_len=196608 ++ ;; ++ + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 +- # ++ # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; ++ sco3.2v5*) ++ lt_cv_sys_max_cmd_len=102400 ++ ;; ++ sysv5* | sco5v6* | sysv4.2uw2*) ++ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` ++ if test -n "$kargmax"; then ++ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` ++ else ++ lt_cv_sys_max_cmd_len=32768 ++ fi ++ ;; + *) + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but +@@ -775,7 +812,7 @@ + + + # _LT_AC_CHECK_DLFCN +-# -------------------- ++# ------------------ + AC_DEFUN([_LT_AC_CHECK_DLFCN], + [AC_CHECK_HEADERS(dlfcn.h)dnl + ])# _LT_AC_CHECK_DLFCN +@@ -783,7 +820,7 @@ + + # _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, + # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +-# ------------------------------------------------------------------ ++# --------------------------------------------------------------------- + AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF], + [AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl + if test "$cross_compiling" = yes; then : +@@ -849,17 +886,19 @@ + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } ++ else ++ puts (dlerror ()); + + exit (status); + }] + EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then +- (./conftest; exit; ) 2>/dev/null ++ (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; +- x$lt_unknown|x*) $3 ;; ++ x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed +@@ -871,7 +910,7 @@ + + + # AC_LIBTOOL_DLOPEN_SELF +-# ------------------- ++# ---------------------- + AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], + [AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl + if test "x$enable_dlopen" != xyes; then +@@ -942,7 +981,7 @@ + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" +- eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" ++ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" +@@ -955,7 +994,7 @@ + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then +- LDFLAGS="$LDFLAGS $link_static_flag" ++ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_AC_TRY_DLOPEN_SELF( +@@ -1003,7 +1042,7 @@ + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ +- -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ ++ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) +@@ -1015,13 +1054,13 @@ + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings +- $echo "X$_lt_compiler_boilerplate" | $Xsed > out/conftest.exp +- $SED '/^$/d' out/conftest.err >out/conftest.er2 +- if test ! -s out/conftest.err || diff out/conftest.exp out/conftest.er2 >/dev/null; then ++ $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp ++ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 ++ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi +- chmod u+w . ++ chmod u+w . 2>&AS_MESSAGE_LOG_FD + $rm conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation +@@ -1281,7 +1320,8 @@ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ +- $install_prog $dir/$dlname \$dldir/$dlname' ++ $install_prog $dir/$dlname \$dldir/$dlname~ ++ chmod a+x \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $rm \$dlpath' +@@ -1334,7 +1374,7 @@ + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH +- shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)' ++ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. + if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` +@@ -1357,22 +1397,17 @@ + dynamic_linker=no + ;; + +-kfreebsd*-gnu) +- version_type=linux +- need_lib_prefix=no +- need_version=no +- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' +- soname_spec='${libname}${release}${shared_ext}$major' +- shlibpath_var=LD_LIBRARY_PATH +- shlibpath_overrides_runpath=no +- hardcode_into_libs=yes +- dynamic_linker='GNU ld.so' +- ;; +- + freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. +- objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` ++ if test -x /usr/bin/objformat; then ++ objformat=`/usr/bin/objformat` ++ else ++ case $host_os in ++ freebsd[[123]]*) objformat=aout ;; ++ *) objformat=elf ;; ++ esac ++ fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) +@@ -1394,10 +1429,15 @@ + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; +- *) # from 3.2 on ++ freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ ++ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; ++ freebsd*) # from 4.6 on ++ shlibpath_overrides_runpath=yes ++ hardcode_into_libs=yes ++ ;; + esac + ;; + +@@ -1417,7 +1457,7 @@ + version_type=sunos + need_lib_prefix=no + need_version=no +- case "$host_cpu" in ++ case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes +@@ -1457,6 +1497,18 @@ + postinstall_cmds='chmod 555 $lib' + ;; + ++interix3*) ++ version_type=linux ++ need_lib_prefix=no ++ need_version=no ++ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' ++ soname_spec='${libname}${release}${shared_ext}$major' ++ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' ++ shlibpath_var=LD_LIBRARY_PATH ++ shlibpath_overrides_runpath=no ++ hardcode_into_libs=yes ++ ;; ++ + irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; +@@ -1500,7 +1552,7 @@ + ;; + + # This must be Linux ELF. +-linux*) ++linux* | k*bsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no +@@ -1514,27 +1566,10 @@ + # before this can be enabled. + hardcode_into_libs=yes + +- # find out which ABI we are using +- libsuff= +- case "$host_cpu" in +- x86_64*|s390x*|powerpc64*) +- echo '[#]line __oline__ "configure"' > conftest.$ac_ext +- if AC_TRY_EVAL(ac_compile); then +- case `/usr/bin/file conftest.$ac_objext` in +- *64-bit*) +- libsuff=64 +- sys_lib_search_path_spec="/lib${libsuff} /usr/lib${libsuff} /usr/local/lib${libsuff}" +- ;; +- esac +- fi +- rm -rf conftest* +- ;; +- esac +- + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` +- sys_lib_dlsearch_path_spec="/lib${libsuff} /usr/lib${libsuff} $lt_ld_extra" ++ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on +@@ -1546,7 +1581,7 @@ + dynamic_linker='GNU/Linux ld.so' + ;; + +-knetbsd*-gnu) ++netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no +@@ -1555,7 +1590,7 @@ + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes +- dynamic_linker='GNU ld.so' ++ dynamic_linker='NetBSD ld.elf_so' + ;; + + netbsd*) +@@ -1595,6 +1630,7 @@ + + openbsd*) + version_type=sunos ++ sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in +@@ -1638,13 +1674,6 @@ + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +-sco3.2v5*) +- version_type=osf +- soname_spec='${libname}${release}${shared_ext}$major' +- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' +- shlibpath_var=LD_LIBRARY_PATH +- ;; +- + solaris*) + version_type=linux + need_lib_prefix=no +@@ -1670,7 +1699,7 @@ + need_version=yes + ;; + +-sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) ++sysv4 | sysv4.3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' +@@ -1703,6 +1732,29 @@ + fi + ;; + ++sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) ++ version_type=freebsd-elf ++ need_lib_prefix=no ++ need_version=no ++ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' ++ soname_spec='${libname}${release}${shared_ext}$major' ++ shlibpath_var=LD_LIBRARY_PATH ++ hardcode_into_libs=yes ++ if test "$with_gnu_ld" = yes; then ++ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' ++ shlibpath_overrides_runpath=no ++ else ++ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' ++ shlibpath_overrides_runpath=yes ++ case $host_os in ++ sco3.2v5*) ++ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ++ ;; ++ esac ++ fi ++ sys_lib_dlsearch_path_spec='/usr/lib' ++ ;; ++ + uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' +@@ -1716,6 +1768,11 @@ + esac + AC_MSG_RESULT([$dynamic_linker]) + test "$dynamic_linker" = no && can_build_shared=no ++ ++variables_saved_for_relink="PATH $shlibpath_var $runpath_var" ++if test "$GCC" = yes; then ++ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" ++fi + ])# AC_LIBTOOL_SYS_DYNAMIC_LINKER + + +@@ -1740,6 +1797,9 @@ + AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile']) + fi + fi ++ if test -z "$LTCFLAGS"; then ++ eval "`$SHELL ${ofile} --config | grep '^LTCFLAGS='`" ++ fi + + # Extract list of available tagged configurations in $ofile. + # Note that this assumes the entire list is on one line. +@@ -1830,7 +1890,7 @@ + + # AC_LIBTOOL_WIN32_DLL + # -------------------- +-# declare package support for building win32 dll's ++# declare package support for building win32 DLLs + AC_DEFUN([AC_LIBTOOL_WIN32_DLL], + [AC_BEFORE([$0], [AC_LIBTOOL_SETUP]) + ])# AC_LIBTOOL_WIN32_DLL +@@ -1868,7 +1928,7 @@ + + # AC_DISABLE_SHARED + # ----------------- +-#- set the default shared flag to --disable-shared ++# set the default shared flag to --disable-shared + AC_DEFUN([AC_DISABLE_SHARED], + [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + AC_ENABLE_SHARED(no) +@@ -2004,7 +2064,7 @@ + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) +- file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`" ++ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then +@@ -2114,7 +2174,7 @@ + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, +- # but apparently some GNU ld's only accept -v. ++ # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in + *GNU* | *'with BFD'*) +@@ -2146,7 +2206,7 @@ + AC_DEFUN([AC_PROG_LD_GNU], + [AC_REQUIRE([AC_PROG_EGREP])dnl + AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld, +-[# I'd rather use --version here, but apparently some GNU ld's only accept -v. ++[# I'd rather use --version here, but apparently some GNU lds only accept -v. + case `$LD -v 2>&1 </dev/null` in + *GNU* | *'with BFD'*) + lt_cv_prog_gnu_ld=yes +@@ -2176,7 +2236,7 @@ + case $host_os in + darwin*) + if test "$GCC" = yes; then +- reload_cmds='$CC -nostdlib ${wl}-r -o $output$reload_objs' ++ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi +@@ -2238,7 +2298,7 @@ + lt_cv_deplibs_check_method=pass_all + ;; + +-freebsd* | kfreebsd*-gnu | dragonfly*) ++freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) +@@ -2260,7 +2320,7 @@ + + hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file +- case "$host_cpu" in ++ case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so +@@ -2276,6 +2336,11 @@ + esac + ;; + ++interix3*) ++ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here ++ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ++ ;; ++ + irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; +@@ -2287,11 +2352,11 @@ + ;; + + # This must be Linux ELF. +-linux*) ++linux* | k*bsd*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; + +-netbsd*) ++netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else +@@ -2321,15 +2386,11 @@ + lt_cv_deplibs_check_method=pass_all + ;; + +-sco3.2v5*) +- lt_cv_deplibs_check_method=pass_all +- ;; +- + solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +-sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) ++sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' +@@ -2350,10 +2411,13 @@ + siemens) + lt_cv_deplibs_check_method=pass_all + ;; ++ pc) ++ lt_cv_deplibs_check_method=pass_all ++ ;; + esac + ;; + +-sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7* | sysv4*uw2*) ++sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + esac +@@ -2373,36 +2437,43 @@ + # Let the user override the test. + lt_cv_path_NM="$NM" + else +- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +- for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do +- IFS="$lt_save_ifs" +- test -z "$ac_dir" && ac_dir=. +- tmp_nm="$ac_dir/${ac_tool_prefix}nm" +- if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then +- # Check to see if the nm accepts a BSD-compat flag. +- # Adding the `sed 1q' prevents false positives on HP-UX, which says: +- # nm: unknown option "B" ignored +- # Tru64's nm complains that /dev/null is an invalid object file +- case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in +- */dev/null* | *'Invalid file or object type'*) +- lt_cv_path_NM="$tmp_nm -B" +- break +- ;; +- *) +- case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in +- */dev/null*) +- lt_cv_path_NM="$tmp_nm -p" ++ lt_nm_to_check="${ac_tool_prefix}nm" ++ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then ++ lt_nm_to_check="$lt_nm_to_check nm" ++ fi ++ for lt_tmp_nm in $lt_nm_to_check; do ++ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ++ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do ++ IFS="$lt_save_ifs" ++ test -z "$ac_dir" && ac_dir=. ++ tmp_nm="$ac_dir/$lt_tmp_nm" ++ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then ++ # Check to see if the nm accepts a BSD-compat flag. ++ # Adding the `sed 1q' prevents false positives on HP-UX, which says: ++ # nm: unknown option "B" ignored ++ # Tru64's nm complains that /dev/null is an invalid object file ++ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in ++ */dev/null* | *'Invalid file or object type'*) ++ lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) +- lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but +- continue # so that we can try to find one that supports BSD flags ++ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in ++ */dev/null*) ++ lt_cv_path_NM="$tmp_nm -p" ++ break ++ ;; ++ *) ++ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but ++ continue # so that we can try to find one that supports BSD flags ++ ;; ++ esac + ;; + esac +- esac +- fi ++ fi ++ done ++ IFS="$lt_save_ifs" + done +- IFS="$lt_save_ifs" + test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm + fi]) + NM="$lt_cv_path_NM" +@@ -2434,13 +2505,13 @@ + # ----------------------------------- + # sets LIBLTDL to the link flags for the libltdl convenience library and + # LTDLINCL to the include flags for the libltdl header and adds +-# --enable-ltdl-convenience to the configure arguments. Note that LIBLTDL +-# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If +-# DIRECTORY is not provided, it is assumed to be `libltdl'. LIBLTDL will +-# be prefixed with '${top_builddir}/' and LTDLINCL will be prefixed with +-# '${top_srcdir}/' (note the single quotes!). If your package is not +-# flat and you're not using automake, define top_builddir and +-# top_srcdir appropriately in the Makefiles. ++# --enable-ltdl-convenience to the configure arguments. Note that ++# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, ++# it is assumed to be `libltdl'. LIBLTDL will be prefixed with ++# '${top_builddir}/' and LTDLINCL will be prefixed with '${top_srcdir}/' ++# (note the single quotes!). If your package is not flat and you're not ++# using automake, define top_builddir and top_srcdir appropriately in ++# the Makefiles. + AC_DEFUN([AC_LIBLTDL_CONVENIENCE], + [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + case $enable_ltdl_convenience in +@@ -2459,13 +2530,13 @@ + # ----------------------------------- + # sets LIBLTDL to the link flags for the libltdl installable library and + # LTDLINCL to the include flags for the libltdl header and adds +-# --enable-ltdl-install to the configure arguments. Note that LIBLTDL +-# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If +-# DIRECTORY is not provided and an installed libltdl is not found, it is +-# assumed to be `libltdl'. LIBLTDL will be prefixed with '${top_builddir}/' +-# and LTDLINCL will be prefixed with '${top_srcdir}/' (note the single +-# quotes!). If your package is not flat and you're not using automake, +-# define top_builddir and top_srcdir appropriately in the Makefiles. ++# --enable-ltdl-install to the configure arguments. Note that ++# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, ++# and an installed libltdl is not found, it is assumed to be `libltdl'. ++# LIBLTDL will be prefixed with '${top_builddir}/'# and LTDLINCL with ++# '${top_srcdir}/' (note the single quotes!). If your package is not ++# flat and you're not using automake, define top_builddir and top_srcdir ++# appropriately in the Makefiles. + # In the future, this macro may have to be called after AC_PROG_LIBTOOL. + AC_DEFUN([AC_LIBLTDL_INSTALLABLE], + [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +@@ -2508,7 +2579,7 @@ + ])# _LT_AC_LANG_CXX + + # _LT_AC_PROG_CXXCPP +-# --------------- ++# ------------------ + AC_DEFUN([_LT_AC_PROG_CXXCPP], + [ + AC_REQUIRE([AC_PROG_CXX]) +@@ -2557,7 +2628,7 @@ + + + # AC_LIBTOOL_RC +-# -------------- ++# ------------- + # enable support for Windows resource files + AC_DEFUN([AC_LIBTOOL_RC], + [AC_REQUIRE([LT_AC_PROG_RC]) +@@ -2594,37 +2665,6 @@ + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + +-# +-# Check for any special shared library compilation flags. +-# +-_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)= +-if test "$GCC" = no; then +- case $host_os in +- sco3.2v5*) +- _LT_AC_TAGVAR(lt_prog_cc_shlib, $1)='-belf' +- ;; +- esac +-fi +-if test -n "$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)"; then +- AC_MSG_WARN([`$CC' requires `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to build shared libraries]) +- if echo "$old_CC $old_CFLAGS " | grep "[[ ]]$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)[[ ]]" >/dev/null; then : +- else +- AC_MSG_WARN([add `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to the CC or CFLAGS env variable and reconfigure]) +- _LT_AC_TAGVAR(lt_cv_prog_cc_can_build_shared, $1)=no +- fi +-fi +- +- +-# +-# Check to make sure the static flag actually works. +-# +-AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $_LT_AC_TAGVAR(lt_prog_compiler_static, $1) works], +- _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1), +- $_LT_AC_TAGVAR(lt_prog_compiler_static, $1), +- [], +- [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=]) +- +- + AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1) + AC_LIBTOOL_PROG_COMPILER_PIC($1) + AC_LIBTOOL_PROG_CC_C_O($1) +@@ -2633,9 +2673,9 @@ + AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) + AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) + AC_LIBTOOL_SYS_LIB_STRIP +-AC_LIBTOOL_DLOPEN_SELF($1) ++AC_LIBTOOL_DLOPEN_SELF + +-# Report which librarie types wil actually be built ++# Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + +@@ -2644,7 +2684,7 @@ + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. +-case "$host_os" in ++case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then +@@ -2694,6 +2734,7 @@ + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + _LT_AC_TAGVAR(hardcode_minus_L, $1)=no ++_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(hardcode_automatic, $1)=no + _LT_AC_TAGVAR(module_cmds, $1)= + _LT_AC_TAGVAR(module_expsym_cmds, $1)= +@@ -2711,7 +2752,7 @@ + _LT_AC_TAGVAR(compiler_lib_search_path, $1)= + + # Source file extension for C++ test sources. +-ac_ext=cc ++ac_ext=cpp + + # Object file extension for compiled C++ test sources. + objext=o +@@ -2721,7 +2762,7 @@ + lt_simple_compile_test_code="int some_variable = 0;\n" + + # Code to be used in simple link tests +-lt_simple_link_test_code='int main(int, char *[]) { return(0); }\n' ++lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }\n' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_AC_SYS_COMPILER +@@ -2740,12 +2781,12 @@ + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else +- unset lt_cv_prog_gnu_ld ++ $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else +- unset lt_cv_path_LD ++ $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} +@@ -2840,6 +2881,7 @@ + ;; + esac + done ++ ;; + esac + + exp_sym_flag='-bexport' +@@ -2877,6 +2919,7 @@ + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + fi ++ ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then +@@ -2908,12 +2951,12 @@ + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + +- _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" +- _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX +@@ -2922,16 +2965,26 @@ + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' +- # -bexpall does not export symbols beginning with underscore (_) +- _LT_AC_TAGVAR(always_export_symbols, $1)=yes + # Exported symbols can be pulled into shared objects from archives +- _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' ' ++ _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes +- # This is similar to how AIX traditionally builds it's shared libraries. +- _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' ++ # This is similar to how AIX traditionally builds its shared libraries. ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; ++ ++ beos*) ++ if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then ++ _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported ++ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc ++ # support --undefined. This deserves some investigation. FIXME ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' ++ else ++ _LT_AC_TAGVAR(ld_shlibs, $1)=no ++ fi ++ ;; ++ + chorus*) + case $cc_basename in + *) +@@ -2941,7 +2994,6 @@ + esac + ;; + +- + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. +@@ -2951,7 +3003,7 @@ + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then +- _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then +@@ -2960,13 +3012,13 @@ + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ +- $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' ++ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + darwin* | rhapsody*) +- case "$host_os" in ++ case $host_os in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}suppress' + ;; +@@ -3004,7 +3056,7 @@ + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + fi + _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' +- # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's ++ # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else +@@ -3017,7 +3069,7 @@ + output_verbose_link_cmd='echo' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' + _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' +- # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's ++ # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + ;; +@@ -3052,7 +3104,7 @@ + freebsd-elf*) + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + ;; +- freebsd* | kfreebsd*-gnu | dragonfly*) ++ freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_AC_TAGVAR(ld_shlibs, $1)=yes +@@ -3097,33 +3149,22 @@ + ;; + hpux10*|hpux11*) + if test $with_gnu_ld = no; then +- case "$host_cpu" in +- hppa*64*) +- _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' ++ _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: ++ ++ case $host_cpu in ++ hppa*64*|ia64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' +- _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: +- ;; +- ia64*) +- _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + *) +- _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' +- _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi +- case "$host_cpu" in +- hppa*64*) +- _LT_AC_TAGVAR(hardcode_direct, $1)=no +- _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +- ;; +- ia64*) ++ case $host_cpu in ++ hppa*64*|ia64*) + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +- _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, +- # but as the default +- # location of the library. + ;; + *) + _LT_AC_TAGVAR(hardcode_direct, $1)=yes +@@ -3139,9 +3180,12 @@ + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) +- case "$host_cpu" in +- hppa*64*|ia64*) +- _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' ++ case $host_cpu in ++ hppa*64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ++ ;; ++ ia64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' +@@ -3160,9 +3204,12 @@ + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then +- case "$host_cpu" in +- ia64*|hppa*64*) +- _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' ++ case $host_cpu in ++ hppa*64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ++ ;; ++ ia64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' +@@ -3176,6 +3223,20 @@ + ;; + esac + ;; ++ interix3*) ++ _LT_AC_TAGVAR(hardcode_direct, $1)=no ++ _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ++ _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ++ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. ++ # Instead, shared libraries are loaded at an image base (0x10000000 by ++ # default) and relocated if they conflict, which is a slow very memory ++ # consuming and fragmenting process. To avoid this, we pick a random, ++ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link ++ # time. Moving up from 0x10000000 also allows more sbrk(2) space. ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ++ ;; + irix5* | irix6*) + case $cc_basename in + CC*) +@@ -3202,7 +3263,7 @@ + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; +- linux*) ++ linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler +@@ -3261,7 +3322,7 @@ + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' +- _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive,`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' ++ _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ +@@ -3304,7 +3365,7 @@ + ;; + esac + ;; +- netbsd*) ++ netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= +@@ -3458,19 +3519,6 @@ + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; +- sco*) +- _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no +- case $cc_basename in +- CC*) +- # FIXME: insert proper C++ library support +- _LT_AC_TAGVAR(ld_shlibs, $1)=no +- ;; +- *) +- # FIXME: insert proper C++ library support +- _LT_AC_TAGVAR(ld_shlibs, $1)=no +- ;; +- esac +- ;; + sunos4*) + case $cc_basename in + CC*) +@@ -3493,10 +3541,11 @@ + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ ++ _LT_AC_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -zdefs' +- _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -nolib -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ +- $CC -G${allow_undefined_flag} -nolib ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' ++ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +@@ -3516,15 +3565,7 @@ + esac + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + +- # Commands to make compiler produce verbose output that lists +- # what "hidden" libraries, object files and flags are used when +- # linking a shared library. +- # +- # There doesn't appear to be a way to prevent this compiler from +- # explicitly linking system object files so we need to strip them +- # from the output so that they don't get included in the library +- # dependencies. +- output_verbose_link_cmd='templist=`$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep "\-[[LR]]"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' ++ output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is +@@ -3570,8 +3611,59 @@ + ;; + esac + ;; +- sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*) ++ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) ++ _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' ++ _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no ++ _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no ++ runpath_var='LD_RUN_PATH' ++ ++ case $cc_basename in ++ CC*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ++ ;; ++ *) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ++ ;; ++ esac ++ ;; ++ sysv5* | sco3.2v5* | sco5v6*) ++ # Note: We can NOT use -z defs as we might desire, because we do not ++ # link with -lc, and that would cause any symbols used from libc to ++ # always be unresolved, which means just about no library would ++ # ever link correctly. If we're not using GNU ld we use -z text ++ # though, which does catch some bad symbols but isn't as heavy-handed ++ # as -z defs. ++ # For security reasons, it is highly recommended that you always ++ # use absolute paths for naming shared libraries, and exclude the ++ # DT_RUNPATH tag from executables and libraries. But doing so ++ # requires that you compile everything twice, which is a pain. ++ # So that behaviour is only enabled if SCOABSPATH is set to a ++ # non-empty value in the environment. Most likely only useful for ++ # creating official distributions of packages. ++ # This is a hack until libtool officially supports absolute path ++ # names for shared libraries. ++ _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' ++ _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no ++ _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' ++ _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' ++ _LT_AC_TAGVAR(link_all_deplibs, $1)=yes ++ _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' ++ runpath_var='LD_RUN_PATH' ++ ++ case $cc_basename in ++ CC*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ ;; ++ *) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ ;; ++ esac + ;; + tandem*) + case $cc_basename in +@@ -3608,8 +3700,6 @@ + AC_LIBTOOL_PROG_LD_SHLIBS($1) + AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) + AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +-AC_LIBTOOL_SYS_LIB_STRIP +-AC_LIBTOOL_DLOPEN_SELF($1) + + AC_LIBTOOL_CONFIG($1) + +@@ -3627,7 +3717,7 @@ + ])# AC_LIBTOOL_LANG_CXX_CONFIG + + # AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME]) +-# ------------------------ ++# ------------------------------------ + # Figure out "hidden" library dependencies from verbose + # compiler output when linking a shared library. + # Parse the compiler output and extract the necessary +@@ -3681,7 +3771,7 @@ + # The `*' in the case matches for architectures that use `case' in + # $output_verbose_cmd can trigger glob expansion during the loop + # eval without this substitution. +- output_verbose_link_cmd="`$echo \"X$output_verbose_link_cmd\" | $Xsed -e \"$no_glob_subst\"`" ++ output_verbose_link_cmd=`$echo "X$output_verbose_link_cmd" | $Xsed -e "$no_glob_subst"` + + for p in `eval $output_verbose_link_cmd`; do + case $p in +@@ -3757,13 +3847,37 @@ + + $rm -f confest.$objext + ++# PORTME: override above test on systems where it is broken ++ifelse([$1],[CXX], ++[case $host_os in ++interix3*) ++ # Interix 3.5 installs completely hosed .la files for C++, so rather than ++ # hack all around it, let's just trust "g++" to DTRT. ++ _LT_AC_TAGVAR(predep_objects,$1)= ++ _LT_AC_TAGVAR(postdep_objects,$1)= ++ _LT_AC_TAGVAR(postdeps,$1)= ++ ;; ++ ++solaris*) ++ case $cc_basename in ++ CC*) ++ # Adding this requires a known-good setup of shared libraries for ++ # Sun compiler versions before 5.6, else PIC objects from an old ++ # archive will be linked into the output, leading to subtle bugs. ++ _LT_AC_TAGVAR(postdeps,$1)='-lCstd -lCrun' ++ ;; ++ esac ++ ;; ++esac ++]) ++ + case " $_LT_AC_TAGVAR(postdeps, $1) " in + *" -lc "*) _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no ;; + esac + ])# AC_LIBTOOL_POSTDEP_PREDEP + + # AC_LIBTOOL_LANG_F77_CONFIG +-# ------------------------ ++# -------------------------- + # Ensure that the configuration vars for the C compiler are + # suitably defined. Those variables are subsequently used by + # AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. +@@ -3826,7 +3940,7 @@ + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. +-case "$host_os" in ++case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then +@@ -3847,8 +3961,6 @@ + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + +-test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no +- + _LT_AC_TAGVAR(GCC, $1)="$G77" + _LT_AC_TAGVAR(LD, $1)="$LD" + +@@ -3858,8 +3970,6 @@ + AC_LIBTOOL_PROG_LD_SHLIBS($1) + AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) + AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +-AC_LIBTOOL_SYS_LIB_STRIP +- + + AC_LIBTOOL_CONFIG($1) + +@@ -3916,8 +4026,6 @@ + AC_LIBTOOL_PROG_LD_SHLIBS($1) + AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) + AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +-AC_LIBTOOL_SYS_LIB_STRIP +-AC_LIBTOOL_DLOPEN_SELF($1) + + AC_LIBTOOL_CONFIG($1) + +@@ -3927,7 +4035,7 @@ + + + # AC_LIBTOOL_LANG_RC_CONFIG +-# -------------------------- ++# ------------------------- + # Ensure that the configuration vars for the Windows resource compiler are + # suitably defined. Those variables are subsequently used by + # AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. +@@ -3990,7 +4098,7 @@ + # Now quote all the things that may contain metacharacters while being + # careful not to overquote the AC_SUBSTed values. We take copies of the + # variables and quote the copies for generation of the libtool script. +- for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \ ++ for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ + SED SHELL STRIP \ + libname_spec library_names_spec soname_spec extract_expsyms_cmds \ + old_striplib striplib file_magic_cmd finish_cmds finish_eval \ +@@ -4159,6 +4267,9 @@ + # A C compiler. + LTCC=$lt_LTCC + ++# LTCC compiler flags. ++LTCFLAGS=$lt_LTCFLAGS ++ + # A language-specific compiler. + CC=$lt_[]_LT_AC_TAGVAR(compiler, $1) + +@@ -4519,7 +4630,7 @@ + lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" + ;; +-linux*) ++linux* | k*bsd*-gnu) + if test "$host_cpu" = ia64; then + symcode='[[ABCDGIRSTW]]' + lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" +@@ -4532,9 +4643,18 @@ + osf*) + symcode='[[BCDEGQRST]]' + ;; +-solaris* | sysv5*) ++solaris*) + symcode='[[BDRT]]' + ;; ++sco3.2v5*) ++ symcode='[[DT]]' ++ ;; ++sysv4.2uw2*) ++ symcode='[[DT]]' ++ ;; ++sysv5* | sco5v6* | unixware* | OpenUNIX*) ++ symcode='[[ABDT]]' ++ ;; + sysv4) + symcode='[[DFNSTU]]' + ;; +@@ -4717,6 +4837,10 @@ + # DJGPP does not support shared libraries at all + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + ;; ++ interix3*) ++ # Interix 3.x gcc -fpic/-fPIC options generate broken code. ++ # Instead, we relocate shared libraries at runtime. ++ ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic +@@ -4725,7 +4849,7 @@ + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. +- case "$host_cpu" in ++ case $host_cpu in + hppa*64*|ia64*) + ;; + *) +@@ -4779,22 +4903,22 @@ + ;; + esac + ;; +- freebsd* | kfreebsd*-gnu | dragonfly*) ++ freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' +- _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" ++ _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' +- _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" +- case "$host_cpu" in ++ _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' ++ case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; +@@ -4807,6 +4931,10 @@ + ;; + esac + ;; ++ interix*) ++ # This is c89, which is MS Visual C++ (no shared libs) ++ # Anyone wants to do a port? ++ ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) +@@ -4818,7 +4946,7 @@ + ;; + esac + ;; +- linux*) ++ linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler +@@ -4835,7 +4963,7 @@ + # Portland Group C++ compiler. + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' +- _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' ++ _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ +@@ -4861,7 +4989,7 @@ + ;; + esac + ;; +- netbsd*) ++ netbsd* | netbsdelf*-gnu) + ;; + osf3* | osf4* | osf5*) + case $cc_basename in +@@ -4886,16 +5014,7 @@ + ;; + psos*) + ;; +- sco*) +- case $cc_basename in +- CC*) +- _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' +- ;; +- *) +- ;; +- esac +- ;; +- solaris*) ++ solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ +@@ -4936,7 +5055,14 @@ + ;; + esac + ;; +- unixware*) ++ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) ++ case $cc_basename in ++ CC*) ++ _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ++ _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ++ _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ++ ;; ++ esac + ;; + vxworks*) + ;; +@@ -4983,6 +5109,11 @@ + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + ++ interix3*) ++ # Interix 3.x gcc -fpic/-fPIC options generate broken code. ++ # Instead, we relocate shared libraries at runtime. ++ ;; ++ + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. +@@ -4999,7 +5130,7 @@ + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. +- case "$host_cpu" in ++ case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; +@@ -5046,7 +5177,7 @@ + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. +- case "$host_cpu" in ++ case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; +@@ -5069,19 +5200,19 @@ + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + +- linux*) ++ linux* | k*bsd*-gnu) + case $cc_basename in + icc* | ecc*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; +- pgcc* | pgf77* | pgf90*) ++ pgcc* | pgf77* | pgf90* | pgf95*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' +- _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' ++ _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' +@@ -5097,11 +5228,6 @@ + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + +- sco3.2v5*) +- _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kpic' +- _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-dn' +- ;; +- + solaris*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' +@@ -5119,7 +5245,7 @@ + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + +- sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) ++ sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' +@@ -5132,6 +5258,12 @@ + fi + ;; + ++ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) ++ _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ++ _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ++ _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ++ ;; ++ + unicos*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no +@@ -5164,7 +5296,7 @@ + [_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) + fi +-case "$host_os" in ++case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= +@@ -5173,6 +5305,16 @@ + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])" + ;; + esac ++ ++# ++# Check to make sure the static flag actually works. ++# ++wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_AC_TAGVAR(lt_prog_compiler_static, $1)\" ++AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], ++ _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1), ++ $lt_tmp_static_flag, ++ [], ++ [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=]) + ]) + + +@@ -5199,6 +5341,9 @@ + cygwin* | mingw*) + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/;/^.* __nm__/s/^.* __nm__\([[^ ]]*\) [[^ ]]*/\1 DATA/;/^I /d;/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' + ;; ++ linux* | k*bsd*-gnu) ++ _LT_AC_TAGVAR(link_all_deplibs, $1)=no ++ ;; + *) + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; +@@ -5251,6 +5396,10 @@ + with_gnu_ld=no + fi + ;; ++ interix*) ++ # we just hope/assume this is gcc and not c89 (= MSVC++) ++ with_gnu_ld=yes ++ ;; + openbsd*) + with_gnu_ld=no + ;; +@@ -5260,7 +5409,7 @@ + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' +- ++ + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. +@@ -5281,7 +5430,7 @@ + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac +- ++ + # See if GNU ld supports shared libraries. + case $host_os in + aix3* | aix4* | aix5*) +@@ -5335,7 +5484,7 @@ + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then +- _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then +@@ -5344,22 +5493,37 @@ + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ +- $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' ++ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + +- linux*) ++ interix3*) ++ _LT_AC_TAGVAR(hardcode_direct, $1)=no ++ _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ++ _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ++ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. ++ # Instead, shared libraries are loaded at an image base (0x10000000 by ++ # default) and relocated if they conflict, which is a slow very memory ++ # consuming and fragmenting process. To avoid this, we pick a random, ++ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link ++ # time. Moving up from 0x10000000 also allows more sbrk(2) space. ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ++ ;; ++ ++ linux* | k*bsd*-gnu) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + tmp_addflag= + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler +- _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive,`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' ++ _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; +- pgf77* | pgf90* ) # Portland Group f77 and f90 compilers +- _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive,`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' ++ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers ++ _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; +@@ -5376,12 +5540,13 @@ + $echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi ++ _LT_AC_TAGVAR(link_all_deplibs, $1)=no + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + +- netbsd*) ++ netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= +@@ -5391,7 +5556,7 @@ + fi + ;; + +- solaris* | sysv5*) ++ solaris*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + _LT_AC_TAGVAR(ld_shlibs, $1)=no + cat <<EOF 1>&2 +@@ -5412,6 +5577,33 @@ + fi + ;; + ++ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) ++ case `$LD -v 2>&1` in ++ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) ++ _LT_AC_TAGVAR(ld_shlibs, $1)=no ++ cat <<_LT_EOF 1>&2 ++ ++*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not ++*** reliably create shared libraries on SCO systems. Therefore, libtool ++*** is disabling shared libraries support. We urge you to upgrade GNU ++*** binutils to release 2.16.91.0.3 or newer. Another option is to modify ++*** your PATH or compiler configuration so that the native linker is ++*** used, and then restart. ++ ++_LT_EOF ++ ;; ++ *) ++ if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' ++ else ++ _LT_AC_TAGVAR(ld_shlibs, $1)=no ++ fi ++ ;; ++ esac ++ ;; ++ + sunos4*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= +@@ -5445,7 +5637,7 @@ + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes +- if test "$GCC" = yes && test -z "$link_static_flag"; then ++ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported +@@ -5479,6 +5671,7 @@ + break + fi + done ++ ;; + esac + + exp_sym_flag='-bexport' +@@ -5516,6 +5709,7 @@ + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + fi ++ ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then +@@ -5528,11 +5722,11 @@ + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else +- if test "$aix_use_runtimelinking" = yes; then ++ if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' +- fi ++ fi + fi + fi + +@@ -5546,12 +5740,12 @@ + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" +- _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" +- _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX +@@ -5560,13 +5754,11 @@ + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' +- # -bexpall does not export symbols beginning with underscore (_) +- _LT_AC_TAGVAR(always_export_symbols, $1)=yes + # Exported symbols can be pulled into shared objects from archives +- _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' ' ++ _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes +- # This is similar to how AIX traditionally builds it's shared libraries. +- _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' ++ # This is similar to how AIX traditionally builds its shared libraries. ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; +@@ -5605,7 +5797,7 @@ + ;; + + darwin* | rhapsody*) +- case "$host_os" in ++ case $host_os in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}suppress' + ;; +@@ -5634,7 +5826,7 @@ + output_verbose_link_cmd='echo' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' +- # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's ++ # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else +@@ -5643,7 +5835,7 @@ + output_verbose_link_cmd='echo' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' + _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' +- # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's ++ # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + ;; +@@ -5684,7 +5876,7 @@ + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. +- freebsd* | kfreebsd*-gnu | dragonfly*) ++ freebsd* | dragonfly*) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes +@@ -5707,47 +5899,62 @@ + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + +- hpux10* | hpux11*) ++ hpux10*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then +- case "$host_cpu" in +- hppa*64*|ia64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ++ else ++ _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' ++ fi ++ if test "$with_gnu_ld" = no; then ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' ++ _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: ++ ++ _LT_AC_TAGVAR(hardcode_direct, $1)=yes ++ _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ++ ++ # hardcode_minus_L: Not really in the search PATH, ++ # but as the default location of the library. ++ _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes ++ fi ++ ;; ++ ++ hpux11*) ++ if test "$GCC" = yes -a "$with_gnu_ld" = no; then ++ case $host_cpu in ++ hppa*64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; ++ ia64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ++ ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else +- case "$host_cpu" in +- hppa*64*|ia64*) +- _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags' ++ case $host_cpu in ++ hppa*64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ ;; ++ ia64*) ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) +- _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then +- case "$host_cpu" in +- hppa*64*) +- _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' ++ _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: ++ ++ case $host_cpu in ++ hppa*64*|ia64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' +- _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: +- _LT_AC_TAGVAR(hardcode_direct, $1)=no +- _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +- ;; +- ia64*) +- _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +- +- # hardcode_minus_L: Not really in the search PATH, +- # but as the default location of the library. +- _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + ;; + *) +- _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' +- _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + +@@ -5771,7 +5978,7 @@ + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + +- netbsd*) ++ netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else +@@ -5849,14 +6056,6 @@ + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + +- sco3.2v5*) +- _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' +- _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +- _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' +- runpath_var=LD_RUN_PATH +- hardcode_runpath_var=yes +- ;; +- + solaris*) + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' + if test "$GCC" = yes; then +@@ -5942,36 +6141,45 @@ + fi + ;; + +- sysv4.2uw2*) +- _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' +- _LT_AC_TAGVAR(hardcode_direct, $1)=yes +- _LT_AC_TAGVAR(hardcode_minus_L, $1)=no ++ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7*) ++ _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' ++ _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +- hardcode_runpath_var=yes +- runpath_var=LD_RUN_PATH +- ;; ++ runpath_var='LD_RUN_PATH' + +- sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*) +- _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z ${wl}text' + if test "$GCC" = yes; then +- _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else +- _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi +- runpath_var='LD_RUN_PATH' +- _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + +- sysv5*) +- _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' +- # $CC -shared without GNU ld will not create a library from C++ +- # object files and a static libstdc++, better avoid it by now +- _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' +- _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ +- $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' +- _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= ++ sysv5* | sco3.2v5* | sco5v6*) ++ # Note: We can NOT use -z defs as we might desire, because we do not ++ # link with -lc, and that would cause any symbols used from libc to ++ # always be unresolved, which means just about no library would ++ # ever link correctly. If we're not using GNU ld we use -z text ++ # though, which does catch some bad symbols but isn't as heavy-handed ++ # as -z defs. ++ _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' ++ _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' ++ _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no ++ _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' ++ _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' ++ _LT_AC_TAGVAR(link_all_deplibs, $1)=yes ++ _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' ++ ++ if test "$GCC" = yes; then ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ else ++ _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' ++ fi + ;; + + uts4*) +@@ -5989,11 +6197,6 @@ + AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) + test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +-variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +-if test "$GCC" = yes; then +- variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +-fi +- + # + # Do we need to explicitly link libc? + # +@@ -6021,6 +6224,7 @@ + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) ++ pic_flag=$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= +@@ -6176,23 +6380,11 @@ + AC_MSG_RESULT([$SED]) + ]) + +-# -*- Autoconf -*- +-# Copyright (C) 2002, 2003 Free Software Foundation, Inc. +-# Generated from amversion.in; do not edit by hand. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + + # AM_AUTOMAKE_VERSION(VERSION) + # ---------------------------- +@@ -6205,26 +6397,15 @@ + # Call AM_AUTOMAKE_VERSION so it can be traced. + # This function is AC_REQUIREd by AC_INIT_AUTOMAKE. + AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +- [AM_AUTOMAKE_VERSION([1.9.4])]) ++ [AM_AUTOMAKE_VERSION([1.9.6])]) + +-# AM_AUX_DIR_EXPAND ++# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +-# Copyright (C) 2001, 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + + # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets + # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +@@ -6272,25 +6453,14 @@ + ]) + + +-# Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002, 2003 ++# Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002, 2003, 2005 + # Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. +- +-# serial 3 ++# serial 4 + + # This was merged into AC_PROG_CC in Autoconf. + +@@ -6305,26 +6475,16 @@ + ]) + AU_DEFUN([fp_PROG_CC_STDC]) + +-# AM_CONDITIONAL -*- Autoconf -*- +- +-# Copyright (C) 1997, 2000, 2001, 2003, 2004 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. ++# AM_CONDITIONAL -*- Autoconf -*- + +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005 ++# Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# serial 6 ++# serial 7 + + # AM_CONDITIONAL(NAME, SHELL-CONDITION) + # ------------------------------------- +@@ -6348,26 +6508,15 @@ + Usually this means the macro was only invoked conditionally.]]) + fi])]) + +-# serial 7 -*- Autoconf -*- + +-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 ++# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + # Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. +- ++# serial 8 + + # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be + # written in clear, in which case automake, when reading aclocal.m4, +@@ -6376,7 +6525,6 @@ + # CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +- + # _AM_DEPENDENCIES(NAME) + # ---------------------- + # See how the compiler implements dependency checking. +@@ -6516,27 +6664,16 @@ + AC_SUBST([AMDEPBACKSLASH]) + ]) + +-# Generate code to set up dependency tracking. -*- Autoconf -*- +- +-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 +-# Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. ++# Generate code to set up dependency tracking. -*- Autoconf -*- + +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 ++# Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-#serial 2 ++#serial 3 + + # _AM_OUTPUT_DEPENDENCY_COMMANDS + # ------------------------------ +@@ -6595,54 +6732,31 @@ + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) + ]) + +-# Like AC_CONFIG_HEADER, but automatically create stamp file. -*- Autoconf -*- +- +-# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 ++# Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# serial 7 ++# serial 8 + + # AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. + AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +-# Do all the work for Automake. -*- Autoconf -*- +- +-# This macro actually does too much some checks are only needed if +-# your package does certain things. But this isn't really a big deal. ++# Do all the work for Automake. -*- Autoconf -*- + +-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 ++# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + # Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# serial 12 + +-# serial 11 ++# This macro actually does too much. Some checks are only needed if ++# your package does certain things. But this isn't really a big deal. + + # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) + # AM_INIT_AUTOMAKE([OPTIONS]) +@@ -6744,51 +6858,27 @@ + done + echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + ++# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ + # AM_PROG_INSTALL_SH + # ------------------ + # Define $install_sh. +- +-# Copyright (C) 2001, 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. +- + AC_DEFUN([AM_PROG_INSTALL_SH], + [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl + install_sh=${install_sh-"$am_aux_dir/install-sh"} + AC_SUBST(install_sh)]) + +-# -*- Autoconf -*- +-# Copyright (C) 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 2003, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# serial 1 ++# serial 2 + + # Check whether the underlying file-system supports filenames + # with a leading dot. For instance MS-DOS doesn't. +@@ -6803,26 +6893,14 @@ + rmdir .tst 2>/dev/null + AC_SUBST([am__leading_dot])]) + +- +-# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ++# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 + # Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. +- +-# serial 4 ++# serial 5 + + # AM_PROG_LEX + # ----------- +@@ -6836,28 +6914,17 @@ + LEX=${am_missing_run}flex + fi]) + +-# Add --enable-maintainer-mode option to configure. ++# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- + # From Jim Meyering + +-# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004 ++# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005 + # Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. +- +-# serial 3 ++# serial 4 + + AC_DEFUN([AM_MAINTAINER_MODE], + [AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) +@@ -6876,26 +6943,15 @@ + + AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + +-# Check to see how 'make' treats includes. -*- Autoconf -*- +- +-# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. ++# Check to see how 'make' treats includes. -*- Autoconf -*- + +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# serial 2 ++# serial 3 + + # AM_MAKE_INCLUDE() + # ----------------- +@@ -6939,27 +6995,16 @@ + rm -f confinc confmf + ]) + +-# -*- Autoconf -*- +- +- +-# Copyright (C) 1997, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. ++# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005 ++# Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# serial 3 ++# serial 4 + + # AM_MISSING_PROG(NAME, PROGRAM) + # ------------------------------ +@@ -6985,27 +7030,16 @@ + fi + ]) + ++# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ + # AM_PROG_MKDIR_P + # --------------- + # Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. +- +-# Copyright (C) 2003, 2004 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. +- ++# + # Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories + # created by `make install' are always world readable, even if the + # installer happens to have an overly restrictive umask (e.g. 077). +@@ -7059,26 +7093,15 @@ + fi + AC_SUBST([mkdir_p])]) + +-# Helper functions for option handling. -*- Autoconf -*- +- +-# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. ++# Helper functions for option handling. -*- Autoconf -*- + +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# serial 2 ++# serial 3 + + # _AM_MANGLE_OPTION(NAME) + # ----------------------- +@@ -7103,28 +7126,16 @@ + AC_DEFUN([_AM_IF_OPTION], + [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +-# +-# Check to make sure that the build environment is sane. +-# +- +-# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc. ++# Check to make sure that the build environment is sane. -*- Autoconf -*- + +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 ++# Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + +-# serial 3 ++# serial 4 + + # AM_SANITY_CHECK + # --------------- +@@ -7167,25 +7178,14 @@ + fi + AC_MSG_RESULT(yes)]) + +-# AM_PROG_INSTALL_STRIP +- +-# Copyright (C) 2001, 2003 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. ++# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + ++# AM_PROG_INSTALL_STRIP ++# --------------------- + # One issue with vendor `install' (even GNU) is that you can't + # specify the program used to strip binaries. This is especially + # annoying in cross-compiling environments, where the build's strip +@@ -7208,25 +7208,13 @@ + + # Check how to create a tarball. -*- Autoconf -*- + +-# Copyright (C) 2004 Free Software Foundation, Inc. +- +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2, or (at your option) +-# any later version. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +- +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +-# 02111-1307, USA. +- +-# serial 1 ++# Copyright (C) 2004, 2005 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. + ++# serial 2 + + # _AM_PROG_TAR(FORMAT) + # -------------------- diff --git a/packages/lesstif/files/disable-docs.patch b/packages/lesstif/files/disable-docs.patch new file mode 100644 index 0000000000..f6038370d5 --- /dev/null +++ b/packages/lesstif/files/disable-docs.patch @@ -0,0 +1,29 @@ +Index: lesstif-0.95.0/Makefile.am +=================================================================== +--- lesstif-0.95.0.orig/Makefile.am ++++ lesstif-0.95.0/Makefile.am +@@ -7,9 +7,9 @@ MAINTAINERCLEANFILES=Makefile.in configu + missing mkinstalldirs \ + BUG-REPORTING INSTALL.txt ReleaseNotes.txt + +-SUBDIRS = . scripts include lib clients doc @BuildTestDir@ autopackage ++SUBDIRS = . scripts include lib clients @BuildTestDir@ autopackage + +-DIST_SUBDIRS = scripts include lib clients doc test autopackage ++DIST_SUBDIRS = scripts include lib clients test autopackage + + # + # Install some information files +Index: lesstif-0.95.0/scripts/Makefile.am +=================================================================== +--- lesstif-0.95.0.orig/scripts/Makefile.am ++++ lesstif-0.95.0/scripts/Makefile.am +@@ -4,7 +4,7 @@ + + MAINTAINERCLEANFILES=Makefile.in typedefs.c + +-SUBDIRS= . FreeBSD RedHat Slackware autoconf OS2 ++SUBDIRS= . FreeBSD RedHat Slackware OS2 + + EXTRA_DIST = LTsysinfo + diff --git a/packages/lesstif/lesstif_0.95.0.bb b/packages/lesstif/lesstif_0.95.0.bb new file mode 100644 index 0000000000..6a01a9c72b --- /dev/null +++ b/packages/lesstif/lesstif_0.95.0.bb @@ -0,0 +1,49 @@ +SECTION = "libs" +DESCRIPTION = "Free OSM/Motif implementation." +LICENSE = "LGPL" +PR = "r0" + +SRC_URI = "\ + ${SOURCEFORGE_MIRROR}/lesstif/${BP}.tar.bz2 \ + file://000_bootstrap_script.diff;patch=1 \ + file://000_libtool_linking.diff;patch=1 \ + file://010_rebootstrap-small.diff;patch=1 \ + file://020_bad_integer_cast.diff;patch=1 \ + file://020_missing_xm_h.diff;patch=1 \ + file://020_render_table_crash.diff;patch=1 \ + file://020_unsigned_int.diff;patch=1 \ + file://020_xpmpipethrough.diff;patch=1 \ + file://021_xim_chained_list_crash.diff;patch=1 \ + file://030_manpage.diff;patch=1 \ + file://disable-docs.patch;patch=1 \ + " + +DEPENDS = "flex-native bison-native libice libsm libx11 libxext libxp libxt libxrender libxft fontconfig freetype" + +inherit autotools + +do_preconfigure() { + mkdir -p m4 + mv ac_debug.m4 m4/ + mv ac_have_libxp.m4 m4/ + mv ac_find_xft.m4 m4/ + mv acinclude.m4 m4/ + + rm aclocal.m4 + + sed -i -e "s|LT_HAVE_FREETYPE|HAVEFREETYPE|" m4/acinclude.m4 + sed -i -e "s|LT_HAVE_XRENDER|HAVEXRENDER|" m4/acinclude.m4 +} + +addtask preconfigure after do_patch before do_configure + +EXTRA_OECONF = "\ + --with-gnu-ld --disable-verbose --disable-build-12 --disable-build-20 \ + --enable-build-21 --enable-xrender --enable-production \ +" + +PACKAGES += "${PN}-bin" + +FILES_${PN} = "${libdir}" +FILES_${PN}-bin = "${bindir}" + diff --git a/packages/libffi/libffi_2.0+gcc4.3.2.bb b/packages/libffi/libffi_2.0+gcc4.3.2.bb new file mode 100644 index 0000000000..300df81564 --- /dev/null +++ b/packages/libffi/libffi_2.0+gcc4.3.2.bb @@ -0,0 +1,64 @@ +SECTION = "libs" +DESCRIPTION = "Foreign Function Interface library" +LICENSE = "libffi" +PRIORITY = "optional" + +inherit autotools gettext + +PACKAGES = "${PN}-dbg ${PN} ${PN}-dev" + +FILES_${PN} = "${libdir}/libffi.so.*" + +FILES_${PN}-dev = "${includedir}/ffi* \ + ${libdir}/libffi.a \ + ${libdir}/libffi.la \ + ${libdir}/libffi.so" + +GCC_VER = "${@bb.data.getVar('PV',d,1).split('gcc')[1]}" + +SRC_URI = "${GNU_MIRROR}/gcc/gcc-${GCC_VER}/gcc-${GCC_VER}.tar.bz2 \ + " + +MIRRORS_prepend () { +${GNU_MIRROR}/gcc/ http://gcc.get-software.com/releases/ +${GNU_MIRROR}/gcc/ http://mirrors.rcn.net/pub/sourceware/gcc/releases/ +} + +S = "${WORKDIR}/gcc-${GCC_VER}/libffi" +B = "${S}/build.${HOST_SYS}.${TARGET_SYS}" + +EXTRA_OECONF = "--with-gnu-ld \ + --enable-shared \ + --enable-target-optspace \ + --enable-languages=c,c++,f77 \ + --enable-threads=posix \ + --enable-multilib \ + --enable-c99 \ + --enable-long-long \ + --enable-symvers=gnu \ + --program-prefix=${TARGET_PREFIX} \ + ${EXTRA_OECONF_PATHS}" + +EXTRA_OECONF_PATHS = "--with-local-prefix=${prefix}/local \ + --with-gxx-include-dir=${includedir}/c++/${PV}" + +do_configure () { + (cd ${S}/.. && gnu-configize) || die "failure running gnu-configize" + oe_runconf +} + +do_install_append() { + # follow debian and move this to $includedir + mv ${D}${libdir}/gcc/${TARGET_SYS}/${GCC_VER}/include/libffi/ffitarget.h ${D}${includedir}/ +} + +ffi_include = "ffi.h ffitarget.h" + +do_stage () { + oe_libinstall -so -C .libs libffi ${STAGING_LIBDIR} + + mkdir -p ${STAGING_INCDIR}/ + for i in ${ffi_include}; do + install -m 0644 include/$i ${STAGING_INCDIR}/ + done +} diff --git a/packages/libsdl/files/fixmfour.patch b/packages/libsdl/files/fixmfour.patch new file mode 100644 index 0000000000..0d534bd9a1 --- /dev/null +++ b/packages/libsdl/files/fixmfour.patch @@ -0,0 +1,14 @@ +Index: SDL-1.2.11/sdl.m4 +=================================================================== +--- SDL-1.2.11.orig/sdl.m4 2008-10-18 21:20:13.482478928 +0200 ++++ SDL-1.2.11/sdl.m4 2008-10-18 21:20:28.979441480 +0200 +@@ -32,9 +32,6 @@ + fi + fi + +- if test "x$prefix" != xNONE; then +- PATH="$prefix/bin:$prefix/usr/bin:$PATH" +- fi + AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH]) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) diff --git a/packages/libsdl/libsdl-image_1.2.6.bb b/packages/libsdl/libsdl-image_1.2.6.bb index 27b5aa7d1a..99fd98f508 100644 --- a/packages/libsdl/libsdl-image_1.2.6.bb +++ b/packages/libsdl/libsdl-image_1.2.6.bb @@ -1,8 +1,12 @@ require libsdl-image.inc +PR = "r1" + +DEPENDS += "tiff" do_unpackpost() { # Removing this file fixes a libtool version mismatch. rm acinclude/libtool.m4 + rm acinclude/sdl.m4 } addtask unpackpost after do_unpack before do_patch diff --git a/packages/libsdl/libsdl-x11_1.2.11.bb b/packages/libsdl/libsdl-x11_1.2.11.bb index 3f9120edb4..ab349d6306 100644 --- a/packages/libsdl/libsdl-x11_1.2.11.bb +++ b/packages/libsdl/libsdl-x11_1.2.11.bb @@ -3,7 +3,7 @@ require libsdl.inc # extra-keys.patch is missing DEFAULT_PREFERENCE = "-1" -PR = "r6" +PR = "r7" SRC_URI = "\ http://www.libsdl.org/release/SDL-${PV}.tar.gz \ @@ -12,6 +12,7 @@ SRC_URI = "\ file://pagesize.patch;patch=1 \ file://kernel-asm-page.patch;patch=1 \ file://sdl-cdfix.patch;patch=1 \ + file://fixmfour.patch;patch=1 \ " EXTRA_OECONF = "--disable-static --disable-debug --enable-cdrom --enable-threads --enable-timers --enable-endian \ diff --git a/packages/linux/linux-omap2-git/beagleboard/defconfig b/packages/linux/linux-omap2-git/beagleboard/defconfig index c726740485..413167de7e 100644 --- a/packages/linux/linux-omap2-git/beagleboard/defconfig +++ b/packages/linux/linux-omap2-git/beagleboard/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.26-omap1 -# Thu Sep 25 07:42:19 2008 +# Wed Oct 22 12:01:26 2008 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -97,7 +97,7 @@ CONFIG_RT_MUTEXES=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 CONFIG_MODULES=y -# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_FORCE_LOAD=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y diff --git a/packages/linux/linux-rp-2.6.26/defconfig-collie b/packages/linux/linux-rp-2.6.26/defconfig-collie index e21cc21da2..e80885f332 100644 --- a/packages/linux/linux-rp-2.6.26/defconfig-collie +++ b/packages/linux/linux-rp-2.6.26/defconfig-collie @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.26 -# Fri Oct 17 18:46:51 2008 +# Mon Oct 20 01:48:37 2008 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -845,7 +845,13 @@ CONFIG_INPUT_TOUCHSCREEN=y # CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set # CONFIG_TOUCHSCREEN_TOUCHWIN is not set # CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set CONFIG_INPUT_UINPUT=m # @@ -954,11 +960,11 @@ CONFIG_SSB_POSSIBLE=y # # Multimedia Capabilities Port drivers # -CONFIG_MCP=m -CONFIG_MCP_SA11X0=m -CONFIG_MCP_UCB1200=m +CONFIG_MCP=y +CONFIG_MCP_SA11X0=y +CONFIG_MCP_UCB1200=y # CONFIG_MCP_UCB1200_TS is not set -CONFIG_MCP_COLLIE_TS=m +CONFIG_MCP_COLLIE_TS=y # # Multimedia devices @@ -1044,7 +1050,47 @@ CONFIG_FONT_8x8=y # CONFIG_SOUND is not set # CONFIG_HID_SUPPORT is not set CONFIG_HID=m -# CONFIG_USB_SUPPORT is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_AT91 is not set +CONFIG_USB_GADGET_SA1100=y +CONFIG_USB_SA1100=y +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +# CONFIG_USB_ZERO is not set +CONFIG_USB_ETH=m +# CONFIG_USB_ETH_RNDIS is not set +# CONFIG_USB_ETH_NO_MDLM is not set +# CONFIG_USB_GADGETFS is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set +CONFIG_USB_G_PRINTER=m CONFIG_MMC=y # CONFIG_MMC_DEBUG is not set CONFIG_MMC_UNSAFE_RESUME=y diff --git a/packages/linux/linux-rp-2.6.26/usb-gadget27bp.patch b/packages/linux/linux-rp-2.6.26/usb-gadget27bp.patch new file mode 100644 index 0000000000..3c86f1a474 --- /dev/null +++ b/packages/linux/linux-rp-2.6.26/usb-gadget27bp.patch @@ -0,0 +1,26674 @@ +diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +index 6e784d2..117d0c7 100644 +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -118,10 +118,10 @@ config USB_AMD5536UDC + config USB_GADGET_ATMEL_USBA + boolean "Atmel USBA" + select USB_GADGET_DUALSPEED +- depends on AVR32 || ARCH_AT91CAP9 ++ depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL + help + USBA is the integrated high-speed USB Device controller on +- the AT32AP700x and AT91CAP9 processors from Atmel. ++ the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. + + config USB_ATMEL_USBA + tristate +@@ -172,7 +172,7 @@ config USB_NET2280 + default USB_GADGET + select USB_GADGET_SELECTED + +-config USB_GADGET_PXA2XX ++config USB_GADGET_PXA25X + boolean "PXA 25x or IXP 4xx" + depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX + help +@@ -184,19 +184,19 @@ config USB_GADGET_PXA2XX + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a +- dynamically linked module called "pxa2xx_udc" and force all ++ dynamically linked module called "pxa25x_udc" and force all + gadget drivers to also be dynamically linked. + +-config USB_PXA2XX ++config USB_PXA25X + tristate +- depends on USB_GADGET_PXA2XX ++ depends on USB_GADGET_PXA25X + default USB_GADGET + select USB_GADGET_SELECTED + + # if there's only one gadget driver, using only two bulk endpoints, + # don't waste memory for the other endpoints +-config USB_PXA2XX_SMALL +- depends on USB_GADGET_PXA2XX ++config USB_PXA25X_SMALL ++ depends on USB_GADGET_PXA25X + bool + default n if USB_ETH_RNDIS + default y if USB_ZERO +@@ -284,6 +284,16 @@ config USB_LH7A40X + default USB_GADGET + select USB_GADGET_SELECTED + ++# built in ../musb along with host support ++config USB_GADGET_MUSB_HDRC ++ boolean "Inventra HDRC USB Peripheral (TI, ...)" ++ depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) ++ select USB_GADGET_DUALSPEED ++ select USB_GADGET_SELECTED ++ help ++ This OTG-capable silicon IP is used in dual designs including ++ the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010. ++ + config USB_GADGET_OMAP + boolean "OMAP USB Device Controller" + depends on ARCH_OMAP +@@ -355,6 +365,20 @@ config USB_AT91 + depends on USB_GADGET_AT91 + default USB_GADGET + ++config USB_GADGET_SA1100 ++ boolean "SA1100 USB Device Port" ++ depends on ARCH_SA1100 ++ help ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "sa1100_udc" and force all ++ gadget drivers to also be dynamically linked. ++ ++config USB_SA1100 ++ tristate ++ depends on USB_GADGET_SA1100 ++ default USB_GADGET ++ select USB_GADGET_SELECTED ++ + config USB_GADGET_DUMMY_HCD + boolean "Dummy HCD (DEVELOPMENT)" + depends on USB=y || (USB=m && USB_GADGET=m) +@@ -504,6 +528,20 @@ config USB_ETH_RNDIS + XP, you'll need to download drivers from Microsoft's website; a URL + is given in comments found in that info file. + ++config USB_ETH_NO_MDLM ++ bool "Disable MDLM support for CDC_SUBSET" ++ depends on USB_ETH ++ default n ++ help ++ A variation on CDC_SUBSET support is used in newer kernel ++ implementations for use with a proprietary Microsoft Windows driver ++ used for example with the Zaurus linux distribution. ++ However, the MDLM extensions break the older host side drivers making ++ g_ether incompatible with older kernels. ++ ++ If you say "y" here, the Ethernet gadget driver will use the older ++ pre-MDLM extensions. ++ + config USB_GADGETFS + tristate "Gadget Filesystem (EXPERIMENTAL)" + depends on EXPERIMENTAL +@@ -586,6 +624,20 @@ config USB_G_PRINTER + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + ++config USB_CDC_COMPOSITE ++ tristate "CDC Composite Device (Ethernet and ACM)" ++ depends on NET ++ help ++ This driver provides two functions in one configuration: ++ a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. ++ ++ This driver requires four bulk and two interrupt endpoints, ++ plus the ability to handle altsettings. Not all peripheral ++ controllers are that capable. ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module. ++ + # put drivers that need isochronous transfer support (for audio + # or video class gadget drivers), or specific hardware, here. + +diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile +index 1235725..79a69ae 100644 +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -8,7 +8,7 @@ endif + obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o + obj-$(CONFIG_USB_NET2280) += net2280.o + obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o +-obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o ++obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o + obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o + obj-$(CONFIG_USB_GOKU) += goku_udc.o + obj-$(CONFIG_USB_OMAP) += omap_udc.o +@@ -18,22 +18,27 @@ obj-$(CONFIG_USB_AT91) += at91_udc.o + obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o + obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o + obj-$(CONFIG_USB_M66592) += m66592-udc.o ++obj-$(CONFIG_USB_SA1100) += sa1100_udc.o + + # + # USB gadget drivers + # +-g_zero-objs := zero.o usbstring.o config.o epautoconf.o +-g_ether-objs := ether.o usbstring.o config.o epautoconf.o +-g_serial-objs := serial.o usbstring.o config.o epautoconf.o ++C_UTILS = composite.o usbstring.o config.o epautoconf.o ++ ++g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) ++g_ether-objs := ether.o u_ether.o f_subset.o f_ecm.o $(C_UTILS) ++g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) + g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o + gadgetfs-objs := inode.o + g_file_storage-objs := file_storage.o usbstring.o config.o \ + epautoconf.o + g_printer-objs := printer.o usbstring.o config.o \ + epautoconf.o ++g_cdc-objs := cdc2.o u_ether.o f_ecm.o \ ++ u_serial.o f_acm.o $(C_UTILS) + + ifeq ($(CONFIG_USB_ETH_RNDIS),y) +- g_ether-objs += rndis.o ++ g_ether-objs += f_rndis.o rndis.o + endif + + obj-$(CONFIG_USB_ZERO) += g_zero.o +@@ -43,4 +48,5 @@ obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o + obj-$(CONFIG_USB_G_SERIAL) += g_serial.o + obj-$(CONFIG_USB_G_PRINTER) += g_printer.o + obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o ++obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o + +diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c +index f261d2a..abf8192 100644 +--- a/drivers/usb/gadget/amd5536udc.c ++++ b/drivers/usb/gadget/amd5536udc.c +@@ -44,7 +44,6 @@ + #include <linux/module.h> + #include <linux/pci.h> + #include <linux/kernel.h> +-#include <linux/version.h> + #include <linux/delay.h> + #include <linux/ioport.h> + #include <linux/sched.h> +@@ -3342,7 +3341,7 @@ static int udc_probe(struct udc *dev) + spin_lock_init(&dev->lock); + dev->gadget.ops = &udc_ops; + +- strcpy(dev->gadget.dev.bus_id, "gadget"); ++ dev_set_name(&dev->gadget.dev, "gadget"); + dev->gadget.dev.release = gadget_release; + dev->gadget.name = name; + dev->gadget.name = name; +diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c +index 274c60a..a8a1de4 100644 +--- a/drivers/usb/gadget/at91_udc.c ++++ b/drivers/usb/gadget/at91_udc.c +@@ -40,16 +40,15 @@ + #include <linux/usb/gadget.h> + + #include <asm/byteorder.h> +-#include <asm/hardware.h> ++#include <mach/hardware.h> + #include <asm/io.h> + #include <asm/irq.h> + #include <asm/system.h> +-#include <asm/mach-types.h> + #include <asm/gpio.h> + +-#include <asm/arch/board.h> +-#include <asm/arch/cpu.h> +-#include <asm/arch/at91sam9261_matrix.h> ++#include <mach/board.h> ++#include <mach/cpu.h> ++#include <mach/at91sam9261_matrix.h> + + #include "at91_udc.h" + +@@ -888,7 +887,7 @@ static void pullup(struct at91_udc *udc, int is_on) + at91_udp_write(udc, AT91_UDP_TXVC, 0); + if (cpu_is_at91rm9200()) + gpio_set_value(udc->board.pullup_pin, active); +- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { ++ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { + u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); + + txvc |= AT91_UDP_TXVC_PUON; +@@ -906,7 +905,7 @@ static void pullup(struct at91_udc *udc, int is_on) + at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); + if (cpu_is_at91rm9200()) + gpio_set_value(udc->board.pullup_pin, !active); +- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { ++ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { + u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); + + txvc &= ~AT91_UDP_TXVC_PUON; +@@ -1687,6 +1686,19 @@ static int __init at91udc_probe(struct platform_device *pdev) + udc->board.pullup_active_low); + } + ++ /* newer chips have more FIFO memory than rm9200 */ ++ if (cpu_is_at91sam9260()) { ++ udc->ep[0].maxpacket = 64; ++ udc->ep[3].maxpacket = 64; ++ udc->ep[4].maxpacket = 512; ++ udc->ep[5].maxpacket = 512; ++ } else if (cpu_is_at91sam9261()) { ++ udc->ep[3].maxpacket = 64; ++ } else if (cpu_is_at91sam9263()) { ++ udc->ep[0].maxpacket = 64; ++ udc->ep[3].maxpacket = 64; ++ } ++ + udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1); + if (!udc->udp_baseaddr) { + retval = -ENOMEM; +diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h +index a973f2a..c65d622 100644 +--- a/drivers/usb/gadget/at91_udc.h ++++ b/drivers/usb/gadget/at91_udc.h +@@ -171,7 +171,7 @@ struct at91_request { + #endif + + #define ERR(stuff...) pr_err("udc: " stuff) +-#define WARN(stuff...) pr_warning("udc: " stuff) ++#define WARNING(stuff...) pr_warning("udc: " stuff) + #define INFO(stuff...) pr_info("udc: " stuff) + #define DBG(stuff...) pr_debug("udc: " stuff) + +diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c +index 07e5a0b..ae30ab1 100644 +--- a/drivers/usb/gadget/atmel_usba_udc.c ++++ b/drivers/usb/gadget/atmel_usba_udc.c +@@ -22,7 +22,7 @@ + #include <linux/delay.h> + + #include <asm/gpio.h> +-#include <asm/arch/board.h> ++#include <mach/board.h> + + #include "atmel_usba_udc.h" + +@@ -334,7 +334,7 @@ static void toggle_bias(int is_on) + + #elif defined(CONFIG_ARCH_AT91) + +-#include <asm/arch/at91_pmc.h> ++#include <mach/at91_pmc.h> + + static void toggle_bias(int is_on) + { +diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c +new file mode 100644 +index 0000000..a39a4b9 +--- /dev/null ++++ b/drivers/usb/gadget/cdc2.c +@@ -0,0 +1,246 @@ ++/* ++ * cdc2.c -- CDC Composite driver, with ECM and ACM support ++ * ++ * Copyright (C) 2008 David Brownell ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++ ++#include "u_ether.h" ++#include "u_serial.h" ++ ++ ++#define DRIVER_DESC "CDC Composite Gadget" ++#define DRIVER_VERSION "King Kamehameha Day 2008" ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! ++ * Instead: allocate your own, using normal USB-IF procedures. ++ */ ++ ++/* Thanks to NetChip Technologies for donating this product ID. ++ * It's for devices with only this composite CDC configuration. ++ */ ++#define CDC_VENDOR_NUM 0x0525 /* NetChip */ ++#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_device_descriptor device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ ++ .bcdUSB = __constant_cpu_to_le16(0x0200), ++ ++ .bDeviceClass = USB_CLASS_COMM, ++ .bDeviceSubClass = 0, ++ .bDeviceProtocol = 0, ++ /* .bMaxPacketSize0 = f(hardware) */ ++ ++ /* Vendor and product id can be overridden by module parameters. */ ++ .idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM), ++ .idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM), ++ /* .bcdDevice = f(hardware) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ /* NO SERIAL NUMBER */ ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_otg_descriptor otg_descriptor = { ++ .bLength = sizeof otg_descriptor, ++ .bDescriptorType = USB_DT_OTG, ++ ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, ++}; ++ ++static const struct usb_descriptor_header *otg_desc[] = { ++ (struct usb_descriptor_header *) &otg_descriptor, ++ NULL, ++}; ++ ++ ++/* string IDs are assigned dynamically */ ++ ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++ ++static char manufacturer[50]; ++ ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = DRIVER_DESC, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, ++}; ++ ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; ++ ++static u8 hostaddr[ETH_ALEN]; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * We _always_ have both CDC ECM and CDC ACM functions. ++ */ ++static int __init cdc_do_config(struct usb_configuration *c) ++{ ++ int status; ++ ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ status = ecm_bind_config(c, hostaddr); ++ if (status < 0) ++ return status; ++ ++ status = acm_bind_config(c, 0); ++ if (status < 0) ++ return status; ++ ++ return 0; ++} ++ ++static struct usb_configuration cdc_config_driver = { ++ .label = "CDC Composite (ECM + ACM)", ++ .bind = cdc_do_config, ++ .bConfigurationValue = 1, ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init cdc_bind(struct usb_composite_dev *cdev) ++{ ++ int gcnum; ++ struct usb_gadget *gadget = cdev->gadget; ++ int status; ++ ++ if (!can_support_ecm(cdev->gadget)) { ++ ERROR(cdev, "controller '%s' not usable\n", gadget->name); ++ return -EINVAL; ++ } ++ ++ /* set up network link layer */ ++ status = gether_setup(cdev->gadget, hostaddr); ++ if (status < 0) ++ return status; ++ ++ /* set up serial link layer */ ++ status = gserial_setup(cdev->gadget, 1); ++ if (status < 0) ++ goto fail0; ++ ++ gcnum = usb_gadget_controller_number(gadget); ++ if (gcnum >= 0) ++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); ++ else { ++ /* We assume that can_support_ecm() tells the truth; ++ * but if the controller isn't recognized at all then ++ * that assumption is a bit more likely to be wrong. ++ */ ++ WARNING(cdev, "controller '%s' not recognized; trying %s\n", ++ gadget->name, ++ cdc_config_driver.label); ++ device_desc.bcdDevice = ++ __constant_cpu_to_le16(0x0300 | 0x0099); ++ } ++ ++ ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ ++ ++ /* device descriptor strings: manufacturer, product */ ++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", ++ init_utsname()->sysname, init_utsname()->release, ++ gadget->name); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail1; ++ strings_dev[STRING_MANUFACTURER_IDX].id = status; ++ device_desc.iManufacturer = status; ++ ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail1; ++ strings_dev[STRING_PRODUCT_IDX].id = status; ++ device_desc.iProduct = status; ++ ++ /* register our configuration */ ++ status = usb_add_config(cdev, &cdc_config_driver); ++ if (status < 0) ++ goto fail1; ++ ++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); ++ ++ return 0; ++ ++fail1: ++ gserial_cleanup(); ++fail0: ++ gether_cleanup(); ++ return status; ++} ++ ++static int __exit cdc_unbind(struct usb_composite_dev *cdev) ++{ ++ gserial_cleanup(); ++ gether_cleanup(); ++ return 0; ++} ++ ++static struct usb_composite_driver cdc_driver = { ++ .name = "g_cdc", ++ .dev = &device_desc, ++ .strings = dev_strings, ++ .bind = cdc_bind, ++ .unbind = __exit_p(cdc_unbind), ++}; ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("David Brownell"); ++MODULE_LICENSE("GPL"); ++ ++static int __init init(void) ++{ ++ return usb_composite_register(&cdc_driver); ++} ++module_init(init); ++ ++static void __exit cleanup(void) ++{ ++ usb_composite_unregister(&cdc_driver); ++} ++module_exit(cleanup); +diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c +new file mode 100644 +index 0000000..85c876c +--- /dev/null ++++ b/drivers/usb/gadget/composite.c +@@ -0,0 +1,1041 @@ ++/* ++ * composite.c - infrastructure for Composite USB Gadgets ++ * ++ * Copyright (C) 2006-2008 David Brownell ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kallsyms.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/device.h> ++ ++#include <linux/usb/composite.h> ++ ++ ++/* ++ * The code in this file is utility code, used to build a gadget driver ++ * from one or more "function" drivers, one or more "configuration" ++ * objects, and a "usb_composite_driver" by gluing them together along ++ * with the relevant device-wide data. ++ */ ++ ++/* big enough to hold our biggest descriptor */ ++#define USB_BUFSIZ 512 ++ ++static struct usb_composite_driver *composite; ++ ++/* Some systems will need runtime overrides for the product identifers ++ * published in the device descriptor, either numbers or strings or both. ++ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). ++ */ ++ ++static ushort idVendor; ++module_param(idVendor, ushort, 0); ++MODULE_PARM_DESC(idVendor, "USB Vendor ID"); ++ ++static ushort idProduct; ++module_param(idProduct, ushort, 0); ++MODULE_PARM_DESC(idProduct, "USB Product ID"); ++ ++static ushort bcdDevice; ++module_param(bcdDevice, ushort, 0); ++MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); ++ ++static char *iManufacturer; ++module_param(iManufacturer, charp, 0); ++MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); ++ ++static char *iProduct; ++module_param(iProduct, charp, 0); ++MODULE_PARM_DESC(iProduct, "USB Product string"); ++ ++static char *iSerialNumber; ++module_param(iSerialNumber, charp, 0); ++MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/** ++ * usb_add_function() - add a function to a configuration ++ * @config: the configuration ++ * @function: the function being added ++ * Context: single threaded during gadget setup ++ * ++ * After initialization, each configuration must have one or more ++ * functions added to it. Adding a function involves calling its @bind() ++ * method to allocate resources such as interface and string identifiers ++ * and endpoints. ++ * ++ * This function returns the value of the function's bind(), which is ++ * zero for success else a negative errno value. ++ */ ++int __init usb_add_function(struct usb_configuration *config, ++ struct usb_function *function) ++{ ++ int value = -EINVAL; ++ ++ DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", ++ function->name, function, ++ config->label, config); ++ ++ if (!function->set_alt || !function->disable) ++ goto done; ++ ++ function->config = config; ++ list_add_tail(&function->list, &config->functions); ++ ++ /* REVISIT *require* function->bind? */ ++ if (function->bind) { ++ value = function->bind(config, function); ++ if (value < 0) { ++ list_del(&function->list); ++ function->config = NULL; ++ } ++ } else ++ value = 0; ++ ++ /* We allow configurations that don't work at both speeds. ++ * If we run into a lowspeed Linux system, treat it the same ++ * as full speed ... it's the function drivers that will need ++ * to avoid bulk and ISO transfers. ++ */ ++ if (!config->fullspeed && function->descriptors) ++ config->fullspeed = true; ++ if (!config->highspeed && function->hs_descriptors) ++ config->highspeed = true; ++ ++done: ++ if (value) ++ DBG(config->cdev, "adding '%s'/%p --> %d\n", ++ function->name, function, value); ++ return value; ++} ++ ++/** ++ * usb_interface_id() - allocate an unused interface ID ++ * @config: configuration associated with the interface ++ * @function: function handling the interface ++ * Context: single threaded during gadget setup ++ * ++ * usb_interface_id() is called from usb_function.bind() callbacks to ++ * allocate new interface IDs. The function driver will then store that ++ * ID in interface, association, CDC union, and other descriptors. It ++ * will also handle any control requests targetted at that interface, ++ * particularly changing its altsetting via set_alt(). There may ++ * also be class-specific or vendor-specific requests to handle. ++ * ++ * All interface identifier should be allocated using this routine, to ++ * ensure that for example different functions don't wrongly assign ++ * different meanings to the same identifier. Note that since interface ++ * identifers are configuration-specific, functions used in more than ++ * one configuration (or more than once in a given configuration) need ++ * multiple versions of the relevant descriptors. ++ * ++ * Returns the interface ID which was allocated; or -ENODEV if no ++ * more interface IDs can be allocated. ++ */ ++int __init usb_interface_id(struct usb_configuration *config, ++ struct usb_function *function) ++{ ++ unsigned id = config->next_interface_id; ++ ++ if (id < MAX_CONFIG_INTERFACES) { ++ config->interface[id] = function; ++ config->next_interface_id = id + 1; ++ return id; ++ } ++ return -ENODEV; ++} ++ ++static int config_buf(struct usb_configuration *config, ++ enum usb_device_speed speed, void *buf, u8 type) ++{ ++ struct usb_config_descriptor *c = buf; ++ void *next = buf + USB_DT_CONFIG_SIZE; ++ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; ++ struct usb_function *f; ++ int status; ++ ++ /* write the config descriptor */ ++ c = buf; ++ c->bLength = USB_DT_CONFIG_SIZE; ++ c->bDescriptorType = type; ++ /* wTotalLength is written later */ ++ c->bNumInterfaces = config->next_interface_id; ++ c->bConfigurationValue = config->bConfigurationValue; ++ c->iConfiguration = config->iConfiguration; ++ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; ++ c->bMaxPower = config->bMaxPower; ++ ++ /* There may be e.g. OTG descriptors */ ++ if (config->descriptors) { ++ status = usb_descriptor_fillbuf(next, len, ++ config->descriptors); ++ if (status < 0) ++ return status; ++ len -= status; ++ next += status; ++ } ++ ++ /* add each function's descriptors */ ++ list_for_each_entry(f, &config->functions, list) { ++ struct usb_descriptor_header **descriptors; ++ ++ if (speed == USB_SPEED_HIGH) ++ descriptors = f->hs_descriptors; ++ else ++ descriptors = f->descriptors; ++ if (!descriptors) ++ continue; ++ status = usb_descriptor_fillbuf(next, len, ++ (const struct usb_descriptor_header **) descriptors); ++ if (status < 0) ++ return status; ++ len -= status; ++ next += status; ++ } ++ ++ len = next - buf; ++ c->wTotalLength = cpu_to_le16(len); ++ return len; ++} ++ ++static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ struct usb_configuration *c; ++ u8 type = w_value >> 8; ++ enum usb_device_speed speed = USB_SPEED_UNKNOWN; ++ ++ if (gadget_is_dualspeed(gadget)) { ++ int hs = 0; ++ ++ if (gadget->speed == USB_SPEED_HIGH) ++ hs = 1; ++ if (type == USB_DT_OTHER_SPEED_CONFIG) ++ hs = !hs; ++ if (hs) ++ speed = USB_SPEED_HIGH; ++ ++ } ++ ++ /* This is a lookup by config *INDEX* */ ++ w_value &= 0xff; ++ list_for_each_entry(c, &cdev->configs, list) { ++ /* ignore configs that won't work at this speed */ ++ if (speed == USB_SPEED_HIGH) { ++ if (!c->highspeed) ++ continue; ++ } else { ++ if (!c->fullspeed) ++ continue; ++ } ++ if (w_value == 0) ++ return config_buf(c, speed, cdev->req->buf, type); ++ w_value--; ++ } ++ return -EINVAL; ++} ++ ++static int count_configs(struct usb_composite_dev *cdev, unsigned type) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ struct usb_configuration *c; ++ unsigned count = 0; ++ int hs = 0; ++ ++ if (gadget_is_dualspeed(gadget)) { ++ if (gadget->speed == USB_SPEED_HIGH) ++ hs = 1; ++ if (type == USB_DT_DEVICE_QUALIFIER) ++ hs = !hs; ++ } ++ list_for_each_entry(c, &cdev->configs, list) { ++ /* ignore configs that won't work at this speed */ ++ if (hs) { ++ if (!c->highspeed) ++ continue; ++ } else { ++ if (!c->fullspeed) ++ continue; ++ } ++ count++; ++ } ++ return count; ++} ++ ++static void device_qual(struct usb_composite_dev *cdev) ++{ ++ struct usb_qualifier_descriptor *qual = cdev->req->buf; ++ ++ qual->bLength = sizeof(*qual); ++ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; ++ /* POLICY: same bcdUSB and device type info at both speeds */ ++ qual->bcdUSB = cdev->desc.bcdUSB; ++ qual->bDeviceClass = cdev->desc.bDeviceClass; ++ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; ++ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; ++ /* ASSUME same EP0 fifo size at both speeds */ ++ qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; ++ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); ++ qual->bRESERVED = 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void reset_config(struct usb_composite_dev *cdev) ++{ ++ struct usb_function *f; ++ ++ DBG(cdev, "reset config\n"); ++ ++ list_for_each_entry(f, &cdev->config->functions, list) { ++ if (f->disable) ++ f->disable(f); ++ } ++ cdev->config = NULL; ++} ++ ++static int set_config(struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *ctrl, unsigned number) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ struct usb_configuration *c = NULL; ++ int result = -EINVAL; ++ unsigned power = gadget_is_otg(gadget) ? 8 : 100; ++ int tmp; ++ ++ if (cdev->config) ++ reset_config(cdev); ++ ++ if (number) { ++ list_for_each_entry(c, &cdev->configs, list) { ++ if (c->bConfigurationValue == number) { ++ result = 0; ++ break; ++ } ++ } ++ if (result < 0) ++ goto done; ++ } else ++ result = 0; ++ ++ INFO(cdev, "%s speed config #%d: %s\n", ++ ({ char *speed; ++ switch (gadget->speed) { ++ case USB_SPEED_LOW: speed = "low"; break; ++ case USB_SPEED_FULL: speed = "full"; break; ++ case USB_SPEED_HIGH: speed = "high"; break; ++ default: speed = "?"; break; ++ } ; speed; }), number, c ? c->label : "unconfigured"); ++ ++ if (!c) ++ goto done; ++ ++ cdev->config = c; ++ ++ /* Initialize all interfaces by setting them to altsetting zero. */ ++ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { ++ struct usb_function *f = c->interface[tmp]; ++ ++ if (!f) ++ break; ++ ++ result = f->set_alt(f, tmp, 0); ++ if (result < 0) { ++ DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", ++ tmp, f->name, f, result); ++ ++ reset_config(cdev); ++ goto done; ++ } ++ } ++ ++ /* when we return, be sure our power usage is valid */ ++ power = 2 * c->bMaxPower; ++done: ++ usb_gadget_vbus_draw(gadget, power); ++ return result; ++} ++ ++/** ++ * usb_add_config() - add a configuration to a device. ++ * @cdev: wraps the USB gadget ++ * @config: the configuration, with bConfigurationValue assigned ++ * Context: single threaded during gadget setup ++ * ++ * One of the main tasks of a composite driver's bind() routine is to ++ * add each of the configurations it supports, using this routine. ++ * ++ * This function returns the value of the configuration's bind(), which ++ * is zero for success else a negative errno value. Binding configurations ++ * assigns global resources including string IDs, and per-configuration ++ * resources such as interface IDs and endpoints. ++ */ ++int __init usb_add_config(struct usb_composite_dev *cdev, ++ struct usb_configuration *config) ++{ ++ int status = -EINVAL; ++ struct usb_configuration *c; ++ ++ DBG(cdev, "adding config #%u '%s'/%p\n", ++ config->bConfigurationValue, ++ config->label, config); ++ ++ if (!config->bConfigurationValue || !config->bind) ++ goto done; ++ ++ /* Prevent duplicate configuration identifiers */ ++ list_for_each_entry(c, &cdev->configs, list) { ++ if (c->bConfigurationValue == config->bConfigurationValue) { ++ status = -EBUSY; ++ goto done; ++ } ++ } ++ ++ config->cdev = cdev; ++ list_add_tail(&config->list, &cdev->configs); ++ ++ INIT_LIST_HEAD(&config->functions); ++ config->next_interface_id = 0; ++ ++ status = config->bind(config); ++ if (status < 0) { ++ list_del(&config->list); ++ config->cdev = NULL; ++ } else { ++ unsigned i; ++ ++ DBG(cdev, "cfg %d/%p speeds:%s%s\n", ++ config->bConfigurationValue, config, ++ config->highspeed ? " high" : "", ++ config->fullspeed ++ ? (gadget_is_dualspeed(cdev->gadget) ++ ? " full" ++ : " full/low") ++ : ""); ++ ++ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { ++ struct usb_function *f = config->interface[i]; ++ ++ if (!f) ++ continue; ++ DBG(cdev, " interface %d = %s/%p\n", ++ i, f->name, f); ++ } ++ } ++ ++ /* set_alt(), or next config->bind(), sets up ++ * ep->driver_data as needed. ++ */ ++ usb_ep_autoconfig_reset(cdev->gadget); ++ ++done: ++ if (status) ++ DBG(cdev, "added config '%s'/%u --> %d\n", config->label, ++ config->bConfigurationValue, status); ++ return status; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* We support strings in multiple languages ... string descriptor zero ++ * says which languages are supported. The typical case will be that ++ * only one language (probably English) is used, with I18N handled on ++ * the host side. ++ */ ++ ++static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) ++{ ++ const struct usb_gadget_strings *s; ++ u16 language; ++ __le16 *tmp; ++ ++ while (*sp) { ++ s = *sp; ++ language = cpu_to_le16(s->language); ++ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { ++ if (*tmp == language) ++ goto repeat; ++ } ++ *tmp++ = language; ++repeat: ++ sp++; ++ } ++} ++ ++static int lookup_string( ++ struct usb_gadget_strings **sp, ++ void *buf, ++ u16 language, ++ int id ++) ++{ ++ struct usb_gadget_strings *s; ++ int value; ++ ++ while (*sp) { ++ s = *sp++; ++ if (s->language != language) ++ continue; ++ value = usb_gadget_get_string(s, id, buf); ++ if (value > 0) ++ return value; ++ } ++ return -EINVAL; ++} ++ ++static int get_string(struct usb_composite_dev *cdev, ++ void *buf, u16 language, int id) ++{ ++ struct usb_configuration *c; ++ struct usb_function *f; ++ int len; ++ ++ /* Yes, not only is USB's I18N support probably more than most ++ * folk will ever care about ... also, it's all supported here. ++ * (Except for UTF8 support for Unicode's "Astral Planes".) ++ */ ++ ++ /* 0 == report all available language codes */ ++ if (id == 0) { ++ struct usb_string_descriptor *s = buf; ++ struct usb_gadget_strings **sp; ++ ++ memset(s, 0, 256); ++ s->bDescriptorType = USB_DT_STRING; ++ ++ sp = composite->strings; ++ if (sp) ++ collect_langs(sp, s->wData); ++ ++ list_for_each_entry(c, &cdev->configs, list) { ++ sp = c->strings; ++ if (sp) ++ collect_langs(sp, s->wData); ++ ++ list_for_each_entry(f, &c->functions, list) { ++ sp = f->strings; ++ if (sp) ++ collect_langs(sp, s->wData); ++ } ++ } ++ ++ for (len = 0; s->wData[len] && len <= 126; len++) ++ continue; ++ if (!len) ++ return -EINVAL; ++ ++ s->bLength = 2 * (len + 1); ++ return s->bLength; ++ } ++ ++ /* Otherwise, look up and return a specified string. String IDs ++ * are device-scoped, so we look up each string table we're told ++ * about. These lookups are infrequent; simpler-is-better here. ++ */ ++ if (composite->strings) { ++ len = lookup_string(composite->strings, buf, language, id); ++ if (len > 0) ++ return len; ++ } ++ list_for_each_entry(c, &cdev->configs, list) { ++ if (c->strings) { ++ len = lookup_string(c->strings, buf, language, id); ++ if (len > 0) ++ return len; ++ } ++ list_for_each_entry(f, &c->functions, list) { ++ if (!f->strings) ++ continue; ++ len = lookup_string(f->strings, buf, language, id); ++ if (len > 0) ++ return len; ++ } ++ } ++ return -EINVAL; ++} ++ ++/** ++ * usb_string_id() - allocate an unused string ID ++ * @cdev: the device whose string descriptor IDs are being allocated ++ * Context: single threaded during gadget setup ++ * ++ * @usb_string_id() is called from bind() callbacks to allocate ++ * string IDs. Drivers for functions, configurations, or gadgets will ++ * then store that ID in the appropriate descriptors and string table. ++ * ++ * All string identifier should be allocated using this routine, to ++ * ensure that for example different functions don't wrongly assign ++ * different meanings to the same identifier. ++ */ ++int __init usb_string_id(struct usb_composite_dev *cdev) ++{ ++ if (cdev->next_string_id < 254) { ++ /* string id 0 is reserved */ ++ cdev->next_string_id++; ++ return cdev->next_string_id; ++ } ++ return -ENODEV; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ if (req->status || req->actual != req->length) ++ DBG((struct usb_composite_dev *) ep->driver_data, ++ "setup complete --> %d, %d/%d\n", ++ req->status, req->actual, req->length); ++} ++ ++/* ++ * The setup() callback implements all the ep0 functionality that's ++ * not handled lower down, in hardware or the hardware driver(like ++ * device and endpoint feature flags, and their status). It's all ++ * housekeeping for the gadget function we're implementing. Most of ++ * the work is in config and function specific setup. ++ */ ++static int ++composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ struct usb_function *f = NULL; ++ ++ /* partial re-init of the response message; the function or the ++ * gadget might need to intercept e.g. a control-OUT completion ++ * when we delegate to it. ++ */ ++ req->zero = 0; ++ req->complete = composite_setup_complete; ++ req->length = USB_BUFSIZ; ++ gadget->ep0->driver_data = cdev; ++ ++ switch (ctrl->bRequest) { ++ ++ /* we handle all standard USB descriptors */ ++ case USB_REQ_GET_DESCRIPTOR: ++ if (ctrl->bRequestType != USB_DIR_IN) ++ goto unknown; ++ switch (w_value >> 8) { ++ ++ case USB_DT_DEVICE: ++ cdev->desc.bNumConfigurations = ++ count_configs(cdev, USB_DT_DEVICE); ++ value = min(w_length, (u16) sizeof cdev->desc); ++ memcpy(req->buf, &cdev->desc, value); ++ break; ++ case USB_DT_DEVICE_QUALIFIER: ++ if (!gadget_is_dualspeed(gadget)) ++ break; ++ device_qual(cdev); ++ value = min_t(int, w_length, ++ sizeof(struct usb_qualifier_descriptor)); ++ break; ++ case USB_DT_OTHER_SPEED_CONFIG: ++ if (!gadget_is_dualspeed(gadget)) ++ break; ++ /* FALLTHROUGH */ ++ case USB_DT_CONFIG: ++ value = config_desc(cdev, w_value); ++ if (value >= 0) ++ value = min(w_length, (u16) value); ++ break; ++ case USB_DT_STRING: ++ value = get_string(cdev, req->buf, ++ w_index, w_value & 0xff); ++ if (value >= 0) ++ value = min(w_length, (u16) value); ++ break; ++ } ++ break; ++ ++ /* any number of configs can work */ ++ case USB_REQ_SET_CONFIGURATION: ++ if (ctrl->bRequestType != 0) ++ goto unknown; ++ if (gadget_is_otg(gadget)) { ++ if (gadget->a_hnp_support) ++ DBG(cdev, "HNP available\n"); ++ else if (gadget->a_alt_hnp_support) ++ DBG(cdev, "HNP on another port\n"); ++ else ++ VDBG(cdev, "HNP inactive\n"); ++ } ++ spin_lock(&cdev->lock); ++ value = set_config(cdev, ctrl, w_value); ++ spin_unlock(&cdev->lock); ++ break; ++ case USB_REQ_GET_CONFIGURATION: ++ if (ctrl->bRequestType != USB_DIR_IN) ++ goto unknown; ++ if (cdev->config) ++ *(u8 *)req->buf = cdev->config->bConfigurationValue; ++ else ++ *(u8 *)req->buf = 0; ++ value = min(w_length, (u16) 1); ++ break; ++ ++ /* function drivers must handle get/set altsetting; if there's ++ * no get() method, we know only altsetting zero works. ++ */ ++ case USB_REQ_SET_INTERFACE: ++ if (ctrl->bRequestType != USB_RECIP_INTERFACE) ++ goto unknown; ++ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) ++ break; ++ f = cdev->config->interface[w_index]; ++ if (!f) ++ break; ++ if (w_value && !f->get_alt) ++ break; ++ value = f->set_alt(f, w_index, w_value); ++ break; ++ case USB_REQ_GET_INTERFACE: ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) ++ goto unknown; ++ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) ++ break; ++ f = cdev->config->interface[w_index]; ++ if (!f) ++ break; ++ /* lots of interfaces only need altsetting zero... */ ++ value = f->get_alt ? f->get_alt(f, w_index) : 0; ++ if (value < 0) ++ break; ++ *((u8 *)req->buf) = value; ++ value = min(w_length, (u16) 1); ++ break; ++ default: ++unknown: ++ VDBG(cdev, ++ "non-core control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ ++ /* functions always handle their interfaces ... punt other ++ * recipients (endpoint, other, WUSB, ...) to the current ++ * configuration code. ++ * ++ * REVISIT it could make sense to let the composite device ++ * take such requests too, if that's ever needed: to work ++ * in config 0, etc. ++ */ ++ if ((ctrl->bRequestType & USB_RECIP_MASK) ++ == USB_RECIP_INTERFACE) { ++ f = cdev->config->interface[w_index]; ++ if (f && f->setup) ++ value = f->setup(f, ctrl); ++ else ++ f = NULL; ++ } ++ if (value < 0 && !f) { ++ struct usb_configuration *c; ++ ++ c = cdev->config; ++ if (c && c->setup) ++ value = c->setup(c, ctrl); ++ } ++ ++ goto done; ++ } ++ ++ /* respond with data transfer before status phase? */ ++ if (value >= 0) { ++ req->length = value; ++ req->zero = value < w_length; ++ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) { ++ DBG(cdev, "ep_queue --> %d\n", value); ++ req->status = 0; ++ composite_setup_complete(gadget->ep0, req); ++ } ++ } ++ ++done: ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static void composite_disconnect(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ unsigned long flags; ++ ++ /* REVISIT: should we have config and device level ++ * disconnect callbacks? ++ */ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ reset_config(cdev); ++ spin_unlock_irqrestore(&cdev->lock, flags); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void /* __init_or_exit */ ++composite_unbind(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ ++ /* composite_disconnect() must already have been called ++ * by the underlying peripheral controller driver! ++ * so there's no i/o concurrency that could affect the ++ * state protected by cdev->lock. ++ */ ++ WARN_ON(cdev->config); ++ ++ while (!list_empty(&cdev->configs)) { ++ struct usb_configuration *c; ++ ++ c = list_first_entry(&cdev->configs, ++ struct usb_configuration, list); ++ while (!list_empty(&c->functions)) { ++ struct usb_function *f; ++ ++ f = list_first_entry(&c->functions, ++ struct usb_function, list); ++ list_del(&f->list); ++ if (f->unbind) { ++ DBG(cdev, "unbind function '%s'/%p\n", ++ f->name, f); ++ f->unbind(c, f); ++ /* may free memory for "f" */ ++ } ++ } ++ list_del(&c->list); ++ if (c->unbind) { ++ DBG(cdev, "unbind config '%s'/%p\n", c->label, c); ++ c->unbind(c); ++ /* may free memory for "c" */ ++ } ++ } ++ if (composite->unbind) ++ composite->unbind(cdev); ++ ++ if (cdev->req) { ++ kfree(cdev->req->buf); ++ usb_ep_free_request(gadget->ep0, cdev->req); ++ } ++ kfree(cdev); ++ set_gadget_data(gadget, NULL); ++ composite = NULL; ++} ++ ++static void __init ++string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) ++{ ++ struct usb_string *str = tab->strings; ++ ++ for (str = tab->strings; str->s; str++) { ++ if (str->id == id) { ++ str->s = s; ++ return; ++ } ++ } ++} ++ ++static void __init ++string_override(struct usb_gadget_strings **tab, u8 id, const char *s) ++{ ++ while (*tab) { ++ string_override_one(*tab, id, s); ++ tab++; ++ } ++} ++ ++static int __init composite_bind(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev; ++ int status = -ENOMEM; ++ ++ cdev = kzalloc(sizeof *cdev, GFP_KERNEL); ++ if (!cdev) ++ return status; ++ ++ spin_lock_init(&cdev->lock); ++ cdev->gadget = gadget; ++ set_gadget_data(gadget, cdev); ++ INIT_LIST_HEAD(&cdev->configs); ++ ++ /* preallocate control response and buffer */ ++ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); ++ if (!cdev->req) ++ goto fail; ++ cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); ++ if (!cdev->req->buf) ++ goto fail; ++ cdev->req->complete = composite_setup_complete; ++ gadget->ep0->driver_data = cdev; ++ ++ cdev->bufsiz = USB_BUFSIZ; ++ cdev->driver = composite; ++ ++ usb_gadget_set_selfpowered(gadget); ++ ++ /* interface and string IDs start at zero via kzalloc. ++ * we force endpoints to start unassigned; few controller ++ * drivers will zero ep->driver_data. ++ */ ++ usb_ep_autoconfig_reset(cdev->gadget); ++ ++ /* composite gadget needs to assign strings for whole device (like ++ * serial number), register function drivers, potentially update ++ * power state and consumption, etc ++ */ ++ status = composite->bind(cdev); ++ if (status < 0) ++ goto fail; ++ ++ cdev->desc = *composite->dev; ++ cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; ++ ++ /* standardized runtime overrides for device ID data */ ++ if (idVendor) ++ cdev->desc.idVendor = cpu_to_le16(idVendor); ++ if (idProduct) ++ cdev->desc.idProduct = cpu_to_le16(idProduct); ++ if (bcdDevice) ++ cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); ++ ++ /* strings can't be assigned before bind() allocates the ++ * releavnt identifiers ++ */ ++ if (cdev->desc.iManufacturer && iManufacturer) ++ string_override(composite->strings, ++ cdev->desc.iManufacturer, iManufacturer); ++ if (cdev->desc.iProduct && iProduct) ++ string_override(composite->strings, ++ cdev->desc.iProduct, iProduct); ++ if (cdev->desc.iSerialNumber && iSerialNumber) ++ string_override(composite->strings, ++ cdev->desc.iSerialNumber, iSerialNumber); ++ ++ INFO(cdev, "%s ready\n", composite->name); ++ return 0; ++ ++fail: ++ composite_unbind(gadget); ++ return status; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ++composite_suspend(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_function *f; ++ ++ /* REVISIT: should we have config and device level ++ * suspend/resume callbacks? ++ */ ++ DBG(cdev, "suspend\n"); ++ if (cdev->config) { ++ list_for_each_entry(f, &cdev->config->functions, list) { ++ if (f->suspend) ++ f->suspend(f); ++ } ++ } ++} ++ ++static void ++composite_resume(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_function *f; ++ ++ /* REVISIT: should we have config and device level ++ * suspend/resume callbacks? ++ */ ++ DBG(cdev, "resume\n"); ++ if (cdev->config) { ++ list_for_each_entry(f, &cdev->config->functions, list) { ++ if (f->resume) ++ f->resume(f); ++ } ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_gadget_driver composite_driver = { ++ .speed = USB_SPEED_HIGH, ++ ++ .bind = composite_bind, ++ .unbind = __exit_p(composite_unbind), ++ ++ .setup = composite_setup, ++ .disconnect = composite_disconnect, ++ ++ .suspend = composite_suspend, ++ .resume = composite_resume, ++ ++ .driver = { ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/** ++ * usb_composite_register() - register a composite driver ++ * @driver: the driver to register ++ * Context: single threaded during gadget setup ++ * ++ * This function is used to register drivers using the composite driver ++ * framework. The return value is zero, or a negative errno value. ++ * Those values normally come from the driver's @bind method, which does ++ * all the work of setting up the driver to match the hardware. ++ * ++ * On successful return, the gadget is ready to respond to requests from ++ * the host, unless one of its components invokes usb_gadget_disconnect() ++ * while it was binding. That would usually be done in order to wait for ++ * some userspace participation. ++ */ ++int __init usb_composite_register(struct usb_composite_driver *driver) ++{ ++ if (!driver || !driver->dev || !driver->bind || composite) ++ return -EINVAL; ++ ++ if (!driver->name) ++ driver->name = "composite"; ++ composite_driver.function = (char *) driver->name; ++ composite_driver.driver.name = driver->name; ++ composite = driver; ++ ++ return usb_gadget_register_driver(&composite_driver); ++} ++ ++/** ++ * usb_composite_unregister() - unregister a composite driver ++ * @driver: the driver to unregister ++ * ++ * This function is used to unregister drivers using the composite ++ * driver framework. ++ */ ++void __exit usb_composite_unregister(struct usb_composite_driver *driver) ++{ ++ if (composite != driver) ++ return; ++ usb_gadget_unregister_driver(&composite_driver); ++} +diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c +index a4e54b2..1ca1c32 100644 +--- a/drivers/usb/gadget/config.c ++++ b/drivers/usb/gadget/config.c +@@ -96,7 +96,7 @@ int usb_gadget_config_buf( + /* config descriptor first */ + if (length < USB_DT_CONFIG_SIZE || !desc) + return -EINVAL; +- *cp = *config; ++ *cp = *config; + + /* then interface/endpoint/class/vendor/... */ + len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, +@@ -115,3 +115,77 @@ int usb_gadget_config_buf( + return len; + } + ++/** ++ * usb_copy_descriptors - copy a vector of USB descriptors ++ * @src: null-terminated vector to copy ++ * Context: initialization code, which may sleep ++ * ++ * This makes a copy of a vector of USB descriptors. Its primary use ++ * is to support usb_function objects which can have multiple copies, ++ * each needing different descriptors. Functions may have static ++ * tables of descriptors, which are used as templates and customized ++ * with identifiers (for interfaces, strings, endpoints, and more) ++ * as needed by a given function instance. ++ */ ++struct usb_descriptor_header **__init ++usb_copy_descriptors(struct usb_descriptor_header **src) ++{ ++ struct usb_descriptor_header **tmp; ++ unsigned bytes; ++ unsigned n_desc; ++ void *mem; ++ struct usb_descriptor_header **ret; ++ ++ /* count descriptors and their sizes; then add vector size */ ++ for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) ++ bytes += (*tmp)->bLength; ++ bytes += (n_desc + 1) * sizeof(*tmp); ++ ++ mem = kmalloc(bytes, GFP_KERNEL); ++ if (!mem) ++ return NULL; ++ ++ /* fill in pointers starting at "tmp", ++ * to descriptors copied starting at "mem"; ++ * and return "ret" ++ */ ++ tmp = mem; ++ ret = mem; ++ mem += (n_desc + 1) * sizeof(*tmp); ++ while (*src) { ++ memcpy(mem, *src, (*src)->bLength); ++ *tmp = mem; ++ tmp++; ++ mem += (*src)->bLength; ++ src++; ++ } ++ *tmp = NULL; ++ ++ return ret; ++} ++ ++/** ++ * usb_find_endpoint - find a copy of an endpoint descriptor ++ * @src: original vector of descriptors ++ * @copy: copy of @src ++ * @ep: endpoint descriptor found in @src ++ * ++ * This returns the copy of the @match descriptor made for @copy. Its ++ * intended use is to help remembering the endpoint descriptor to use ++ * when enabling a given endpoint. ++ */ ++struct usb_endpoint_descriptor *__init ++usb_find_endpoint( ++ struct usb_descriptor_header **src, ++ struct usb_descriptor_header **copy, ++ struct usb_endpoint_descriptor *match ++) ++{ ++ while (*src) { ++ if (*src == (void *) match) ++ return (void *)*copy; ++ src++; ++ copy++; ++ } ++ return NULL; ++} +diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c +index 4203619..7600a0c 100644 +--- a/drivers/usb/gadget/dummy_hcd.c ++++ b/drivers/usb/gadget/dummy_hcd.c +@@ -542,13 +542,14 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, + req->req.context = dum; + req->req.complete = fifo_complete; + ++ list_add_tail(&req->queue, &ep->queue); + spin_unlock (&dum->lock); + _req->actual = _req->length; + _req->status = 0; + _req->complete (_ep, _req); + spin_lock (&dum->lock); +- } +- list_add_tail (&req->queue, &ep->queue); ++ } else ++ list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore (&dum->lock, flags); + + /* real hardware would likely enable transfers here, in case +@@ -862,7 +863,7 @@ static int dummy_udc_probe (struct platform_device *pdev) + /* maybe claim OTG support, though we won't complete HNP */ + dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0); + +- strcpy (dum->gadget.dev.bus_id, "gadget"); ++ dev_set_name(&dum->gadget.dev, "gadget"); + dum->gadget.dev.parent = &pdev->dev; + dum->gadget.dev.release = dummy_gadget_release; + rc = device_register (&dum->gadget.dev); +@@ -1865,7 +1866,7 @@ static int dummy_hcd_probe(struct platform_device *pdev) + + dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); + +- hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id); ++ hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + the_controller = hcd_to_dummy (hcd); +diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c +index 8bdad22..9462e30 100644 +--- a/drivers/usb/gadget/epautoconf.c ++++ b/drivers/usb/gadget/epautoconf.c +@@ -159,6 +159,7 @@ ep_matches ( + /* MATCH!! */ + + /* report address */ ++ desc->bEndpointAddress &= USB_DIR_IN; + if (isdigit (ep->name [2])) { + u8 num = simple_strtol (&ep->name [2], NULL, 10); + desc->bEndpointAddress |= num; +diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c +index 8d61ea6..bcac2e6 100644 +--- a/drivers/usb/gadget/ether.c ++++ b/drivers/usb/gadget/ether.c +@@ -1,8 +1,9 @@ + /* + * ether.c -- Ethernet gadget driver, with CDC and non-CDC options + * +- * Copyright (C) 2003-2005 David Brownell ++ * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger ++ * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -23,18 +24,9 @@ + + #include <linux/kernel.h> + #include <linux/utsname.h> +-#include <linux/device.h> +-#include <linux/ctype.h> +-#include <linux/etherdevice.h> +-#include <linux/ethtool.h> + +-#include <linux/usb/ch9.h> +-#include <linux/usb/cdc.h> +-#include <linux/usb/gadget.h> ++#include "u_ether.h" + +-#include "gadget_chips.h" +- +-/*-------------------------------------------------------------------------*/ + + /* + * Ethernet gadget driver -- with CDC and non-CDC options +@@ -46,7 +38,11 @@ + * this USB-IF standard as its open-systems interoperability solution; + * most host side USB stacks (except from Microsoft) support it. + * +- * There's some hardware that can't talk CDC. We make that hardware ++ * This is sometimes called "CDC ECM" (Ethernet Control Model) to support ++ * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new ++ * "CDC EEM" (Ethernet Emulation Model) is starting to spread. ++ * ++ * There's some hardware that can't talk CDC ECM. We make that hardware + * implement a "minimalist" vendor-agnostic CDC core: same framing, but + * link-level setup only requires activating the configuration. Only the + * endpoint descriptors, and product/vendor IDs, are relevant; no control +@@ -64,70 +60,40 @@ + * A third option is also in use. Rather than CDC Ethernet, or something + * simpler, Microsoft pushes their own approach: RNDIS. The published + * RNDIS specs are ambiguous and appear to be incomplete, and are also +- * needlessly complex. ++ * needlessly complex. They borrow more from CDC ACM than CDC ECM. + */ + + #define DRIVER_DESC "Ethernet Gadget" +-#define DRIVER_VERSION "May Day 2005" +- +-static const char shortname [] = "ether"; +-static const char driver_desc [] = DRIVER_DESC; +- +-#define RX_EXTRA 20 /* guard against rx overflows */ +- +-#include "rndis.h" ++#define DRIVER_VERSION "Memorial Day 2008" + +-#ifndef CONFIG_USB_ETH_RNDIS +-#define rndis_uninit(x) do{}while(0) +-#define rndis_deregister(c) do{}while(0) +-#define rndis_exit() do{}while(0) ++#ifdef CONFIG_USB_ETH_RNDIS ++#define PREFIX "RNDIS/" ++#else ++#define PREFIX "" + #endif + +-/* CDC and RNDIS support the same host-chosen outgoing packet filters. */ +-#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ +- |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ +- |USB_CDC_PACKET_TYPE_PROMISCUOUS \ +- |USB_CDC_PACKET_TYPE_DIRECTED) +- +- +-/*-------------------------------------------------------------------------*/ +- +-struct eth_dev { +- spinlock_t lock; +- struct usb_gadget *gadget; +- struct usb_request *req; /* for control responses */ +- struct usb_request *stat_req; /* for cdc & rndis status */ +- +- u8 config; +- struct usb_ep *in_ep, *out_ep, *status_ep; +- const struct usb_endpoint_descriptor +- *in, *out, *status; +- +- spinlock_t req_lock; +- struct list_head tx_reqs, rx_reqs; +- +- struct net_device *net; +- struct net_device_stats stats; +- atomic_t tx_qlen; +- +- struct work_struct work; +- unsigned zlp:1; +- unsigned cdc:1; +- unsigned rndis:1; +- unsigned suspended:1; +- u16 cdc_filter; +- unsigned long todo; +-#define WORK_RX_MEMORY 0 +- int rndis_config; +- u8 host_mac [ETH_ALEN]; +-}; +- +-/* This version autoconfigures as much as possible at run-time. ++/* ++ * This driver aims for interoperability by using CDC ECM unless ++ * ++ * can_support_ecm() ++ * ++ * returns false, in which case it supports the CDC Subset. By default, ++ * that returns true; most hardware has no problems with CDC ECM, that's ++ * a good default. Previous versions of this driver had no default; this ++ * version changes that, removing overhead for new controller support. + * +- * It also ASSUMES a self-powered device, without remote wakeup, +- * although remote wakeup support would make sense. ++ * IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE! + */ + ++static inline bool has_rndis(void) ++{ ++#ifdef CONFIG_USB_ETH_RNDIS ++ return true; ++#else ++ return false; ++#endif ++} ++ + /*-------------------------------------------------------------------------*/ + + /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! +@@ -137,8 +103,8 @@ struct eth_dev { + /* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only CDC Ethernet configurations. + */ +-#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +-#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ ++#define CDC_VENDOR_NUM 0x0525 /* NetChip */ ++#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ + + /* For hardware that can't talk CDC, we use the same vendor ID that + * ARM Linux has used for ethernet-over-usb, both with sa1100 and +@@ -162,274 +128,9 @@ struct eth_dev { + #define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ + #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ + +- +-/* Some systems will want different product identifers published in the +- * device descriptor, either numbers or strings or both. These string +- * parameters are in UTF-8 (superset of ASCII's 7 bit characters). +- */ +- +-static ushort idVendor; +-module_param(idVendor, ushort, S_IRUGO); +-MODULE_PARM_DESC(idVendor, "USB Vendor ID"); +- +-static ushort idProduct; +-module_param(idProduct, ushort, S_IRUGO); +-MODULE_PARM_DESC(idProduct, "USB Product ID"); +- +-static ushort bcdDevice; +-module_param(bcdDevice, ushort, S_IRUGO); +-MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); +- +-static char *iManufacturer; +-module_param(iManufacturer, charp, S_IRUGO); +-MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); +- +-static char *iProduct; +-module_param(iProduct, charp, S_IRUGO); +-MODULE_PARM_DESC(iProduct, "USB Product string"); +- +-static char *iSerialNumber; +-module_param(iSerialNumber, charp, S_IRUGO); +-MODULE_PARM_DESC(iSerialNumber, "SerialNumber"); +- +-/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ +-static char *dev_addr; +-module_param(dev_addr, charp, S_IRUGO); +-MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); +- +-/* this address is invisible to ifconfig */ +-static char *host_addr; +-module_param(host_addr, charp, S_IRUGO); +-MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); +- +- +-/*-------------------------------------------------------------------------*/ +- +-/* Include CDC support if we could run on CDC-capable hardware. */ +- +-#ifdef CONFIG_USB_GADGET_NET2280 +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_DUMMY_HCD +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_GOKU +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_LH7A40X +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_MQ11XX +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_OMAP +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_N9604 +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_S3C2410 +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_AT91 +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_MUSBHSFC +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_MUSB_HDRC +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_ATMEL_USBA +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_FSL_USB2 +-#define DEV_CONFIG_CDC +-#endif +- +-/* For CDC-incapable hardware, choose the simple cdc subset. +- * Anything that talks bulk (without notable bugs) can do this. +- */ +-#ifdef CONFIG_USB_GADGET_PXA2XX +-#define DEV_CONFIG_SUBSET +-#endif +- +-#ifdef CONFIG_USB_GADGET_PXA27X +-#define DEV_CONFIG_SUBSET +-#endif +- +-#ifdef CONFIG_USB_GADGET_SUPERH +-#define DEV_CONFIG_SUBSET +-#endif +- +-#ifdef CONFIG_USB_GADGET_SA1100 +-/* use non-CDC for backwards compatibility */ +-#define DEV_CONFIG_SUBSET +-#endif +- +-#ifdef CONFIG_USB_GADGET_M66592 +-#define DEV_CONFIG_CDC +-#endif +- +-#ifdef CONFIG_USB_GADGET_AMD5536UDC +-#define DEV_CONFIG_CDC +-#endif +- +- +-/*-------------------------------------------------------------------------*/ +- +-/* "main" config is either CDC, or its simple subset */ +-static inline int is_cdc(struct eth_dev *dev) +-{ +-#if !defined(DEV_CONFIG_SUBSET) +- return 1; /* only cdc possible */ +-#elif !defined (DEV_CONFIG_CDC) +- return 0; /* only subset possible */ +-#else +- return dev->cdc; /* depends on what hardware we found */ +-#endif +-} +- +-/* "secondary" RNDIS config may sometimes be activated */ +-static inline int rndis_active(struct eth_dev *dev) +-{ +-#ifdef CONFIG_USB_ETH_RNDIS +- return dev->rndis; +-#else +- return 0; +-#endif +-} +- +-#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev)) +-#define cdc_active(dev) ( is_cdc(dev) && !rndis_active(dev)) +- +- +- +-#define DEFAULT_QLEN 2 /* double buffering by default */ +- +-/* peak bulk transfer bits-per-second */ +-#define HS_BPS (13 * 512 * 8 * 1000 * 8) +-#define FS_BPS (19 * 64 * 1 * 1000 * 8) +- +-#ifdef CONFIG_USB_GADGET_DUALSPEED +-#define DEVSPEED USB_SPEED_HIGH +- +-static unsigned qmult = 5; +-module_param (qmult, uint, S_IRUGO|S_IWUSR); +- +- +-/* for dual-speed hardware, use deeper queues at highspeed */ +-#define qlen(gadget) \ +- (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) +- +-static inline int BITRATE(struct usb_gadget *g) +-{ +- return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS; +-} +- +-#else /* full speed (low speed doesn't do bulk) */ +- +-#define qmult 1 +- +-#define DEVSPEED USB_SPEED_FULL +- +-#define qlen(gadget) DEFAULT_QLEN +- +-static inline int BITRATE(struct usb_gadget *g) +-{ +- return FS_BPS; +-} +-#endif +- +- +-/*-------------------------------------------------------------------------*/ +- +-#define xprintk(d,level,fmt,args...) \ +- printk(level "%s: " fmt , (d)->net->name , ## args) +- +-#ifdef DEBUG +-#undef DEBUG +-#define DEBUG(dev,fmt,args...) \ +- xprintk(dev , KERN_DEBUG , fmt , ## args) +-#else +-#define DEBUG(dev,fmt,args...) \ +- do { } while (0) +-#endif /* DEBUG */ +- +-#ifdef VERBOSE_DEBUG +-#define VDEBUG DEBUG +-#else +-#define VDEBUG(dev,fmt,args...) \ +- do { } while (0) +-#endif /* DEBUG */ +- +-#define ERROR(dev,fmt,args...) \ +- xprintk(dev , KERN_ERR , fmt , ## args) +-#define WARN(dev,fmt,args...) \ +- xprintk(dev , KERN_WARNING , fmt , ## args) +-#define INFO(dev,fmt,args...) \ +- xprintk(dev , KERN_INFO , fmt , ## args) +- + /*-------------------------------------------------------------------------*/ + +-/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly +- * ep0 implementation: descriptors, config management, setup(). +- * also optional class-specific notification interrupt transfer. +- */ +- +-/* +- * DESCRIPTORS ... most are static, but strings and (full) configuration +- * descriptors are built on demand. For now we do either full CDC, or +- * our simple subset, with RNDIS as an optional second configuration. +- * +- * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But +- * the class descriptors match a modem (they're ignored; it's really just +- * Ethernet functionality), they don't need the NOP altsetting, and the +- * status transfer endpoint isn't optional. +- */ +- +-#define STRING_MANUFACTURER 1 +-#define STRING_PRODUCT 2 +-#define STRING_ETHADDR 3 +-#define STRING_DATA 4 +-#define STRING_CONTROL 5 +-#define STRING_RNDIS_CONTROL 6 +-#define STRING_CDC 7 +-#define STRING_SUBSET 8 +-#define STRING_RNDIS 9 +-#define STRING_SERIALNUMBER 10 +- +-/* holds our biggest descriptor (or RNDIS response) */ +-#define USB_BUFSIZ 256 +- +-/* +- * This device advertises one configuration, eth_config, unless RNDIS +- * is enabled (rndis_config) on hardware supporting at least two configs. +- * +- * NOTE: Controllers like superh_udc should probably be able to use +- * an RNDIS-only configuration. +- * +- * FIXME define some higher-powered configurations to make it easier +- * to recharge batteries ... +- */ +- +-#define DEV_CONFIG_VALUE 1 /* cdc or subset */ +-#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ +- +-static struct usb_device_descriptor +-device_desc = { ++static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + +@@ -438,2220 +139,234 @@ device_desc = { + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, ++ /* .bMaxPacketSize0 = f(hardware) */ + ++ /* Vendor and product id defaults change according to what configs ++ * we support. (As does bNumConfigurations.) These values can ++ * also be overridden by module parameters. ++ */ + .idVendor = __constant_cpu_to_le16 (CDC_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16 (CDC_PRODUCT_NUM), +- .iManufacturer = STRING_MANUFACTURER, +- .iProduct = STRING_PRODUCT, ++ /* .bcdDevice = f(hardware) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, + }; + +-static struct usb_otg_descriptor +-otg_descriptor = { ++static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + +- .bmAttributes = USB_OTG_SRP, +-}; +- +-static struct usb_config_descriptor +-eth_config = { +- .bLength = sizeof eth_config, +- .bDescriptorType = USB_DT_CONFIG, +- +- /* compute wTotalLength on the fly */ +- .bNumInterfaces = 2, +- .bConfigurationValue = DEV_CONFIG_VALUE, +- .iConfiguration = STRING_CDC, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 50, +-}; +- +-#ifdef CONFIG_USB_ETH_RNDIS +-static struct usb_config_descriptor +-rndis_config = { +- .bLength = sizeof rndis_config, +- .bDescriptorType = USB_DT_CONFIG, +- +- /* compute wTotalLength on the fly */ +- .bNumInterfaces = 2, +- .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, +- .iConfiguration = STRING_RNDIS, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 50, +-}; +-#endif +- +-/* +- * Compared to the simple CDC subset, the full CDC Ethernet model adds +- * three class descriptors, two interface descriptors, optional status +- * endpoint. Both have a "data" interface and two bulk endpoints. +- * There are also differences in how control requests are handled. +- * +- * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the +- * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it +- * may hang or oops. Since bugfixes (or accurate specs, letting Linux +- * work around those bugs) are unlikely to ever come from MSFT, you may +- * wish to avoid using RNDIS. +- * +- * MCCI offers an alternative to RNDIS if you need to connect to Windows +- * but have hardware that can't support CDC Ethernet. We add descriptors +- * to present the CDC Subset as a (nonconformant) CDC MDLM variant called +- * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can +- * get those drivers from MCCI, or bundled with various products. +- */ +- +-#ifdef DEV_CONFIG_CDC +-static struct usb_interface_descriptor +-control_intf = { +- .bLength = sizeof control_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bInterfaceNumber = 0, +- /* status endpoint is optional; this may be patched later */ +- .bNumEndpoints = 1, +- .bInterfaceClass = USB_CLASS_COMM, +- .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, +- .bInterfaceProtocol = USB_CDC_PROTO_NONE, +- .iInterface = STRING_CONTROL, +-}; +-#endif +- +-#ifdef CONFIG_USB_ETH_RNDIS +-static const struct usb_interface_descriptor +-rndis_control_intf = { +- .bLength = sizeof rndis_control_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bInterfaceNumber = 0, +- .bNumEndpoints = 1, +- .bInterfaceClass = USB_CLASS_COMM, +- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, +- .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, +- .iInterface = STRING_RNDIS_CONTROL, +-}; +-#endif +- +-static const struct usb_cdc_header_desc header_desc = { +- .bLength = sizeof header_desc, +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_HEADER_TYPE, +- +- .bcdCDC = __constant_cpu_to_le16 (0x0110), +-}; +- +-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +- +-static const struct usb_cdc_union_desc union_desc = { +- .bLength = sizeof union_desc, +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_UNION_TYPE, +- +- .bMasterInterface0 = 0, /* index of control interface */ +- .bSlaveInterface0 = 1, /* index of DATA interface */ +-}; +- +-#endif /* CDC || RNDIS */ +- +-#ifdef CONFIG_USB_ETH_RNDIS +- +-static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { +- .bLength = sizeof call_mgmt_descriptor, +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, +- +- .bmCapabilities = 0x00, +- .bDataInterface = 0x01, +-}; +- +-static const struct usb_cdc_acm_descriptor acm_descriptor = { +- .bLength = sizeof acm_descriptor, +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_ACM_TYPE, +- +- .bmCapabilities = 0x00, +-}; +- +-#endif +- +-#ifndef DEV_CONFIG_CDC +- +-/* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various +- * ways: data endpoints live in the control interface, there's no data +- * interface, and it's not used to talk to a cell phone radio. +- */ +- +-static const struct usb_cdc_mdlm_desc mdlm_desc = { +- .bLength = sizeof mdlm_desc, +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_MDLM_TYPE, +- +- .bcdVersion = __constant_cpu_to_le16(0x0100), +- .bGUID = { +- 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, +- 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, +- }, +-}; +- +-/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we +- * can't really use its struct. All we do here is say that we're using +- * the submode of "SAFE" which directly matches the CDC Subset. +- */ +-static const u8 mdlm_detail_desc[] = { +- 6, +- USB_DT_CS_INTERFACE, +- USB_CDC_MDLM_DETAIL_TYPE, +- +- 0, /* "SAFE" */ +- 0, /* network control capabilities (none) */ +- 0, /* network data capabilities ("raw" encapsulation) */ +-}; +- +-#endif +- +-static const struct usb_cdc_ether_desc ether_desc = { +- .bLength = sizeof ether_desc, +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, +- +- /* this descriptor actually adds value, surprise! */ +- .iMACAddress = STRING_ETHADDR, +- .bmEthernetStatistics = __constant_cpu_to_le32 (0), /* no statistics */ +- .wMaxSegmentSize = __constant_cpu_to_le16 (ETH_FRAME_LEN), +- .wNumberMCFilters = __constant_cpu_to_le16 (0), +- .bNumberPowerFilters = 0, +-}; +- +- +-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +- +-/* include the status endpoint if we can, even where it's optional. +- * use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one +- * packet, to simplify cancellation; and a big transfer interval, to +- * waste less bandwidth. +- * +- * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even +- * if they ignore the connect/disconnect notifications that real aether +- * can provide. more advanced cdc configurations might want to support +- * encapsulated commands (vendor-specific, using control-OUT). +- * +- * RNDIS requires the status endpoint, since it uses that encapsulation +- * mechanism for its funky RPC scheme. +- */ +- +-#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +-#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ +- +-static struct usb_endpoint_descriptor +-fs_status_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), +- .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, +-}; +-#endif +- +-#ifdef DEV_CONFIG_CDC +- +-/* the default data interface has no endpoints ... */ +- +-static const struct usb_interface_descriptor +-data_nop_intf = { +- .bLength = sizeof data_nop_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bInterfaceNumber = 1, +- .bAlternateSetting = 0, +- .bNumEndpoints = 0, +- .bInterfaceClass = USB_CLASS_CDC_DATA, +- .bInterfaceSubClass = 0, +- .bInterfaceProtocol = 0, +-}; +- +-/* ... but the "real" data interface has two bulk endpoints */ +- +-static const struct usb_interface_descriptor +-data_intf = { +- .bLength = sizeof data_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bInterfaceNumber = 1, +- .bAlternateSetting = 1, +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_CDC_DATA, +- .bInterfaceSubClass = 0, +- .bInterfaceProtocol = 0, +- .iInterface = STRING_DATA, +-}; +- +-#endif +- +-#ifdef CONFIG_USB_ETH_RNDIS +- +-/* RNDIS doesn't activate by changing to the "real" altsetting */ +- +-static const struct usb_interface_descriptor +-rndis_data_intf = { +- .bLength = sizeof rndis_data_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bInterfaceNumber = 1, +- .bAlternateSetting = 0, +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_CDC_DATA, +- .bInterfaceSubClass = 0, +- .bInterfaceProtocol = 0, +- .iInterface = STRING_DATA, +-}; +- +-#endif +- +-#ifdef DEV_CONFIG_SUBSET +- +-/* +- * "Simple" CDC-subset option is a simple vendor-neutral model that most +- * full speed controllers can handle: one interface, two bulk endpoints. +- * +- * To assist host side drivers, we fancy it up a bit, and add descriptors +- * so some host side drivers will understand it as a "SAFE" variant. +- */ +- +-static const struct usb_interface_descriptor +-subset_data_intf = { +- .bLength = sizeof subset_data_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bInterfaceNumber = 0, +- .bAlternateSetting = 0, +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_COMM, +- .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, +- .bInterfaceProtocol = 0, +- .iInterface = STRING_DATA, +-}; +- +-#endif /* SUBSET */ +- +- +-static struct usb_endpoint_descriptor +-fs_source_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static struct usb_endpoint_descriptor +-fs_sink_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bEndpointAddress = USB_DIR_OUT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }; + +-static const struct usb_descriptor_header *fs_eth_function [11] = { ++static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, +-#ifdef DEV_CONFIG_CDC +- /* "cdc" mode descriptors */ +- (struct usb_descriptor_header *) &control_intf, +- (struct usb_descriptor_header *) &header_desc, +- (struct usb_descriptor_header *) &union_desc, +- (struct usb_descriptor_header *) ðer_desc, +- /* NOTE: status endpoint may need to be removed */ +- (struct usb_descriptor_header *) &fs_status_desc, +- /* data interface, with altsetting */ +- (struct usb_descriptor_header *) &data_nop_intf, +- (struct usb_descriptor_header *) &data_intf, +- (struct usb_descriptor_header *) &fs_source_desc, +- (struct usb_descriptor_header *) &fs_sink_desc, + NULL, +-#endif /* DEV_CONFIG_CDC */ + }; + +-static inline void __init fs_subset_descriptors(void) +-{ +-#ifdef DEV_CONFIG_SUBSET +- /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ +- fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; +- fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; +- fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; +- fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; +- fs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; +- fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc; +- fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc; +- fs_eth_function[8] = NULL; +-#else +- fs_eth_function[1] = NULL; +-#endif +-} + +-#ifdef CONFIG_USB_ETH_RNDIS +-static const struct usb_descriptor_header *fs_rndis_function [] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- /* control interface matches ACM, not Ethernet */ +- (struct usb_descriptor_header *) &rndis_control_intf, +- (struct usb_descriptor_header *) &header_desc, +- (struct usb_descriptor_header *) &call_mgmt_descriptor, +- (struct usb_descriptor_header *) &acm_descriptor, +- (struct usb_descriptor_header *) &union_desc, +- (struct usb_descriptor_header *) &fs_status_desc, +- /* data interface has no altsetting */ +- (struct usb_descriptor_header *) &rndis_data_intf, +- (struct usb_descriptor_header *) &fs_source_desc, +- (struct usb_descriptor_header *) &fs_sink_desc, +- NULL, +-}; +-#endif +- +-/* +- * usb 2.0 devices need to expose both high speed and full speed +- * descriptors, unless they only run at full speed. +- */ +- +-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +-static struct usb_endpoint_descriptor +-hs_status_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), +- .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +-}; +-#endif /* DEV_CONFIG_CDC */ +- +-static struct usb_endpoint_descriptor +-hs_source_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16 (512), +-}; +- +-static struct usb_endpoint_descriptor +-hs_sink_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16 (512), +-}; ++/* string IDs are assigned dynamically */ + +-static struct usb_qualifier_descriptor +-dev_qualifier = { +- .bLength = sizeof dev_qualifier, +- .bDescriptorType = USB_DT_DEVICE_QUALIFIER, ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 + +- .bcdUSB = __constant_cpu_to_le16 (0x0200), +- .bDeviceClass = USB_CLASS_COMM, ++static char manufacturer[50]; + +- .bNumConfigurations = 1, ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC, ++ { } /* end of list */ + }; + +-static const struct usb_descriptor_header *hs_eth_function [11] = { +- (struct usb_descriptor_header *) &otg_descriptor, +-#ifdef DEV_CONFIG_CDC +- /* "cdc" mode descriptors */ +- (struct usb_descriptor_header *) &control_intf, +- (struct usb_descriptor_header *) &header_desc, +- (struct usb_descriptor_header *) &union_desc, +- (struct usb_descriptor_header *) ðer_desc, +- /* NOTE: status endpoint may need to be removed */ +- (struct usb_descriptor_header *) &hs_status_desc, +- /* data interface, with altsetting */ +- (struct usb_descriptor_header *) &data_nop_intf, +- (struct usb_descriptor_header *) &data_intf, +- (struct usb_descriptor_header *) &hs_source_desc, +- (struct usb_descriptor_header *) &hs_sink_desc, +- NULL, +-#endif /* DEV_CONFIG_CDC */ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, + }; + +-static inline void __init hs_subset_descriptors(void) +-{ +-#ifdef DEV_CONFIG_SUBSET +- /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ +- hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; +- hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; +- hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; +- hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; +- hs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; +- hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc; +- hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc; +- hs_eth_function[8] = NULL; +-#else +- hs_eth_function[1] = NULL; +-#endif +-} +- +-#ifdef CONFIG_USB_ETH_RNDIS +-static const struct usb_descriptor_header *hs_rndis_function [] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- /* control interface matches ACM, not Ethernet */ +- (struct usb_descriptor_header *) &rndis_control_intf, +- (struct usb_descriptor_header *) &header_desc, +- (struct usb_descriptor_header *) &call_mgmt_descriptor, +- (struct usb_descriptor_header *) &acm_descriptor, +- (struct usb_descriptor_header *) &union_desc, +- (struct usb_descriptor_header *) &hs_status_desc, +- /* data interface has no altsetting */ +- (struct usb_descriptor_header *) &rndis_data_intf, +- (struct usb_descriptor_header *) &hs_source_desc, +- (struct usb_descriptor_header *) &hs_sink_desc, ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, + NULL, + }; +-#endif +- +- +-/* maxpacket and other transfer characteristics vary by speed. */ +-static inline struct usb_endpoint_descriptor * +-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, +- struct usb_endpoint_descriptor *fs) +-{ +- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) +- return hs; +- return fs; +-} + ++static u8 hostaddr[ETH_ALEN]; + + /*-------------------------------------------------------------------------*/ + +-/* descriptors that are built on-demand */ +- +-static char manufacturer [50]; +-static char product_desc [40] = DRIVER_DESC; +-static char serial_number [20]; +- +-/* address that the host will use ... usually assigned at random */ +-static char ethaddr [2 * ETH_ALEN + 1]; +- +-/* static strings, in UTF-8 */ +-static struct usb_string strings [] = { +- { STRING_MANUFACTURER, manufacturer, }, +- { STRING_PRODUCT, product_desc, }, +- { STRING_SERIALNUMBER, serial_number, }, +- { STRING_DATA, "Ethernet Data", }, +- { STRING_ETHADDR, ethaddr, }, +-#ifdef DEV_CONFIG_CDC +- { STRING_CDC, "CDC Ethernet", }, +- { STRING_CONTROL, "CDC Communications Control", }, +-#endif +-#ifdef DEV_CONFIG_SUBSET +- { STRING_SUBSET, "CDC Ethernet Subset", }, +-#endif +-#ifdef CONFIG_USB_ETH_RNDIS +- { STRING_RNDIS, "RNDIS", }, +- { STRING_RNDIS_CONTROL, "RNDIS Communications Control", }, +-#endif +- { } /* end of list */ +-}; +- +-static struct usb_gadget_strings stringtab = { +- .language = 0x0409, /* en-us */ +- .strings = strings, +-}; +- + /* +- * one config, two interfaces: control, data. +- * complications: class descriptors, and an altsetting. +- */ +-static int +-config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg) +-{ +- int len; +- const struct usb_config_descriptor *config; +- const struct usb_descriptor_header **function; +- int hs = 0; +- +- if (gadget_is_dualspeed(g)) { +- hs = (g->speed == USB_SPEED_HIGH); +- if (type == USB_DT_OTHER_SPEED_CONFIG) +- hs = !hs; +- } +-#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function) +- +- if (index >= device_desc.bNumConfigurations) +- return -EINVAL; +- +-#ifdef CONFIG_USB_ETH_RNDIS +- /* list the RNDIS config first, to make Microsoft's drivers +- * happy. DOCSIS 1.0 needs this too. +- */ +- if (device_desc.bNumConfigurations == 2 && index == 0) { +- config = &rndis_config; +- function = which_fn (rndis); +- } else +-#endif +- { +- config = ð_config; +- function = which_fn (eth); +- } +- +- /* for now, don't advertise srp-only devices */ +- if (!is_otg) +- function++; +- +- len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function); +- if (len < 0) +- return len; +- ((struct usb_config_descriptor *) buf)->bDescriptorType = type; +- return len; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void eth_start (struct eth_dev *dev, gfp_t gfp_flags); +-static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags); +- +-static int +-set_ether_config (struct eth_dev *dev, gfp_t gfp_flags) +-{ +- int result = 0; +- struct usb_gadget *gadget = dev->gadget; +- +-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +- /* status endpoint used for RNDIS and (optionally) CDC */ +- if (!subset_active(dev) && dev->status_ep) { +- dev->status = ep_desc (gadget, &hs_status_desc, +- &fs_status_desc); +- dev->status_ep->driver_data = dev; +- +- result = usb_ep_enable (dev->status_ep, dev->status); +- if (result != 0) { +- DEBUG (dev, "enable %s --> %d\n", +- dev->status_ep->name, result); +- goto done; +- } +- } +-#endif +- +- dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc); +- dev->in_ep->driver_data = dev; +- +- dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); +- dev->out_ep->driver_data = dev; +- +- /* With CDC, the host isn't allowed to use these two data +- * endpoints in the default altsetting for the interface. +- * so we don't activate them yet. Reset from SET_INTERFACE. +- * +- * Strictly speaking RNDIS should work the same: activation is +- * a side effect of setting a packet filter. Deactivation is +- * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. +- */ +- if (!cdc_active(dev)) { +- result = usb_ep_enable (dev->in_ep, dev->in); +- if (result != 0) { +- DEBUG(dev, "enable %s --> %d\n", +- dev->in_ep->name, result); +- goto done; +- } +- +- result = usb_ep_enable (dev->out_ep, dev->out); +- if (result != 0) { +- DEBUG (dev, "enable %s --> %d\n", +- dev->out_ep->name, result); +- goto done; +- } +- } +- +-done: +- if (result == 0) +- result = alloc_requests (dev, qlen (gadget), gfp_flags); +- +- /* on error, disable any endpoints */ +- if (result < 0) { +- if (!subset_active(dev) && dev->status_ep) +- (void) usb_ep_disable (dev->status_ep); +- dev->status = NULL; +- (void) usb_ep_disable (dev->in_ep); +- (void) usb_ep_disable (dev->out_ep); +- dev->in = NULL; +- dev->out = NULL; +- } +- +- /* activate non-CDC configs right away +- * this isn't strictly according to the RNDIS spec +- */ +- else if (!cdc_active (dev)) { +- netif_carrier_on (dev->net); +- if (netif_running (dev->net)) { +- spin_unlock (&dev->lock); +- eth_start (dev, GFP_ATOMIC); +- spin_lock (&dev->lock); +- } +- } +- +- if (result == 0) +- DEBUG (dev, "qlen %d\n", qlen (gadget)); +- +- /* caller is responsible for cleanup on error */ +- return result; +-} +- +-static void eth_reset_config (struct eth_dev *dev) +-{ +- struct usb_request *req; +- +- if (dev->config == 0) +- return; +- +- DEBUG (dev, "%s\n", __func__); +- +- netif_stop_queue (dev->net); +- netif_carrier_off (dev->net); +- rndis_uninit(dev->rndis_config); +- +- /* disable endpoints, forcing (synchronous) completion of +- * pending i/o. then free the requests. +- */ +- if (dev->in) { +- usb_ep_disable (dev->in_ep); +- spin_lock(&dev->req_lock); +- while (likely (!list_empty (&dev->tx_reqs))) { +- req = container_of (dev->tx_reqs.next, +- struct usb_request, list); +- list_del (&req->list); +- +- spin_unlock(&dev->req_lock); +- usb_ep_free_request (dev->in_ep, req); +- spin_lock(&dev->req_lock); +- } +- spin_unlock(&dev->req_lock); +- } +- if (dev->out) { +- usb_ep_disable (dev->out_ep); +- spin_lock(&dev->req_lock); +- while (likely (!list_empty (&dev->rx_reqs))) { +- req = container_of (dev->rx_reqs.next, +- struct usb_request, list); +- list_del (&req->list); +- +- spin_unlock(&dev->req_lock); +- usb_ep_free_request (dev->out_ep, req); +- spin_lock(&dev->req_lock); +- } +- spin_unlock(&dev->req_lock); +- } +- +- if (dev->status) { +- usb_ep_disable (dev->status_ep); +- } +- dev->rndis = 0; +- dev->cdc_filter = 0; +- dev->config = 0; +-} +- +-/* change our operational config. must agree with the code +- * that returns config descriptors, and altsetting code. ++ * We may not have an RNDIS configuration, but if we do it needs to be ++ * the first one present. That's to make Microsoft's drivers happy, ++ * and to follow DOCSIS 1.0 (cable modem standard). + */ +-static int +-eth_set_config (struct eth_dev *dev, unsigned number, gfp_t gfp_flags) ++static int __init rndis_do_config(struct usb_configuration *c) + { +- int result = 0; +- struct usb_gadget *gadget = dev->gadget; +- +- if (gadget_is_sa1100 (gadget) +- && dev->config +- && atomic_read (&dev->tx_qlen) != 0) { +- /* tx fifo is full, but we can't clear it...*/ +- INFO (dev, "can't change configurations\n"); +- return -ESPIPE; +- } +- eth_reset_config (dev); +- +- switch (number) { +- case DEV_CONFIG_VALUE: +- result = set_ether_config (dev, gfp_flags); +- break; +-#ifdef CONFIG_USB_ETH_RNDIS +- case DEV_RNDIS_CONFIG_VALUE: +- dev->rndis = 1; +- result = set_ether_config (dev, gfp_flags); +- break; +-#endif +- default: +- result = -EINVAL; +- /* FALL THROUGH */ +- case 0: +- break; +- } +- +- if (result) { +- if (number) +- eth_reset_config (dev); +- usb_gadget_vbus_draw(dev->gadget, +- gadget_is_otg(dev->gadget) ? 8 : 100); +- } else { +- char *speed; +- unsigned power; +- +- power = 2 * eth_config.bMaxPower; +- usb_gadget_vbus_draw(dev->gadget, power); ++ /* FIXME alloc iConfiguration string, set it in c->strings */ + +- switch (gadget->speed) { +- case USB_SPEED_FULL: speed = "full"; break; +-#ifdef CONFIG_USB_GADGET_DUALSPEED +- case USB_SPEED_HIGH: speed = "high"; break; +-#endif +- default: speed = "?"; break; +- } +- +- dev->config = number; +- INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", +- speed, number, power, driver_desc, +- rndis_active(dev) +- ? "RNDIS" +- : (cdc_active(dev) +- ? "CDC Ethernet" +- : "CDC Ethernet Subset")); ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } +- return result; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-#ifdef DEV_CONFIG_CDC + +-/* The interrupt endpoint is used in CDC networking models (Ethernet, ATM) +- * only to notify the host about link status changes (which we support) or +- * report completion of some encapsulated command (as used in RNDIS). Since +- * we want this CDC Ethernet code to be vendor-neutral, we don't use that +- * command mechanism; and only one status request is ever queued. +- */ +- +-static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) +-{ +- struct usb_cdc_notification *event = req->buf; +- int value = req->status; +- struct eth_dev *dev = ep->driver_data; +- +- /* issue the second notification if host reads the first */ +- if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION +- && value == 0) { +- __le32 *data = req->buf + sizeof *event; +- +- event->bmRequestType = 0xA1; +- event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; +- event->wValue = __constant_cpu_to_le16 (0); +- event->wIndex = __constant_cpu_to_le16 (1); +- event->wLength = __constant_cpu_to_le16 (8); +- +- /* SPEED_CHANGE data is up/down speeds in bits/sec */ +- data [0] = data [1] = cpu_to_le32 (BITRATE (dev->gadget)); +- +- req->length = STATUS_BYTECOUNT; +- value = usb_ep_queue (ep, req, GFP_ATOMIC); +- DEBUG (dev, "send SPEED_CHANGE --> %d\n", value); +- if (value == 0) +- return; +- } else if (value != -ECONNRESET) +- DEBUG (dev, "event %02x --> %d\n", +- event->bNotificationType, value); +- req->context = NULL; ++ return rndis_bind_config(c, hostaddr); + } + +-static void issue_start_status (struct eth_dev *dev) +-{ +- struct usb_request *req = dev->stat_req; +- struct usb_cdc_notification *event; +- int value; +- +- DEBUG (dev, "%s, flush old status first\n", __func__); +- +- /* flush old status +- * +- * FIXME ugly idiom, maybe we'd be better with just +- * a "cancel the whole queue" primitive since any +- * unlink-one primitive has way too many error modes. +- * here, we "know" toggle is already clear... +- * +- * FIXME iff req->context != null just dequeue it +- */ +- usb_ep_disable (dev->status_ep); +- usb_ep_enable (dev->status_ep, dev->status); +- +- /* 3.8.1 says to issue first NETWORK_CONNECTION, then +- * a SPEED_CHANGE. could be useful in some configs. +- */ +- event = req->buf; +- event->bmRequestType = 0xA1; +- event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; +- event->wValue = __constant_cpu_to_le16 (1); /* connected */ +- event->wIndex = __constant_cpu_to_le16 (1); +- event->wLength = 0; +- +- req->length = sizeof *event; +- req->complete = eth_status_complete; +- req->context = dev; +- +- value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC); +- if (value < 0) +- DEBUG (dev, "status buf queue --> %d\n", value); +-} +- +-#endif ++static struct usb_configuration rndis_config_driver = { ++ .label = "RNDIS", ++ .bind = rndis_do_config, ++ .bConfigurationValue = 2, ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++}; + + /*-------------------------------------------------------------------------*/ + +-static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req) +-{ +- if (req->status || req->actual != req->length) +- DEBUG ((struct eth_dev *) ep->driver_data, +- "setup complete --> %d, %d/%d\n", +- req->status, req->actual, req->length); +-} +- +-#ifdef CONFIG_USB_ETH_RNDIS +- +-static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) +-{ +- if (req->status || req->actual != req->length) +- DEBUG ((struct eth_dev *) ep->driver_data, +- "rndis response complete --> %d, %d/%d\n", +- req->status, req->actual, req->length); +- +- /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ +-} +- +-static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) +-{ +- struct eth_dev *dev = ep->driver_data; +- int status; +- +- /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ +- spin_lock(&dev->lock); +- status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf); +- if (status < 0) +- ERROR(dev, "%s: rndis parse error %d\n", __func__, status); +- spin_unlock(&dev->lock); +-} +- +-#endif /* RNDIS */ +- + /* +- * The setup() callback implements all the ep0 functionality that's not +- * handled lower down. CDC has a number of less-common features: +- * +- * - two interfaces: control, and ethernet data +- * - Ethernet data interface has two altsettings: default, and active +- * - class-specific descriptors for the control interface +- * - class-specific control requests ++ * We _always_ have an ECM or CDC Subset configuration. + */ +-static int +-eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ++static int __init eth_do_config(struct usb_configuration *c) + { +- struct eth_dev *dev = get_gadget_data (gadget); +- struct usb_request *req = dev->req; +- int value = -EOPNOTSUPP; +- u16 wIndex = le16_to_cpu(ctrl->wIndex); +- u16 wValue = le16_to_cpu(ctrl->wValue); +- u16 wLength = le16_to_cpu(ctrl->wLength); +- +- /* descriptors just go into the pre-allocated ep0 buffer, +- * while config change events may enable network traffic. +- */ +- req->complete = eth_setup_complete; +- switch (ctrl->bRequest) { +- +- case USB_REQ_GET_DESCRIPTOR: +- if (ctrl->bRequestType != USB_DIR_IN) +- break; +- switch (wValue >> 8) { +- +- case USB_DT_DEVICE: +- value = min (wLength, (u16) sizeof device_desc); +- memcpy (req->buf, &device_desc, value); +- break; +- case USB_DT_DEVICE_QUALIFIER: +- if (!gadget_is_dualspeed(gadget)) +- break; +- value = min (wLength, (u16) sizeof dev_qualifier); +- memcpy (req->buf, &dev_qualifier, value); +- break; +- +- case USB_DT_OTHER_SPEED_CONFIG: +- if (!gadget_is_dualspeed(gadget)) +- break; +- // FALLTHROUGH +- case USB_DT_CONFIG: +- value = config_buf(gadget, req->buf, +- wValue >> 8, +- wValue & 0xff, +- gadget_is_otg(gadget)); +- if (value >= 0) +- value = min (wLength, (u16) value); +- break; +- +- case USB_DT_STRING: +- value = usb_gadget_get_string (&stringtab, +- wValue & 0xff, req->buf); +- if (value >= 0) +- value = min (wLength, (u16) value); +- break; +- } +- break; +- +- case USB_REQ_SET_CONFIGURATION: +- if (ctrl->bRequestType != 0) +- break; +- if (gadget->a_hnp_support) +- DEBUG (dev, "HNP available\n"); +- else if (gadget->a_alt_hnp_support) +- DEBUG (dev, "HNP needs a different root port\n"); +- spin_lock (&dev->lock); +- value = eth_set_config (dev, wValue, GFP_ATOMIC); +- spin_unlock (&dev->lock); +- break; +- case USB_REQ_GET_CONFIGURATION: +- if (ctrl->bRequestType != USB_DIR_IN) +- break; +- *(u8 *)req->buf = dev->config; +- value = min (wLength, (u16) 1); +- break; +- +- case USB_REQ_SET_INTERFACE: +- if (ctrl->bRequestType != USB_RECIP_INTERFACE +- || !dev->config +- || wIndex > 1) +- break; +- if (!cdc_active(dev) && wIndex != 0) +- break; +- spin_lock (&dev->lock); +- +- /* PXA hardware partially handles SET_INTERFACE; +- * we need to kluge around that interference. +- */ +- if (gadget_is_pxa (gadget)) { +- value = eth_set_config (dev, DEV_CONFIG_VALUE, +- GFP_ATOMIC); +- goto done_set_intf; +- } +- +-#ifdef DEV_CONFIG_CDC +- switch (wIndex) { +- case 0: /* control/master intf */ +- if (wValue != 0) +- break; +- if (dev->status) { +- usb_ep_disable (dev->status_ep); +- usb_ep_enable (dev->status_ep, dev->status); +- } +- value = 0; +- break; +- case 1: /* data intf */ +- if (wValue > 1) +- break; +- usb_ep_disable (dev->in_ep); +- usb_ep_disable (dev->out_ep); +- +- /* CDC requires the data transfers not be done from +- * the default interface setting ... also, setting +- * the non-default interface resets filters etc. +- */ +- if (wValue == 1) { +- if (!cdc_active (dev)) +- break; +- usb_ep_enable (dev->in_ep, dev->in); +- usb_ep_enable (dev->out_ep, dev->out); +- dev->cdc_filter = DEFAULT_FILTER; +- netif_carrier_on (dev->net); +- if (dev->status) +- issue_start_status (dev); +- if (netif_running (dev->net)) { +- spin_unlock (&dev->lock); +- eth_start (dev, GFP_ATOMIC); +- spin_lock (&dev->lock); +- } +- } else { +- netif_stop_queue (dev->net); +- netif_carrier_off (dev->net); +- } +- value = 0; +- break; +- } +-#else +- /* FIXME this is wrong, as is the assumption that +- * all non-PXA hardware talks real CDC ... +- */ +- dev_warn (&gadget->dev, "set_interface ignored!\n"); +-#endif /* DEV_CONFIG_CDC */ +- +-done_set_intf: +- spin_unlock (&dev->lock); +- break; +- case USB_REQ_GET_INTERFACE: +- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) +- || !dev->config +- || wIndex > 1) +- break; +- if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0) +- break; +- +- /* for CDC, iff carrier is on, data interface is active. */ +- if (rndis_active(dev) || wIndex != 1) +- *(u8 *)req->buf = 0; +- else +- *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; +- value = min (wLength, (u16) 1); +- break; +- +-#ifdef DEV_CONFIG_CDC +- case USB_CDC_SET_ETHERNET_PACKET_FILTER: +- /* see 6.2.30: no data, wIndex = interface, +- * wValue = packet filter bitmap +- */ +- if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) +- || !cdc_active(dev) +- || wLength != 0 +- || wIndex > 1) +- break; +- DEBUG (dev, "packet filter %02x\n", wValue); +- dev->cdc_filter = wValue; +- value = 0; +- break; +- +- /* and potentially: +- * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: +- * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: +- * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: +- * case USB_CDC_GET_ETHERNET_STATISTIC: +- */ +- +-#endif /* DEV_CONFIG_CDC */ ++ /* FIXME alloc iConfiguration string, set it in c->strings */ + +-#ifdef CONFIG_USB_ETH_RNDIS +- /* RNDIS uses the CDC command encapsulation mechanism to implement +- * an RPC scheme, with much getting/setting of attributes by OID. +- */ +- case USB_CDC_SEND_ENCAPSULATED_COMMAND: +- if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) +- || !rndis_active(dev) +- || wLength > USB_BUFSIZ +- || wValue +- || rndis_control_intf.bInterfaceNumber +- != wIndex) +- break; +- /* read the request, then process it */ +- value = wLength; +- req->complete = rndis_command_complete; +- /* later, rndis_control_ack () sends a notification */ +- break; +- +- case USB_CDC_GET_ENCAPSULATED_RESPONSE: +- if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) +- == ctrl->bRequestType +- && rndis_active(dev) +- // && wLength >= 0x0400 +- && !wValue +- && rndis_control_intf.bInterfaceNumber +- == wIndex) { +- u8 *buf; +- u32 n; +- +- /* return the result */ +- buf = rndis_get_next_response(dev->rndis_config, &n); +- if (buf) { +- memcpy(req->buf, buf, n); +- req->complete = rndis_response_complete; +- rndis_free_response(dev->rndis_config, buf); +- value = n; +- } +- /* else stalls ... spec says to avoid that */ +- } +- break; +-#endif /* RNDIS */ +- +- default: +- VDEBUG (dev, +- "unknown control req%02x.%02x v%04x i%04x l%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- wValue, wIndex, wLength); ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + +- /* respond with data transfer before status phase? */ +- if (value >= 0) { +- req->length = value; +- req->zero = value < wLength +- && (value % gadget->ep0->maxpacket) == 0; +- value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); +- if (value < 0) { +- DEBUG (dev, "ep_queue --> %d\n", value); +- req->status = 0; +- eth_setup_complete (gadget->ep0, req); +- } +- } +- +- /* host either stalls (value < 0) or reports success */ +- return value; +-} +- +-static void +-eth_disconnect (struct usb_gadget *gadget) +-{ +- struct eth_dev *dev = get_gadget_data (gadget); +- unsigned long flags; +- +- spin_lock_irqsave (&dev->lock, flags); +- netif_stop_queue (dev->net); +- netif_carrier_off (dev->net); +- eth_reset_config (dev); +- spin_unlock_irqrestore (&dev->lock, flags); +- +- /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ +- +- /* next we may get setup() calls to enumerate new connections; +- * or an unbind() during shutdown (including removing module). +- */ +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* NETWORK DRIVER HOOKUP (to the layer above this driver) */ +- +-static int eth_change_mtu (struct net_device *net, int new_mtu) +-{ +- struct eth_dev *dev = netdev_priv(net); +- +- if (dev->rndis) +- return -EBUSY; +- +- if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) +- return -ERANGE; +- /* no zero-length packet read wanted after mtu-sized packets */ +- if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0) +- return -EDOM; +- net->mtu = new_mtu; +- return 0; +-} +- +-static struct net_device_stats *eth_get_stats (struct net_device *net) +-{ +- return &((struct eth_dev *)netdev_priv(net))->stats; +-} +- +-static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) +-{ +- struct eth_dev *dev = netdev_priv(net); +- strlcpy(p->driver, shortname, sizeof p->driver); +- strlcpy(p->version, DRIVER_VERSION, sizeof p->version); +- strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); +- strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info); +-} +- +-static u32 eth_get_link(struct net_device *net) +-{ +- struct eth_dev *dev = netdev_priv(net); +- return dev->gadget->speed != USB_SPEED_UNKNOWN; +-} +- +-static struct ethtool_ops ops = { +- .get_drvinfo = eth_get_drvinfo, +- .get_link = eth_get_link +-}; +- +-static void defer_kevent (struct eth_dev *dev, int flag) +-{ +- if (test_and_set_bit (flag, &dev->todo)) +- return; +- if (!schedule_work (&dev->work)) +- ERROR (dev, "kevent %d may have been dropped\n", flag); ++ if (can_support_ecm(c->cdev->gadget)) ++ return ecm_bind_config(c, hostaddr); + else +- DEBUG (dev, "kevent %d scheduled\n", flag); +-} +- +-static void rx_complete (struct usb_ep *ep, struct usb_request *req); +- +-static int +-rx_submit (struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) +-{ +- struct sk_buff *skb; +- int retval = -ENOMEM; +- size_t size; +- +- /* Padding up to RX_EXTRA handles minor disagreements with host. +- * Normally we use the USB "terminate on short read" convention; +- * so allow up to (N*maxpacket), since that memory is normally +- * already allocated. Some hardware doesn't deal well with short +- * reads (e.g. DMA must be N*maxpacket), so for now don't trim a +- * byte off the end (to force hardware errors on overflow). +- * +- * RNDIS uses internal framing, and explicitly allows senders to +- * pad to end-of-packet. That's potentially nice for speed, +- * but means receivers can't recover synch on their own. +- */ +- size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); +- size += dev->out_ep->maxpacket - 1; +- if (rndis_active(dev)) +- size += sizeof (struct rndis_packet_msg_type); +- size -= size % dev->out_ep->maxpacket; +- +- skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); +- if (skb == NULL) { +- DEBUG (dev, "no rx skb\n"); +- goto enomem; +- } +- +- /* Some platforms perform better when IP packets are aligned, +- * but on at least one, checksumming fails otherwise. Note: +- * RNDIS headers involve variable numbers of LE32 values. +- */ +- skb_reserve(skb, NET_IP_ALIGN); +- +- req->buf = skb->data; +- req->length = size; +- req->complete = rx_complete; +- req->context = skb; +- +- retval = usb_ep_queue (dev->out_ep, req, gfp_flags); +- if (retval == -ENOMEM) +-enomem: +- defer_kevent (dev, WORK_RX_MEMORY); +- if (retval) { +- DEBUG (dev, "rx submit --> %d\n", retval); +- if (skb) +- dev_kfree_skb_any(skb); +- spin_lock(&dev->req_lock); +- list_add (&req->list, &dev->rx_reqs); +- spin_unlock(&dev->req_lock); +- } +- return retval; +-} +- +-static void rx_complete (struct usb_ep *ep, struct usb_request *req) +-{ +- struct sk_buff *skb = req->context; +- struct eth_dev *dev = ep->driver_data; +- int status = req->status; +- +- switch (status) { +- +- /* normal completion */ +- case 0: +- skb_put (skb, req->actual); +- /* we know MaxPacketsPerTransfer == 1 here */ +- if (rndis_active(dev)) +- status = rndis_rm_hdr (skb); +- if (status < 0 +- || ETH_HLEN > skb->len +- || skb->len > ETH_FRAME_LEN) { +- dev->stats.rx_errors++; +- dev->stats.rx_length_errors++; +- DEBUG (dev, "rx length %d\n", skb->len); +- break; +- } +- +- skb->protocol = eth_type_trans (skb, dev->net); +- dev->stats.rx_packets++; +- dev->stats.rx_bytes += skb->len; +- +- /* no buffer copies needed, unless hardware can't +- * use skb buffers. +- */ +- status = netif_rx (skb); +- skb = NULL; +- break; +- +- /* software-driven interface shutdown */ +- case -ECONNRESET: // unlink +- case -ESHUTDOWN: // disconnect etc +- VDEBUG (dev, "rx shutdown, code %d\n", status); +- goto quiesce; +- +- /* for hardware automagic (such as pxa) */ +- case -ECONNABORTED: // endpoint reset +- DEBUG (dev, "rx %s reset\n", ep->name); +- defer_kevent (dev, WORK_RX_MEMORY); +-quiesce: +- dev_kfree_skb_any (skb); +- goto clean; +- +- /* data overrun */ +- case -EOVERFLOW: +- dev->stats.rx_over_errors++; +- // FALLTHROUGH +- +- default: +- dev->stats.rx_errors++; +- DEBUG (dev, "rx status %d\n", status); +- break; +- } +- +- if (skb) +- dev_kfree_skb_any (skb); +- if (!netif_running (dev->net)) { +-clean: +- spin_lock(&dev->req_lock); +- list_add (&req->list, &dev->rx_reqs); +- spin_unlock(&dev->req_lock); +- req = NULL; +- } +- if (req) +- rx_submit (dev, req, GFP_ATOMIC); +-} +- +-static int prealloc (struct list_head *list, struct usb_ep *ep, +- unsigned n, gfp_t gfp_flags) +-{ +- unsigned i; +- struct usb_request *req; +- +- if (!n) +- return -ENOMEM; +- +- /* queue/recycle up to N requests */ +- i = n; +- list_for_each_entry (req, list, list) { +- if (i-- == 0) +- goto extra; +- } +- while (i--) { +- req = usb_ep_alloc_request (ep, gfp_flags); +- if (!req) +- return list_empty (list) ? -ENOMEM : 0; +- list_add (&req->list, list); +- } +- return 0; +- +-extra: +- /* free extras */ +- for (;;) { +- struct list_head *next; +- +- next = req->list.next; +- list_del (&req->list); +- usb_ep_free_request (ep, req); +- +- if (next == list) +- break; +- +- req = container_of (next, struct usb_request, list); +- } +- return 0; +-} +- +-static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags) +-{ +- int status; +- +- spin_lock(&dev->req_lock); +- status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags); +- if (status < 0) +- goto fail; +- status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags); +- if (status < 0) +- goto fail; +- goto done; +-fail: +- DEBUG (dev, "can't alloc requests\n"); +-done: +- spin_unlock(&dev->req_lock); +- return status; +-} +- +-static void rx_fill (struct eth_dev *dev, gfp_t gfp_flags) +-{ +- struct usb_request *req; +- unsigned long flags; +- +- /* fill unused rxq slots with some skb */ +- spin_lock_irqsave(&dev->req_lock, flags); +- while (!list_empty (&dev->rx_reqs)) { +- req = container_of (dev->rx_reqs.next, +- struct usb_request, list); +- list_del_init (&req->list); +- spin_unlock_irqrestore(&dev->req_lock, flags); +- +- if (rx_submit (dev, req, gfp_flags) < 0) { +- defer_kevent (dev, WORK_RX_MEMORY); +- return; +- } +- +- spin_lock_irqsave(&dev->req_lock, flags); +- } +- spin_unlock_irqrestore(&dev->req_lock, flags); +-} +- +-static void eth_work (struct work_struct *work) +-{ +- struct eth_dev *dev = container_of(work, struct eth_dev, work); +- +- if (test_and_clear_bit (WORK_RX_MEMORY, &dev->todo)) { +- if (netif_running (dev->net)) +- rx_fill (dev, GFP_KERNEL); +- } +- +- if (dev->todo) +- DEBUG (dev, "work done, flags = 0x%lx\n", dev->todo); +-} +- +-static void tx_complete (struct usb_ep *ep, struct usb_request *req) +-{ +- struct sk_buff *skb = req->context; +- struct eth_dev *dev = ep->driver_data; +- +- switch (req->status) { +- default: +- dev->stats.tx_errors++; +- VDEBUG (dev, "tx err %d\n", req->status); +- /* FALLTHROUGH */ +- case -ECONNRESET: // unlink +- case -ESHUTDOWN: // disconnect etc +- break; +- case 0: +- dev->stats.tx_bytes += skb->len; +- } +- dev->stats.tx_packets++; +- +- spin_lock(&dev->req_lock); +- list_add (&req->list, &dev->tx_reqs); +- spin_unlock(&dev->req_lock); +- dev_kfree_skb_any (skb); +- +- atomic_dec (&dev->tx_qlen); +- if (netif_carrier_ok (dev->net)) +- netif_wake_queue (dev->net); +-} +- +-static inline int eth_is_promisc (struct eth_dev *dev) +-{ +- /* no filters for the CDC subset; always promisc */ +- if (subset_active (dev)) +- return 1; +- return dev->cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; +-} +- +-static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) +-{ +- struct eth_dev *dev = netdev_priv(net); +- int length = skb->len; +- int retval; +- struct usb_request *req = NULL; +- unsigned long flags; +- +- /* apply outgoing CDC or RNDIS filters */ +- if (!eth_is_promisc (dev)) { +- u8 *dest = skb->data; +- +- if (is_multicast_ether_addr(dest)) { +- u16 type; +- +- /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host +- * SET_ETHERNET_MULTICAST_FILTERS requests +- */ +- if (is_broadcast_ether_addr(dest)) +- type = USB_CDC_PACKET_TYPE_BROADCAST; +- else +- type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; +- if (!(dev->cdc_filter & type)) { +- dev_kfree_skb_any (skb); +- return 0; +- } +- } +- /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ +- } +- +- spin_lock_irqsave(&dev->req_lock, flags); +- /* +- * this freelist can be empty if an interrupt triggered disconnect() +- * and reconfigured the gadget (shutting down this queue) after the +- * network stack decided to xmit but before we got the spinlock. +- */ +- if (list_empty(&dev->tx_reqs)) { +- spin_unlock_irqrestore(&dev->req_lock, flags); +- return 1; +- } +- +- req = container_of (dev->tx_reqs.next, struct usb_request, list); +- list_del (&req->list); +- +- /* temporarily stop TX queue when the freelist empties */ +- if (list_empty (&dev->tx_reqs)) +- netif_stop_queue (net); +- spin_unlock_irqrestore(&dev->req_lock, flags); +- +- /* no buffer copies needed, unless the network stack did it +- * or the hardware can't use skb buffers. +- * or there's not enough space for any RNDIS headers we need +- */ +- if (rndis_active(dev)) { +- struct sk_buff *skb_rndis; +- +- skb_rndis = skb_realloc_headroom (skb, +- sizeof (struct rndis_packet_msg_type)); +- if (!skb_rndis) +- goto drop; +- +- dev_kfree_skb_any (skb); +- skb = skb_rndis; +- rndis_add_hdr (skb); +- length = skb->len; +- } +- req->buf = skb->data; +- req->context = skb; +- req->complete = tx_complete; +- +- /* use zlp framing on tx for strict CDC-Ether conformance, +- * though any robust network rx path ignores extra padding. +- * and some hardware doesn't like to write zlps. +- */ +- req->zero = 1; +- if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0) +- length++; +- +- req->length = length; +- +- /* throttle highspeed IRQ rate back slightly */ +- if (gadget_is_dualspeed(dev->gadget)) +- req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) +- ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) +- : 0; +- +- retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC); +- switch (retval) { +- default: +- DEBUG (dev, "tx queue err %d\n", retval); +- break; +- case 0: +- net->trans_start = jiffies; +- atomic_inc (&dev->tx_qlen); +- } +- +- if (retval) { +-drop: +- dev->stats.tx_dropped++; +- dev_kfree_skb_any (skb); +- spin_lock_irqsave(&dev->req_lock, flags); +- if (list_empty (&dev->tx_reqs)) +- netif_start_queue (net); +- list_add (&req->list, &dev->tx_reqs); +- spin_unlock_irqrestore(&dev->req_lock, flags); +- } +- return 0; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-#ifdef CONFIG_USB_ETH_RNDIS +- +-/* The interrupt endpoint is used in RNDIS to notify the host when messages +- * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT +- * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even +- * REMOTE_NDIS_KEEPALIVE_MSG. +- * +- * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and +- * normally just one notification will be queued. +- */ +- +-static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, gfp_t); +-static void eth_req_free (struct usb_ep *ep, struct usb_request *req); +- +-static void +-rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) +-{ +- struct eth_dev *dev = ep->driver_data; +- +- if (req->status || req->actual != req->length) +- DEBUG (dev, +- "rndis control ack complete --> %d, %d/%d\n", +- req->status, req->actual, req->length); +- req->context = NULL; +- +- if (req != dev->stat_req) +- eth_req_free(ep, req); +-} +- +-static int rndis_control_ack (struct net_device *net) +-{ +- struct eth_dev *dev = netdev_priv(net); +- int length; +- struct usb_request *resp = dev->stat_req; +- +- /* in case RNDIS calls this after disconnect */ +- if (!dev->status) { +- DEBUG (dev, "status ENODEV\n"); +- return -ENODEV; +- } +- +- /* in case queue length > 1 */ +- if (resp->context) { +- resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC); +- if (!resp) +- return -ENOMEM; +- } +- +- /* Send RNDIS RESPONSE_AVAILABLE notification; +- * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too +- */ +- resp->length = 8; +- resp->complete = rndis_control_ack_complete; +- resp->context = dev; +- +- *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1); +- *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); +- +- length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC); +- if (length < 0) { +- resp->status = 0; +- rndis_control_ack_complete (dev->status_ep, resp); +- } +- +- return 0; +-} +- +-#else +- +-#define rndis_control_ack NULL +- +-#endif /* RNDIS */ +- +-static void eth_start (struct eth_dev *dev, gfp_t gfp_flags) +-{ +- DEBUG (dev, "%s\n", __func__); +- +- /* fill the rx queue */ +- rx_fill (dev, gfp_flags); +- +- /* and open the tx floodgates */ +- atomic_set (&dev->tx_qlen, 0); +- netif_wake_queue (dev->net); +- if (rndis_active(dev)) { +- rndis_set_param_medium (dev->rndis_config, +- NDIS_MEDIUM_802_3, +- BITRATE(dev->gadget)/100); +- (void) rndis_signal_connect (dev->rndis_config); +- } +-} +- +-static int eth_open (struct net_device *net) +-{ +- struct eth_dev *dev = netdev_priv(net); +- +- DEBUG (dev, "%s\n", __func__); +- if (netif_carrier_ok (dev->net)) +- eth_start (dev, GFP_KERNEL); +- return 0; ++ return geth_bind_config(c, hostaddr); + } + +-static int eth_stop (struct net_device *net) +-{ +- struct eth_dev *dev = netdev_priv(net); +- +- VDEBUG (dev, "%s\n", __func__); +- netif_stop_queue (net); +- +- DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", +- dev->stats.rx_packets, dev->stats.tx_packets, +- dev->stats.rx_errors, dev->stats.tx_errors +- ); +- +- /* ensure there are no more active requests */ +- if (dev->config) { +- usb_ep_disable (dev->in_ep); +- usb_ep_disable (dev->out_ep); +- if (netif_carrier_ok (dev->net)) { +- DEBUG (dev, "host still using in/out endpoints\n"); +- // FIXME idiom may leave toggle wrong here +- usb_ep_enable (dev->in_ep, dev->in); +- usb_ep_enable (dev->out_ep, dev->out); +- } +- if (dev->status_ep) { +- usb_ep_disable (dev->status_ep); +- usb_ep_enable (dev->status_ep, dev->status); +- } +- } +- +- if (rndis_active(dev)) { +- rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0); +- (void) rndis_signal_disconnect (dev->rndis_config); +- } +- +- return 0; +-} ++static struct usb_configuration eth_config_driver = { ++ /* .label = f(hardware) */ ++ .bind = eth_do_config, ++ .bConfigurationValue = 1, ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++}; + + /*-------------------------------------------------------------------------*/ + +-static struct usb_request * +-eth_req_alloc (struct usb_ep *ep, unsigned size, gfp_t gfp_flags) ++static int __init eth_bind(struct usb_composite_dev *cdev) + { +- struct usb_request *req; +- +- req = usb_ep_alloc_request (ep, gfp_flags); +- if (!req) +- return NULL; +- +- req->buf = kmalloc (size, gfp_flags); +- if (!req->buf) { +- usb_ep_free_request (ep, req); +- req = NULL; +- } +- return req; +-} +- +-static void +-eth_req_free (struct usb_ep *ep, struct usb_request *req) +-{ +- kfree (req->buf); +- usb_ep_free_request (ep, req); +-} +- +- +-static void /* __init_or_exit */ +-eth_unbind (struct usb_gadget *gadget) +-{ +- struct eth_dev *dev = get_gadget_data (gadget); +- +- DEBUG (dev, "unbind\n"); +- rndis_deregister (dev->rndis_config); +- rndis_exit (); +- +- /* we've already been disconnected ... no i/o is active */ +- if (dev->req) { +- eth_req_free (gadget->ep0, dev->req); +- dev->req = NULL; +- } +- if (dev->stat_req) { +- eth_req_free (dev->status_ep, dev->stat_req); +- dev->stat_req = NULL; +- } +- +- unregister_netdev (dev->net); +- free_netdev(dev->net); +- +- /* assuming we used keventd, it must quiesce too */ +- flush_scheduled_work (); +- set_gadget_data (gadget, NULL); +-} +- +-static u8 __init nibble (unsigned char c) +-{ +- if (likely (isdigit (c))) +- return c - '0'; +- c = toupper (c); +- if (likely (isxdigit (c))) +- return 10 + c - 'A'; +- return 0; +-} ++ int gcnum; ++ struct usb_gadget *gadget = cdev->gadget; ++ int status; + +-static int __init get_ether_addr(const char *str, u8 *dev_addr) +-{ +- if (str) { +- unsigned i; ++ /* set up network link layer */ ++ status = gether_setup(cdev->gadget, hostaddr); ++ if (status < 0) ++ return status; + +- for (i = 0; i < 6; i++) { +- unsigned char num; ++ /* set up main config label and device descriptor */ ++ if (can_support_ecm(cdev->gadget)) { ++ /* ECM */ ++ eth_config_driver.label = "CDC Ethernet (ECM)"; ++ } else { ++ /* CDC Subset */ ++ eth_config_driver.label = "CDC Subset/SAFE"; + +- if((*str == '.') || (*str == ':')) +- str++; +- num = nibble(*str++) << 4; +- num |= (nibble(*str++)); +- dev_addr [i] = num; +- } +- if (is_valid_ether_addr (dev_addr)) +- return 0; ++ device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM), ++ device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM), ++ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + } +- random_ether_addr(dev_addr); +- return 1; +-} +- +-static int __init +-eth_bind (struct usb_gadget *gadget) +-{ +- struct eth_dev *dev; +- struct net_device *net; +- u8 cdc = 1, zlp = 1, rndis = 1; +- struct usb_ep *in_ep, *out_ep, *status_ep = NULL; +- int status = -ENOMEM; +- int gcnum; +- +- /* these flags are only ever cleared; compiler take note */ +-#ifndef DEV_CONFIG_CDC +- cdc = 0; +-#endif +-#ifndef CONFIG_USB_ETH_RNDIS +- rndis = 0; +-#endif + +- /* Because most host side USB stacks handle CDC Ethernet, that +- * standard protocol is _strongly_ preferred for interop purposes. +- * (By everyone except Microsoft.) +- */ +- if (gadget_is_pxa (gadget)) { +- /* pxa doesn't support altsettings */ +- cdc = 0; +- } else if (gadget_is_musbhdrc(gadget)) { +- /* reduce tx dma overhead by avoiding special cases */ +- zlp = 0; +- } else if (gadget_is_sh(gadget)) { +- /* sh doesn't support multiple interfaces or configs */ +- cdc = 0; +- rndis = 0; +- } else if (gadget_is_sa1100 (gadget)) { +- /* hardware can't write zlps */ +- zlp = 0; +- /* sa1100 CAN do CDC, without status endpoint ... we use +- * non-CDC to be compatible with ARM Linux-2.4 "usb-eth". +- */ +- cdc = 0; ++ if (has_rndis()) { ++ /* RNDIS plus ECM-or-Subset */ ++ device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM), ++ device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM), ++ device_desc.bNumConfigurations = 2; + } + +- gcnum = usb_gadget_controller_number (gadget); ++ gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) +- device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum); ++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + else { +- /* can't assume CDC works. don't want to default to +- * anything less functional on CDC-capable hardware, +- * so we fail in this case. ++ /* We assume that can_support_ecm() tells the truth; ++ * but if the controller isn't recognized at all then ++ * that assumption is a bit more likely to be wrong. + */ +- dev_err (&gadget->dev, +- "controller '%s' not recognized\n", +- gadget->name); +- return -ENODEV; +- } +- snprintf (manufacturer, sizeof manufacturer, "%s %s/%s", +- init_utsname()->sysname, init_utsname()->release, +- gadget->name); +- +- /* If there's an RNDIS configuration, that's what Windows wants to +- * be using ... so use these product IDs here and in the "linux.inf" +- * needed to install MSFT drivers. Current Linux kernels will use +- * the second configuration if it's CDC Ethernet, and need some help +- * to choose the right configuration otherwise. +- */ +- if (rndis) { +- device_desc.idVendor = +- __constant_cpu_to_le16(RNDIS_VENDOR_NUM); +- device_desc.idProduct = +- __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); +- snprintf (product_desc, sizeof product_desc, +- "RNDIS/%s", driver_desc); +- +- /* CDC subset ... recognized by Linux since 2.4.10, but Windows +- * drivers aren't widely available. (That may be improved by +- * supporting one submode of the "SAFE" variant of MDLM.) +- */ +- } else if (!cdc) { +- device_desc.idVendor = +- __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); +- device_desc.idProduct = +- __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); +- } +- +- /* support optional vendor/distro customization */ +- if (idVendor) { +- if (!idProduct) { +- dev_err (&gadget->dev, "idVendor needs idProduct!\n"); +- return -ENODEV; +- } +- device_desc.idVendor = cpu_to_le16(idVendor); +- device_desc.idProduct = cpu_to_le16(idProduct); +- if (bcdDevice) +- device_desc.bcdDevice = cpu_to_le16(bcdDevice); +- } +- if (iManufacturer) +- strlcpy (manufacturer, iManufacturer, sizeof manufacturer); +- if (iProduct) +- strlcpy (product_desc, iProduct, sizeof product_desc); +- if (iSerialNumber) { +- device_desc.iSerialNumber = STRING_SERIALNUMBER, +- strlcpy(serial_number, iSerialNumber, sizeof serial_number); +- } +- +- /* all we really need is bulk IN/OUT */ +- usb_ep_autoconfig_reset (gadget); +- in_ep = usb_ep_autoconfig (gadget, &fs_source_desc); +- if (!in_ep) { +-autoconf_fail: +- dev_err (&gadget->dev, +- "can't autoconfigure on %s\n", +- gadget->name); +- return -ENODEV; +- } +- in_ep->driver_data = in_ep; /* claim */ +- +- out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc); +- if (!out_ep) +- goto autoconf_fail; +- out_ep->driver_data = out_ep; /* claim */ +- +-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +- /* CDC Ethernet control interface doesn't require a status endpoint. +- * Since some hosts expect one, try to allocate one anyway. +- */ +- if (cdc || rndis) { +- status_ep = usb_ep_autoconfig (gadget, &fs_status_desc); +- if (status_ep) { +- status_ep->driver_data = status_ep; /* claim */ +- } else if (rndis) { +- dev_err (&gadget->dev, +- "can't run RNDIS on %s\n", +- gadget->name); +- return -ENODEV; +-#ifdef DEV_CONFIG_CDC +- /* pxa25x only does CDC subset; often used with RNDIS */ +- } else if (cdc) { +- control_intf.bNumEndpoints = 0; +- /* FIXME remove endpoint from descriptor list */ +-#endif +- } +- } +-#endif +- +- /* one config: cdc, else minimal subset */ +- if (!cdc) { +- eth_config.bNumInterfaces = 1; +- eth_config.iConfiguration = STRING_SUBSET; +- +- /* use functions to set these up, in case we're built to work +- * with multiple controllers and must override CDC Ethernet. +- */ +- fs_subset_descriptors(); +- hs_subset_descriptors(); +- } +- +- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; +- usb_gadget_set_selfpowered (gadget); +- +- /* For now RNDIS is always a second config */ +- if (rndis) +- device_desc.bNumConfigurations = 2; +- +- if (gadget_is_dualspeed(gadget)) { +- if (rndis) +- dev_qualifier.bNumConfigurations = 2; +- else if (!cdc) +- dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC; +- +- /* assumes ep0 uses the same value for both speeds ... */ +- dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; +- +- /* and that all endpoints are dual-speed */ +- hs_source_desc.bEndpointAddress = +- fs_source_desc.bEndpointAddress; +- hs_sink_desc.bEndpointAddress = +- fs_sink_desc.bEndpointAddress; +-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +- if (status_ep) +- hs_status_desc.bEndpointAddress = +- fs_status_desc.bEndpointAddress; +-#endif ++ WARNING(cdev, "controller '%s' not recognized; trying %s\n", ++ gadget->name, ++ eth_config_driver.label); ++ device_desc.bcdDevice = ++ __constant_cpu_to_le16(0x0300 | 0x0099); + } + +- if (gadget_is_otg(gadget)) { +- otg_descriptor.bmAttributes |= USB_OTG_HNP, +- eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- eth_config.bMaxPower = 4; +-#ifdef CONFIG_USB_ETH_RNDIS +- rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- rndis_config.bMaxPower = 4; +-#endif +- } +- +- net = alloc_etherdev (sizeof *dev); +- if (!net) +- return status; +- dev = netdev_priv(net); +- spin_lock_init (&dev->lock); +- spin_lock_init (&dev->req_lock); +- INIT_WORK (&dev->work, eth_work); +- INIT_LIST_HEAD (&dev->tx_reqs); +- INIT_LIST_HEAD (&dev->rx_reqs); +- +- /* network device setup */ +- dev->net = net; +- strcpy (net->name, "usb%d"); +- dev->cdc = cdc; +- dev->zlp = zlp; + +- dev->in_ep = in_ep; +- dev->out_ep = out_ep; +- dev->status_ep = status_ep; +- +- /* Module params for these addresses should come from ID proms. +- * The host side address is used with CDC and RNDIS, and commonly +- * ends up in a persistent config database. It's not clear if +- * host side code for the SAFE thing cares -- its original BLAN +- * thing didn't, Sharp never assigned those addresses on Zaurii. ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. + */ +- if (get_ether_addr(dev_addr, net->dev_addr)) +- dev_warn(&gadget->dev, +- "using random %s ethernet address\n", "self"); +- if (get_ether_addr(host_addr, dev->host_mac)) +- dev_warn(&gadget->dev, +- "using random %s ethernet address\n", "host"); +- snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", +- dev->host_mac [0], dev->host_mac [1], +- dev->host_mac [2], dev->host_mac [3], +- dev->host_mac [4], dev->host_mac [5]); +- +- if (rndis) { +- status = rndis_init(); +- if (status < 0) { +- dev_err (&gadget->dev, "can't init RNDIS, %d\n", +- status); +- goto fail; +- } +- } + +- net->change_mtu = eth_change_mtu; +- net->get_stats = eth_get_stats; +- net->hard_start_xmit = eth_start_xmit; +- net->open = eth_open; +- net->stop = eth_stop; +- // watchdog_timeo, tx_timeout ... +- // set_multicast_list +- SET_ETHTOOL_OPS(net, &ops); ++ /* device descriptor strings: manufacturer, product */ ++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", ++ init_utsname()->sysname, init_utsname()->release, ++ gadget->name); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail; ++ strings_dev[STRING_MANUFACTURER_IDX].id = status; ++ device_desc.iManufacturer = status; + +- /* preallocate control message data and buffer */ +- dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ, GFP_KERNEL); +- if (!dev->req) ++ status = usb_string_id(cdev); ++ if (status < 0) + goto fail; +- dev->req->complete = eth_setup_complete; ++ strings_dev[STRING_PRODUCT_IDX].id = status; ++ device_desc.iProduct = status; + +- /* ... and maybe likewise for status transfer */ +-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +- if (dev->status_ep) { +- dev->stat_req = eth_req_alloc (dev->status_ep, +- STATUS_BYTECOUNT, GFP_KERNEL); +- if (!dev->stat_req) { +- eth_req_free (gadget->ep0, dev->req); ++ /* register our configuration(s); RNDIS first, if it's used */ ++ if (has_rndis()) { ++ status = usb_add_config(cdev, &rndis_config_driver); ++ if (status < 0) + goto fail; +- } +- dev->stat_req->context = NULL; + } +-#endif +- +- /* finish hookup to lower layer ... */ +- dev->gadget = gadget; +- set_gadget_data (gadget, dev); +- gadget->ep0->driver_data = dev; + +- /* two kinds of host-initiated state changes: +- * - iff DATA transfer is active, carrier is "on" +- * - tx queueing enabled if open *and* carrier is "on" +- */ +- netif_stop_queue (dev->net); +- netif_carrier_off (dev->net); +- +- SET_NETDEV_DEV (dev->net, &gadget->dev); +- status = register_netdev (dev->net); ++ status = usb_add_config(cdev, ð_config_driver); + if (status < 0) +- goto fail1; +- +- INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); +- INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, +- out_ep->name, in_ep->name, +- status_ep ? " STATUS " : "", +- status_ep ? status_ep->name : "" +- ); +- INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", +- net->dev_addr [0], net->dev_addr [1], +- net->dev_addr [2], net->dev_addr [3], +- net->dev_addr [4], net->dev_addr [5]); +- +- if (cdc || rndis) +- INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n", +- dev->host_mac [0], dev->host_mac [1], +- dev->host_mac [2], dev->host_mac [3], +- dev->host_mac [4], dev->host_mac [5]); +- +- if (rndis) { +- u32 vendorID = 0; +- +- /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ +- +- dev->rndis_config = rndis_register (rndis_control_ack); +- if (dev->rndis_config < 0) { +-fail0: +- unregister_netdev (dev->net); +- status = -ENODEV; +- goto fail; +- } ++ goto fail; + +- /* these set up a lot of the OIDs that RNDIS needs */ +- rndis_set_host_mac (dev->rndis_config, dev->host_mac); +- if (rndis_set_param_dev (dev->rndis_config, dev->net, +- &dev->stats, &dev->cdc_filter)) +- goto fail0; +- if (rndis_set_param_vendor(dev->rndis_config, vendorID, +- manufacturer)) +- goto fail0; +- if (rndis_set_param_medium(dev->rndis_config, +- NDIS_MEDIUM_802_3, 0)) +- goto fail0; +- INFO (dev, "RNDIS ready\n"); +- } ++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); + +- return status; ++ return 0; + +-fail1: +- dev_dbg(&gadget->dev, "register_netdev failed, %d\n", status); + fail: +- eth_unbind (gadget); ++ gether_cleanup(); + return status; + } + +-/*-------------------------------------------------------------------------*/ +- +-static void +-eth_suspend (struct usb_gadget *gadget) +-{ +- struct eth_dev *dev = get_gadget_data (gadget); +- +- DEBUG (dev, "suspend\n"); +- dev->suspended = 1; +-} +- +-static void +-eth_resume (struct usb_gadget *gadget) ++static int __exit eth_unbind(struct usb_composite_dev *cdev) + { +- struct eth_dev *dev = get_gadget_data (gadget); +- +- DEBUG (dev, "resume\n"); +- dev->suspended = 0; ++ gether_cleanup(); ++ return 0; + } + +-/*-------------------------------------------------------------------------*/ +- +-static struct usb_gadget_driver eth_driver = { +- .speed = DEVSPEED, +- +- .function = (char *) driver_desc, ++static struct usb_composite_driver eth_driver = { ++ .name = "g_ether", ++ .dev = &device_desc, ++ .strings = dev_strings, + .bind = eth_bind, +- .unbind = eth_unbind, +- +- .setup = eth_setup, +- .disconnect = eth_disconnect, +- +- .suspend = eth_suspend, +- .resume = eth_resume, +- +- .driver = { +- .name = (char *) shortname, +- .owner = THIS_MODULE, +- }, ++ .unbind = __exit_p(eth_unbind), + }; + +-MODULE_DESCRIPTION (DRIVER_DESC); +-MODULE_AUTHOR ("David Brownell, Benedikt Spanger"); +-MODULE_LICENSE ("GPL"); ++MODULE_DESCRIPTION(PREFIX DRIVER_DESC); ++MODULE_AUTHOR("David Brownell, Benedikt Spanger"); ++MODULE_LICENSE("GPL"); + +- +-static int __init init (void) ++static int __init init(void) + { +- return usb_gadget_register_driver (ð_driver); ++ return usb_composite_register(ð_driver); + } +-module_init (init); ++module_init(init); + +-static void __exit cleanup (void) ++static void __exit cleanup(void) + { +- usb_gadget_unregister_driver (ð_driver); ++ usb_composite_unregister(ð_driver); + } +-module_exit (cleanup); +- ++module_exit(cleanup); +diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c +new file mode 100644 +index 0000000..5ee1590 +--- /dev/null ++++ b/drivers/usb/gadget/f_acm.c +@@ -0,0 +1,759 @@ ++/* ++ * f_acm.c -- USB CDC serial (ACM) function driver ++ * ++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) ++ * Copyright (C) 2008 by David Brownell ++ * Copyright (C) 2008 by Nokia Corporation ++ * ++ * This software is distributed under the terms of the GNU General ++ * Public License ("GPL") as published by the Free Software Foundation, ++ * either version 2 of that License or (at your option) any later version. ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++ ++#include "u_serial.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * This CDC ACM function support just wraps control functions and ++ * notifications around the generic serial-over-usb code. ++ * ++ * Because CDC ACM is standardized by the USB-IF, many host operating ++ * systems have drivers for it. Accordingly, ACM is the preferred ++ * interop solution for serial-port type connections. The control ++ * models are often not necessary, and in any case don't do much in ++ * this bare-bones implementation. ++ * ++ * Note that even MS-Windows has some support for ACM. However, that ++ * support is somewhat broken because when you use ACM in a composite ++ * device, having multiple interfaces confuses the poor OS. It doesn't ++ * seem to understand CDC Union descriptors. The new "association" ++ * descriptors (roughly equivalent to CDC Unions) may sometimes help. ++ */ ++ ++struct acm_ep_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ struct usb_endpoint_descriptor *notify; ++}; ++ ++struct f_acm { ++ struct gserial port; ++ u8 ctrl_id, data_id; ++ u8 port_num; ++ ++ u8 pending; ++ ++ /* lock is mostly for pending and notify_req ... they get accessed ++ * by callbacks both from tty (open/close/break) under its spinlock, ++ * and notify_req.complete() which can't use that lock. ++ */ ++ spinlock_t lock; ++ ++ struct acm_ep_descs fs; ++ struct acm_ep_descs hs; ++ ++ struct usb_ep *notify; ++ struct usb_endpoint_descriptor *notify_desc; ++ struct usb_request *notify_req; ++ ++ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ ++ ++ /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */ ++ u16 port_handshake_bits; ++#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */ ++#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */ ++ ++ /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */ ++ u16 serial_state; ++#define ACM_CTRL_OVERRUN (1 << 6) ++#define ACM_CTRL_PARITY (1 << 5) ++#define ACM_CTRL_FRAMING (1 << 4) ++#define ACM_CTRL_RI (1 << 3) ++#define ACM_CTRL_BRK (1 << 2) ++#define ACM_CTRL_DSR (1 << 1) ++#define ACM_CTRL_DCD (1 << 0) ++}; ++ ++static inline struct f_acm *func_to_acm(struct usb_function *f) ++{ ++ return container_of(f, struct f_acm, port.func); ++} ++ ++static inline struct f_acm *port_to_acm(struct gserial *p) ++{ ++ return container_of(p, struct f_acm, port); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* notification endpoint uses smallish and infrequent fixed-size messages */ ++ ++#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ ++#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */ ++ ++/* interface and class descriptors: */ ++ ++static struct usb_interface_descriptor acm_control_interface_desc __initdata = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, ++ .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_interface_descriptor acm_data_interface_desc __initdata = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc acm_header_desc __initdata = { ++ .bLength = sizeof(acm_header_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_call_mgmt_descriptor ++acm_call_mgmt_descriptor __initdata = { ++ .bLength = sizeof(acm_call_mgmt_descriptor), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, ++ .bmCapabilities = 0, ++ /* .bDataInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { ++ .bLength = sizeof(acm_descriptor), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ACM_TYPE, ++ .bmCapabilities = USB_CDC_CAP_LINE, ++}; ++ ++static struct usb_cdc_union_desc acm_union_desc __initdata = { ++ .bLength = sizeof(acm_union_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_UNION_TYPE, ++ /* .bMasterInterface0 = DYNAMIC */ ++ /* .bSlaveInterface0 = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), ++ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, ++}; ++ ++static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *acm_fs_function[] __initdata = { ++ (struct usb_descriptor_header *) &acm_control_interface_desc, ++ (struct usb_descriptor_header *) &acm_header_desc, ++ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &acm_union_desc, ++ (struct usb_descriptor_header *) &acm_fs_notify_desc, ++ (struct usb_descriptor_header *) &acm_data_interface_desc, ++ (struct usb_descriptor_header *) &acm_fs_in_desc, ++ (struct usb_descriptor_header *) &acm_fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), ++ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, ++}; ++ ++static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *acm_hs_function[] __initdata = { ++ (struct usb_descriptor_header *) &acm_control_interface_desc, ++ (struct usb_descriptor_header *) &acm_header_desc, ++ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &acm_union_desc, ++ (struct usb_descriptor_header *) &acm_hs_notify_desc, ++ (struct usb_descriptor_header *) &acm_data_interface_desc, ++ (struct usb_descriptor_header *) &acm_hs_in_desc, ++ (struct usb_descriptor_header *) &acm_hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++#define ACM_CTRL_IDX 0 ++#define ACM_DATA_IDX 1 ++ ++/* static strings, in UTF-8 */ ++static struct usb_string acm_string_defs[] = { ++ [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", ++ [ACM_DATA_IDX].s = "CDC ACM Data", ++ { /* ZEROES END LIST */ }, ++}; ++ ++static struct usb_gadget_strings acm_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = acm_string_defs, ++}; ++ ++static struct usb_gadget_strings *acm_strings[] = { ++ &acm_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ACM control ... data handling is delegated to tty library code. ++ * The main task of this function is to activate and deactivate ++ * that code based on device state; track parameters like line ++ * speed, handshake state, and so on; and issue notifications. ++ */ ++ ++static void acm_complete_set_line_coding(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ struct f_acm *acm = ep->driver_data; ++ struct usb_composite_dev *cdev = acm->port.func.config->cdev; ++ ++ if (req->status != 0) { ++ DBG(cdev, "acm ttyGS%d completion, err %d\n", ++ acm->port_num, req->status); ++ return; ++ } ++ ++ /* normal completion */ ++ if (req->actual != sizeof(acm->port_line_coding)) { ++ DBG(cdev, "acm ttyGS%d short resp, len %d\n", ++ acm->port_num, req->actual); ++ usb_ep_set_halt(ep); ++ } else { ++ struct usb_cdc_line_coding *value = req->buf; ++ ++ /* REVISIT: we currently just remember this data. ++ * If we change that, (a) validate it first, then ++ * (b) update whatever hardware needs updating, ++ * (c) worry about locking. This is information on ++ * the order of 9600-8-N-1 ... most of which means ++ * nothing unless we control a real RS232 line. ++ */ ++ acm->port_line_coding = *value; ++ } ++} ++ ++static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct f_acm *acm = func_to_acm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything except ++ * CDC class messages; interface activation uses set_alt(). ++ * ++ * Note CDC spec table 4 lists the ACM request profile. It requires ++ * encapsulated command support ... we don't handle any, and respond ++ * to them by stalling. Options include get/set/clear comm features ++ * (not that useful) and SEND_BREAK. ++ */ ++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { ++ ++ /* SET_LINE_CODING ... just read and save what the host sends */ ++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_REQ_SET_LINE_CODING: ++ if (w_length != sizeof(struct usb_cdc_line_coding) ++ || w_index != acm->ctrl_id) ++ goto invalid; ++ ++ value = w_length; ++ cdev->gadget->ep0->driver_data = acm; ++ req->complete = acm_complete_set_line_coding; ++ break; ++ ++ /* GET_LINE_CODING ... return what host sent, or initial value */ ++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_REQ_GET_LINE_CODING: ++ if (w_index != acm->ctrl_id) ++ goto invalid; ++ ++ value = min_t(unsigned, w_length, ++ sizeof(struct usb_cdc_line_coding)); ++ memcpy(req->buf, &acm->port_line_coding, value); ++ break; ++ ++ /* SET_CONTROL_LINE_STATE ... save what the host sent */ ++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_REQ_SET_CONTROL_LINE_STATE: ++ if (w_index != acm->ctrl_id) ++ goto invalid; ++ ++ value = 0; ++ ++ /* FIXME we should not allow data to flow until the ++ * host sets the ACM_CTRL_DTR bit; and when it clears ++ * that bit, we should return to that no-flow state. ++ */ ++ acm->port_handshake_bits = w_value; ++ break; ++ ++ default: ++invalid: ++ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", ++ acm->port_num, ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "acm response on ttyGS%d, err %d\n", ++ acm->port_num, value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_acm *acm = func_to_acm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt == 0, so this is an activation or a reset */ ++ ++ if (intf == acm->ctrl_id) { ++ if (acm->notify->driver_data) { ++ VDBG(cdev, "reset acm control interface %d\n", intf); ++ usb_ep_disable(acm->notify); ++ } else { ++ VDBG(cdev, "init acm ctrl interface %d\n", intf); ++ acm->notify_desc = ep_choose(cdev->gadget, ++ acm->hs.notify, ++ acm->fs.notify); ++ } ++ usb_ep_enable(acm->notify, acm->notify_desc); ++ acm->notify->driver_data = acm; ++ ++ } else if (intf == acm->data_id) { ++ if (acm->port.in->driver_data) { ++ DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); ++ gserial_disconnect(&acm->port); ++ } else { ++ DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); ++ acm->port.in_desc = ep_choose(cdev->gadget, ++ acm->hs.in, acm->fs.in); ++ acm->port.out_desc = ep_choose(cdev->gadget, ++ acm->hs.out, acm->fs.out); ++ } ++ gserial_connect(&acm->port, acm->port_num); ++ ++ } else ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void acm_disable(struct usb_function *f) ++{ ++ struct f_acm *acm = func_to_acm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); ++ gserial_disconnect(&acm->port); ++ usb_ep_disable(acm->notify); ++ acm->notify->driver_data = NULL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/** ++ * acm_cdc_notify - issue CDC notification to host ++ * @acm: wraps host to be notified ++ * @type: notification type ++ * @value: Refer to cdc specs, wValue field. ++ * @data: data to be sent ++ * @length: size of data ++ * Context: irqs blocked, acm->lock held, acm_notify_req non-null ++ * ++ * Returns zero on sucess or a negative errno. ++ * ++ * See section 6.3.5 of the CDC 1.1 specification for information ++ * about the only notification we issue: SerialState change. ++ */ ++static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value, ++ void *data, unsigned length) ++{ ++ struct usb_ep *ep = acm->notify; ++ struct usb_request *req; ++ struct usb_cdc_notification *notify; ++ const unsigned len = sizeof(*notify) + length; ++ void *buf; ++ int status; ++ ++ req = acm->notify_req; ++ acm->notify_req = NULL; ++ acm->pending = false; ++ ++ req->length = len; ++ notify = req->buf; ++ buf = notify + 1; ++ ++ notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS ++ | USB_RECIP_INTERFACE; ++ notify->bNotificationType = type; ++ notify->wValue = cpu_to_le16(value); ++ notify->wIndex = cpu_to_le16(acm->ctrl_id); ++ notify->wLength = cpu_to_le16(length); ++ memcpy(buf, data, length); ++ ++ status = usb_ep_queue(ep, req, GFP_ATOMIC); ++ if (status < 0) { ++ ERROR(acm->port.func.config->cdev, ++ "acm ttyGS%d can't notify serial state, %d\n", ++ acm->port_num, status); ++ acm->notify_req = req; ++ } ++ ++ return status; ++} ++ ++static int acm_notify_serial_state(struct f_acm *acm) ++{ ++ struct usb_composite_dev *cdev = acm->port.func.config->cdev; ++ int status; ++ ++ spin_lock(&acm->lock); ++ if (acm->notify_req) { ++ DBG(cdev, "acm ttyGS%d serial state %04x\n", ++ acm->port_num, acm->serial_state); ++ status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, ++ 0, &acm->serial_state, sizeof(acm->serial_state)); ++ } else { ++ acm->pending = true; ++ status = 0; ++ } ++ spin_unlock(&acm->lock); ++ return status; ++} ++ ++static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_acm *acm = req->context; ++ u8 doit = false; ++ ++ /* on this call path we do NOT hold the port spinlock, ++ * which is why ACM needs its own spinlock ++ */ ++ spin_lock(&acm->lock); ++ if (req->status != -ESHUTDOWN) ++ doit = acm->pending; ++ acm->notify_req = req; ++ spin_unlock(&acm->lock); ++ ++ if (doit) ++ acm_notify_serial_state(acm); ++} ++ ++/* connect == the TTY link is open */ ++ ++static void acm_connect(struct gserial *port) ++{ ++ struct f_acm *acm = port_to_acm(port); ++ ++ acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD; ++ acm_notify_serial_state(acm); ++} ++ ++static void acm_disconnect(struct gserial *port) ++{ ++ struct f_acm *acm = port_to_acm(port); ++ ++ acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD); ++ acm_notify_serial_state(acm); ++} ++ ++static int acm_send_break(struct gserial *port, int duration) ++{ ++ struct f_acm *acm = port_to_acm(port); ++ u16 state; ++ ++ state = acm->serial_state; ++ state &= ~ACM_CTRL_BRK; ++ if (duration) ++ state |= ACM_CTRL_BRK; ++ ++ acm->serial_state = state; ++ return acm_notify_serial_state(acm); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ACM function driver setup/binding */ ++static int __init ++acm_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_acm *acm = func_to_acm(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs, and patch descriptors */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ acm->ctrl_id = status; ++ ++ acm_control_interface_desc.bInterfaceNumber = status; ++ acm_union_desc .bMasterInterface0 = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ acm->data_id = status; ++ ++ acm_data_interface_desc.bInterfaceNumber = status; ++ acm_union_desc.bSlaveInterface0 = status; ++ acm_call_mgmt_descriptor.bDataInterface = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); ++ if (!ep) ++ goto fail; ++ acm->port.in = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); ++ if (!ep) ++ goto fail; ++ acm->port.out = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); ++ if (!ep) ++ goto fail; ++ acm->notify = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* allocate notification */ ++ acm->notify_req = gs_alloc_req(ep, ++ sizeof(struct usb_cdc_notification) + 2, ++ GFP_KERNEL); ++ if (!acm->notify_req) ++ goto fail; ++ ++ acm->notify_req->complete = acm_cdc_notify_complete; ++ acm->notify_req->context = acm; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(acm_fs_function); ++ if (!f->descriptors) ++ goto fail; ++ ++ acm->fs.in = usb_find_endpoint(acm_fs_function, ++ f->descriptors, &acm_fs_in_desc); ++ acm->fs.out = usb_find_endpoint(acm_fs_function, ++ f->descriptors, &acm_fs_out_desc); ++ acm->fs.notify = usb_find_endpoint(acm_fs_function, ++ f->descriptors, &acm_fs_notify_desc); ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ acm_hs_in_desc.bEndpointAddress = ++ acm_fs_in_desc.bEndpointAddress; ++ acm_hs_out_desc.bEndpointAddress = ++ acm_fs_out_desc.bEndpointAddress; ++ acm_hs_notify_desc.bEndpointAddress = ++ acm_fs_notify_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(acm_hs_function); ++ ++ acm->hs.in = usb_find_endpoint(acm_hs_function, ++ f->hs_descriptors, &acm_hs_in_desc); ++ acm->hs.out = usb_find_endpoint(acm_hs_function, ++ f->hs_descriptors, &acm_hs_out_desc); ++ acm->hs.notify = usb_find_endpoint(acm_hs_function, ++ f->hs_descriptors, &acm_hs_notify_desc); ++ } ++ ++ DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", ++ acm->port_num, ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ acm->port.in->name, acm->port.out->name, ++ acm->notify->name); ++ return 0; ++ ++fail: ++ if (acm->notify_req) ++ gs_free_req(acm->notify, acm->notify_req); ++ ++ /* we might as well release our claims on endpoints */ ++ if (acm->notify) ++ acm->notify->driver_data = NULL; ++ if (acm->port.out) ++ acm->port.out->driver_data = NULL; ++ if (acm->port.in) ++ acm->port.in->driver_data = NULL; ++ ++ ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); ++ ++ return status; ++} ++ ++static void ++acm_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct f_acm *acm = func_to_acm(f); ++ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ gs_free_req(acm->notify, acm->notify_req); ++ kfree(acm); ++} ++ ++/* Some controllers can't support CDC ACM ... */ ++static inline bool can_support_cdc(struct usb_configuration *c) ++{ ++ /* SH3 doesn't support multiple interfaces */ ++ if (gadget_is_sh(c->cdev->gadget)) ++ return false; ++ ++ /* sa1100 doesn't have a third interrupt endpoint */ ++ if (gadget_is_sa1100(c->cdev->gadget)) ++ return false; ++ ++ /* everything else is *probably* fine ... */ ++ return true; ++} ++ ++/** ++ * acm_bind_config - add a CDC ACM function to a configuration ++ * @c: the configuration to support the CDC ACM instance ++ * @port_num: /dev/ttyGS* port this interface will use ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gserial_setup() with enough ports to ++ * handle all the ones it binds. Caller is also responsible ++ * for calling @gserial_cleanup() before module unload. ++ */ ++int __init acm_bind_config(struct usb_configuration *c, u8 port_num) ++{ ++ struct f_acm *acm; ++ int status; ++ ++ if (!can_support_cdc(c)) ++ return -EINVAL; ++ ++ /* REVISIT might want instance-specific strings to help ++ * distinguish instances ... ++ */ ++ ++ /* maybe allocate device-global string IDs, and patch descriptors */ ++ if (acm_string_defs[ACM_CTRL_IDX].id == 0) { ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ acm_string_defs[ACM_CTRL_IDX].id = status; ++ ++ acm_control_interface_desc.iInterface = status; ++ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ acm_string_defs[ACM_DATA_IDX].id = status; ++ ++ acm_data_interface_desc.iInterface = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ acm = kzalloc(sizeof *acm, GFP_KERNEL); ++ if (!acm) ++ return -ENOMEM; ++ ++ spin_lock_init(&acm->lock); ++ ++ acm->port_num = port_num; ++ ++ acm->port.connect = acm_connect; ++ acm->port.disconnect = acm_disconnect; ++ acm->port.send_break = acm_send_break; ++ ++ acm->port.func.name = "acm"; ++ acm->port.func.strings = acm_strings; ++ /* descriptors are per-instance copies */ ++ acm->port.func.bind = acm_bind; ++ acm->port.func.unbind = acm_unbind; ++ acm->port.func.set_alt = acm_set_alt; ++ acm->port.func.setup = acm_setup; ++ acm->port.func.disable = acm_disable; ++ ++ status = usb_add_function(c, &acm->port.func); ++ if (status) ++ kfree(acm); ++ return status; ++} +diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c +new file mode 100644 +index 0000000..a2b5c09 +--- /dev/null ++++ b/drivers/usb/gadget/f_ecm.c +@@ -0,0 +1,831 @@ ++/* ++ * f_ecm.c -- USB CDC Ethernet (ECM) link function driver ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/etherdevice.h> ++ ++#include "u_ether.h" ++ ++ ++/* ++ * This function is a "CDC Ethernet Networking Control Model" (CDC ECM) ++ * Ethernet link. The data transfer model is simple (packets sent and ++ * received over bulk endpoints using normal short packet termination), ++ * and the control model exposes various data and optional notifications. ++ * ++ * ECM is well standardized and (except for Microsoft) supported by most ++ * operating systems with USB host support. It's the preferred interop ++ * solution for Ethernet over USB, at least for firmware based solutions. ++ * (Hardware solutions tend to be more minimalist.) A newer and simpler ++ * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on. ++ * ++ * Note that ECM requires the use of "alternate settings" for its data ++ * interface. This means that the set_alt() method has real work to do, ++ * and also means that a get_alt() method is required. ++ */ ++ ++struct ecm_ep_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ struct usb_endpoint_descriptor *notify; ++}; ++ ++enum ecm_notify_state { ++ ECM_NOTIFY_NONE, /* don't notify */ ++ ECM_NOTIFY_CONNECT, /* issue CONNECT next */ ++ ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */ ++}; ++ ++struct f_ecm { ++ struct gether port; ++ u8 ctrl_id, data_id; ++ ++ char ethaddr[14]; ++ ++ struct ecm_ep_descs fs; ++ struct ecm_ep_descs hs; ++ ++ struct usb_ep *notify; ++ struct usb_endpoint_descriptor *notify_desc; ++ struct usb_request *notify_req; ++ u8 notify_state; ++ bool is_open; ++ ++ /* FIXME is_open needs some irq-ish locking ++ * ... possibly the same as port.ioport ++ */ ++}; ++ ++static inline struct f_ecm *func_to_ecm(struct usb_function *f) ++{ ++ return container_of(f, struct f_ecm, port.func); ++} ++ ++/* peak (theoretical) bulk transfer rate in bits-per-second */ ++static inline unsigned bitrate(struct usb_gadget *g) ++{ ++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) ++ return 13 * 512 * 8 * 1000 * 8; ++ else ++ return 19 * 64 * 1 * 1000 * 8; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Include the status endpoint if we can, even though it's optional. ++ * ++ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one ++ * packet, to simplify cancellation; and a big transfer interval, to ++ * waste less bandwidth. ++ * ++ * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even ++ * if they ignore the connect/disconnect notifications that real aether ++ * can provide. More advanced cdc configurations might want to support ++ * encapsulated commands (vendor-specific, using control-OUT). ++ */ ++ ++#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ ++#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ ++ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor ecm_control_intf __initdata = { ++ .bLength = sizeof ecm_control_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ /* status endpoint is optional; this could be patched later */ ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, ++ .bInterfaceProtocol = USB_CDC_PROTO_NONE, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc header_desc __initdata = { ++ .bLength = sizeof header_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_union_desc ecm_union_desc __initdata = { ++ .bLength = sizeof(ecm_union_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_UNION_TYPE, ++ /* .bMasterInterface0 = DYNAMIC */ ++ /* .bSlaveInterface0 = DYNAMIC */ ++}; ++ ++static struct usb_cdc_ether_desc ether_desc __initdata = { ++ .bLength = sizeof ether_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, ++ ++ /* this descriptor actually adds value, surprise! */ ++ /* .iMACAddress = DYNAMIC */ ++ .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ ++ .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), ++ .wNumberMCFilters = __constant_cpu_to_le16(0), ++ .bNumberPowerFilters = 0, ++}; ++ ++/* the default data interface has no endpoints ... */ ++ ++static struct usb_interface_descriptor ecm_data_nop_intf __initdata = { ++ .bLength = sizeof ecm_data_nop_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* ... but the "real" data interface has two bulk endpoints */ ++ ++static struct usb_interface_descriptor ecm_data_intf __initdata = { ++ .bLength = sizeof ecm_data_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, ++}; ++ ++static struct usb_endpoint_descriptor fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *eth_fs_function[] __initdata = { ++ /* CDC ECM control descriptors */ ++ (struct usb_descriptor_header *) &ecm_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &ecm_union_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ /* NOTE: status endpoint might need to be removed */ ++ (struct usb_descriptor_header *) &fs_notify_desc, ++ /* data interface, altsettings 0 and 1 */ ++ (struct usb_descriptor_header *) &ecm_data_nop_intf, ++ (struct usb_descriptor_header *) &ecm_data_intf, ++ (struct usb_descriptor_header *) &fs_in_desc, ++ (struct usb_descriptor_header *) &fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, ++}; ++static struct usb_endpoint_descriptor hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *eth_hs_function[] __initdata = { ++ /* CDC ECM control descriptors */ ++ (struct usb_descriptor_header *) &ecm_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &ecm_union_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ /* NOTE: status endpoint might need to be removed */ ++ (struct usb_descriptor_header *) &hs_notify_desc, ++ /* data interface, altsettings 0 and 1 */ ++ (struct usb_descriptor_header *) &ecm_data_nop_intf, ++ (struct usb_descriptor_header *) &ecm_data_intf, ++ (struct usb_descriptor_header *) &hs_in_desc, ++ (struct usb_descriptor_header *) &hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string ecm_string_defs[] = { ++ [0].s = "CDC Ethernet Control Model (ECM)", ++ [1].s = NULL /* DYNAMIC */, ++ [2].s = "CDC Ethernet Data", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings ecm_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = ecm_string_defs, ++}; ++ ++static struct usb_gadget_strings *ecm_strings[] = { ++ &ecm_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ecm_do_notify(struct f_ecm *ecm) ++{ ++ struct usb_request *req = ecm->notify_req; ++ struct usb_cdc_notification *event; ++ struct usb_composite_dev *cdev = ecm->port.func.config->cdev; ++ __le32 *data; ++ int status; ++ ++ /* notification already in flight? */ ++ if (!req) ++ return; ++ ++ event = req->buf; ++ switch (ecm->notify_state) { ++ case ECM_NOTIFY_NONE: ++ return; ++ ++ case ECM_NOTIFY_CONNECT: ++ event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; ++ if (ecm->is_open) ++ event->wValue = cpu_to_le16(1); ++ else ++ event->wValue = cpu_to_le16(0); ++ event->wLength = 0; ++ req->length = sizeof *event; ++ ++ DBG(cdev, "notify connect %s\n", ++ ecm->is_open ? "true" : "false"); ++ ecm->notify_state = ECM_NOTIFY_SPEED; ++ break; ++ ++ case ECM_NOTIFY_SPEED: ++ event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; ++ event->wValue = cpu_to_le16(0); ++ event->wLength = cpu_to_le16(8); ++ req->length = STATUS_BYTECOUNT; ++ ++ /* SPEED_CHANGE data is up/down speeds in bits/sec */ ++ data = req->buf + sizeof *event; ++ data[0] = cpu_to_le32(bitrate(cdev->gadget)); ++ data[1] = data[0]; ++ ++ DBG(cdev, "notify speed %d\n", bitrate(cdev->gadget)); ++ ecm->notify_state = ECM_NOTIFY_NONE; ++ break; ++ } ++ event->bmRequestType = 0xA1; ++ event->wIndex = cpu_to_le16(ecm->ctrl_id); ++ ++ ecm->notify_req = NULL; ++ status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); ++ if (status < 0) { ++ ecm->notify_req = req; ++ DBG(cdev, "notify --> %d\n", status); ++ } ++} ++ ++static void ecm_notify(struct f_ecm *ecm) ++{ ++ /* NOTE on most versions of Linux, host side cdc-ethernet ++ * won't listen for notifications until its netdevice opens. ++ * The first notification then sits in the FIFO for a long ++ * time, and the second one is queued. ++ */ ++ ecm->notify_state = ECM_NOTIFY_CONNECT; ++ ecm_do_notify(ecm); ++} ++ ++static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_ecm *ecm = req->context; ++ struct usb_composite_dev *cdev = ecm->port.func.config->cdev; ++ struct usb_cdc_notification *event = req->buf; ++ ++ switch (req->status) { ++ case 0: ++ /* no fault */ ++ break; ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ ecm->notify_state = ECM_NOTIFY_NONE; ++ break; ++ default: ++ DBG(cdev, "event %02x --> %d\n", ++ event->bNotificationType, req->status); ++ break; ++ } ++ ecm->notify_req = req; ++ ecm_do_notify(ecm); ++} ++ ++static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything except ++ * CDC class messages; interface activation uses set_alt(). ++ */ ++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { ++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_SET_ETHERNET_PACKET_FILTER: ++ /* see 6.2.30: no data, wIndex = interface, ++ * wValue = packet filter bitmap ++ */ ++ if (w_length != 0 || w_index != ecm->ctrl_id) ++ goto invalid; ++ DBG(cdev, "packet filter %02x\n", w_value); ++ /* REVISIT locking of cdc_filter. This assumes the UDC ++ * driver won't have a concurrent packet TX irq running on ++ * another CPU; or that if it does, this write is atomic... ++ */ ++ ecm->port.cdc_filter = w_value; ++ value = 0; ++ break; ++ ++ /* and optionally: ++ * case USB_CDC_SEND_ENCAPSULATED_COMMAND: ++ * case USB_CDC_GET_ENCAPSULATED_RESPONSE: ++ * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: ++ * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: ++ * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: ++ * case USB_CDC_GET_ETHERNET_STATISTIC: ++ */ ++ ++ default: ++invalid: ++ DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "ecm req %02x.%02x response err %d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++ ++static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* Control interface has only altsetting 0 */ ++ if (intf == ecm->ctrl_id) { ++ if (alt != 0) ++ goto fail; ++ ++ if (ecm->notify->driver_data) { ++ VDBG(cdev, "reset ecm control %d\n", intf); ++ usb_ep_disable(ecm->notify); ++ } else { ++ VDBG(cdev, "init ecm ctrl %d\n", intf); ++ ecm->notify_desc = ep_choose(cdev->gadget, ++ ecm->hs.notify, ++ ecm->fs.notify); ++ } ++ usb_ep_enable(ecm->notify, ecm->notify_desc); ++ ecm->notify->driver_data = ecm; ++ ++ /* Data interface has two altsettings, 0 and 1 */ ++ } else if (intf == ecm->data_id) { ++ if (alt > 1) ++ goto fail; ++ ++ if (ecm->port.in_ep->driver_data) { ++ DBG(cdev, "reset ecm\n"); ++ gether_disconnect(&ecm->port); ++ } ++ ++ if (!ecm->port.in) { ++ DBG(cdev, "init ecm\n"); ++ ecm->port.in = ep_choose(cdev->gadget, ++ ecm->hs.in, ecm->fs.in); ++ ecm->port.out = ep_choose(cdev->gadget, ++ ecm->hs.out, ecm->fs.out); ++ } ++ ++ /* CDC Ethernet only sends data in non-default altsettings. ++ * Changing altsettings resets filters, statistics, etc. ++ */ ++ if (alt == 1) { ++ struct net_device *net; ++ ++ /* Enable zlps by default for ECM conformance; ++ * override for musb_hdrc (avoids txdma ovhead) ++ * and sa1100 (can't). ++ */ ++ ecm->port.is_zlp_ok = !( ++ gadget_is_sa1100(cdev->gadget) ++ || gadget_is_musbhdrc(cdev->gadget) ++ ); ++ ecm->port.cdc_filter = DEFAULT_FILTER; ++ DBG(cdev, "activate ecm\n"); ++ net = gether_connect(&ecm->port); ++ if (IS_ERR(net)) ++ return PTR_ERR(net); ++ } ++ ++ /* NOTE this can be a minor disagreement with the ECM spec, ++ * which says speed notifications will "always" follow ++ * connection notifications. But we allow one connect to ++ * follow another (if the first is in flight), and instead ++ * just guarantee that a speed notification is always sent. ++ */ ++ ecm_notify(ecm); ++ } else ++ goto fail; ++ ++ return 0; ++fail: ++ return -EINVAL; ++} ++ ++/* Because the data interface supports multiple altsettings, ++ * this ECM function *MUST* implement a get_alt() method. ++ */ ++static int ecm_get_alt(struct usb_function *f, unsigned intf) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ ++ if (intf == ecm->ctrl_id) ++ return 0; ++ return ecm->port.in_ep->driver_data ? 1 : 0; ++} ++ ++static void ecm_disable(struct usb_function *f) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "ecm deactivated\n"); ++ ++ if (ecm->port.in_ep->driver_data) ++ gether_disconnect(&ecm->port); ++ ++ if (ecm->notify->driver_data) { ++ usb_ep_disable(ecm->notify); ++ ecm->notify->driver_data = NULL; ++ ecm->notify_desc = NULL; ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Callbacks let us notify the host about connect/disconnect when the ++ * net device is opened or closed. ++ * ++ * For testing, note that link states on this side include both opened ++ * and closed variants of: ++ * ++ * - disconnected/unconfigured ++ * - configured but inactive (data alt 0) ++ * - configured and active (data alt 1) ++ * ++ * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and ++ * SET_INTERFACE (altsetting). Remember also that "configured" doesn't ++ * imply the host is actually polling the notification endpoint, and ++ * likewise that "active" doesn't imply it's actually using the data ++ * endpoints for traffic. ++ */ ++ ++static void ecm_open(struct gether *geth) ++{ ++ struct f_ecm *ecm = func_to_ecm(&geth->func); ++ ++ DBG(ecm->port.func.config->cdev, "%s\n", __func__); ++ ++ ecm->is_open = true; ++ ecm_notify(ecm); ++} ++ ++static void ecm_close(struct gether *geth) ++{ ++ struct f_ecm *ecm = func_to_ecm(&geth->func); ++ ++ DBG(ecm->port.func.config->cdev, "%s\n", __func__); ++ ++ ecm->is_open = false; ++ ecm_notify(ecm); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ethernet function driver setup/binding */ ++ ++static int __init ++ecm_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_ecm *ecm = func_to_ecm(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ ecm->ctrl_id = status; ++ ++ ecm_control_intf.bInterfaceNumber = status; ++ ecm_union_desc.bMasterInterface0 = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ ecm->data_id = status; ++ ++ ecm_data_nop_intf.bInterfaceNumber = status; ++ ecm_data_intf.bInterfaceNumber = status; ++ ecm_union_desc.bSlaveInterface0 = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); ++ if (!ep) ++ goto fail; ++ ecm->port.in_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); ++ if (!ep) ++ goto fail; ++ ecm->port.out_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* NOTE: a status/notification endpoint is *OPTIONAL* but we ++ * don't treat it that way. It's simpler, and some newer CDC ++ * profiles (wireless handsets) no longer treat it as optional. ++ */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); ++ if (!ep) ++ goto fail; ++ ecm->notify = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ status = -ENOMEM; ++ ++ /* allocate notification request and buffer */ ++ ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!ecm->notify_req) ++ goto fail; ++ ecm->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); ++ if (!ecm->notify_req->buf) ++ goto fail; ++ ecm->notify_req->context = ecm; ++ ecm->notify_req->complete = ecm_notify_complete; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(eth_fs_function); ++ if (!f->descriptors) ++ goto fail; ++ ++ ecm->fs.in = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_in_desc); ++ ecm->fs.out = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_out_desc); ++ ecm->fs.notify = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_notify_desc); ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_in_desc.bEndpointAddress = ++ fs_in_desc.bEndpointAddress; ++ hs_out_desc.bEndpointAddress = ++ fs_out_desc.bEndpointAddress; ++ hs_notify_desc.bEndpointAddress = ++ fs_notify_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(eth_hs_function); ++ if (!f->hs_descriptors) ++ goto fail; ++ ++ ecm->hs.in = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_in_desc); ++ ecm->hs.out = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_out_desc); ++ ecm->hs.notify = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_notify_desc); ++ } ++ ++ /* NOTE: all that is done without knowing or caring about ++ * the network link ... which is unavailable to this code ++ * until we're activated via set_alt(). ++ */ ++ ++ ecm->port.open = ecm_open; ++ ecm->port.close = ecm_close; ++ ++ DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ ecm->port.in_ep->name, ecm->port.out_ep->name, ++ ecm->notify->name); ++ return 0; ++ ++fail: ++ if (f->descriptors) ++ usb_free_descriptors(f->descriptors); ++ ++ if (ecm->notify_req) { ++ kfree(ecm->notify_req->buf); ++ usb_ep_free_request(ecm->notify, ecm->notify_req); ++ } ++ ++ /* we might as well release our claims on endpoints */ ++ if (ecm->notify) ++ ecm->notify->driver_data = NULL; ++ if (ecm->port.out) ++ ecm->port.out_ep->driver_data = NULL; ++ if (ecm->port.in) ++ ecm->port.in_ep->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++ecm_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ ++ DBG(c->cdev, "ecm unbind\n"); ++ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ ++ kfree(ecm->notify_req->buf); ++ usb_ep_free_request(ecm->notify, ecm->notify_req); ++ ++ ecm_string_defs[1].s = NULL; ++ kfree(ecm); ++} ++ ++/** ++ * ecm_bind_config - add CDC Ethernet network link to a configuration ++ * @c: the configuration to support the network link ++ * @ethaddr: a buffer in which the ethernet address of the host side ++ * side of the link was recorded ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gether_setup(). Caller is also responsible ++ * for calling @gether_cleanup() before module unload. ++ */ ++int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ struct f_ecm *ecm; ++ int status; ++ ++ if (!can_support_ecm(c->cdev->gadget) || !ethaddr) ++ return -EINVAL; ++ ++ /* maybe allocate device-global string IDs */ ++ if (ecm_string_defs[0].id == 0) { ++ ++ /* control interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ ecm_string_defs[0].id = status; ++ ecm_control_intf.iInterface = status; ++ ++ /* data interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ ecm_string_defs[2].id = status; ++ ecm_data_intf.iInterface = status; ++ ++ /* MAC address */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ ecm_string_defs[1].id = status; ++ ether_desc.iMACAddress = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ ecm = kzalloc(sizeof *ecm, GFP_KERNEL); ++ if (!ecm) ++ return -ENOMEM; ++ ++ /* export host's Ethernet address in CDC format */ ++ snprintf(ecm->ethaddr, sizeof ecm->ethaddr, ++ "%02X%02X%02X%02X%02X%02X", ++ ethaddr[0], ethaddr[1], ethaddr[2], ++ ethaddr[3], ethaddr[4], ethaddr[5]); ++ ecm_string_defs[1].s = ecm->ethaddr; ++ ++ ecm->port.cdc_filter = DEFAULT_FILTER; ++ ++ ecm->port.func.name = "cdc_ethernet"; ++ ecm->port.func.strings = ecm_strings; ++ /* descriptors are per-instance copies */ ++ ecm->port.func.bind = ecm_bind; ++ ecm->port.func.unbind = ecm_unbind; ++ ecm->port.func.set_alt = ecm_set_alt; ++ ecm->port.func.get_alt = ecm_get_alt; ++ ecm->port.func.setup = ecm_setup; ++ ecm->port.func.disable = ecm_disable; ++ ++ status = usb_add_function(c, &ecm->port.func); ++ if (status) { ++ ecm_string_defs[1].s = NULL; ++ kfree(ecm); ++ } ++ return status; ++} +diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c +new file mode 100644 +index 0000000..eda4cde +--- /dev/null ++++ b/drivers/usb/gadget/f_loopback.c +@@ -0,0 +1,381 @@ ++/* ++ * f_loopback.c - USB peripheral loopback configuration driver ++ * ++ * Copyright (C) 2003-2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/device.h> ++ ++#include "g_zero.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, ++ * ++ * This takes messages of various sizes written OUT to a device, and loops ++ * them back so they can be read IN from it. It has been used by certain ++ * test applications. It supports limited testing of data queueing logic. ++ * ++ * ++ * This is currently packaged as a configuration driver, which can't be ++ * combined with other functions to make composite devices. However, it ++ * can be combined with other independent configurations. ++ */ ++struct f_loopback { ++ struct usb_function function; ++ ++ struct usb_ep *in_ep; ++ struct usb_ep *out_ep; ++}; ++ ++static inline struct f_loopback *func_to_loop(struct usb_function *f) ++{ ++ return container_of(f, struct f_loopback, function); ++} ++ ++static unsigned qlen = 32; ++module_param(qlen, uint, 0); ++MODULE_PARM_DESC(qlenn, "depth of loopback queue"); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_interface_descriptor loopback_intf = { ++ .bLength = sizeof loopback_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_loopback_descs[] = { ++ (struct usb_descriptor_header *) &loopback_intf, ++ (struct usb_descriptor_header *) &fs_sink_desc, ++ (struct usb_descriptor_header *) &fs_source_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *hs_loopback_descs[] = { ++ (struct usb_descriptor_header *) &loopback_intf, ++ (struct usb_descriptor_header *) &hs_source_desc, ++ (struct usb_descriptor_header *) &hs_sink_desc, ++ NULL, ++}; ++ ++/* function-specific strings: */ ++ ++static struct usb_string strings_loopback[] = { ++ [0].s = "loop input to output", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_loop = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_loopback, ++}; ++ ++static struct usb_gadget_strings *loopback_strings[] = { ++ &stringtab_loop, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init ++loopback_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_loopback *loop = func_to_loop(f); ++ int id; ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ loopback_intf.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ++ loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); ++ if (!loop->in_ep) { ++autoconf_fail: ++ ERROR(cdev, "%s: can't autoconfigure on %s\n", ++ f->name, cdev->gadget->name); ++ return -ENODEV; ++ } ++ loop->in_ep->driver_data = cdev; /* claim */ ++ ++ loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); ++ if (!loop->out_ep) ++ goto autoconf_fail; ++ loop->out_ep->driver_data = cdev; /* claim */ ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_source_desc.bEndpointAddress = ++ fs_source_desc.bEndpointAddress; ++ hs_sink_desc.bEndpointAddress = ++ fs_sink_desc.bEndpointAddress; ++ f->hs_descriptors = hs_loopback_descs; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, loop->in_ep->name, loop->out_ep->name); ++ return 0; ++} ++ ++static void ++loopback_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ kfree(func_to_loop(f)); ++} ++ ++static void loopback_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_loopback *loop = ep->driver_data; ++ struct usb_composite_dev *cdev = loop->function.config->cdev; ++ int status = req->status; ++ ++ switch (status) { ++ ++ case 0: /* normal completion? */ ++ if (ep == loop->out_ep) { ++ /* loop this OUT packet back IN to the host */ ++ req->zero = (req->actual < req->length); ++ req->length = req->actual; ++ status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC); ++ if (status == 0) ++ return; ++ ++ /* "should never get here" */ ++ ERROR(cdev, "can't loop %s to %s: %d\n", ++ ep->name, loop->in_ep->name, ++ status); ++ } ++ ++ /* queue the buffer for some later OUT packet */ ++ req->length = buflen; ++ status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); ++ if (status == 0) ++ return; ++ ++ /* "should never get here" */ ++ /* FALLTHROUGH */ ++ ++ default: ++ ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, ++ status, req->actual, req->length); ++ /* FALLTHROUGH */ ++ ++ /* NOTE: since this driver doesn't maintain an explicit record ++ * of requests it submitted (just maintains qlen count), we ++ * rely on the hardware driver to clean up on disconnect or ++ * endpoint disable. ++ */ ++ case -ECONNABORTED: /* hardware forced ep reset */ ++ case -ECONNRESET: /* request dequeued */ ++ case -ESHUTDOWN: /* disconnect from host */ ++ free_ep_req(ep, req); ++ return; ++ } ++} ++ ++static void disable_loopback(struct f_loopback *loop) ++{ ++ struct usb_composite_dev *cdev; ++ ++ cdev = loop->function.config->cdev; ++ disable_endpoints(cdev, loop->in_ep, loop->out_ep); ++ VDBG(cdev, "%s disabled\n", loop->function.name); ++} ++ ++static int ++enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) ++{ ++ int result = 0; ++ const struct usb_endpoint_descriptor *src, *sink; ++ struct usb_ep *ep; ++ struct usb_request *req; ++ unsigned i; ++ ++ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); ++ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); ++ ++ /* one endpoint writes data back IN to the host */ ++ ep = loop->in_ep; ++ result = usb_ep_enable(ep, src); ++ if (result < 0) ++ return result; ++ ep->driver_data = loop; ++ ++ /* one endpoint just reads OUT packets */ ++ ep = loop->out_ep; ++ result = usb_ep_enable(ep, sink); ++ if (result < 0) { ++fail0: ++ ep = loop->in_ep; ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ return result; ++ } ++ ep->driver_data = loop; ++ ++ /* allocate a bunch of read buffers and queue them all at once. ++ * we buffer at most 'qlen' transfers; fewer if any need more ++ * than 'buflen' bytes each. ++ */ ++ for (i = 0; i < qlen && result == 0; i++) { ++ req = alloc_ep_req(ep); ++ if (req) { ++ req->complete = loopback_complete; ++ result = usb_ep_queue(ep, req, GFP_ATOMIC); ++ if (result) ++ ERROR(cdev, "%s queue req --> %d\n", ++ ep->name, result); ++ } else { ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ result = -ENOMEM; ++ goto fail0; ++ } ++ } ++ ++ DBG(cdev, "%s enabled\n", loop->function.name); ++ return result; ++} ++ ++static int loopback_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct f_loopback *loop = func_to_loop(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt is zero */ ++ if (loop->in_ep->driver_data) ++ disable_loopback(loop); ++ return enable_loopback(cdev, loop); ++} ++ ++static void loopback_disable(struct usb_function *f) ++{ ++ struct f_loopback *loop = func_to_loop(f); ++ ++ disable_loopback(loop); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init loopback_bind_config(struct usb_configuration *c) ++{ ++ struct f_loopback *loop; ++ int status; ++ ++ loop = kzalloc(sizeof *loop, GFP_KERNEL); ++ if (!loop) ++ return -ENOMEM; ++ ++ loop->function.name = "loopback"; ++ loop->function.descriptors = fs_loopback_descs; ++ loop->function.bind = loopback_bind; ++ loop->function.unbind = loopback_unbind; ++ loop->function.set_alt = loopback_set_alt; ++ loop->function.disable = loopback_disable; ++ ++ status = usb_add_function(c, &loop->function); ++ if (status) ++ kfree(loop); ++ return status; ++} ++ ++static struct usb_configuration loopback_driver = { ++ .label = "loopback", ++ .strings = loopback_strings, ++ .bind = loopback_bind_config, ++ .bConfigurationValue = 2, ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++ /* .iConfiguration = DYNAMIC */ ++}; ++ ++/** ++ * loopback_add - add a loopback testing configuration to a device ++ * @cdev: the device to support the loopback configuration ++ */ ++int __init loopback_add(struct usb_composite_dev *cdev) ++{ ++ int id; ++ ++ /* allocate string ID(s) */ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_loopback[0].id = id; ++ ++ loopback_intf.iInterface = id; ++ loopback_driver.iConfiguration = id; ++ ++ /* support OTG systems */ ++ if (gadget_is_otg(cdev->gadget)) { ++ loopback_driver.descriptors = otg_desc; ++ loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ return usb_add_config(cdev, &loopback_driver); ++} +diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c +new file mode 100644 +index 0000000..659b3d9 +--- /dev/null ++++ b/drivers/usb/gadget/f_rndis.c +@@ -0,0 +1,825 @@ ++/* ++ * f_rndis.c -- RNDIS link function driver ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/etherdevice.h> ++ ++#include <asm/atomic.h> ++ ++#include "u_ether.h" ++#include "rndis.h" ++ ++ ++/* ++ * This function is an RNDIS Ethernet port -- a Microsoft protocol that's ++ * been promoted instead of the standard CDC Ethernet. The published RNDIS ++ * spec is ambiguous, incomplete, and needlessly complex. Variants such as ++ * ActiveSync have even worse status in terms of specification. ++ * ++ * In short: it's a protocol controlled by (and for) Microsoft, not for an ++ * Open ecosystem or markets. Linux supports it *only* because Microsoft ++ * doesn't support the CDC Ethernet standard. ++ * ++ * The RNDIS data transfer model is complex, with multiple Ethernet packets ++ * per USB message, and out of band data. The control model is built around ++ * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM ++ * (modem, not Ethernet) veneer, with those ACM descriptors being entirely ++ * useless (they're ignored). RNDIS expects to be the only function in its ++ * configuration, so it's no real help if you need composite devices; and ++ * it expects to be the first configuration too. ++ * ++ * There is a single technical advantage of RNDIS over CDC Ethernet, if you ++ * discount the fluff that its RPC can be made to deliver: it doesn't need ++ * a NOP altsetting for the data interface. That lets it work on some of the ++ * "so smart it's stupid" hardware which takes over configuration changes ++ * from the software, and adds restrictions like "no altsettings". ++ * ++ * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and ++ * have all sorts of contrary-to-specification oddities that can prevent ++ * them from working sanely. Since bugfixes (or accurate specs, letting ++ * Linux work around those bugs) are unlikely to ever come from MSFT, you ++ * may want to avoid using RNDIS on purely operational grounds. ++ * ++ * Omissions from the RNDIS 1.0 specification include: ++ * ++ * - Power management ... references data that's scattered around lots ++ * of other documentation, which is incorrect/incomplete there too. ++ * ++ * - There are various undocumented protocol requirements, like the need ++ * to send garbage in some control-OUT messages. ++ * ++ * - MS-Windows drivers sometimes emit undocumented requests. ++ */ ++ ++struct rndis_ep_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ struct usb_endpoint_descriptor *notify; ++}; ++ ++struct f_rndis { ++ struct gether port; ++ u8 ctrl_id, data_id; ++ u8 ethaddr[ETH_ALEN]; ++ int config; ++ ++ struct rndis_ep_descs fs; ++ struct rndis_ep_descs hs; ++ ++ struct usb_ep *notify; ++ struct usb_endpoint_descriptor *notify_desc; ++ struct usb_request *notify_req; ++ atomic_t notify_count; ++}; ++ ++static inline struct f_rndis *func_to_rndis(struct usb_function *f) ++{ ++ return container_of(f, struct f_rndis, port.func); ++} ++ ++/* peak (theoretical) bulk transfer rate in bits-per-second */ ++static unsigned int bitrate(struct usb_gadget *g) ++{ ++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) ++ return 13 * 512 * 8 * 1000 * 8; ++ else ++ return 19 * 64 * 1 * 1000 * 8; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ */ ++ ++#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ ++#define STATUS_BYTECOUNT 8 /* 8 bytes data */ ++ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor rndis_control_intf __initdata = { ++ .bLength = sizeof rndis_control_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ /* status endpoint is optional; this could be patched later */ ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, ++ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc header_desc __initdata = { ++ .bLength = sizeof header_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { ++ .bLength = sizeof call_mgmt_descriptor, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, ++ ++ .bmCapabilities = 0x00, ++ .bDataInterface = 0x01, ++}; ++ ++static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { ++ .bLength = sizeof acm_descriptor, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ACM_TYPE, ++ ++ .bmCapabilities = 0x00, ++}; ++ ++static struct usb_cdc_union_desc rndis_union_desc __initdata = { ++ .bLength = sizeof(rndis_union_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_UNION_TYPE, ++ /* .bMasterInterface0 = DYNAMIC */ ++ /* .bSlaveInterface0 = DYNAMIC */ ++}; ++ ++/* the data interface has two bulk endpoints */ ++ ++static struct usb_interface_descriptor rndis_data_intf __initdata = { ++ .bLength = sizeof rndis_data_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, ++}; ++ ++static struct usb_endpoint_descriptor fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *eth_fs_function[] __initdata = { ++ /* control interface matches ACM, not Ethernet */ ++ (struct usb_descriptor_header *) &rndis_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &rndis_union_desc, ++ (struct usb_descriptor_header *) &fs_notify_desc, ++ /* data interface has no altsetting */ ++ (struct usb_descriptor_header *) &rndis_data_intf, ++ (struct usb_descriptor_header *) &fs_in_desc, ++ (struct usb_descriptor_header *) &fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, ++}; ++static struct usb_endpoint_descriptor hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *eth_hs_function[] __initdata = { ++ /* control interface matches ACM, not Ethernet */ ++ (struct usb_descriptor_header *) &rndis_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &rndis_union_desc, ++ (struct usb_descriptor_header *) &hs_notify_desc, ++ /* data interface has no altsetting */ ++ (struct usb_descriptor_header *) &rndis_data_intf, ++ (struct usb_descriptor_header *) &hs_in_desc, ++ (struct usb_descriptor_header *) &hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string rndis_string_defs[] = { ++ [0].s = "RNDIS Communications Control", ++ [1].s = "RNDIS Ethernet Data", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings rndis_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = rndis_string_defs, ++}; ++ ++static struct usb_gadget_strings *rndis_strings[] = { ++ &rndis_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct sk_buff *rndis_add_header(struct sk_buff *skb) ++{ ++ skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); ++ if (skb) ++ rndis_add_hdr(skb); ++ return skb; ++} ++ ++static void rndis_response_available(void *_rndis) ++{ ++ struct f_rndis *rndis = _rndis; ++ struct usb_request *req = rndis->notify_req; ++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev; ++ __le32 *data = req->buf; ++ int status; ++ ++ if (atomic_inc_return(&rndis->notify_count)) ++ return; ++ ++ /* Send RNDIS RESPONSE_AVAILABLE notification; a ++ * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too ++ * ++ * This is the only notification defined by RNDIS. ++ */ ++ data[0] = cpu_to_le32(1); ++ data[1] = cpu_to_le32(0); ++ ++ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); ++ if (status) { ++ atomic_dec(&rndis->notify_count); ++ DBG(cdev, "notify/0 --> %d\n", status); ++ } ++} ++ ++static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_rndis *rndis = req->context; ++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev; ++ int status = req->status; ++ ++ /* after TX: ++ * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) ++ * - RNDIS_RESPONSE_AVAILABLE (status/irq) ++ */ ++ switch (status) { ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ /* connection gone */ ++ atomic_set(&rndis->notify_count, 0); ++ break; ++ default: ++ DBG(cdev, "RNDIS %s response error %d, %d/%d\n", ++ ep->name, status, ++ req->actual, req->length); ++ /* FALLTHROUGH */ ++ case 0: ++ if (ep != rndis->notify) ++ break; ++ ++ /* handle multiple pending RNDIS_RESPONSE_AVAILABLE ++ * notifications by resending until we're done ++ */ ++ if (atomic_dec_and_test(&rndis->notify_count)) ++ break; ++ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); ++ if (status) { ++ atomic_dec(&rndis->notify_count); ++ DBG(cdev, "notify/1 --> %d\n", status); ++ } ++ break; ++ } ++} ++ ++static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_rndis *rndis = req->context; ++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev; ++ int status; ++ ++ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ ++// spin_lock(&dev->lock); ++ status = rndis_msg_parser(rndis->config, (u8 *) req->buf); ++ if (status < 0) ++ ERROR(cdev, "RNDIS command error %d, %d/%d\n", ++ status, req->actual, req->length); ++// spin_unlock(&dev->lock); ++} ++ ++static int ++rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything except ++ * CDC class messages; interface activation uses set_alt(). ++ */ ++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { ++ ++ /* RNDIS uses the CDC command encapsulation mechanism to implement ++ * an RPC scheme, with much getting/setting of attributes by OID. ++ */ ++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_SEND_ENCAPSULATED_COMMAND: ++ if (w_length > req->length || w_value ++ || w_index != rndis->ctrl_id) ++ goto invalid; ++ /* read the request; process it later */ ++ value = w_length; ++ req->complete = rndis_command_complete; ++ req->context = rndis; ++ /* later, rndis_response_available() sends a notification */ ++ break; ++ ++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_GET_ENCAPSULATED_RESPONSE: ++ if (w_value || w_index != rndis->ctrl_id) ++ goto invalid; ++ else { ++ u8 *buf; ++ u32 n; ++ ++ /* return the result */ ++ buf = rndis_get_next_response(rndis->config, &n); ++ if (buf) { ++ memcpy(req->buf, buf, n); ++ req->complete = rndis_response_complete; ++ rndis_free_response(rndis->config, buf); ++ value = n; ++ } ++ /* else stalls ... spec says to avoid that */ ++ } ++ break; ++ ++ default: ++invalid: ++ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "rndis response on err %d\n", value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++ ++static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt == 0 */ ++ ++ if (intf == rndis->ctrl_id) { ++ if (rndis->notify->driver_data) { ++ VDBG(cdev, "reset rndis control %d\n", intf); ++ usb_ep_disable(rndis->notify); ++ } else { ++ VDBG(cdev, "init rndis ctrl %d\n", intf); ++ rndis->notify_desc = ep_choose(cdev->gadget, ++ rndis->hs.notify, ++ rndis->fs.notify); ++ } ++ usb_ep_enable(rndis->notify, rndis->notify_desc); ++ rndis->notify->driver_data = rndis; ++ ++ } else if (intf == rndis->data_id) { ++ struct net_device *net; ++ ++ if (rndis->port.in_ep->driver_data) { ++ DBG(cdev, "reset rndis\n"); ++ gether_disconnect(&rndis->port); ++ } else { ++ DBG(cdev, "init rndis\n"); ++ rndis->port.in = ep_choose(cdev->gadget, ++ rndis->hs.in, rndis->fs.in); ++ rndis->port.out = ep_choose(cdev->gadget, ++ rndis->hs.out, rndis->fs.out); ++ } ++ ++ /* Avoid ZLPs; they can be troublesome. */ ++ rndis->port.is_zlp_ok = false; ++ ++ /* RNDIS should be in the "RNDIS uninitialized" state, ++ * either never activated or after rndis_uninit(). ++ * ++ * We don't want data to flow here until a nonzero packet ++ * filter is set, at which point it enters "RNDIS data ++ * initialized" state ... but we do want the endpoints ++ * to be activated. It's a strange little state. ++ * ++ * REVISIT the RNDIS gadget code has done this wrong for a ++ * very long time. We need another call to the link layer ++ * code -- gether_updown(...bool) maybe -- to do it right. ++ */ ++ rndis->port.cdc_filter = 0; ++ ++ DBG(cdev, "RNDIS RX/TX early activation ... \n"); ++ net = gether_connect(&rndis->port); ++ if (IS_ERR(net)) ++ return PTR_ERR(net); ++ ++ rndis_set_param_dev(rndis->config, net, ++ &rndis->port.cdc_filter); ++ } else ++ goto fail; ++ ++ return 0; ++fail: ++ return -EINVAL; ++} ++ ++static void rndis_disable(struct usb_function *f) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ if (!rndis->notify->driver_data) ++ return; ++ ++ DBG(cdev, "rndis deactivated\n"); ++ ++ rndis_uninit(rndis->config); ++ gether_disconnect(&rndis->port); ++ ++ usb_ep_disable(rndis->notify); ++ rndis->notify->driver_data = NULL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * This isn't quite the same mechanism as CDC Ethernet, since the ++ * notification scheme passes less data, but the same set of link ++ * states must be tested. A key difference is that altsettings are ++ * not used to tell whether the link should send packets or not. ++ */ ++ ++static void rndis_open(struct gether *geth) ++{ ++ struct f_rndis *rndis = func_to_rndis(&geth->func); ++ struct usb_composite_dev *cdev = geth->func.config->cdev; ++ ++ DBG(cdev, "%s\n", __func__); ++ ++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, ++ bitrate(cdev->gadget) / 100); ++ rndis_signal_connect(rndis->config); ++} ++ ++static void rndis_close(struct gether *geth) ++{ ++ struct f_rndis *rndis = func_to_rndis(&geth->func); ++ ++ DBG(geth->func.config->cdev, "%s\n", __func__); ++ ++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); ++ rndis_signal_disconnect(rndis->config); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ethernet function driver setup/binding */ ++ ++static int __init ++rndis_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_rndis *rndis = func_to_rndis(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ rndis->ctrl_id = status; ++ ++ rndis_control_intf.bInterfaceNumber = status; ++ rndis_union_desc.bMasterInterface0 = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ rndis->data_id = status; ++ ++ rndis_data_intf.bInterfaceNumber = status; ++ rndis_union_desc.bSlaveInterface0 = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); ++ if (!ep) ++ goto fail; ++ rndis->port.in_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); ++ if (!ep) ++ goto fail; ++ rndis->port.out_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* NOTE: a status/notification endpoint is, strictly speaking, ++ * optional. We don't treat it that way though! It's simpler, ++ * and some newer profiles don't treat it as optional. ++ */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); ++ if (!ep) ++ goto fail; ++ rndis->notify = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ status = -ENOMEM; ++ ++ /* allocate notification request and buffer */ ++ rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!rndis->notify_req) ++ goto fail; ++ rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); ++ if (!rndis->notify_req->buf) ++ goto fail; ++ rndis->notify_req->length = STATUS_BYTECOUNT; ++ rndis->notify_req->context = rndis; ++ rndis->notify_req->complete = rndis_response_complete; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(eth_fs_function); ++ if (!f->descriptors) ++ goto fail; ++ ++ rndis->fs.in = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_in_desc); ++ rndis->fs.out = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_out_desc); ++ rndis->fs.notify = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_notify_desc); ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_in_desc.bEndpointAddress = ++ fs_in_desc.bEndpointAddress; ++ hs_out_desc.bEndpointAddress = ++ fs_out_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(eth_hs_function); ++ ++ if (!f->hs_descriptors) ++ goto fail; ++ ++ rndis->hs.in = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_in_desc); ++ rndis->hs.out = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_out_desc); ++ } ++ ++ rndis->port.open = rndis_open; ++ rndis->port.close = rndis_close; ++ ++ status = rndis_register(rndis_response_available, rndis); ++ if (status < 0) ++ goto fail; ++ rndis->config = status; ++ ++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); ++ rndis_set_host_mac(rndis->config, rndis->ethaddr); ++ ++#if 0 ++// FIXME ++ if (rndis_set_param_vendor(rndis->config, vendorID, ++ manufacturer)) ++ goto fail0; ++#endif ++ ++ /* NOTE: all that is done without knowing or caring about ++ * the network link ... which is unavailable to this code ++ * until we're activated via set_alt(). ++ */ ++ ++ DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ rndis->port.in_ep->name, rndis->port.out_ep->name, ++ rndis->notify->name); ++ return 0; ++ ++fail: ++ if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) ++ usb_free_descriptors(f->hs_descriptors); ++ if (f->descriptors) ++ usb_free_descriptors(f->descriptors); ++ ++ if (rndis->notify_req) { ++ kfree(rndis->notify_req->buf); ++ usb_ep_free_request(rndis->notify, rndis->notify_req); ++ } ++ ++ /* we might as well release our claims on endpoints */ ++ if (rndis->notify) ++ rndis->notify->driver_data = NULL; ++ if (rndis->port.out) ++ rndis->port.out_ep->driver_data = NULL; ++ if (rndis->port.in) ++ rndis->port.in_ep->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++rndis_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ ++ rndis_deregister(rndis->config); ++ rndis_exit(); ++ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ ++ kfree(rndis->notify_req->buf); ++ usb_ep_free_request(rndis->notify, rndis->notify_req); ++ ++ kfree(rndis); ++} ++ ++/* Some controllers can't support RNDIS ... */ ++static inline bool can_support_rndis(struct usb_configuration *c) ++{ ++ /* only two endpoints on sa1100 */ ++ if (gadget_is_sa1100(c->cdev->gadget)) ++ return false; ++ ++ /* everything else is *presumably* fine */ ++ return true; ++} ++ ++/** ++ * rndis_bind_config - add RNDIS network link to a configuration ++ * @c: the configuration to support the network link ++ * @ethaddr: a buffer in which the ethernet address of the host side ++ * side of the link was recorded ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gether_setup(). Caller is also responsible ++ * for calling @gether_cleanup() before module unload. ++ */ ++int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ struct f_rndis *rndis; ++ int status; ++ ++ if (!can_support_rndis(c) || !ethaddr) ++ return -EINVAL; ++ ++ /* maybe allocate device-global string IDs */ ++ if (rndis_string_defs[0].id == 0) { ++ ++ /* ... and setup RNDIS itself */ ++ status = rndis_init(); ++ if (status < 0) ++ return status; ++ ++ /* control interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ rndis_string_defs[0].id = status; ++ rndis_control_intf.iInterface = status; ++ ++ /* data interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ rndis_string_defs[1].id = status; ++ rndis_data_intf.iInterface = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ status = -ENOMEM; ++ rndis = kzalloc(sizeof *rndis, GFP_KERNEL); ++ if (!rndis) ++ goto fail; ++ ++ memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); ++ ++ /* RNDIS activates when the host changes this filter */ ++ rndis->port.cdc_filter = 0; ++ ++ /* RNDIS has special (and complex) framing */ ++ rndis->port.header_len = sizeof(struct rndis_packet_msg_type); ++ rndis->port.wrap = rndis_add_header; ++ rndis->port.unwrap = rndis_rm_hdr; ++ ++ rndis->port.func.name = "rndis"; ++ rndis->port.func.strings = rndis_strings; ++ /* descriptors are per-instance copies */ ++ rndis->port.func.bind = rndis_bind; ++ rndis->port.func.unbind = rndis_unbind; ++ rndis->port.func.set_alt = rndis_set_alt; ++ rndis->port.func.setup = rndis_setup; ++ rndis->port.func.disable = rndis_disable; ++ ++ status = usb_add_function(c, &rndis->port.func); ++ if (status) { ++ kfree(rndis); ++fail: ++ rndis_exit(); ++ } ++ return status; ++} +diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c +new file mode 100644 +index 0000000..fe5674d +--- /dev/null ++++ b/drivers/usb/gadget/f_serial.c +@@ -0,0 +1,294 @@ ++/* ++ * f_serial.c - generic USB serial function driver ++ * ++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) ++ * Copyright (C) 2008 by David Brownell ++ * Copyright (C) 2008 by Nokia Corporation ++ * ++ * This software is distributed under the terms of the GNU General ++ * Public License ("GPL") as published by the Free Software Foundation, ++ * either version 2 of that License or (at your option) any later version. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++ ++#include "u_serial.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * This function packages a simple "generic serial" port with no real ++ * control mechanisms, just raw data transfer over two bulk endpoints. ++ * ++ * Because it's not standardized, this isn't as interoperable as the ++ * CDC ACM driver. However, for many purposes it's just as functional ++ * if you can arrange appropriate host side drivers. ++ */ ++ ++struct gser_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++}; ++ ++struct f_gser { ++ struct gserial port; ++ u8 data_id; ++ u8 port_num; ++ ++ struct gser_descs fs; ++ struct gser_descs hs; ++}; ++ ++static inline struct f_gser *func_to_gser(struct usb_function *f) ++{ ++ return container_of(f, struct f_gser, port.func); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor gser_interface_desc __initdata = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *gser_fs_function[] __initdata = { ++ (struct usb_descriptor_header *) &gser_interface_desc, ++ (struct usb_descriptor_header *) &gser_fs_in_desc, ++ (struct usb_descriptor_header *) &gser_fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *gser_hs_function[] __initdata = { ++ (struct usb_descriptor_header *) &gser_interface_desc, ++ (struct usb_descriptor_header *) &gser_hs_in_desc, ++ (struct usb_descriptor_header *) &gser_hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string gser_string_defs[] = { ++ [0].s = "Generic Serial", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings gser_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = gser_string_defs, ++}; ++ ++static struct usb_gadget_strings *gser_strings[] = { ++ &gser_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_gser *gser = func_to_gser(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt == 0, so this is an activation or a reset */ ++ ++ if (gser->port.in->driver_data) { ++ DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); ++ gserial_disconnect(&gser->port); ++ } else { ++ DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); ++ gser->port.in_desc = ep_choose(cdev->gadget, ++ gser->hs.in, gser->fs.in); ++ gser->port.out_desc = ep_choose(cdev->gadget, ++ gser->hs.out, gser->fs.out); ++ } ++ gserial_connect(&gser->port, gser->port_num); ++ return 0; ++} ++ ++static void gser_disable(struct usb_function *f) ++{ ++ struct f_gser *gser = func_to_gser(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); ++ gserial_disconnect(&gser->port); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* serial function driver setup/binding */ ++ ++static int __init ++gser_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_gser *gser = func_to_gser(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ gser->data_id = status; ++ gser_interface_desc.bInterfaceNumber = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); ++ if (!ep) ++ goto fail; ++ gser->port.in = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); ++ if (!ep) ++ goto fail; ++ gser->port.out = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(gser_fs_function); ++ ++ gser->fs.in = usb_find_endpoint(gser_fs_function, ++ f->descriptors, &gser_fs_in_desc); ++ gser->fs.out = usb_find_endpoint(gser_fs_function, ++ f->descriptors, &gser_fs_out_desc); ++ ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ gser_hs_in_desc.bEndpointAddress = ++ gser_fs_in_desc.bEndpointAddress; ++ gser_hs_out_desc.bEndpointAddress = ++ gser_fs_out_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(gser_hs_function); ++ ++ gser->hs.in = usb_find_endpoint(gser_hs_function, ++ f->hs_descriptors, &gser_hs_in_desc); ++ gser->hs.out = usb_find_endpoint(gser_hs_function, ++ f->hs_descriptors, &gser_hs_out_desc); ++ } ++ ++ DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", ++ gser->port_num, ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ gser->port.in->name, gser->port.out->name); ++ return 0; ++ ++fail: ++ /* we might as well release our claims on endpoints */ ++ if (gser->port.out) ++ gser->port.out->driver_data = NULL; ++ if (gser->port.in) ++ gser->port.in->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++gser_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ kfree(func_to_gser(f)); ++} ++ ++/** ++ * gser_bind_config - add a generic serial function to a configuration ++ * @c: the configuration to support the serial instance ++ * @port_num: /dev/ttyGS* port this interface will use ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gserial_setup() with enough ports to ++ * handle all the ones it binds. Caller is also responsible ++ * for calling @gserial_cleanup() before module unload. ++ */ ++int __init gser_bind_config(struct usb_configuration *c, u8 port_num) ++{ ++ struct f_gser *gser; ++ int status; ++ ++ /* REVISIT might want instance-specific strings to help ++ * distinguish instances ... ++ */ ++ ++ /* maybe allocate device-global string ID */ ++ if (gser_string_defs[0].id == 0) { ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ gser_string_defs[0].id = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ gser = kzalloc(sizeof *gser, GFP_KERNEL); ++ if (!gser) ++ return -ENOMEM; ++ ++ gser->port_num = port_num; ++ ++ gser->port.func.name = "gser"; ++ gser->port.func.strings = gser_strings; ++ gser->port.func.bind = gser_bind; ++ gser->port.func.unbind = gser_unbind; ++ gser->port.func.set_alt = gser_set_alt; ++ gser->port.func.disable = gser_disable; ++ ++ status = usb_add_function(c, &gser->port.func); ++ if (status) ++ kfree(gser); ++ return status; ++} +diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c +new file mode 100644 +index 0000000..f18c3a1 +--- /dev/null ++++ b/drivers/usb/gadget/f_sourcesink.c +@@ -0,0 +1,587 @@ ++/* ++ * f_sourcesink.c - USB peripheral source/sink configuration driver ++ * ++ * Copyright (C) 2003-2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/device.h> ++ ++#include "g_zero.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral ++ * controller drivers. ++ * ++ * This just sinks bulk packets OUT to the peripheral and sources them IN ++ * to the host, optionally with specific data patterns for integrity tests. ++ * As such it supports basic functionality and load tests. ++ * ++ * In terms of control messaging, this supports all the standard requests ++ * plus two that support control-OUT tests. If the optional "autoresume" ++ * mode is enabled, it provides good functional coverage for the "USBCV" ++ * test harness from USB-IF. ++ * ++ * Note that because this doesn't queue more than one request at a time, ++ * some other function must be used to test queueing logic. The network ++ * link (g_ether) is the best overall option for that, since its TX and RX ++ * queues are relatively independent, will receive a range of packet sizes, ++ * and can often be made to run out completely. Those issues are important ++ * when stress testing peripheral controller drivers. ++ * ++ * ++ * This is currently packaged as a configuration driver, which can't be ++ * combined with other functions to make composite devices. However, it ++ * can be combined with other independent configurations. ++ */ ++struct f_sourcesink { ++ struct usb_function function; ++ ++ struct usb_ep *in_ep; ++ struct usb_ep *out_ep; ++ struct timer_list resume; ++}; ++ ++static inline struct f_sourcesink *func_to_ss(struct usb_function *f) ++{ ++ return container_of(f, struct f_sourcesink, function); ++} ++ ++static unsigned autoresume; ++module_param(autoresume, uint, 0); ++MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); ++ ++static unsigned pattern; ++module_param(pattern, uint, 0); ++MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 "); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_interface_descriptor source_sink_intf = { ++ .bLength = sizeof source_sink_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_source_sink_descs[] = { ++ (struct usb_descriptor_header *) &source_sink_intf, ++ (struct usb_descriptor_header *) &fs_sink_desc, ++ (struct usb_descriptor_header *) &fs_source_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *hs_source_sink_descs[] = { ++ (struct usb_descriptor_header *) &source_sink_intf, ++ (struct usb_descriptor_header *) &hs_source_desc, ++ (struct usb_descriptor_header *) &hs_sink_desc, ++ NULL, ++}; ++ ++/* function-specific strings: */ ++ ++static struct usb_string strings_sourcesink[] = { ++ [0].s = "source and sink data", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_sourcesink = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_sourcesink, ++}; ++ ++static struct usb_gadget_strings *sourcesink_strings[] = { ++ &stringtab_sourcesink, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void sourcesink_autoresume(unsigned long _c) ++{ ++ struct usb_composite_dev *cdev = (void *)_c; ++ struct usb_gadget *g = cdev->gadget; ++ ++ /* Normally the host would be woken up for something ++ * more significant than just a timer firing; likely ++ * because of some direct user request. ++ */ ++ if (g->speed != USB_SPEED_UNKNOWN) { ++ int status = usb_gadget_wakeup(g); ++ DBG(cdev, "%s --> %d\n", __func__, status); ++ } ++} ++ ++static int __init ++sourcesink_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_sourcesink *ss = func_to_ss(f); ++ int id; ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ source_sink_intf.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); ++ if (!ss->in_ep) { ++autoconf_fail: ++ ERROR(cdev, "%s: can't autoconfigure on %s\n", ++ f->name, cdev->gadget->name); ++ return -ENODEV; ++ } ++ ss->in_ep->driver_data = cdev; /* claim */ ++ ++ ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); ++ if (!ss->out_ep) ++ goto autoconf_fail; ++ ss->out_ep->driver_data = cdev; /* claim */ ++ ++ setup_timer(&ss->resume, sourcesink_autoresume, ++ (unsigned long) c->cdev); ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_source_desc.bEndpointAddress = ++ fs_source_desc.bEndpointAddress; ++ hs_sink_desc.bEndpointAddress = ++ fs_sink_desc.bEndpointAddress; ++ f->hs_descriptors = hs_source_sink_descs; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, ss->in_ep->name, ss->out_ep->name); ++ return 0; ++} ++ ++static void ++sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ kfree(func_to_ss(f)); ++} ++ ++/* optionally require specific source/sink data patterns */ ++static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) ++{ ++ unsigned i; ++ u8 *buf = req->buf; ++ struct usb_composite_dev *cdev = ss->function.config->cdev; ++ ++ for (i = 0; i < req->actual; i++, buf++) { ++ switch (pattern) { ++ ++ /* all-zeroes has no synchronization issues */ ++ case 0: ++ if (*buf == 0) ++ continue; ++ break; ++ ++ /* "mod63" stays in sync with short-terminated transfers, ++ * OR otherwise when host and gadget agree on how large ++ * each usb transfer request should be. Resync is done ++ * with set_interface or set_config. (We *WANT* it to ++ * get quickly out of sync if controllers or their drivers ++ * stutter for any reason, including buffer duplcation...) ++ */ ++ case 1: ++ if (*buf == (u8)(i % 63)) ++ continue; ++ break; ++ } ++ ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf); ++ usb_ep_set_halt(ss->out_ep); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) ++{ ++ unsigned i; ++ u8 *buf = req->buf; ++ ++ switch (pattern) { ++ case 0: ++ memset(req->buf, 0, req->length); ++ break; ++ case 1: ++ for (i = 0; i < req->length; i++) ++ *buf++ = (u8) (i % 63); ++ break; ++ } ++} ++ ++static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_sourcesink *ss = ep->driver_data; ++ struct usb_composite_dev *cdev = ss->function.config->cdev; ++ int status = req->status; ++ ++ switch (status) { ++ ++ case 0: /* normal completion? */ ++ if (ep == ss->out_ep) { ++ check_read_data(ss, req); ++ memset(req->buf, 0x55, req->length); ++ } else ++ reinit_write_data(ep, req); ++ break; ++ ++ /* this endpoint is normally active while we're configured */ ++ case -ECONNABORTED: /* hardware forced ep reset */ ++ case -ECONNRESET: /* request dequeued */ ++ case -ESHUTDOWN: /* disconnect from host */ ++ VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, ++ req->actual, req->length); ++ if (ep == ss->out_ep) ++ check_read_data(ss, req); ++ free_ep_req(ep, req); ++ return; ++ ++ case -EOVERFLOW: /* buffer overrun on read means that ++ * we didn't provide a big enough ++ * buffer. ++ */ ++ default: ++#if 1 ++ DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, ++ status, req->actual, req->length); ++#endif ++ case -EREMOTEIO: /* short read */ ++ break; ++ } ++ ++ status = usb_ep_queue(ep, req, GFP_ATOMIC); ++ if (status) { ++ ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", ++ ep->name, req->length, status); ++ usb_ep_set_halt(ep); ++ /* FIXME recover later ... somehow */ ++ } ++} ++ ++static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in) ++{ ++ struct usb_ep *ep; ++ struct usb_request *req; ++ int status; ++ ++ ep = is_in ? ss->in_ep : ss->out_ep; ++ req = alloc_ep_req(ep); ++ if (!req) ++ return -ENOMEM; ++ ++ req->complete = source_sink_complete; ++ if (is_in) ++ reinit_write_data(ep, req); ++ else ++ memset(req->buf, 0x55, req->length); ++ ++ status = usb_ep_queue(ep, req, GFP_ATOMIC); ++ if (status) { ++ struct usb_composite_dev *cdev; ++ ++ cdev = ss->function.config->cdev; ++ ERROR(cdev, "start %s %s --> %d\n", ++ is_in ? "IN" : "OUT", ++ ep->name, status); ++ free_ep_req(ep, req); ++ } ++ ++ return status; ++} ++ ++static void disable_source_sink(struct f_sourcesink *ss) ++{ ++ struct usb_composite_dev *cdev; ++ ++ cdev = ss->function.config->cdev; ++ disable_endpoints(cdev, ss->in_ep, ss->out_ep); ++ del_timer(&ss->resume); ++ VDBG(cdev, "%s disabled\n", ss->function.name); ++} ++ ++static int ++enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss) ++{ ++ int result = 0; ++ const struct usb_endpoint_descriptor *src, *sink; ++ struct usb_ep *ep; ++ ++ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); ++ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); ++ ++ /* one endpoint writes (sources) zeroes IN (to the host) */ ++ ep = ss->in_ep; ++ result = usb_ep_enable(ep, src); ++ if (result < 0) ++ return result; ++ ep->driver_data = ss; ++ ++ result = source_sink_start_ep(ss, true); ++ if (result < 0) { ++fail: ++ ep = ss->in_ep; ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ return result; ++ } ++ ++ /* one endpoint reads (sinks) anything OUT (from the host) */ ++ ep = ss->out_ep; ++ result = usb_ep_enable(ep, sink); ++ if (result < 0) ++ goto fail; ++ ep->driver_data = ss; ++ ++ result = source_sink_start_ep(ss, false); ++ if (result < 0) { ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ goto fail; ++ } ++ ++ DBG(cdev, "%s enabled\n", ss->function.name); ++ return result; ++} ++ ++static int sourcesink_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt is zero */ ++ if (ss->in_ep->driver_data) ++ disable_source_sink(ss); ++ return enable_source_sink(cdev, ss); ++} ++ ++static void sourcesink_disable(struct usb_function *f) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ ++ disable_source_sink(ss); ++} ++ ++static void sourcesink_suspend(struct usb_function *f) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ if (cdev->gadget->speed == USB_SPEED_UNKNOWN) ++ return; ++ ++ if (autoresume) { ++ mod_timer(&ss->resume, jiffies + (HZ * autoresume)); ++ DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); ++ } else ++ DBG(cdev, "%s\n", __func__); ++} ++ ++static void sourcesink_resume(struct usb_function *f) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "%s\n", __func__); ++ del_timer(&ss->resume); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init sourcesink_bind_config(struct usb_configuration *c) ++{ ++ struct f_sourcesink *ss; ++ int status; ++ ++ ss = kzalloc(sizeof *ss, GFP_KERNEL); ++ if (!ss) ++ return -ENOMEM; ++ ++ ss->function.name = "source/sink"; ++ ss->function.descriptors = fs_source_sink_descs; ++ ss->function.bind = sourcesink_bind; ++ ss->function.unbind = sourcesink_unbind; ++ ss->function.set_alt = sourcesink_set_alt; ++ ss->function.disable = sourcesink_disable; ++ ss->function.suspend = sourcesink_suspend; ++ ss->function.resume = sourcesink_resume; ++ ++ status = usb_add_function(c, &ss->function); ++ if (status) ++ kfree(ss); ++ return status; ++} ++ ++static int sourcesink_setup(struct usb_configuration *c, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_request *req = c->cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything except ++ * the two control test requests. ++ */ ++ switch (ctrl->bRequest) { ++ ++ /* ++ * These are the same vendor-specific requests supported by ++ * Intel's USB 2.0 compliance test devices. We exceed that ++ * device spec by allowing multiple-packet requests. ++ * ++ * NOTE: the Control-OUT data stays in req->buf ... better ++ * would be copying it into a scratch buffer, so that other ++ * requests may safely intervene. ++ */ ++ case 0x5b: /* control WRITE test -- fill the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (w_value || w_index) ++ break; ++ /* just read that many bytes into the buffer */ ++ if (w_length > req->length) ++ break; ++ value = w_length; ++ break; ++ case 0x5c: /* control READ test -- return the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (w_value || w_index) ++ break; ++ /* expect those bytes are still in the buffer; send back */ ++ if (w_length > req->length) ++ break; ++ value = w_length; ++ break; ++ ++ default: ++unknown: ++ VDBG(c->cdev, ++ "unknown control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(c->cdev, "source/sinkc response, err %d\n", ++ value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static struct usb_configuration sourcesink_driver = { ++ .label = "source/sink", ++ .strings = sourcesink_strings, ++ .bind = sourcesink_bind_config, ++ .setup = sourcesink_setup, ++ .bConfigurationValue = 3, ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++ /* .iConfiguration = DYNAMIC */ ++}; ++ ++/** ++ * sourcesink_add - add a source/sink testing configuration to a device ++ * @cdev: the device to support the configuration ++ */ ++int __init sourcesink_add(struct usb_composite_dev *cdev) ++{ ++ int id; ++ ++ /* allocate string ID(s) */ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_sourcesink[0].id = id; ++ ++ source_sink_intf.iInterface = id; ++ sourcesink_driver.iConfiguration = id; ++ ++ /* support autoresume for remote wakeup testing */ ++ if (autoresume) ++ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ ++ /* support OTG systems */ ++ if (gadget_is_otg(cdev->gadget)) { ++ sourcesink_driver.descriptors = otg_desc; ++ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ return usb_add_config(cdev, &sourcesink_driver); ++} +diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c +new file mode 100644 +index 0000000..acb8d23 +--- /dev/null ++++ b/drivers/usb/gadget/f_subset.c +@@ -0,0 +1,421 @@ ++/* ++ * f_subset.c -- "CDC Subset" Ethernet link function driver ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/etherdevice.h> ++ ++#include "u_ether.h" ++ ++ ++/* ++ * This function packages a simple "CDC Subset" Ethernet port with no real ++ * control mechanisms; just raw data transfer over two bulk endpoints. ++ * The data transfer model is exactly that of CDC Ethernet, which is ++ * why we call it the "CDC Subset". ++ * ++ * Because it's not standardized, this has some interoperability issues. ++ * They mostly relate to driver binding, since the data transfer model is ++ * so simple (CDC Ethernet). The original versions of this protocol used ++ * specific product/vendor IDs: byteswapped IDs for Digital Equipment's ++ * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported ++ * daughtercards with USB peripheral connectors. (It was used more often ++ * with other boards, using the Itsy identifiers.) Linux hosts recognized ++ * this with CONFIG_USB_ARMLINUX; these devices have only one configuration ++ * and one interface. ++ * ++ * At some point, MCCI defined a (nonconformant) CDC MDLM variant called ++ * "SAFE", which happens to have a mode which is identical to the "CDC ++ * Subset" in terms of data transfer and lack of control model. This was ++ * adopted by later Sharp Zaurus models, and by some other software which ++ * Linux hosts recognize with CONFIG_USB_NET_ZAURUS. ++ * ++ * Because Microsoft's RNDIS drivers are far from robust, we added a few ++ * descriptors to the CDC Subset code, making this code look like a SAFE ++ * implementation. This lets you use MCCI's host side MS-Windows drivers ++ * if you get fed up with RNDIS. It also makes it easier for composite ++ * drivers to work, since they can use class based binding instead of ++ * caring about specific product and vendor IDs. ++ */ ++ ++struct geth_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++}; ++ ++struct f_gether { ++ struct gether port; ++ ++ char ethaddr[14]; ++ ++ struct geth_descs fs; ++ struct geth_descs hs; ++}; ++ ++static inline struct f_gether *func_to_geth(struct usb_function *f) ++{ ++ return container_of(f, struct f_gether, port.func); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * "Simple" CDC-subset option is a simple vendor-neutral model that most ++ * full speed controllers can handle: one interface, two bulk endpoints. ++ * To assist host side drivers, we fancy it up a bit, and add descriptors so ++ * some host side drivers will understand it as a "SAFE" variant. ++ * ++ * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways. ++ * Data endpoints live in the control interface, there's no data interface. ++ * And it's not used to talk to a cell phone radio. ++ */ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor subset_data_intf __initdata = { ++ .bLength = sizeof subset_data_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc header_desc __initdata = { ++ .bLength = sizeof header_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_mdlm_desc mdlm_desc __initdata = { ++ .bLength = sizeof mdlm_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_MDLM_TYPE, ++ ++ .bcdVersion = __constant_cpu_to_le16(0x0100), ++ .bGUID = { ++ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, ++ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, ++ }, ++}; ++ ++/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we ++ * can't really use its struct. All we do here is say that we're using ++ * the submode of "SAFE" which directly matches the CDC Subset. ++ */ ++static u8 mdlm_detail_desc[] __initdata = { ++ 6, ++ USB_DT_CS_INTERFACE, ++ USB_CDC_MDLM_DETAIL_TYPE, ++ ++ 0, /* "SAFE" */ ++ 0, /* network control capabilities (none) */ ++ 0, /* network data capabilities ("raw" encapsulation) */ ++}; ++ ++static struct usb_cdc_ether_desc ether_desc __initdata = { ++ .bLength = sizeof ether_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, ++ ++ /* this descriptor actually adds value, surprise! */ ++ /* .iMACAddress = DYNAMIC */ ++ .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ ++ .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), ++ .wNumberMCFilters = __constant_cpu_to_le16(0), ++ .bNumberPowerFilters = 0, ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_eth_function[] __initdata = { ++ (struct usb_descriptor_header *) &subset_data_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &mdlm_desc, ++ (struct usb_descriptor_header *) &mdlm_detail_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ (struct usb_descriptor_header *) &fs_in_desc, ++ (struct usb_descriptor_header *) &fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *hs_eth_function[] __initdata = { ++ (struct usb_descriptor_header *) &subset_data_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &mdlm_desc, ++ (struct usb_descriptor_header *) &mdlm_detail_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ (struct usb_descriptor_header *) &hs_in_desc, ++ (struct usb_descriptor_header *) &hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string geth_string_defs[] = { ++ [0].s = "CDC Ethernet Subset/SAFE", ++ [1].s = NULL /* DYNAMIC */, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings geth_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = geth_string_defs, ++}; ++ ++static struct usb_gadget_strings *geth_strings[] = { ++ &geth_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_gether *geth = func_to_geth(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct net_device *net; ++ ++ /* we know alt == 0, so this is an activation or a reset */ ++ ++ if (geth->port.in_ep->driver_data) { ++ DBG(cdev, "reset cdc subset\n"); ++ gether_disconnect(&geth->port); ++ } ++ ++ DBG(cdev, "init + activate cdc subset\n"); ++ geth->port.in = ep_choose(cdev->gadget, ++ geth->hs.in, geth->fs.in); ++ geth->port.out = ep_choose(cdev->gadget, ++ geth->hs.out, geth->fs.out); ++ ++ net = gether_connect(&geth->port); ++ return IS_ERR(net) ? PTR_ERR(net) : 0; ++} ++ ++static void geth_disable(struct usb_function *f) ++{ ++ struct f_gether *geth = func_to_geth(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "net deactivated\n"); ++ gether_disconnect(&geth->port); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* serial function driver setup/binding */ ++ ++static int __init ++geth_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_gether *geth = func_to_geth(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ subset_data_intf.bInterfaceNumber = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); ++ if (!ep) ++ goto fail; ++ geth->port.in_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); ++ if (!ep) ++ goto fail; ++ geth->port.out_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(fs_eth_function); ++ ++ geth->fs.in = usb_find_endpoint(fs_eth_function, ++ f->descriptors, &fs_in_desc); ++ geth->fs.out = usb_find_endpoint(fs_eth_function, ++ f->descriptors, &fs_out_desc); ++ ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_in_desc.bEndpointAddress = ++ fs_in_desc.bEndpointAddress; ++ hs_out_desc.bEndpointAddress = ++ fs_out_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(hs_eth_function); ++ ++ geth->hs.in = usb_find_endpoint(hs_eth_function, ++ f->hs_descriptors, &hs_in_desc); ++ geth->hs.out = usb_find_endpoint(hs_eth_function, ++ f->hs_descriptors, &hs_out_desc); ++ } ++ ++ /* NOTE: all that is done without knowing or caring about ++ * the network link ... which is unavailable to this code ++ * until we're activated via set_alt(). ++ */ ++ ++ DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ geth->port.in_ep->name, geth->port.out_ep->name); ++ return 0; ++ ++fail: ++ /* we might as well release our claims on endpoints */ ++ if (geth->port.out) ++ geth->port.out_ep->driver_data = NULL; ++ if (geth->port.in) ++ geth->port.in_ep->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++geth_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ geth_string_defs[1].s = NULL; ++ kfree(func_to_geth(f)); ++} ++ ++/** ++ * geth_bind_config - add CDC Subset network link to a configuration ++ * @c: the configuration to support the network link ++ * @ethaddr: a buffer in which the ethernet address of the host side ++ * side of the link was recorded ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gether_setup(). Caller is also responsible ++ * for calling @gether_cleanup() before module unload. ++ */ ++int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ struct f_gether *geth; ++ int status; ++ ++ if (!ethaddr) ++ return -EINVAL; ++ ++ /* maybe allocate device-global string IDs */ ++ if (geth_string_defs[0].id == 0) { ++ ++ /* interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ geth_string_defs[0].id = status; ++ subset_data_intf.iInterface = status; ++ ++ /* MAC address */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ geth_string_defs[1].id = status; ++ ether_desc.iMACAddress = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ geth = kzalloc(sizeof *geth, GFP_KERNEL); ++ if (!geth) ++ return -ENOMEM; ++ ++ /* export host's Ethernet address in CDC format */ ++ snprintf(geth->ethaddr, sizeof geth->ethaddr, ++ "%02X%02X%02X%02X%02X%02X", ++ ethaddr[0], ethaddr[1], ethaddr[2], ++ ethaddr[3], ethaddr[4], ethaddr[5]); ++ geth_string_defs[1].s = geth->ethaddr; ++ ++ geth->port.cdc_filter = DEFAULT_FILTER; ++ ++ geth->port.func.name = "cdc_subset"; ++ geth->port.func.strings = geth_strings; ++ geth->port.func.bind = geth_bind; ++ geth->port.func.unbind = geth_unbind; ++ geth->port.func.set_alt = geth_set_alt; ++ geth->port.func.disable = geth_disable; ++ ++ status = usb_add_function(c, &geth->port.func); ++ if (status) { ++ geth_string_defs[1].s = NULL; ++ kfree(geth); ++ } ++ return status; ++} +diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c +index 47bb9f0..ea2c31d 100644 +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -308,7 +308,7 @@ MODULE_LICENSE("Dual BSD/GPL"); + dev_vdbg(&(d)->gadget->dev , fmt , ## args) + #define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +-#define WARN(d, fmt, args...) \ ++#define WARNING(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) + #define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) +@@ -1091,7 +1091,7 @@ static int ep0_queue(struct fsg_dev *fsg) + if (rc != 0 && rc != -ESHUTDOWN) { + + /* We can't do much more than wait for a reset */ +- WARN(fsg, "error in submission: %s --> %d\n", ++ WARNING(fsg, "error in submission: %s --> %d\n", + fsg->ep0->name, rc); + } + return rc; +@@ -1227,7 +1227,7 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) + + /* Save the command for later */ + if (fsg->cbbuf_cmnd_size) +- WARN(fsg, "CB[I] overwriting previous command\n"); ++ WARNING(fsg, "CB[I] overwriting previous command\n"); + fsg->cbbuf_cmnd_size = req->actual; + memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); + +@@ -1506,7 +1506,7 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + * submissions if DMA is enabled. */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && + req->length == 0)) +- WARN(fsg, "error in submission: %s --> %d\n", ++ WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } + } +@@ -2294,7 +2294,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { +- WARN(fsg, "usb_ep_set_halt -> %d\n", rc); ++ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } +@@ -2317,7 +2317,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { +- WARN(fsg, "usb_ep_set_wedge -> %d\n", rc); ++ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } +@@ -3755,7 +3755,7 @@ static int __init check_parameters(struct fsg_dev *fsg) + if (gcnum >= 0) + mod_data.release = 0x0300 + gcnum; + else { +- WARN(fsg, "controller '%s' not recognized\n", ++ WARNING(fsg, "controller '%s' not recognized\n", + fsg->gadget->name); + mod_data.release = 0x0399; + } +@@ -3867,8 +3867,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) + curlun->dev.parent = &gadget->dev; + curlun->dev.driver = &fsg_driver.driver; + dev_set_drvdata(&curlun->dev, fsg); +- snprintf(curlun->dev.bus_id, BUS_ID_SIZE, +- "%s-lun%d", gadget->dev.bus_id, i); ++ dev_set_name(&curlun->dev,"%s-lun%d", ++ dev_name(&gadget->dev), i); + + if ((rc = device_register(&curlun->dev)) != 0) { + INFO(fsg, "failed to register LUN%d: %d\n", i, rc); +diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c +index 1868754..45ad556 100644 +--- a/drivers/usb/gadget/fsl_usb2_udc.c ++++ b/drivers/usb/gadget/fsl_usb2_udc.c +@@ -223,7 +223,7 @@ static int dr_controller_setup(struct fsl_udc *udc) + fsl_writel(tmp, &dr_regs->endpointlistaddr); + + VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x", +- (int)udc->ep_qh, (int)tmp, ++ udc->ep_qh, (int)tmp, + fsl_readl(&dr_regs->endpointlistaddr)); + + /* Config PHY interface */ +@@ -1538,7 +1538,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) + + /* If the ep is configured */ + if (curr_ep->name == NULL) { +- WARN("Invalid EP?"); ++ WARNING("Invalid EP?"); + continue; + } + +@@ -2331,7 +2331,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) + udc_controller->gadget.name = driver_name; + + /* Setup gadget.dev and register with kernel */ +- strcpy(udc_controller->gadget.dev.bus_id, "gadget"); ++ dev_set_name(&udc_controller->gadget.dev, "gadget"); + udc_controller->gadget.dev.release = fsl_udc_release; + udc_controller->gadget.dev.parent = &pdev->dev; + ret = device_register(&udc_controller->gadget.dev); +diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h +index 98b1483..6131752 100644 +--- a/drivers/usb/gadget/fsl_usb2_udc.h ++++ b/drivers/usb/gadget/fsl_usb2_udc.h +@@ -552,7 +552,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) + #endif + + #define ERR(stuff...) pr_err("udc: " stuff) +-#define WARN(stuff...) pr_warning("udc: " stuff) ++#define WARNING(stuff...) pr_warning("udc: " stuff) + #define INFO(stuff...) pr_info("udc: " stuff) + + /*-------------------------------------------------------------------------*/ +diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h +new file mode 100644 +index 0000000..dd2f16a +--- /dev/null ++++ b/drivers/usb/gadget/g_zero.h +@@ -0,0 +1,25 @@ ++/* ++ * This header declares the utility functions used by "Gadget Zero", plus ++ * interfaces to its two single-configuration function drivers. ++ */ ++ ++#ifndef __G_ZERO_H ++#define __G_ZERO_H ++ ++#include <linux/usb/composite.h> ++ ++/* global state */ ++extern unsigned buflen; ++extern const struct usb_descriptor_header *otg_desc[]; ++ ++/* common utilities */ ++struct usb_request *alloc_ep_req(struct usb_ep *ep); ++void free_ep_req(struct usb_ep *ep, struct usb_request *req); ++void disable_endpoints(struct usb_composite_dev *cdev, ++ struct usb_ep *in, struct usb_ep *out); ++ ++/* configuration-specific linkup */ ++int sourcesink_add(struct usb_composite_dev *cdev); ++int loopback_add(struct usb_composite_dev *cdev); ++ ++#endif /* __G_ZERO_H */ +diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h +index f7f159c..17d9905 100644 +--- a/drivers/usb/gadget/gadget_chips.h ++++ b/drivers/usb/gadget/gadget_chips.h +@@ -11,6 +11,10 @@ + * Some are available on 2.4 kernels; several are available, but not + * yet pushed in the 2.6 mainline tree. + */ ++ ++#ifndef __GADGET_CHIPS_H ++#define __GADGET_CHIPS_H ++ + #ifdef CONFIG_USB_GADGET_NET2280 + #define gadget_is_net2280(g) !strcmp("net2280", (g)->name) + #else +@@ -29,8 +33,8 @@ + #define gadget_is_dummy(g) 0 + #endif + +-#ifdef CONFIG_USB_GADGET_PXA2XX +-#define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) ++#ifdef CONFIG_USB_GADGET_PXA25X ++#define gadget_is_pxa(g) !strcmp("pxa25x_udc", (g)->name) + #else + #define gadget_is_pxa(g) 0 + #endif +@@ -214,3 +218,28 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) + return 0x21; + return -ENOENT; + } ++ ++ ++/** ++ * gadget_supports_altsettings - return true if altsettings work ++ * @gadget: the gadget in question ++ */ ++static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) ++{ ++ /* PXA 21x/25x/26x has no altsettings at all */ ++ if (gadget_is_pxa(gadget)) ++ return false; ++ ++ /* PXA 27x and 3xx have *broken* altsetting support */ ++ if (gadget_is_pxa27x(gadget)) ++ return false; ++ ++ /* SH3 hardware just doesn't do altsettings */ ++ if (gadget_is_sh(gadget)) ++ return false; ++ ++ /* Everything else is *presumably* fine ... */ ++ return true; ++} ++ ++#endif /* __GADGET_CHIPS_H */ +diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c +index 7f4d482..ea8651e 100644 +--- a/drivers/usb/gadget/gmidi.c ++++ b/drivers/usb/gadget/gmidi.c +@@ -138,8 +138,6 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); + dev_vdbg(&(d)->gadget->dev , fmt , ## args) + #define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +-#define WARN(d, fmt, args...) \ +- dev_warn(&(d)->gadget->dev , fmt , ## args) + #define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c +index be6613a..60aa048 100644 +--- a/drivers/usb/gadget/goku_udc.c ++++ b/drivers/usb/gadget/goku_udc.c +@@ -1768,7 +1768,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) + * usb_gadget_driver_{register,unregister}() must change. + */ + if (the_controller) { +- WARN(dev, "ignoring %s\n", pci_name(pdev)); ++ WARNING(dev, "ignoring %s\n", pci_name(pdev)); + return -EBUSY; + } + if (!pdev->irq) { +@@ -1790,7 +1790,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) + dev->gadget.ops = &goku_ops; + + /* the "gadget" abstracts/virtualizes the controller */ +- strcpy(dev->gadget.dev.bus_id, "gadget"); ++ dev_set_name(&dev->gadget.dev, "gadget"); + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + dev->gadget.dev.release = gadget_release; +diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h +index bc4eb1e..566cb23 100644 +--- a/drivers/usb/gadget/goku_udc.h ++++ b/drivers/usb/gadget/goku_udc.h +@@ -285,7 +285,7 @@ struct goku_udc { + + #define ERROR(dev,fmt,args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +-#define WARN(dev,fmt,args...) \ ++#define WARNING(dev,fmt,args...) \ + xprintk(dev , KERN_WARNING , fmt , ## args) + #define INFO(dev,fmt,args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) +diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c +index 69b0a27..f4585d3 100644 +--- a/drivers/usb/gadget/inode.c ++++ b/drivers/usb/gadget/inode.c +@@ -32,6 +32,7 @@ + #include <asm/uaccess.h> + #include <linux/slab.h> + #include <linux/poll.h> ++#include <linux/smp_lock.h> + + #include <linux/device.h> + #include <linux/moduleparam.h> +@@ -261,8 +262,6 @@ static const char *CHIP; + + #define ERROR(dev,fmt,args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +-#define WARN(dev,fmt,args...) \ +- xprintk(dev , KERN_WARNING , fmt , ## args) + #define INFO(dev,fmt,args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) + +@@ -483,8 +482,7 @@ ep_release (struct inode *inode, struct file *fd) + return 0; + } + +-static int ep_ioctl (struct inode *inode, struct file *fd, +- unsigned code, unsigned long value) ++static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) + { + struct ep_data *data = fd->private_data; + int status; +@@ -740,7 +738,7 @@ static const struct file_operations ep_io_operations = { + + .read = ep_read, + .write = ep_write, +- .ioctl = ep_ioctl, ++ .unlocked_ioctl = ep_ioctl, + .release = ep_release, + + .aio_read = ep_aio_read, +@@ -1294,15 +1292,18 @@ out: + return mask; + } + +-static int dev_ioctl (struct inode *inode, struct file *fd, +- unsigned code, unsigned long value) ++static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) + { + struct dev_data *dev = fd->private_data; + struct usb_gadget *gadget = dev->gadget; ++ long ret = -ENOTTY; + +- if (gadget->ops->ioctl) +- return gadget->ops->ioctl (gadget, code, value); +- return -ENOTTY; ++ if (gadget->ops->ioctl) { ++ lock_kernel(); ++ ret = gadget->ops->ioctl (gadget, code, value); ++ unlock_kernel(); ++ } ++ return ret; + } + + /* used after device configuration */ +@@ -1314,7 +1315,7 @@ static const struct file_operations ep0_io_operations = { + .write = ep0_write, + .fasync = ep0_fasync, + .poll = ep0_poll, +- .ioctl = dev_ioctl, ++ .unlocked_ioctl = dev_ioctl, + .release = dev_release, + }; + +@@ -1501,7 +1502,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) + } + break; + +-#ifndef CONFIG_USB_GADGET_PXA2XX ++#ifndef CONFIG_USB_GADGET_PXA25X + /* PXA automagically handles this request too */ + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != 0x80) +@@ -1964,7 +1965,7 @@ static const struct file_operations dev_init_operations = { + .open = dev_open, + .write = dev_config, + .fasync = ep0_fasync, +- .ioctl = dev_ioctl, ++ .unlocked_ioctl = dev_ioctl, + .release = dev_release, + }; + +diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c +index 825abd2..c6e7df0 100644 +--- a/drivers/usb/gadget/lh7a40x_udc.c ++++ b/drivers/usb/gadget/lh7a40x_udc.c +@@ -1970,7 +1970,7 @@ static const struct usb_gadget_ops lh7a40x_udc_ops = { + + static void nop_release(struct device *dev) + { +- DEBUG("%s %s\n", __func__, dev->bus_id); ++ DEBUG("%s %s\n", __func__, dev_name(dev)); + } + + static struct lh7a40x_udc memory = { +diff --git a/drivers/usb/gadget/lh7a40x_udc.h b/drivers/usb/gadget/lh7a40x_udc.h +index 1ecfd63..ca86120 100644 +--- a/drivers/usb/gadget/lh7a40x_udc.h ++++ b/drivers/usb/gadget/lh7a40x_udc.h +@@ -47,7 +47,7 @@ + #include <asm/irq.h> + #include <asm/system.h> + #include <asm/unaligned.h> +-#include <asm/hardware.h> ++#include <mach/hardware.h> + + #include <linux/usb/ch9.h> + #include <linux/usb/gadget.h> +diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c +index ee6b35f..77b44fb 100644 +--- a/drivers/usb/gadget/m66592-udc.c ++++ b/drivers/usb/gadget/m66592-udc.c +@@ -1593,7 +1593,7 @@ static int __init m66592_probe(struct platform_device *pdev) + + m66592->gadget.ops = &m66592_gadget_ops; + device_initialize(&m66592->gadget.dev); +- strcpy(m66592->gadget.dev.bus_id, "gadget"); ++ dev_set_name(&m66592->gadget.dev, "gadget"); + m66592->gadget.is_dualspeed = 1; + m66592->gadget.dev.parent = &pdev->dev; + m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; +diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h +index 09e3ee4..df886ce 100644 +--- a/drivers/usb/gadget/ndis.h ++++ b/drivers/usb/gadget/ndis.h +@@ -1,11 +1,11 @@ + /* +- * ndis.h +- * ++ * ndis.h ++ * + * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de> +- * +- * Thanks to the cygwin development team, ++ * ++ * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net> +- * ++ * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may +diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c +index e018623..5cfb5eb 100644 +--- a/drivers/usb/gadget/net2280.c ++++ b/drivers/usb/gadget/net2280.c +@@ -1007,7 +1007,7 @@ static void scan_dma_completions (struct net2280_ep *ep) + * 0122, and 0124; not all cases trigger the warning. + */ + if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { +- WARN (ep->dev, "%s lost packet sync!\n", ++ WARNING (ep->dev, "%s lost packet sync!\n", + ep->ep.name); + req->req.status = -EOVERFLOW; + } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { +@@ -2768,7 +2768,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) + dev->gadget.is_dualspeed = 1; + + /* the "gadget" abstracts/virtualizes the controller */ +- strcpy (dev->gadget.dev.bus_id, "gadget"); ++ dev_set_name(&dev->gadget.dev, "gadget"); + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + dev->gadget.dev.release = gadget_release; +diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h +index 1f2af39..81a71db 100644 +--- a/drivers/usb/gadget/net2280.h ++++ b/drivers/usb/gadget/net2280.h +@@ -272,7 +272,7 @@ static inline void net2280_led_shutdown (struct net2280 *dev) + + #define ERROR(dev,fmt,args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +-#define WARN(dev,fmt,args...) \ ++#define WARNING(dev,fmt,args...) \ + xprintk(dev , KERN_WARNING , fmt , ## args) + #define INFO(dev,fmt,args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) +diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c +index 881d74c..bb54cca 100644 +--- a/drivers/usb/gadget/omap_udc.c ++++ b/drivers/usb/gadget/omap_udc.c +@@ -52,8 +52,9 @@ + #include <asm/unaligned.h> + #include <asm/mach-types.h> + +-#include <asm/arch/dma.h> +-#include <asm/arch/usb.h> ++#include <mach/dma.h> ++#include <mach/usb.h> ++#include <mach/control.h> + + #include "omap_udc.h" + +@@ -135,13 +136,17 @@ static void use_ep(struct omap_ep *ep, u16 select) + + if (ep->bEndpointAddress & USB_DIR_IN) + num |= UDC_EP_DIR; +- UDC_EP_NUM_REG = num | select; ++ omap_writew(num | select, UDC_EP_NUM); + /* when select, MUST deselect later !! */ + } + + static inline void deselect_ep(void) + { +- UDC_EP_NUM_REG &= ~UDC_EP_SEL; ++ u16 w; ++ ++ w = omap_readw(UDC_EP_NUM); ++ w &= ~UDC_EP_SEL; ++ omap_writew(w, UDC_EP_NUM); + /* 6 wait states before TX will happen */ + } + +@@ -216,7 +221,7 @@ static int omap_ep_enable(struct usb_ep *_ep, + ep->has_dma = 0; + ep->lch = -1; + use_ep(ep, UDC_EP_SEL); +- UDC_CTRL_REG = udc->clr_halt; ++ omap_writew(udc->clr_halt, UDC_CTRL); + ep->ackwait = 0; + deselect_ep(); + +@@ -232,7 +237,7 @@ static int omap_ep_enable(struct usb_ep *_ep, + if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC + && !ep->has_dma + && !(ep->bEndpointAddress & USB_DIR_IN)) { +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1 + ep->double_buf; + } + +@@ -259,7 +264,7 @@ static int omap_ep_disable(struct usb_ep *_ep) + nuke (ep, -ESHUTDOWN); + ep->ep.maxpacket = ep->maxpacket; + ep->has_dma = 0; +- UDC_CTRL_REG = UDC_SET_HALT; ++ omap_writew(UDC_SET_HALT, UDC_CTRL); + list_del_init(&ep->iso); + del_timer(&ep->timer); + +@@ -360,13 +365,13 @@ write_packet(u8 *buf, struct omap_req *req, unsigned max) + if (likely((((int)buf) & 1) == 0)) { + wp = (u16 *)buf; + while (max >= 2) { +- UDC_DATA_REG = *wp++; ++ omap_writew(*wp++, UDC_DATA); + max -= 2; + } + buf = (u8 *)wp; + } + while (max--) +- *(volatile u8 *)&UDC_DATA_REG = *buf++; ++ omap_writeb(*buf++, UDC_DATA); + return len; + } + +@@ -385,13 +390,13 @@ static int write_fifo(struct omap_ep *ep, struct omap_req *req) + prefetch(buf); + + /* PIO-IN isn't double buffered except for iso */ +- ep_stat = UDC_STAT_FLG_REG; ++ ep_stat = omap_readw(UDC_STAT_FLG); + if (ep_stat & UDC_FIFO_UNWRITABLE) + return 0; + + count = ep->ep.maxpacket; + count = write_packet(buf, req, count); +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1; + + /* last packet is often short (sometimes a zlp) */ +@@ -425,13 +430,13 @@ read_packet(u8 *buf, struct omap_req *req, unsigned avail) + if (likely((((int)buf) & 1) == 0)) { + wp = (u16 *)buf; + while (avail >= 2) { +- *wp++ = UDC_DATA_REG; ++ *wp++ = omap_readw(UDC_DATA); + avail -= 2; + } + buf = (u8 *)wp; + } + while (avail--) +- *buf++ = *(volatile u8 *)&UDC_DATA_REG; ++ *buf++ = omap_readb(UDC_DATA); + return len; + } + +@@ -446,7 +451,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) + prefetchw(buf); + + for (;;) { +- u16 ep_stat = UDC_STAT_FLG_REG; ++ u16 ep_stat = omap_readw(UDC_STAT_FLG); + + is_last = 0; + if (ep_stat & FIFO_EMPTY) { +@@ -460,7 +465,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) + if (ep_stat & UDC_FIFO_FULL) + avail = ep->ep.maxpacket; + else { +- avail = UDC_RXFSTAT_REG; ++ avail = omap_readw(UDC_RXFSTAT); + ep->fnf = ep->double_buf; + } + count = read_packet(buf, req, avail); +@@ -473,7 +478,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) + req->req.status = -EOVERFLOW; + avail -= count; + while (avail--) +- (void) *(volatile u8 *)&UDC_DATA_REG; ++ omap_readw(UDC_DATA); + } + } else if (req->req.length == req->req.actual) + is_last = 1; +@@ -491,32 +496,6 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) + + /*-------------------------------------------------------------------------*/ + +-static inline dma_addr_t dma_csac(unsigned lch) +-{ +- dma_addr_t csac; +- +- /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is +- * read before the DMA controller finished disabling the channel. +- */ +- csac = OMAP_DMA_CSAC_REG(lch); +- if (csac == 0) +- csac = OMAP_DMA_CSAC_REG(lch); +- return csac; +-} +- +-static inline dma_addr_t dma_cdac(unsigned lch) +-{ +- dma_addr_t cdac; +- +- /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is +- * read before the DMA controller finished disabling the channel. +- */ +- cdac = OMAP_DMA_CDAC_REG(lch); +- if (cdac == 0) +- cdac = OMAP_DMA_CDAC_REG(lch); +- return cdac; +-} +- + static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) + { + dma_addr_t end; +@@ -527,7 +506,7 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) + if (cpu_is_omap15xx()) + return 0; + +- end = dma_csac(ep->lch); ++ end = omap_get_dma_src_pos(ep->lch); + if (end == ep->dma_counter) + return 0; + +@@ -537,15 +516,11 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) + return end - start; + } + +-#define DMA_DEST_LAST(x) (cpu_is_omap15xx() \ +- ? OMAP_DMA_CSAC_REG(x) /* really: CPC */ \ +- : dma_cdac(x)) +- + static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) + { + dma_addr_t end; + +- end = DMA_DEST_LAST(ep->lch); ++ end = omap_get_dma_dst_pos(ep->lch); + if (end == ep->dma_counter) + return 0; + +@@ -565,7 +540,7 @@ static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) + + static void next_in_dma(struct omap_ep *ep, struct omap_req *req) + { +- u16 txdma_ctrl; ++ u16 txdma_ctrl, w; + unsigned length = req->req.length - req->req.actual; + const int sync_mode = cpu_is_omap15xx() + ? OMAP_DMA_SYNC_FRAME +@@ -596,14 +571,18 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req) + 0, 0); + + omap_start_dma(ep->lch); +- ep->dma_counter = dma_csac(ep->lch); +- UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel); +- UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl; ++ ep->dma_counter = omap_get_dma_src_pos(ep->lch); ++ w = omap_readw(UDC_DMA_IRQ_EN); ++ w |= UDC_TX_DONE_IE(ep->dma_channel); ++ omap_writew(w, UDC_DMA_IRQ_EN); ++ omap_writew(UDC_TXN_START | txdma_ctrl, UDC_TXDMA(ep->dma_channel)); + req->dma_bytes = length; + } + + static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) + { ++ u16 w; ++ + if (status == 0) { + req->req.actual += req->dma_bytes; + +@@ -620,7 +599,9 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) + + /* tx completion */ + omap_stop_dma(ep->lch); +- UDC_DMA_IRQ_EN_REG &= ~UDC_TX_DONE_IE(ep->dma_channel); ++ w = omap_readw(UDC_DMA_IRQ_EN); ++ w &= ~UDC_TX_DONE_IE(ep->dma_channel); ++ omap_writew(w, UDC_DMA_IRQ_EN); + done(ep, req, status); + } + +@@ -628,6 +609,7 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) + { + unsigned packets = req->req.length - req->req.actual; + int dma_trigger = 0; ++ u16 w; + + if (cpu_is_omap24xx()) + dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel); +@@ -654,12 +636,14 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) + omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, + 0, 0); +- ep->dma_counter = DMA_DEST_LAST(ep->lch); ++ ep->dma_counter = omap_get_dma_dst_pos(ep->lch); + +- UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); +- UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel); +- UDC_EP_NUM_REG = (ep->bEndpointAddress & 0xf); +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_RXN_STOP | (packets - 1), UDC_RXDMA(ep->dma_channel)); ++ w = omap_readw(UDC_DMA_IRQ_EN); ++ w |= UDC_RX_EOT_IE(ep->dma_channel); ++ omap_writew(w, UDC_DMA_IRQ_EN); ++ omap_writew(ep->bEndpointAddress & 0xf, UDC_EP_NUM); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + + omap_start_dma(ep->lch); + } +@@ -667,7 +651,7 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) + static void + finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one) + { +- u16 count; ++ u16 count, w; + + if (status == 0) + ep->dma_counter = (u16) (req->req.dma + req->req.actual); +@@ -686,13 +670,15 @@ finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one) + return; + + /* rx completion */ +- UDC_DMA_IRQ_EN_REG &= ~UDC_RX_EOT_IE(ep->dma_channel); ++ w = omap_readw(UDC_DMA_IRQ_EN); ++ w &= ~UDC_RX_EOT_IE(ep->dma_channel); ++ omap_writew(w, UDC_DMA_IRQ_EN); + done(ep, req, status); + } + + static void dma_irq(struct omap_udc *udc, u16 irq_src) + { +- u16 dman_stat = UDC_DMAN_STAT_REG; ++ u16 dman_stat = omap_readw(UDC_DMAN_STAT); + struct omap_ep *ep; + struct omap_req *req; + +@@ -706,7 +692,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) + struct omap_req, queue); + finish_in_dma(ep, req, 0); + } +- UDC_IRQ_SRC_REG = UDC_TXN_DONE; ++ omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC); + + if (!list_empty (&ep->queue)) { + req = container_of(ep->queue.next, +@@ -725,7 +711,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) + struct omap_req, queue); + finish_out_dma(ep, req, 0, dman_stat & UDC_DMA_RX_SB); + } +- UDC_IRQ_SRC_REG = UDC_RXN_EOT; ++ omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC); + + if (!list_empty (&ep->queue)) { + req = container_of(ep->queue.next, +@@ -739,7 +725,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) + ep->irqs++; + /* omap15xx does this unasked... */ + VDBG("%s, RX_CNT irq?\n", ep->ep.name); +- UDC_IRQ_SRC_REG = UDC_RXN_CNT; ++ omap_writew(UDC_RXN_CNT, UDC_IRQ_SRC); + } + } + +@@ -762,9 +748,9 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) + + is_in = ep->bEndpointAddress & USB_DIR_IN; + if (is_in) +- reg = UDC_TXDMA_CFG_REG; ++ reg = omap_readw(UDC_TXDMA_CFG); + else +- reg = UDC_RXDMA_CFG_REG; ++ reg = omap_readw(UDC_RXDMA_CFG); + reg |= UDC_DMA_REQ; /* "pulse" activated */ + + ep->dma_channel = 0; +@@ -792,7 +778,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) + status = omap_request_dma(dma_channel, + ep->ep.name, dma_error, ep, &ep->lch); + if (status == 0) { +- UDC_TXDMA_CFG_REG = reg; ++ omap_writew(reg, UDC_TXDMA_CFG); + /* EMIFF or SDRC */ + omap_set_dma_src_burst_mode(ep->lch, + OMAP_DMA_DATA_BURST_4); +@@ -801,7 +787,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) + omap_set_dma_dest_params(ep->lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, +- (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), ++ UDC_DATA_DMA, + 0, 0); + } + } else { +@@ -813,12 +799,12 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) + status = omap_request_dma(dma_channel, + ep->ep.name, dma_error, ep, &ep->lch); + if (status == 0) { +- UDC_RXDMA_CFG_REG = reg; ++ omap_writew(reg, UDC_RXDMA_CFG); + /* TIPB */ + omap_set_dma_src_params(ep->lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, +- (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), ++ UDC_DATA_DMA, + 0, 0); + /* EMIFF or SDRC */ + omap_set_dma_dest_burst_mode(ep->lch, +@@ -834,7 +820,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) + + /* channel type P: hw synch (fifo) */ + if (cpu_class_is_omap1() && !cpu_is_omap15xx()) +- OMAP1_DMA_LCH_CTRL_REG(ep->lch) = 2; ++ omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P); + } + + just_restart: +@@ -860,7 +846,7 @@ just_restart: + (is_in ? write_fifo : read_fifo)(ep, req); + deselect_ep(); + if (!is_in) { +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1 + ep->double_buf; + } + /* IN: 6 wait states before it'll tx */ +@@ -881,7 +867,7 @@ static void dma_channel_release(struct omap_ep *ep) + else + req = NULL; + +- active = ((1 << 7) & OMAP_DMA_CCR_REG(ep->lch)) != 0; ++ active = omap_get_dma_active_status(ep->lch); + + DBG("%s release %s %cxdma%d %p\n", ep->ep.name, + active ? "active" : "idle", +@@ -894,23 +880,25 @@ static void dma_channel_release(struct omap_ep *ep) + + /* wait till current packet DMA finishes, and fifo empties */ + if (ep->bEndpointAddress & USB_DIR_IN) { +- UDC_TXDMA_CFG_REG = (UDC_TXDMA_CFG_REG & ~mask) | UDC_DMA_REQ; ++ omap_writew((omap_readw(UDC_TXDMA_CFG) & ~mask) | UDC_DMA_REQ, ++ UDC_TXDMA_CFG); + + if (req) { + finish_in_dma(ep, req, -ECONNRESET); + + /* clear FIFO; hosts probably won't empty it */ + use_ep(ep, UDC_EP_SEL); +- UDC_CTRL_REG = UDC_CLR_EP; ++ omap_writew(UDC_CLR_EP, UDC_CTRL); + deselect_ep(); + } +- while (UDC_TXDMA_CFG_REG & mask) ++ while (omap_readw(UDC_TXDMA_CFG) & mask) + udelay(10); + } else { +- UDC_RXDMA_CFG_REG = (UDC_RXDMA_CFG_REG & ~mask) | UDC_DMA_REQ; ++ omap_writew((omap_readw(UDC_RXDMA_CFG) & ~mask) | UDC_DMA_REQ, ++ UDC_RXDMA_CFG); + + /* dma empties the fifo */ +- while (UDC_RXDMA_CFG_REG & mask) ++ while (omap_readw(UDC_RXDMA_CFG) & mask) + udelay(10); + if (req) + finish_out_dma(ep, req, -ECONNRESET, 0); +@@ -997,9 +985,13 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) + req->req.actual = 0; + + /* maybe kickstart non-iso i/o queues */ +- if (is_iso) +- UDC_IRQ_EN_REG |= UDC_SOF_IE; +- else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) { ++ if (is_iso) { ++ u16 w; ++ ++ w = omap_readw(UDC_IRQ_EN); ++ w |= UDC_SOF_IE; ++ omap_writew(w, UDC_IRQ_EN); ++ } else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) { + int is_in; + + if (ep->bEndpointAddress == 0) { +@@ -1017,23 +1009,23 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) + * requests to non-control endpoints + */ + if (udc->ep0_set_config) { +- u16 irq_en = UDC_IRQ_EN_REG; ++ u16 irq_en = omap_readw(UDC_IRQ_EN); + + irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE; + if (!udc->ep0_reset_config) + irq_en |= UDC_EPN_RX_IE + | UDC_EPN_TX_IE; +- UDC_IRQ_EN_REG = irq_en; ++ omap_writew(irq_en, UDC_IRQ_EN); + } + + /* STATUS for zero length DATA stages is + * always an IN ... even for IN transfers, + * a weird case which seem to stall OMAP. + */ +- UDC_EP_NUM_REG = (UDC_EP_SEL|UDC_EP_DIR); +- UDC_CTRL_REG = UDC_CLR_EP; +- UDC_CTRL_REG = UDC_SET_FIFO_EN; +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); ++ omap_writew(UDC_CLR_EP, UDC_CTRL); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + + /* cleanup */ + udc->ep0_pending = 0; +@@ -1042,11 +1034,11 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) + + /* non-empty DATA stage */ + } else if (is_in) { +- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; ++ omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); + } else { + if (udc->ep0_setup) + goto irq_wait; +- UDC_EP_NUM_REG = UDC_EP_SEL; ++ omap_writew(UDC_EP_SEL, UDC_EP_NUM); + } + } else { + is_in = ep->bEndpointAddress & USB_DIR_IN; +@@ -1062,7 +1054,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) + req = NULL; + deselect_ep(); + if (!is_in) { +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1 + ep->double_buf; + } + /* IN: 6 wait states before it'll tx */ +@@ -1129,10 +1121,10 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) + status = -EINVAL; + else if (value) { + if (ep->udc->ep0_set_config) { +- WARN("error changing config?\n"); +- UDC_SYSCON2_REG = UDC_CLR_CFG; ++ WARNING("error changing config?\n"); ++ omap_writew(UDC_CLR_CFG, UDC_SYSCON2); + } +- UDC_SYSCON2_REG = UDC_STALL_CMD; ++ omap_writew(UDC_STALL_CMD, UDC_SYSCON2); + ep->udc->ep0_pending = 0; + status = 0; + } else /* NOP */ +@@ -1159,8 +1151,8 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) + channel = 0; + + use_ep(ep, UDC_EP_SEL); +- if (UDC_STAT_FLG_REG & UDC_NON_ISO_FIFO_EMPTY) { +- UDC_CTRL_REG = UDC_SET_HALT; ++ if (omap_readw(UDC_STAT_FLG) & UDC_NON_ISO_FIFO_EMPTY) { ++ omap_writew(UDC_SET_HALT, UDC_CTRL); + status = 0; + } else + status = -EAGAIN; +@@ -1170,10 +1162,10 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) + dma_channel_claim(ep, channel); + } else { + use_ep(ep, 0); +- UDC_CTRL_REG = ep->udc->clr_halt; ++ omap_writew(ep->udc->clr_halt, UDC_CTRL); + ep->ackwait = 0; + if (!(ep->bEndpointAddress & USB_DIR_IN)) { +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1 + ep->double_buf; + } + } +@@ -1205,7 +1197,7 @@ static struct usb_ep_ops omap_ep_ops = { + + static int omap_get_frame(struct usb_gadget *gadget) + { +- u16 sof = UDC_SOF_REG; ++ u16 sof = omap_readw(UDC_SOF); + return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC; + } + +@@ -1224,7 +1216,7 @@ static int omap_wakeup(struct usb_gadget *gadget) + */ + if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) { + DBG("remote wakeup...\n"); +- UDC_SYSCON2_REG = UDC_RMT_WKP; ++ omap_writew(UDC_RMT_WKP, UDC_SYSCON2); + retval = 0; + } + +@@ -1247,12 +1239,12 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); +- syscon1 = UDC_SYSCON1_REG; ++ syscon1 = omap_readw(UDC_SYSCON1); + if (is_selfpowered) + syscon1 |= UDC_SELF_PWR; + else + syscon1 &= ~UDC_SELF_PWR; +- UDC_SYSCON1_REG = syscon1; ++ omap_writew(syscon1, UDC_SYSCON1); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +@@ -1265,18 +1257,36 @@ static int can_pullup(struct omap_udc *udc) + + static void pullup_enable(struct omap_udc *udc) + { +- UDC_SYSCON1_REG |= UDC_PULLUP_EN; +- if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) +- OTG_CTRL_REG |= OTG_BSESSVLD; +- UDC_IRQ_EN_REG = UDC_DS_CHG_IE; ++ u16 w; ++ ++ w = omap_readw(UDC_SYSCON1); ++ w |= UDC_PULLUP_EN; ++ omap_writew(w, UDC_SYSCON1); ++ if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) { ++ u32 l; ++ ++ l = omap_readl(OTG_CTRL); ++ l |= OTG_BSESSVLD; ++ omap_writel(l, OTG_CTRL); ++ } ++ omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN); + } + + static void pullup_disable(struct omap_udc *udc) + { +- if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) +- OTG_CTRL_REG &= ~OTG_BSESSVLD; +- UDC_IRQ_EN_REG = UDC_DS_CHG_IE; +- UDC_SYSCON1_REG &= ~UDC_PULLUP_EN; ++ u16 w; ++ ++ if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) { ++ u32 l; ++ ++ l = omap_readl(OTG_CTRL); ++ l &= ~OTG_BSESSVLD; ++ omap_writel(l, OTG_CTRL); ++ } ++ omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN); ++ w = omap_readw(UDC_SYSCON1); ++ w &= ~UDC_PULLUP_EN; ++ omap_writew(w, UDC_SYSCON1); + } + + static struct omap_udc *udc; +@@ -1304,6 +1314,7 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) + { + struct omap_udc *udc; + unsigned long flags; ++ u32 l; + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); +@@ -1311,10 +1322,12 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) + udc->vbus_active = (is_active != 0); + if (cpu_is_omap15xx()) { + /* "software" detect, ignored if !VBUS_MODE_1510 */ ++ l = omap_readl(FUNC_MUX_CTRL_0); + if (is_active) +- FUNC_MUX_CTRL_0_REG |= VBUS_CTRL_1510; ++ l |= VBUS_CTRL_1510; + else +- FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510; ++ l &= ~VBUS_CTRL_1510; ++ omap_writel(l, FUNC_MUX_CTRL_0); + } + if (udc->dc_clk != NULL && is_active) { + if (!udc->clk_requested) { +@@ -1384,9 +1397,9 @@ static void nuke(struct omap_ep *ep, int status) + dma_channel_release(ep); + + use_ep(ep, 0); +- UDC_CTRL_REG = UDC_CLR_EP; ++ omap_writew(UDC_CLR_EP, UDC_CTRL); + if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC) +- UDC_CTRL_REG = UDC_SET_HALT; ++ omap_writew(UDC_SET_HALT, UDC_CTRL); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct omap_req, queue); +@@ -1414,8 +1427,8 @@ static void update_otg(struct omap_udc *udc) + if (!gadget_is_otg(&udc->gadget)) + return; + +- if (OTG_CTRL_REG & OTG_ID) +- devstat = UDC_DEVSTAT_REG; ++ if (omap_readl(OTG_CTRL) & OTG_ID) ++ devstat = omap_readw(UDC_DEVSTAT); + else + devstat = 0; + +@@ -1426,9 +1439,14 @@ static void update_otg(struct omap_udc *udc) + /* Enable HNP early, avoiding races on suspend irq path. + * ASSUMES OTG state machine B_BUS_REQ input is true. + */ +- if (udc->gadget.b_hnp_enable) +- OTG_CTRL_REG = (OTG_CTRL_REG | OTG_B_HNPEN | OTG_B_BUSREQ) +- & ~OTG_PULLUP; ++ if (udc->gadget.b_hnp_enable) { ++ u32 l; ++ ++ l = omap_readl(OTG_CTRL); ++ l |= OTG_B_HNPEN | OTG_B_BUSREQ; ++ l &= ~OTG_PULLUP; ++ omap_writel(l, OTG_CTRL); ++ } + } + + static void ep0_irq(struct omap_udc *udc, u16 irq_src) +@@ -1446,7 +1464,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + + nuke(ep0, 0); + if (ack) { +- UDC_IRQ_SRC_REG = ack; ++ omap_writew(ack, UDC_IRQ_SRC); + irq_src = UDC_SETUP; + } + } +@@ -1466,9 +1484,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + if (irq_src & UDC_EP0_TX) { + int stat; + +- UDC_IRQ_SRC_REG = UDC_EP0_TX; +- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; +- stat = UDC_STAT_FLG_REG; ++ omap_writew(UDC_EP0_TX, UDC_IRQ_SRC); ++ omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); ++ stat = omap_readw(UDC_STAT_FLG); + if (stat & UDC_ACK) { + if (udc->ep0_in) { + /* write next IN packet from response, +@@ -1476,26 +1494,26 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + */ + if (req) + stat = write_fifo(ep0, req); +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + if (!req && udc->ep0_pending) { +- UDC_EP_NUM_REG = UDC_EP_SEL; +- UDC_CTRL_REG = UDC_CLR_EP; +- UDC_CTRL_REG = UDC_SET_FIFO_EN; +- UDC_EP_NUM_REG = 0; ++ omap_writew(UDC_EP_SEL, UDC_EP_NUM); ++ omap_writew(UDC_CLR_EP, UDC_CTRL); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ++ omap_writew(0, UDC_EP_NUM); + udc->ep0_pending = 0; + } /* else: 6 wait states before it'll tx */ + } else { + /* ack status stage of OUT transfer */ +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + if (req) + done(ep0, req, 0); + } + req = NULL; + } else if (stat & UDC_STALL) { +- UDC_CTRL_REG = UDC_CLR_HALT; +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_CLR_HALT, UDC_CTRL); ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + } else { +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + } + } + +@@ -1503,9 +1521,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + if (irq_src & UDC_EP0_RX) { + int stat; + +- UDC_IRQ_SRC_REG = UDC_EP0_RX; +- UDC_EP_NUM_REG = UDC_EP_SEL; +- stat = UDC_STAT_FLG_REG; ++ omap_writew(UDC_EP0_RX, UDC_IRQ_SRC); ++ omap_writew(UDC_EP_SEL, UDC_EP_NUM); ++ stat = omap_readw(UDC_STAT_FLG); + if (stat & UDC_ACK) { + if (!udc->ep0_in) { + stat = 0; +@@ -1513,34 +1531,35 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + * reactiviting the fifo; stall on errors. + */ + if (!req || (stat = read_fifo(ep0, req)) < 0) { +- UDC_SYSCON2_REG = UDC_STALL_CMD; ++ omap_writew(UDC_STALL_CMD, UDC_SYSCON2); + udc->ep0_pending = 0; + stat = 0; + } else if (stat == 0) +- UDC_CTRL_REG = UDC_SET_FIFO_EN; +- UDC_EP_NUM_REG = 0; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ++ omap_writew(0, UDC_EP_NUM); + + /* activate status stage */ + if (stat == 1) { + done(ep0, req, 0); + /* that may have STALLed ep0... */ +- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; +- UDC_CTRL_REG = UDC_CLR_EP; +- UDC_CTRL_REG = UDC_SET_FIFO_EN; +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_EP_SEL | UDC_EP_DIR, ++ UDC_EP_NUM); ++ omap_writew(UDC_CLR_EP, UDC_CTRL); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + udc->ep0_pending = 0; + } + } else { + /* ack status stage of IN transfer */ +- UDC_EP_NUM_REG = 0; ++ omap_writew(0, UDC_EP_NUM); + if (req) + done(ep0, req, 0); + } + } else if (stat & UDC_STALL) { +- UDC_CTRL_REG = UDC_CLR_HALT; +- UDC_EP_NUM_REG = 0; ++ omap_writew(UDC_CLR_HALT, UDC_CTRL); ++ omap_writew(0, UDC_EP_NUM); + } else { +- UDC_EP_NUM_REG = 0; ++ omap_writew(0, UDC_EP_NUM); + } + } + +@@ -1555,14 +1574,14 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + + /* read the (latest) SETUP message */ + do { +- UDC_EP_NUM_REG = UDC_SETUP_SEL; ++ omap_writew(UDC_SETUP_SEL, UDC_EP_NUM); + /* two bytes at a time */ +- u.word[0] = UDC_DATA_REG; +- u.word[1] = UDC_DATA_REG; +- u.word[2] = UDC_DATA_REG; +- u.word[3] = UDC_DATA_REG; +- UDC_EP_NUM_REG = 0; +- } while (UDC_IRQ_SRC_REG & UDC_SETUP); ++ u.word[0] = omap_readw(UDC_DATA); ++ u.word[1] = omap_readw(UDC_DATA); ++ u.word[2] = omap_readw(UDC_DATA); ++ u.word[3] = omap_readw(UDC_DATA); ++ omap_writew(0, UDC_EP_NUM); ++ } while (omap_readw(UDC_IRQ_SRC) & UDC_SETUP); + + #define w_value le16_to_cpu(u.r.wValue) + #define w_index le16_to_cpu(u.r.wIndex) +@@ -1593,9 +1612,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + * later if it fails the request. + */ + if (udc->ep0_reset_config) +- UDC_SYSCON2_REG = UDC_CLR_CFG; ++ omap_writew(UDC_CLR_CFG, UDC_SYSCON2); + else +- UDC_SYSCON2_REG = UDC_DEV_CFG; ++ omap_writew(UDC_DEV_CFG, UDC_SYSCON2); + update_otg(udc); + goto delegate; + case USB_REQ_CLEAR_FEATURE: +@@ -1613,10 +1632,10 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + || !ep->desc) + goto do_stall; + use_ep(ep, 0); +- UDC_CTRL_REG = udc->clr_halt; ++ omap_writew(udc->clr_halt, UDC_CTRL); + ep->ackwait = 0; + if (!(ep->bEndpointAddress & USB_DIR_IN)) { +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1 + ep->double_buf; + } + /* NOTE: assumes the host behaves sanely, +@@ -1649,15 +1668,15 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) + } + use_ep(ep, 0); + /* can't halt if fifo isn't empty... */ +- UDC_CTRL_REG = UDC_CLR_EP; +- UDC_CTRL_REG = UDC_SET_HALT; ++ omap_writew(UDC_CLR_EP, UDC_CTRL); ++ omap_writew(UDC_SET_HALT, UDC_CTRL); + VDBG("%s halted by host\n", ep->name); + ep0out_status_stage: + status = 0; +- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; +- UDC_CTRL_REG = UDC_CLR_EP; +- UDC_CTRL_REG = UDC_SET_FIFO_EN; +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); ++ omap_writew(UDC_CLR_EP, UDC_CTRL); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + udc->ep0_pending = 0; + break; + case USB_REQ_GET_STATUS: +@@ -1694,10 +1713,10 @@ intf_status: + + zero_status: + /* return two zero bytes */ +- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; +- UDC_DATA_REG = 0; +- UDC_CTRL_REG = UDC_SET_FIFO_EN; +- UDC_EP_NUM_REG = UDC_EP_DIR; ++ omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); ++ omap_writew(0, UDC_DATA); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ++ omap_writew(UDC_EP_DIR, UDC_EP_NUM); + status = 0; + VDBG("GET_STATUS, interface %d\n", w_index); + /* next, status stage */ +@@ -1706,8 +1725,8 @@ zero_status: + delegate: + /* activate the ep0out fifo right away */ + if (!udc->ep0_in && w_length) { +- UDC_EP_NUM_REG = 0; +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(0, UDC_EP_NUM); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + } + + /* gadget drivers see class/vendor specific requests, +@@ -1746,11 +1765,11 @@ do_stall: + u.r.bRequestType, u.r.bRequest, status); + if (udc->ep0_set_config) { + if (udc->ep0_reset_config) +- WARN("error resetting config?\n"); ++ WARNING("error resetting config?\n"); + else +- UDC_SYSCON2_REG = UDC_CLR_CFG; ++ omap_writew(UDC_CLR_CFG, UDC_SYSCON2); + } +- UDC_SYSCON2_REG = UDC_STALL_CMD; ++ omap_writew(UDC_STALL_CMD, UDC_SYSCON2); + udc->ep0_pending = 0; + } + } +@@ -1764,7 +1783,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) + { + u16 devstat, change; + +- devstat = UDC_DEVSTAT_REG; ++ devstat = omap_readw(UDC_DEVSTAT); + change = devstat ^ udc->devstat; + udc->devstat = devstat; + +@@ -1804,7 +1823,8 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) + INFO("USB reset done, gadget %s\n", + udc->driver->driver.name); + /* ep0 traffic is legal from now on */ +- UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE; ++ omap_writew(UDC_DS_CHG_IE | UDC_EP0_IE, ++ UDC_IRQ_EN); + } + change &= ~UDC_USB_RESET; + } +@@ -1848,7 +1868,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) + VDBG("devstat %03x, ignore change %03x\n", + devstat, change); + +- UDC_IRQ_SRC_REG = UDC_DS_CHG; ++ omap_writew(UDC_DS_CHG, UDC_IRQ_SRC); + } + + static irqreturn_t omap_udc_irq(int irq, void *_udc) +@@ -1859,7 +1879,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc) + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); +- irq_src = UDC_IRQ_SRC_REG; ++ irq_src = omap_readw(UDC_IRQ_SRC); + + /* Device state change (usb ch9 stuff) */ + if (irq_src & UDC_DS_CHG) { +@@ -1882,7 +1902,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc) + irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT); + } + +- irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX); ++ irq_src &= ~(UDC_IRQ_SOF | UDC_EPN_TX|UDC_EPN_RX); + if (irq_src) + DBG("udc_irq, unhandled %03x\n", irq_src); + spin_unlock_irqrestore(&udc->lock, flags); +@@ -1903,7 +1923,7 @@ static void pio_out_timer(unsigned long _ep) + spin_lock_irqsave(&ep->udc->lock, flags); + if (!list_empty(&ep->queue) && ep->ackwait) { + use_ep(ep, UDC_EP_SEL); +- stat_flg = UDC_STAT_FLG_REG; ++ stat_flg = omap_readw(UDC_STAT_FLG); + + if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN) + || (ep->double_buf && HALF_FULL(stat_flg)))) { +@@ -1913,8 +1933,8 @@ static void pio_out_timer(unsigned long _ep) + req = container_of(ep->queue.next, + struct omap_req, queue); + (void) read_fifo(ep, req); +- UDC_EP_NUM_REG = ep->bEndpointAddress; +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(ep->bEndpointAddress, UDC_EP_NUM); ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1 + ep->double_buf; + } else + deselect_ep(); +@@ -1934,20 +1954,20 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); +- epn_stat = UDC_EPN_STAT_REG; +- irq_src = UDC_IRQ_SRC_REG; ++ epn_stat = omap_readw(UDC_EPN_STAT); ++ irq_src = omap_readw(UDC_IRQ_SRC); + + /* handle OUT first, to avoid some wasteful NAKs */ + if (irq_src & UDC_EPN_RX) { + epnum = (epn_stat >> 8) & 0x0f; +- UDC_IRQ_SRC_REG = UDC_EPN_RX; ++ omap_writew(UDC_EPN_RX, UDC_IRQ_SRC); + status = IRQ_HANDLED; + ep = &udc->ep[epnum]; + ep->irqs++; + +- UDC_EP_NUM_REG = epnum | UDC_EP_SEL; ++ omap_writew(epnum | UDC_EP_SEL, UDC_EP_NUM); + ep->fnf = 0; +- if ((UDC_STAT_FLG_REG & UDC_ACK)) { ++ if (omap_readw(UDC_STAT_FLG) & UDC_ACK) { + ep->ackwait--; + if (!list_empty(&ep->queue)) { + int stat; +@@ -1959,15 +1979,15 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) + } + } + /* min 6 clock delay before clearing EP_SEL ... */ +- epn_stat = UDC_EPN_STAT_REG; +- epn_stat = UDC_EPN_STAT_REG; +- UDC_EP_NUM_REG = epnum; ++ epn_stat = omap_readw(UDC_EPN_STAT); ++ epn_stat = omap_readw(UDC_EPN_STAT); ++ omap_writew(epnum, UDC_EP_NUM); + + /* enabling fifo _after_ clearing ACK, contrary to docs, + * reduces lossage; timer still needed though (sigh). + */ + if (ep->fnf) { +- UDC_CTRL_REG = UDC_SET_FIFO_EN; ++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + ep->ackwait = 1 + ep->double_buf; + } + mod_timer(&ep->timer, PIO_OUT_TIMEOUT); +@@ -1976,13 +1996,13 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) + /* then IN transfers */ + else if (irq_src & UDC_EPN_TX) { + epnum = epn_stat & 0x0f; +- UDC_IRQ_SRC_REG = UDC_EPN_TX; ++ omap_writew(UDC_EPN_TX, UDC_IRQ_SRC); + status = IRQ_HANDLED; + ep = &udc->ep[16 + epnum]; + ep->irqs++; + +- UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL; +- if ((UDC_STAT_FLG_REG & UDC_ACK)) { ++ omap_writew(epnum | UDC_EP_DIR | UDC_EP_SEL, UDC_EP_NUM); ++ if (omap_readw(UDC_STAT_FLG) & UDC_ACK) { + ep->ackwait = 0; + if (!list_empty(&ep->queue)) { + req = container_of(ep->queue.next, +@@ -1991,9 +2011,9 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) + } + } + /* min 6 clock delay before clearing EP_SEL ... */ +- epn_stat = UDC_EPN_STAT_REG; +- epn_stat = UDC_EPN_STAT_REG; +- UDC_EP_NUM_REG = epnum | UDC_EP_DIR; ++ epn_stat = omap_readw(UDC_EPN_STAT); ++ epn_stat = omap_readw(UDC_EPN_STAT); ++ omap_writew(epnum | UDC_EP_DIR, UDC_EP_NUM); + /* then 6 clocks before it'd tx */ + } + +@@ -2021,7 +2041,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) + req = list_entry(ep->queue.next, struct omap_req, queue); + + use_ep(ep, UDC_EP_SEL); +- stat = UDC_STAT_FLG_REG; ++ stat = omap_readw(UDC_STAT_FLG); + + /* NOTE: like the other controller drivers, this isn't + * currently reporting lost or damaged frames. +@@ -2053,9 +2073,14 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) + if (!list_empty(&ep->queue)) + pending = 1; + } +- if (!pending) +- UDC_IRQ_EN_REG &= ~UDC_SOF_IE; +- UDC_IRQ_SRC_REG = UDC_SOF; ++ if (!pending) { ++ u16 w; ++ ++ w = omap_readw(UDC_IRQ_EN); ++ w &= ~UDC_SOF_IE; ++ omap_writew(w, UDC_IRQ_EN); ++ } ++ omap_writew(UDC_IRQ_SOF, UDC_IRQ_SRC); + + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; +@@ -2104,7 +2129,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + continue; + use_ep(ep, 0); +- UDC_CTRL_REG = UDC_SET_HALT; ++ omap_writew(UDC_SET_HALT, UDC_CTRL); + } + udc->ep0_pending = 0; + udc->ep[0].irqs = 0; +@@ -2128,7 +2153,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) + } + DBG("bound to driver %s\n", driver->driver.name); + +- UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; ++ omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); + + /* connect to bus through transceiver */ + if (udc->transceiver) { +@@ -2225,7 +2250,7 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) + else + buf[0] = 0; + +- stat_flg = UDC_STAT_FLG_REG; ++ stat_flg = omap_readw(UDC_STAT_FLG); + seq_printf(s, + "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", + ep->name, buf, +@@ -2286,17 +2311,17 @@ static int proc_otg_show(struct seq_file *s) + u32 trans; + char *ctrl_name; + +- tmp = OTG_REV_REG; ++ tmp = omap_readl(OTG_REV); + if (cpu_is_omap24xx()) { + ctrl_name = "control_devconf"; +- trans = CONTROL_DEVCONF_REG; ++ trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); + } else { + ctrl_name = "tranceiver_ctrl"; +- trans = USB_TRANSCEIVER_CTRL_REG; ++ trans = omap_readw(USB_TRANSCEIVER_CTRL); + } + seq_printf(s, "\nOTG rev %d.%d, %s %05x\n", + tmp >> 4, tmp & 0xf, ctrl_name, trans); +- tmp = OTG_SYSCON_1_REG; ++ tmp = omap_readw(OTG_SYSCON_1); + seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," + FOURBITS "\n", tmp, + trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R), +@@ -2308,7 +2333,7 @@ static int proc_otg_show(struct seq_file *s) + (tmp & HST_IDLE_EN) ? " !host" : "", + (tmp & DEV_IDLE_EN) ? " !dev" : "", + (tmp & OTG_RESET_DONE) ? " reset_done" : " reset_active"); +- tmp = OTG_SYSCON_2_REG; ++ tmp = omap_readl(OTG_SYSCON_2); + seq_printf(s, "otg_syscon2 %08x%s" EIGHTBITS + " b_ase_brst=%d hmc=%d\n", tmp, + (tmp & OTG_EN) ? " otg_en" : "", +@@ -2323,7 +2348,7 @@ static int proc_otg_show(struct seq_file *s) + (tmp & HMC_TLLATTACH) ? " tllattach" : "", + B_ASE_BRST(tmp), + OTG_HMC(tmp)); +- tmp = OTG_CTRL_REG; ++ tmp = omap_readl(OTG_CTRL); + seq_printf(s, "otg_ctrl %06x" EIGHTBITS EIGHTBITS "%s\n", tmp, + (tmp & OTG_ASESSVLD) ? " asess" : "", + (tmp & OTG_BSESSEND) ? " bsess_end" : "", +@@ -2343,13 +2368,13 @@ static int proc_otg_show(struct seq_file *s) + (tmp & OTG_PU_VBUS) ? " pu_vb" : "", + (tmp & OTG_PU_ID) ? " pu_id" : "" + ); +- tmp = OTG_IRQ_EN_REG; ++ tmp = omap_readw(OTG_IRQ_EN); + seq_printf(s, "otg_irq_en %04x" "\n", tmp); +- tmp = OTG_IRQ_SRC_REG; ++ tmp = omap_readw(OTG_IRQ_SRC); + seq_printf(s, "otg_irq_src %04x" "\n", tmp); +- tmp = OTG_OUTCTRL_REG; ++ tmp = omap_readw(OTG_OUTCTRL); + seq_printf(s, "otg_outctrl %04x" "\n", tmp); +- tmp = OTG_TEST_REG; ++ tmp = omap_readw(OTG_TEST); + seq_printf(s, "otg_test %04x" "\n", tmp); + return 0; + } +@@ -2370,7 +2395,7 @@ static int proc_udc_show(struct seq_file *s, void *_) + driver_desc, + use_dma ? " (dma)" : ""); + +- tmp = UDC_REV_REG & 0xff; ++ tmp = omap_readw(UDC_REV) & 0xff; + seq_printf(s, + "UDC rev %d.%d, fifo mode %d, gadget %s\n" + "hmc %d, transceiver %s\n", +@@ -2384,16 +2409,16 @@ static int proc_udc_show(struct seq_file *s, void *_) + ? "external" : "(none)")); + if (cpu_class_is_omap1()) { + seq_printf(s, "ULPD control %04x req %04x status %04x\n", +- __REG16(ULPD_CLOCK_CTRL), +- __REG16(ULPD_SOFT_REQ), +- __REG16(ULPD_STATUS_REQ)); ++ omap_readw(ULPD_CLOCK_CTRL), ++ omap_readw(ULPD_SOFT_REQ), ++ omap_readw(ULPD_STATUS_REQ)); + } + + /* OTG controller registers */ + if (!cpu_is_omap15xx()) + proc_otg_show(s); + +- tmp = UDC_SYSCON1_REG; ++ tmp = omap_readw(UDC_SYSCON1); + seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, + (tmp & UDC_CFG_LOCK) ? " cfg_lock" : "", + (tmp & UDC_DATA_ENDIAN) ? " data_endian" : "", +@@ -2412,7 +2437,7 @@ static int proc_udc_show(struct seq_file *s, void *_) + return 0; + } + +- tmp = UDC_DEVSTAT_REG; ++ tmp = omap_readw(UDC_DEVSTAT); + seq_printf(s, "devstat %04x" EIGHTBITS "%s%s\n", tmp, + (tmp & UDC_B_HNP_ENABLE) ? " b_hnp" : "", + (tmp & UDC_A_HNP_SUPPORT) ? " a_hnp" : "", +@@ -2424,20 +2449,20 @@ static int proc_udc_show(struct seq_file *s, void *_) + (tmp & UDC_ADD) ? " ADD" : "", + (tmp & UDC_DEF) ? " DEF" : "", + (tmp & UDC_ATT) ? " ATT" : ""); +- seq_printf(s, "sof %04x\n", UDC_SOF_REG); +- tmp = UDC_IRQ_EN_REG; ++ seq_printf(s, "sof %04x\n", omap_readw(UDC_SOF)); ++ tmp = omap_readw(UDC_IRQ_EN); + seq_printf(s, "irq_en %04x" FOURBITS "%s\n", tmp, + (tmp & UDC_SOF_IE) ? " sof" : "", + (tmp & UDC_EPN_RX_IE) ? " epn_rx" : "", + (tmp & UDC_EPN_TX_IE) ? " epn_tx" : "", + (tmp & UDC_DS_CHG_IE) ? " ds_chg" : "", + (tmp & UDC_EP0_IE) ? " ep0" : ""); +- tmp = UDC_IRQ_SRC_REG; ++ tmp = omap_readw(UDC_IRQ_SRC); + seq_printf(s, "irq_src %04x" EIGHTBITS "%s%s\n", tmp, + (tmp & UDC_TXN_DONE) ? " txn_done" : "", + (tmp & UDC_RXN_CNT) ? " rxn_cnt" : "", + (tmp & UDC_RXN_EOT) ? " rxn_eot" : "", +- (tmp & UDC_SOF) ? " sof" : "", ++ (tmp & UDC_IRQ_SOF) ? " sof" : "", + (tmp & UDC_EPN_RX) ? " epn_rx" : "", + (tmp & UDC_EPN_TX) ? " epn_tx" : "", + (tmp & UDC_DS_CHG) ? " ds_chg" : "", +@@ -2447,7 +2472,7 @@ static int proc_udc_show(struct seq_file *s, void *_) + if (use_dma) { + unsigned i; + +- tmp = UDC_DMA_IRQ_EN_REG; ++ tmp = omap_readw(UDC_DMA_IRQ_EN); + seq_printf(s, "dma_irq_en %04x%s" EIGHTBITS "\n", tmp, + (tmp & UDC_TX_DONE_IE(3)) ? " tx2_done" : "", + (tmp & UDC_RX_CNT_IE(3)) ? " rx2_cnt" : "", +@@ -2461,29 +2486,29 @@ static int proc_udc_show(struct seq_file *s, void *_) + (tmp & UDC_RX_CNT_IE(1)) ? " rx0_cnt" : "", + (tmp & UDC_RX_EOT_IE(1)) ? " rx0_eot" : ""); + +- tmp = UDC_RXDMA_CFG_REG; ++ tmp = omap_readw(UDC_RXDMA_CFG); + seq_printf(s, "rxdma_cfg %04x\n", tmp); + if (tmp) { + for (i = 0; i < 3; i++) { + if ((tmp & (0x0f << (i * 4))) == 0) + continue; + seq_printf(s, "rxdma[%d] %04x\n", i, +- UDC_RXDMA_REG(i + 1)); ++ omap_readw(UDC_RXDMA(i + 1))); + } + } +- tmp = UDC_TXDMA_CFG_REG; ++ tmp = omap_readw(UDC_TXDMA_CFG); + seq_printf(s, "txdma_cfg %04x\n", tmp); + if (tmp) { + for (i = 0; i < 3; i++) { + if (!(tmp & (0x0f << (i * 4)))) + continue; + seq_printf(s, "txdma[%d] %04x\n", i, +- UDC_TXDMA_REG(i + 1)); ++ omap_readw(UDC_TXDMA(i + 1))); + } + } + } + +- tmp = UDC_DEVSTAT_REG; ++ tmp = omap_readw(UDC_DEVSTAT); + if (tmp & UDC_ATT) { + proc_ep_show(s, &udc->ep[0]); + if (tmp & UDC_ADD) { +@@ -2535,7 +2560,7 @@ static inline void remove_proc_file(void) {} + * buffer space among the endpoints we'll be operating. + * + * NOTE: as of OMAP 1710 ES2.0, writing a new endpoint config when +- * UDC_SYSCON_1_REG.CFG_LOCK is set can now work. We won't use that ++ * UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that + * capability yet though. + */ + static unsigned __init +@@ -2597,9 +2622,9 @@ omap_ep_setup(char *name, u8 addr, u8 type, + name, addr, epn_rxtx, maxp, dbuf ? "x2" : "", buf); + + if (addr & USB_DIR_IN) +- UDC_EP_TX_REG(addr & 0xf) = epn_rxtx; ++ omap_writew(epn_rxtx, UDC_EP_TX(addr & 0xf)); + else +- UDC_EP_RX_REG(addr) = epn_rxtx; ++ omap_writew(epn_rxtx, UDC_EP_RX(addr)); + + /* next endpoint's buffer starts after this one's */ + buf += maxp; +@@ -2638,15 +2663,15 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) + unsigned tmp, buf; + + /* abolish any previous hardware state */ +- UDC_SYSCON1_REG = 0; +- UDC_IRQ_EN_REG = 0; +- UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; +- UDC_DMA_IRQ_EN_REG = 0; +- UDC_RXDMA_CFG_REG = 0; +- UDC_TXDMA_CFG_REG = 0; ++ omap_writew(0, UDC_SYSCON1); ++ omap_writew(0, UDC_IRQ_EN); ++ omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); ++ omap_writew(0, UDC_DMA_IRQ_EN); ++ omap_writew(0, UDC_RXDMA_CFG); ++ omap_writew(0, UDC_TXDMA_CFG); + + /* UDC_PULLUP_EN gates the chip clock */ +- // OTG_SYSCON_1_REG |= DEV_IDLE_EN; ++ // OTG_SYSCON_1 |= DEV_IDLE_EN; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) +@@ -2662,7 +2687,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) + udc->gadget.name = driver_name; + + device_initialize(&udc->gadget.dev); +- strcpy (udc->gadget.dev.bus_id, "gadget"); ++ dev_set_name(&udc->gadget.dev, "gadget"); + udc->gadget.dev.release = omap_udc_release; + udc->gadget.dev.parent = &odev->dev; + if (use_dma) +@@ -2677,8 +2702,8 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) + + /* initially disable all non-ep0 endpoints */ + for (tmp = 1; tmp < 15; tmp++) { +- UDC_EP_RX_REG(tmp) = 0; +- UDC_EP_TX_REG(tmp) = 0; ++ omap_writew(0, UDC_EP_RX(tmp)); ++ omap_writew(0, UDC_EP_TX(tmp)); + } + + #define OMAP_BULK_EP(name,addr) \ +@@ -2763,7 +2788,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) + ERR("unsupported fifo_mode #%d\n", fifo_mode); + return -ENODEV; + } +- UDC_SYSCON1_REG = UDC_CFG_LOCK|UDC_SELF_PWR; ++ omap_writew(UDC_CFG_LOCK|UDC_SELF_PWR, UDC_SYSCON1); + INFO("fifo mode %d, %d bytes not used\n", fifo_mode, 2048 - buf); + return 0; + } +@@ -2807,7 +2832,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) + } + + INFO("OMAP UDC rev %d.%d%s\n", +- UDC_REV_REG >> 4, UDC_REV_REG & 0xf, ++ omap_readw(UDC_REV) >> 4, omap_readw(UDC_REV) & 0xf, + config->otg ? ", Mini-AB" : ""); + + /* use the mode given to us by board init code */ +@@ -2822,12 +2847,12 @@ static int __init omap_udc_probe(struct platform_device *pdev) + * know when to turn PULLUP_EN on/off; and that + * means we always "need" the 48MHz clock. + */ +- u32 tmp = FUNC_MUX_CTRL_0_REG; +- +- FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510; ++ u32 tmp = omap_readl(FUNC_MUX_CTRL_0); ++ tmp &= ~VBUS_CTRL_1510; ++ omap_writel(tmp, FUNC_MUX_CTRL_0); + tmp |= VBUS_MODE_1510; + tmp &= ~VBUS_CTRL_1510; +- FUNC_MUX_CTRL_0_REG = tmp; ++ omap_writel(tmp, FUNC_MUX_CTRL_0); + } + } else { + /* The transceiver may package some GPIO logic or handle +@@ -2907,7 +2932,7 @@ known: + #endif + + /* starting with omap1710 es2.0, clear toggle is a separate bit */ +- if (UDC_REV_REG >= 0x61) ++ if (omap_readw(UDC_REV) >= 0x61) + udc->clr_halt = UDC_RESET_EP | UDC_CLRDATA_TOGGLE; + else + udc->clr_halt = UDC_RESET_EP; +@@ -3005,7 +3030,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) + put_device(udc->transceiver->dev); + udc->transceiver = NULL; + } +- UDC_SYSCON1_REG = 0; ++ omap_writew(0, UDC_SYSCON1); + + remove_proc_file(); + +@@ -3036,7 +3061,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) + * + * REVISIT we should probably reject suspend requests when there's a host + * session active, rather than disconnecting, at least on boards that can +- * report VBUS irqs (UDC_DEVSTAT_REG.UDC_ATT). And in any case, we need to ++ * report VBUS irqs (UDC_DEVSTAT.UDC_ATT). And in any case, we need to + * make host resumes and VBUS detection trigger OMAP wakeup events; that + * may involve talking to an external transceiver (e.g. isp1301). + */ +@@ -3045,14 +3070,14 @@ static int omap_udc_suspend(struct platform_device *dev, pm_message_t message) + { + u32 devstat; + +- devstat = UDC_DEVSTAT_REG; ++ devstat = omap_readw(UDC_DEVSTAT); + + /* we're requesting 48 MHz clock if the pullup is enabled + * (== we're attached to the host) and we're not suspended, + * which would prevent entry to deep sleep... + */ + if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) { +- WARN("session active; suspend requires disconnect\n"); ++ WARNING("session active; suspend requires disconnect\n"); + omap_pullup(&udc->gadget, 0); + } + +diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h +index c6b9cbc..29edc51 100644 +--- a/drivers/usb/gadget/omap_udc.h ++++ b/drivers/usb/gadget/omap_udc.h +@@ -8,23 +8,22 @@ + /* + * USB device/endpoint management registers + */ +-#define UDC_REG(offset) __REG16(UDC_BASE + (offset)) + +-#define UDC_REV_REG UDC_REG(0x0) /* Revision */ +-#define UDC_EP_NUM_REG UDC_REG(0x4) /* Which endpoint */ ++#define UDC_REV (UDC_BASE + 0x0) /* Revision */ ++#define UDC_EP_NUM (UDC_BASE + 0x4) /* Which endpoint */ + # define UDC_SETUP_SEL (1 << 6) + # define UDC_EP_SEL (1 << 5) + # define UDC_EP_DIR (1 << 4) + /* low 4 bits for endpoint number */ +-#define UDC_DATA_REG UDC_REG(0x08) /* Endpoint FIFO */ +-#define UDC_CTRL_REG UDC_REG(0x0C) /* Endpoint control */ ++#define UDC_DATA (UDC_BASE + 0x08) /* Endpoint FIFO */ ++#define UDC_CTRL (UDC_BASE + 0x0C) /* Endpoint control */ + # define UDC_CLR_HALT (1 << 7) + # define UDC_SET_HALT (1 << 6) + # define UDC_CLRDATA_TOGGLE (1 << 3) + # define UDC_SET_FIFO_EN (1 << 2) + # define UDC_CLR_EP (1 << 1) + # define UDC_RESET_EP (1 << 0) +-#define UDC_STAT_FLG_REG UDC_REG(0x10) /* Endpoint status */ ++#define UDC_STAT_FLG (UDC_BASE + 0x10) /* Endpoint status */ + # define UDC_NO_RXPACKET (1 << 15) + # define UDC_MISS_IN (1 << 14) + # define UDC_DATA_FLUSH (1 << 13) +@@ -38,8 +37,8 @@ + # define UDC_FIFO_EN (1 << 2) + # define UDC_NON_ISO_FIFO_EMPTY (1 << 1) + # define UDC_NON_ISO_FIFO_FULL (1 << 0) +-#define UDC_RXFSTAT_REG UDC_REG(0x14) /* OUT bytecount */ +-#define UDC_SYSCON1_REG UDC_REG(0x18) /* System config 1 */ ++#define UDC_RXFSTAT (UDC_BASE + 0x14) /* OUT bytecount */ ++#define UDC_SYSCON1 (UDC_BASE + 0x18) /* System config 1 */ + # define UDC_CFG_LOCK (1 << 8) + # define UDC_DATA_ENDIAN (1 << 7) + # define UDC_DMA_ENDIAN (1 << 6) +@@ -48,12 +47,12 @@ + # define UDC_SELF_PWR (1 << 2) + # define UDC_SOFF_DIS (1 << 1) + # define UDC_PULLUP_EN (1 << 0) +-#define UDC_SYSCON2_REG UDC_REG(0x1C) /* System config 2 */ ++#define UDC_SYSCON2 (UDC_BASE + 0x1C) /* System config 2 */ + # define UDC_RMT_WKP (1 << 6) + # define UDC_STALL_CMD (1 << 5) + # define UDC_DEV_CFG (1 << 3) + # define UDC_CLR_CFG (1 << 2) +-#define UDC_DEVSTAT_REG UDC_REG(0x20) /* Device status */ ++#define UDC_DEVSTAT (UDC_BASE + 0x20) /* Device status */ + # define UDC_B_HNP_ENABLE (1 << 9) + # define UDC_A_HNP_SUPPORT (1 << 8) + # define UDC_A_ALT_HNP_SUPPORT (1 << 7) +@@ -64,26 +63,26 @@ + # define UDC_ADD (1 << 2) + # define UDC_DEF (1 << 1) + # define UDC_ATT (1 << 0) +-#define UDC_SOF_REG UDC_REG(0x24) /* Start of frame */ ++#define UDC_SOF (UDC_BASE + 0x24) /* Start of frame */ + # define UDC_FT_LOCK (1 << 12) + # define UDC_TS_OK (1 << 11) + # define UDC_TS 0x03ff +-#define UDC_IRQ_EN_REG UDC_REG(0x28) /* Interrupt enable */ ++#define UDC_IRQ_EN (UDC_BASE + 0x28) /* Interrupt enable */ + # define UDC_SOF_IE (1 << 7) + # define UDC_EPN_RX_IE (1 << 5) + # define UDC_EPN_TX_IE (1 << 4) + # define UDC_DS_CHG_IE (1 << 3) + # define UDC_EP0_IE (1 << 0) +-#define UDC_DMA_IRQ_EN_REG UDC_REG(0x2C) /* DMA irq enable */ ++#define UDC_DMA_IRQ_EN (UDC_BASE + 0x2C) /* DMA irq enable */ + /* rx/tx dma channels numbered 1-3 not 0-2 */ + # define UDC_TX_DONE_IE(n) (1 << (4 * (n) - 2)) + # define UDC_RX_CNT_IE(n) (1 << (4 * (n) - 3)) + # define UDC_RX_EOT_IE(n) (1 << (4 * (n) - 4)) +-#define UDC_IRQ_SRC_REG UDC_REG(0x30) /* Interrupt source */ ++#define UDC_IRQ_SRC (UDC_BASE + 0x30) /* Interrupt source */ + # define UDC_TXN_DONE (1 << 10) + # define UDC_RXN_CNT (1 << 9) + # define UDC_RXN_EOT (1 << 8) +-# define UDC_SOF (1 << 7) ++# define UDC_IRQ_SOF (1 << 7) + # define UDC_EPN_RX (1 << 5) + # define UDC_EPN_TX (1 << 4) + # define UDC_DS_CHG (1 << 3) +@@ -91,41 +90,41 @@ + # define UDC_EP0_RX (1 << 1) + # define UDC_EP0_TX (1 << 0) + # define UDC_IRQ_SRC_MASK 0x7bf +-#define UDC_EPN_STAT_REG UDC_REG(0x34) /* EP irq status */ +-#define UDC_DMAN_STAT_REG UDC_REG(0x38) /* DMA irq status */ ++#define UDC_EPN_STAT (UDC_BASE + 0x34) /* EP irq status */ ++#define UDC_DMAN_STAT (UDC_BASE + 0x38) /* DMA irq status */ + # define UDC_DMA_RX_SB (1 << 12) + # define UDC_DMA_RX_SRC(x) (((x)>>8) & 0xf) + # define UDC_DMA_TX_SRC(x) (((x)>>0) & 0xf) + + + /* DMA configuration registers: up to three channels in each direction. */ +-#define UDC_RXDMA_CFG_REG UDC_REG(0x40) /* 3 eps for RX DMA */ ++#define UDC_RXDMA_CFG (UDC_BASE + 0x40) /* 3 eps for RX DMA */ + # define UDC_DMA_REQ (1 << 12) +-#define UDC_TXDMA_CFG_REG UDC_REG(0x44) /* 3 eps for TX DMA */ +-#define UDC_DATA_DMA_REG UDC_REG(0x48) /* rx/tx fifo addr */ ++#define UDC_TXDMA_CFG (UDC_BASE + 0x44) /* 3 eps for TX DMA */ ++#define UDC_DATA_DMA (UDC_BASE + 0x48) /* rx/tx fifo addr */ + + /* rx/tx dma control, numbering channels 1-3 not 0-2 */ +-#define UDC_TXDMA_REG(chan) UDC_REG(0x50 - 4 + 4 * (chan)) ++#define UDC_TXDMA(chan) (UDC_BASE + 0x50 - 4 + 4 * (chan)) + # define UDC_TXN_EOT (1 << 15) /* bytes vs packets */ + # define UDC_TXN_START (1 << 14) /* start transfer */ + # define UDC_TXN_TSC 0x03ff /* units in xfer */ +-#define UDC_RXDMA_REG(chan) UDC_REG(0x60 - 4 + 4 * (chan)) ++#define UDC_RXDMA(chan) (UDC_BASE + 0x60 - 4 + 4 * (chan)) + # define UDC_RXN_STOP (1 << 15) /* enable EOT irq */ + # define UDC_RXN_TC 0x00ff /* packets in xfer */ + + + /* + * Endpoint configuration registers (used before CFG_LOCK is set) +- * UDC_EP_TX_REG(0) is unused ++ * UDC_EP_TX(0) is unused + */ +-#define UDC_EP_RX_REG(endpoint) UDC_REG(0x80 + (endpoint)*4) ++#define UDC_EP_RX(endpoint) (UDC_BASE + 0x80 + (endpoint)*4) + # define UDC_EPN_RX_VALID (1 << 15) + # define UDC_EPN_RX_DB (1 << 14) + /* buffer size in bits 13, 12 */ + # define UDC_EPN_RX_ISO (1 << 11) + /* buffer pointer in low 11 bits */ +-#define UDC_EP_TX_REG(endpoint) UDC_REG(0xc0 + (endpoint)*4) +- /* same bitfields as in RX_REG */ ++#define UDC_EP_TX(endpoint) (UDC_BASE + 0xc0 + (endpoint)*4) ++ /* same bitfields as in RX */ + + /*-------------------------------------------------------------------------*/ + +@@ -189,20 +188,20 @@ struct omap_udc { + #endif + + #define ERR(stuff...) pr_err("udc: " stuff) +-#define WARN(stuff...) pr_warning("udc: " stuff) ++#define WARNING(stuff...) pr_warning("udc: " stuff) + #define INFO(stuff...) pr_info("udc: " stuff) + #define DBG(stuff...) pr_debug("udc: " stuff) + + /*-------------------------------------------------------------------------*/ + +-#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0) +-#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */ ++/* MOD_CONF_CTRL_0 */ ++#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */ + +-#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0) ++/* FUNC_MUX_CTRL_0 */ + #define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */ + #define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */ + +-#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) +-#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f) ++#define HMC_1510 ((omap_readl(MOD_CONF_CTRL_0) >> 1) & 0x3f) ++#define HMC_1610 (omap_readl(OTG_SYSCON_2) & 0x3f) + #define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610) + +diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c +index 76be75e..e009008 100644 +--- a/drivers/usb/gadget/printer.c ++++ b/drivers/usb/gadget/printer.c +@@ -179,7 +179,7 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); + + #define ERROR(dev, fmt, args...) \ + xprintk(dev, KERN_ERR, fmt, ## args) +-#define WARN(dev, fmt, args...) \ ++#define WARNING(dev, fmt, args...) \ + xprintk(dev, KERN_WARNING, fmt, ## args) + #define INFO(dev, fmt, args...) \ + xprintk(dev, KERN_INFO, fmt, ## args) +@@ -462,6 +462,7 @@ printer_open(struct inode *inode, struct file *fd) + unsigned long flags; + int ret = -EBUSY; + ++ lock_kernel(); + dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); + + spin_lock_irqsave(&dev->lock, flags); +@@ -477,7 +478,7 @@ printer_open(struct inode *inode, struct file *fd) + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "printer_open returned %x\n", ret); +- ++ unlock_kernel(); + return ret; + } + +@@ -827,9 +828,8 @@ printer_poll(struct file *fd, poll_table *wait) + return status; + } + +-static int +-printer_ioctl(struct inode *inode, struct file *fd, unsigned int code, +- unsigned long arg) ++static long ++printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) + { + struct printer_dev *dev = fd->private_data; + unsigned long flags; +@@ -868,7 +868,7 @@ static struct file_operations printer_io_operations = { + .write = printer_write, + .fsync = printer_fsync, + .poll = printer_poll, +- .ioctl = printer_ioctl, ++ .unlocked_ioctl = printer_ioctl, + .release = printer_close + }; + +@@ -1360,8 +1360,8 @@ printer_bind(struct usb_gadget *gadget) + + + /* Setup the sysfs files for the printer gadget. */ +- dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, +- "g_printer"); ++ dev->pdev = device_create_drvdata(usb_gadget_class, NULL, ++ g_printer_devno, NULL, "g_printer"); + if (IS_ERR(dev->pdev)) { + ERROR(dev, "Failed to create device: g_printer\n"); + goto fail; +diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c +new file mode 100644 +index 0000000..da6e93c +--- /dev/null ++++ b/drivers/usb/gadget/pxa25x_udc.c +@@ -0,0 +1,2390 @@ ++/* ++ * Intel PXA25x and IXP4xx on-chip full speed USB device controllers ++ * ++ * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) ++ * Copyright (C) 2003 Robert Schwebel, Pengutronix ++ * Copyright (C) 2003 Benedikt Spranger, Pengutronix ++ * Copyright (C) 2003 David Brownell ++ * Copyright (C) 2003 Joshua Wise ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/ioport.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/timer.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/mm.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++#include <linux/irq.h> ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/seq_file.h> ++#include <linux/debugfs.h> ++#include <linux/io.h> ++ ++#include <asm/byteorder.h> ++#include <asm/dma.h> ++#include <asm/gpio.h> ++#include <asm/system.h> ++#include <asm/mach-types.h> ++#include <asm/unaligned.h> ++ ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++ ++/* ++ * This driver is PXA25x only. Grab the right register definitions. ++ */ ++#ifdef CONFIG_ARCH_PXA ++#include <mach/pxa25x-udc.h> ++#endif ++ ++#include <asm/mach/udc_pxa2xx.h> ++ ++ ++/* ++ * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x ++ * series processors. The UDC for the IXP 4xx series is very similar. ++ * There are fifteen endpoints, in addition to ep0. ++ * ++ * Such controller drivers work with a gadget driver. The gadget driver ++ * returns descriptors, implements configuration and data protocols used ++ * by the host to interact with this device, and allocates endpoints to ++ * the different protocol interfaces. The controller driver virtualizes ++ * usb hardware so that the gadget drivers will be more portable. ++ * ++ * This UDC hardware wants to implement a bit too much USB protocol, so ++ * it constrains the sorts of USB configuration change events that work. ++ * The errata for these chips are misleading; some "fixed" bugs from ++ * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. ++ * ++ * Note that the UDC hardware supports DMA (except on IXP) but that's ++ * not used here. IN-DMA (to host) is simple enough, when the data is ++ * suitably aligned (16 bytes) ... the network stack doesn't do that, ++ * other software can. OUT-DMA is buggy in most chip versions, as well ++ * as poorly designed (data toggle not automatic). So this driver won't ++ * bother using DMA. (Mostly-working IN-DMA support was available in ++ * kernels before 2.6.23, but was never enabled or well tested.) ++ */ ++ ++#define DRIVER_VERSION "30-June-2007" ++#define DRIVER_DESC "PXA 25x USB Device Controller driver" ++ ++ ++static const char driver_name [] = "pxa25x_udc"; ++ ++static const char ep0name [] = "ep0"; ++ ++ ++#ifdef CONFIG_ARCH_IXP4XX ++ ++/* cpu-specific register addresses are compiled in to this code */ ++#ifdef CONFIG_ARCH_PXA ++#error "Can't configure both IXP and PXA" ++#endif ++ ++/* IXP doesn't yet support <linux/clk.h> */ ++#define clk_get(dev,name) NULL ++#define clk_enable(clk) do { } while (0) ++#define clk_disable(clk) do { } while (0) ++#define clk_put(clk) do { } while (0) ++ ++#endif ++ ++#include "pxa25x_udc.h" ++ ++ ++#ifdef CONFIG_USB_PXA25X_SMALL ++#define SIZE_STR " (small)" ++#else ++#define SIZE_STR "" ++#endif ++ ++/* --------------------------------------------------------------------------- ++ * endpoint related parts of the api to the usb controller hardware, ++ * used by gadget driver; and the inner talker-to-hardware core. ++ * --------------------------------------------------------------------------- ++ */ ++ ++static void pxa25x_ep_fifo_flush (struct usb_ep *ep); ++static void nuke (struct pxa25x_ep *, int status); ++ ++/* one GPIO should be used to detect VBUS from the host */ ++static int is_vbus_present(void) ++{ ++ struct pxa2xx_udc_mach_info *mach = the_controller->mach; ++ ++ if (mach->gpio_vbus) { ++ int value = gpio_get_value(mach->gpio_vbus); ++ return mach->gpio_vbus_inverted ? !value : value; ++ } ++ if (mach->udc_is_connected) ++ return mach->udc_is_connected(); ++ return 1; ++} ++ ++/* one GPIO should control a D+ pullup, so host sees this device (or not) */ ++static void pullup_off(void) ++{ ++ struct pxa2xx_udc_mach_info *mach = the_controller->mach; ++ int off_level = mach->gpio_pullup_inverted; ++ ++ if (mach->gpio_pullup) ++ gpio_set_value(mach->gpio_pullup, off_level); ++ else if (mach->udc_command) ++ mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); ++} ++ ++static void pullup_on(void) ++{ ++ struct pxa2xx_udc_mach_info *mach = the_controller->mach; ++ int on_level = !mach->gpio_pullup_inverted; ++ ++ if (mach->gpio_pullup) ++ gpio_set_value(mach->gpio_pullup, on_level); ++ else if (mach->udc_command) ++ mach->udc_command(PXA2XX_UDC_CMD_CONNECT); ++} ++ ++static void pio_irq_enable(int bEndpointAddress) ++{ ++ bEndpointAddress &= 0xf; ++ if (bEndpointAddress < 8) ++ UICR0 &= ~(1 << bEndpointAddress); ++ else { ++ bEndpointAddress -= 8; ++ UICR1 &= ~(1 << bEndpointAddress); ++ } ++} ++ ++static void pio_irq_disable(int bEndpointAddress) ++{ ++ bEndpointAddress &= 0xf; ++ if (bEndpointAddress < 8) ++ UICR0 |= 1 << bEndpointAddress; ++ else { ++ bEndpointAddress -= 8; ++ UICR1 |= 1 << bEndpointAddress; ++ } ++} ++ ++/* The UDCCR reg contains mask and interrupt status bits, ++ * so using '|=' isn't safe as it may ack an interrupt. ++ */ ++#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE) ++ ++static inline void udc_set_mask_UDCCR(int mask) ++{ ++ UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); ++} ++ ++static inline void udc_clear_mask_UDCCR(int mask) ++{ ++ UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); ++} ++ ++static inline void udc_ack_int_UDCCR(int mask) ++{ ++ /* udccr contains the bits we dont want to change */ ++ __u32 udccr = UDCCR & UDCCR_MASK_BITS; ++ ++ UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); ++} ++ ++/* ++ * endpoint enable/disable ++ * ++ * we need to verify the descriptors used to enable endpoints. since pxa25x ++ * endpoint configurations are fixed, and are pretty much always enabled, ++ * there's not a lot to manage here. ++ * ++ * because pxa25x can't selectively initialize bulk (or interrupt) endpoints, ++ * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except ++ * for a single interface (with only the default altsetting) and for gadget ++ * drivers that don't halt endpoints (not reset by set_interface). that also ++ * means that if you use ISO, you must violate the USB spec rule that all ++ * iso endpoints must be in non-default altsettings. ++ */ ++static int pxa25x_ep_enable (struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct pxa25x_ep *ep; ++ struct pxa25x_udc *dev; ++ ++ ep = container_of (_ep, struct pxa25x_ep, ep); ++ if (!_ep || !desc || ep->desc || _ep->name == ep0name ++ || desc->bDescriptorType != USB_DT_ENDPOINT ++ || ep->bEndpointAddress != desc->bEndpointAddress ++ || ep->fifo_size < le16_to_cpu ++ (desc->wMaxPacketSize)) { ++ DMSG("%s, bad ep or descriptor\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* xfer types must match, except that interrupt ~= bulk */ ++ if (ep->bmAttributes != desc->bmAttributes ++ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK ++ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { ++ DMSG("%s, %s type mismatch\n", __func__, _ep->name); ++ return -EINVAL; ++ } ++ ++ /* hardware _could_ do smaller, but driver doesn't */ ++ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK ++ && le16_to_cpu (desc->wMaxPacketSize) ++ != BULK_FIFO_SIZE) ++ || !desc->wMaxPacketSize) { ++ DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); ++ return -ERANGE; ++ } ++ ++ dev = ep->dev; ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { ++ DMSG("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ ep->desc = desc; ++ ep->stopped = 0; ++ ep->pio_irqs = 0; ++ ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); ++ ++ /* flush fifo (mostly for OUT buffers) */ ++ pxa25x_ep_fifo_flush (_ep); ++ ++ /* ... reset halt state too, if we could ... */ ++ ++ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); ++ return 0; ++} ++ ++static int pxa25x_ep_disable (struct usb_ep *_ep) ++{ ++ struct pxa25x_ep *ep; ++ unsigned long flags; ++ ++ ep = container_of (_ep, struct pxa25x_ep, ep); ++ if (!_ep || !ep->desc) { ++ DMSG("%s, %s not enabled\n", __func__, ++ _ep ? ep->ep.name : NULL); ++ return -EINVAL; ++ } ++ local_irq_save(flags); ++ ++ nuke (ep, -ESHUTDOWN); ++ ++ /* flush fifo (mostly for IN buffers) */ ++ pxa25x_ep_fifo_flush (_ep); ++ ++ ep->desc = NULL; ++ ep->stopped = 1; ++ ++ local_irq_restore(flags); ++ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* for the pxa25x, these can just wrap kmalloc/kfree. gadget drivers ++ * must still pass correctly initialized endpoints, since other controller ++ * drivers may care about how it's currently set up (dma issues etc). ++ */ ++ ++/* ++ * pxa25x_ep_alloc_request - allocate a request data structure ++ */ ++static struct usb_request * ++pxa25x_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) ++{ ++ struct pxa25x_request *req; ++ ++ req = kzalloc(sizeof(*req), gfp_flags); ++ if (!req) ++ return NULL; ++ ++ INIT_LIST_HEAD (&req->queue); ++ return &req->req; ++} ++ ++ ++/* ++ * pxa25x_ep_free_request - deallocate a request data structure ++ */ ++static void ++pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct pxa25x_request *req; ++ ++ req = container_of (_req, struct pxa25x_request, req); ++ WARN_ON(!list_empty (&req->queue)); ++ kfree(req); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * done - retire a request; caller blocked irqs ++ */ ++static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status) ++{ ++ unsigned stopped = ep->stopped; ++ ++ list_del_init(&req->queue); ++ ++ if (likely (req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", ++ ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ req->req.complete(&ep->ep, &req->req); ++ ep->stopped = stopped; ++} ++ ++ ++static inline void ep0_idle (struct pxa25x_udc *dev) ++{ ++ dev->ep0state = EP0_IDLE; ++} ++ ++static int ++write_packet(volatile u32 *uddr, struct pxa25x_request *req, unsigned max) ++{ ++ u8 *buf; ++ unsigned length, count; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetch(buf); ++ ++ /* how big will this packet be? */ ++ length = min(req->req.length - req->req.actual, max); ++ req->req.actual += length; ++ ++ count = length; ++ while (likely(count--)) ++ *uddr = *buf++; ++ ++ return length; ++} ++ ++/* ++ * write to an IN endpoint fifo, as many packets as possible. ++ * irqs will use this to write the rest later. ++ * caller guarantees at least one packet buffer is ready (or a zlp). ++ */ ++static int ++write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) ++{ ++ unsigned max; ++ ++ max = le16_to_cpu(ep->desc->wMaxPacketSize); ++ do { ++ unsigned count; ++ int is_last, is_short; ++ ++ count = write_packet(ep->reg_uddr, req, max); ++ ++ /* last packet is usually short (or a zlp) */ ++ if (unlikely (count != max)) ++ is_last = is_short = 1; ++ else { ++ if (likely(req->req.length != req->req.actual) ++ || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ /* interrupt/iso maxpacket may not fill the fifo */ ++ is_short = unlikely (max < ep->fifo_size); ++ } ++ ++ DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", ++ ep->ep.name, count, ++ is_last ? "/L" : "", is_short ? "/S" : "", ++ req->req.length - req->req.actual, req); ++ ++ /* let loose that packet. maybe try writing another one, ++ * double buffering might work. TSP, TPC, and TFS ++ * bit values are the same for all normal IN endpoints. ++ */ ++ *ep->reg_udccs = UDCCS_BI_TPC; ++ if (is_short) ++ *ep->reg_udccs = UDCCS_BI_TSP; ++ ++ /* requests complete when all IN data is in the FIFO */ ++ if (is_last) { ++ done (ep, req, 0); ++ if (list_empty(&ep->queue)) ++ pio_irq_disable (ep->bEndpointAddress); ++ return 1; ++ } ++ ++ // TODO experiment: how robust can fifo mode tweaking be? ++ // double buffering is off in the default fifo mode, which ++ // prevents TFS from being set here. ++ ++ } while (*ep->reg_udccs & UDCCS_BI_TFS); ++ return 0; ++} ++ ++/* caller asserts req->pending (ep0 irq status nyet cleared); starts ++ * ep0 data stage. these chips want very simple state transitions. ++ */ ++static inline ++void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag) ++{ ++ UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR; ++ USIR0 = USIR0_IR0; ++ dev->req_pending = 0; ++ DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", ++ __func__, tag, UDCCS0, flags); ++} ++ ++static int ++write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) ++{ ++ unsigned count; ++ int is_short; ++ ++ count = write_packet(&UDDR0, req, EP0_FIFO_SIZE); ++ ep->dev->stats.write.bytes += count; ++ ++ /* last packet "must be" short (or a zlp) */ ++ is_short = (count != EP0_FIFO_SIZE); ++ ++ DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, ++ req->req.length - req->req.actual, req); ++ ++ if (unlikely (is_short)) { ++ if (ep->dev->req_pending) ++ ep0start(ep->dev, UDCCS0_IPR, "short IN"); ++ else ++ UDCCS0 = UDCCS0_IPR; ++ ++ count = req->req.length; ++ done (ep, req, 0); ++ ep0_idle(ep->dev); ++#ifndef CONFIG_ARCH_IXP4XX ++#if 1 ++ /* This seems to get rid of lost status irqs in some cases: ++ * host responds quickly, or next request involves config ++ * change automagic, or should have been hidden, or ... ++ * ++ * FIXME get rid of all udelays possible... ++ */ ++ if (count >= EP0_FIFO_SIZE) { ++ count = 100; ++ do { ++ if ((UDCCS0 & UDCCS0_OPR) != 0) { ++ /* clear OPR, generate ack */ ++ UDCCS0 = UDCCS0_OPR; ++ break; ++ } ++ count--; ++ udelay(1); ++ } while (count); ++ } ++#endif ++#endif ++ } else if (ep->dev->req_pending) ++ ep0start(ep->dev, 0, "IN"); ++ return is_short; ++} ++ ++ ++/* ++ * read_fifo - unload packet(s) from the fifo we use for usb OUT ++ * transfers and put them into the request. caller should have made ++ * sure there's at least one packet ready. ++ * ++ * returns true if the request completed because of short packet or the ++ * request buffer having filled (and maybe overran till end-of-packet). ++ */ ++static int ++read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) ++{ ++ for (;;) { ++ u32 udccs; ++ u8 *buf; ++ unsigned bufferspace, count, is_short; ++ ++ /* make sure there's a packet in the FIFO. ++ * UDCCS_{BO,IO}_RPC are all the same bit value. ++ * UDCCS_{BO,IO}_RNE are all the same bit value. ++ */ ++ udccs = *ep->reg_udccs; ++ if (unlikely ((udccs & UDCCS_BO_RPC) == 0)) ++ break; ++ buf = req->req.buf + req->req.actual; ++ prefetchw(buf); ++ bufferspace = req->req.length - req->req.actual; ++ ++ /* read all bytes from this packet */ ++ if (likely (udccs & UDCCS_BO_RNE)) { ++ count = 1 + (0x0ff & *ep->reg_ubcr); ++ req->req.actual += min (count, bufferspace); ++ } else /* zlp */ ++ count = 0; ++ is_short = (count < ep->ep.maxpacket); ++ DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", ++ ep->ep.name, udccs, count, ++ is_short ? "/S" : "", ++ req, req->req.actual, req->req.length); ++ while (likely (count-- != 0)) { ++ u8 byte = (u8) *ep->reg_uddr; ++ ++ if (unlikely (bufferspace == 0)) { ++ /* this happens when the driver's buffer ++ * is smaller than what the host sent. ++ * discard the extra data. ++ */ ++ if (req->req.status != -EOVERFLOW) ++ DMSG("%s overflow %d\n", ++ ep->ep.name, count); ++ req->req.status = -EOVERFLOW; ++ } else { ++ *buf++ = byte; ++ bufferspace--; ++ } ++ } ++ *ep->reg_udccs = UDCCS_BO_RPC; ++ /* RPC/RSP/RNE could now reflect the other packet buffer */ ++ ++ /* iso is one request per packet */ ++ if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { ++ if (udccs & UDCCS_IO_ROF) ++ req->req.status = -EHOSTUNREACH; ++ /* more like "is_done" */ ++ is_short = 1; ++ } ++ ++ /* completion */ ++ if (is_short || req->req.actual == req->req.length) { ++ done (ep, req, 0); ++ if (list_empty(&ep->queue)) ++ pio_irq_disable (ep->bEndpointAddress); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ } ++ return 0; ++} ++ ++/* ++ * special ep0 version of the above. no UBCR0 or double buffering; status ++ * handshaking is magic. most device protocols don't need control-OUT. ++ * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other ++ * protocols do use them. ++ */ ++static int ++read_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) ++{ ++ u8 *buf, byte; ++ unsigned bufferspace; ++ ++ buf = req->req.buf + req->req.actual; ++ bufferspace = req->req.length - req->req.actual; ++ ++ while (UDCCS0 & UDCCS0_RNE) { ++ byte = (u8) UDDR0; ++ ++ if (unlikely (bufferspace == 0)) { ++ /* this happens when the driver's buffer ++ * is smaller than what the host sent. ++ * discard the extra data. ++ */ ++ if (req->req.status != -EOVERFLOW) ++ DMSG("%s overflow\n", ep->ep.name); ++ req->req.status = -EOVERFLOW; ++ } else { ++ *buf++ = byte; ++ req->req.actual++; ++ bufferspace--; ++ } ++ } ++ ++ UDCCS0 = UDCCS0_OPR | UDCCS0_IPR; ++ ++ /* completion */ ++ if (req->req.actual >= req->req.length) ++ return 1; ++ ++ /* finished that packet. the next one may be waiting... */ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) ++{ ++ struct pxa25x_request *req; ++ struct pxa25x_ep *ep; ++ struct pxa25x_udc *dev; ++ unsigned long flags; ++ ++ req = container_of(_req, struct pxa25x_request, req); ++ if (unlikely (!_req || !_req->complete || !_req->buf ++ || !list_empty(&req->queue))) { ++ DMSG("%s, bad params\n", __func__); ++ return -EINVAL; ++ } ++ ++ ep = container_of(_ep, struct pxa25x_ep, ep); ++ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { ++ DMSG("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ if (unlikely (!dev->driver ++ || dev->gadget.speed == USB_SPEED_UNKNOWN)) { ++ DMSG("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ /* iso is always one packet per request, that's the only way ++ * we can report per-packet status. that also helps with dma. ++ */ ++ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC ++ && req->req.length > le16_to_cpu ++ (ep->desc->wMaxPacketSize))) ++ return -EMSGSIZE; ++ ++ DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", ++ _ep->name, _req, _req->length, _req->buf); ++ ++ local_irq_save(flags); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++ /* kickstart this i/o queue? */ ++ if (list_empty(&ep->queue) && !ep->stopped) { ++ if (ep->desc == NULL/* ep0 */) { ++ unsigned length = _req->length; ++ ++ switch (dev->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ dev->stats.write.ops++; ++ if (write_ep0_fifo(ep, req)) ++ req = NULL; ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ dev->stats.read.ops++; ++ /* messy ... */ ++ if (dev->req_config) { ++ DBG(DBG_VERBOSE, "ep0 config ack%s\n", ++ dev->has_cfr ? "" : " raced"); ++ if (dev->has_cfr) ++ UDCCFR = UDCCFR_AREN|UDCCFR_ACM ++ |UDCCFR_MB1; ++ done(ep, req, 0); ++ dev->ep0state = EP0_END_XFER; ++ local_irq_restore (flags); ++ return 0; ++ } ++ if (dev->req_pending) ++ ep0start(dev, UDCCS0_IPR, "OUT"); ++ if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0 ++ && read_ep0_fifo(ep, req))) { ++ ep0_idle(dev); ++ done(ep, req, 0); ++ req = NULL; ++ } ++ break; ++ ++ default: ++ DMSG("ep0 i/o, odd state %d\n", dev->ep0state); ++ local_irq_restore (flags); ++ return -EL2HLT; ++ } ++ /* can the FIFO can satisfy the request immediately? */ ++ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { ++ if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 ++ && write_fifo(ep, req)) ++ req = NULL; ++ } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 ++ && read_fifo(ep, req)) { ++ req = NULL; ++ } ++ ++ if (likely (req && ep->desc)) ++ pio_irq_enable(ep->bEndpointAddress); ++ } ++ ++ /* pio or dma irq handler advances the queue. */ ++ if (likely(req != NULL)) ++ list_add_tail(&req->queue, &ep->queue); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++ ++/* ++ * nuke - dequeue ALL requests ++ */ ++static void nuke(struct pxa25x_ep *ep, int status) ++{ ++ struct pxa25x_request *req; ++ ++ /* called with irqs blocked */ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, ++ struct pxa25x_request, ++ queue); ++ done(ep, req, status); ++ } ++ if (ep->desc) ++ pio_irq_disable (ep->bEndpointAddress); ++} ++ ++ ++/* dequeue JUST ONE request */ ++static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct pxa25x_ep *ep; ++ struct pxa25x_request *req; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct pxa25x_ep, ep); ++ if (!_ep || ep->ep.name == ep0name) ++ return -EINVAL; ++ ++ local_irq_save(flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry (req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ if (&req->req != _req) { ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ ++ done(ep, req, -ECONNRESET); ++ ++ local_irq_restore(flags); ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) ++{ ++ struct pxa25x_ep *ep; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct pxa25x_ep, ep); ++ if (unlikely (!_ep ++ || (!ep->desc && ep->ep.name != ep0name)) ++ || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { ++ DMSG("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ if (value == 0) { ++ /* this path (reset toggle+halt) is needed to implement ++ * SET_INTERFACE on normal hardware. but it can't be ++ * done from software on the PXA UDC, and the hardware ++ * forgets to do it as part of SET_INTERFACE automagic. ++ */ ++ DMSG("only host can clear %s halt\n", _ep->name); ++ return -EROFS; ++ } ++ ++ local_irq_save(flags); ++ ++ if ((ep->bEndpointAddress & USB_DIR_IN) != 0 ++ && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 ++ || !list_empty(&ep->queue))) { ++ local_irq_restore(flags); ++ return -EAGAIN; ++ } ++ ++ /* FST bit is the same for control, bulk in, bulk out, interrupt in */ ++ *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; ++ ++ /* ep0 needs special care */ ++ if (!ep->desc) { ++ start_watchdog(ep->dev); ++ ep->dev->req_pending = 0; ++ ep->dev->ep0state = EP0_STALL; ++ ++ /* and bulk/intr endpoints like dropping stalls too */ ++ } else { ++ unsigned i; ++ for (i = 0; i < 1000; i += 20) { ++ if (*ep->reg_udccs & UDCCS_BI_SST) ++ break; ++ udelay(20); ++ } ++ } ++ local_irq_restore(flags); ++ ++ DBG(DBG_VERBOSE, "%s halt\n", _ep->name); ++ return 0; ++} ++ ++static int pxa25x_ep_fifo_status(struct usb_ep *_ep) ++{ ++ struct pxa25x_ep *ep; ++ ++ ep = container_of(_ep, struct pxa25x_ep, ep); ++ if (!_ep) { ++ DMSG("%s, bad ep\n", __func__); ++ return -ENODEV; ++ } ++ /* pxa can't report unclaimed bytes from IN fifos */ ++ if ((ep->bEndpointAddress & USB_DIR_IN) != 0) ++ return -EOPNOTSUPP; ++ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN ++ || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) ++ return 0; ++ else ++ return (*ep->reg_ubcr & 0xfff) + 1; ++} ++ ++static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) ++{ ++ struct pxa25x_ep *ep; ++ ++ ep = container_of(_ep, struct pxa25x_ep, ep); ++ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { ++ DMSG("%s, bad ep\n", __func__); ++ return; ++ } ++ ++ /* toggle and halt bits stay unchanged */ ++ ++ /* for OUT, just read and discard the FIFO contents. */ ++ if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { ++ while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) ++ (void) *ep->reg_uddr; ++ return; ++ } ++ ++ /* most IN status is the same, but ISO can't stall */ ++ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR ++ | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) ++ ? 0 : UDCCS_BI_SST; ++} ++ ++ ++static struct usb_ep_ops pxa25x_ep_ops = { ++ .enable = pxa25x_ep_enable, ++ .disable = pxa25x_ep_disable, ++ ++ .alloc_request = pxa25x_ep_alloc_request, ++ .free_request = pxa25x_ep_free_request, ++ ++ .queue = pxa25x_ep_queue, ++ .dequeue = pxa25x_ep_dequeue, ++ ++ .set_halt = pxa25x_ep_set_halt, ++ .fifo_status = pxa25x_ep_fifo_status, ++ .fifo_flush = pxa25x_ep_fifo_flush, ++}; ++ ++ ++/* --------------------------------------------------------------------------- ++ * device-scoped parts of the api to the usb controller hardware ++ * --------------------------------------------------------------------------- ++ */ ++ ++static int pxa25x_udc_get_frame(struct usb_gadget *_gadget) ++{ ++ return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff); ++} ++ ++static int pxa25x_udc_wakeup(struct usb_gadget *_gadget) ++{ ++ /* host may not have enabled remote wakeup */ ++ if ((UDCCS0 & UDCCS0_DRWF) == 0) ++ return -EHOSTUNREACH; ++ udc_set_mask_UDCCR(UDCCR_RSM); ++ return 0; ++} ++ ++static void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *); ++static void udc_enable (struct pxa25x_udc *); ++static void udc_disable(struct pxa25x_udc *); ++ ++/* We disable the UDC -- and its 48 MHz clock -- whenever it's not ++ * in active use. ++ */ ++static int pullup(struct pxa25x_udc *udc) ++{ ++ int is_active = udc->vbus && udc->pullup && !udc->suspended; ++ DMSG("%s\n", is_active ? "active" : "inactive"); ++ if (is_active) { ++ if (!udc->active) { ++ udc->active = 1; ++ /* Enable clock for USB device */ ++ clk_enable(udc->clk); ++ udc_enable(udc); ++ } ++ } else { ++ if (udc->active) { ++ if (udc->gadget.speed != USB_SPEED_UNKNOWN) { ++ DMSG("disconnect %s\n", udc->driver ++ ? udc->driver->driver.name ++ : "(no driver)"); ++ stop_activity(udc, udc->driver); ++ } ++ udc_disable(udc); ++ /* Disable clock for USB device */ ++ clk_disable(udc->clk); ++ udc->active = 0; ++ } ++ ++ } ++ return 0; ++} ++ ++/* VBUS reporting logically comes from a transceiver */ ++static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active) ++{ ++ struct pxa25x_udc *udc; ++ ++ udc = container_of(_gadget, struct pxa25x_udc, gadget); ++ udc->vbus = (is_active != 0); ++ DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); ++ pullup(udc); ++ return 0; ++} ++ ++/* drivers may have software control over D+ pullup */ ++static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) ++{ ++ struct pxa25x_udc *udc; ++ ++ udc = container_of(_gadget, struct pxa25x_udc, gadget); ++ ++ /* not all boards support pullup control */ ++ if (!udc->mach->gpio_pullup && !udc->mach->udc_command) ++ return -EOPNOTSUPP; ++ ++ udc->pullup = (is_active != 0); ++ pullup(udc); ++ return 0; ++} ++ ++static const struct usb_gadget_ops pxa25x_udc_ops = { ++ .get_frame = pxa25x_udc_get_frame, ++ .wakeup = pxa25x_udc_wakeup, ++ .vbus_session = pxa25x_udc_vbus_session, ++ .pullup = pxa25x_udc_pullup, ++ ++ // .vbus_draw ... boards may consume current from VBUS, up to ++ // 100-500mA based on config. the 500uA suspend ceiling means ++ // that exclusively vbus-powered PXA designs violate USB specs. ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_USB_GADGET_DEBUG_FS ++ ++static int ++udc_seq_show(struct seq_file *m, void *_d) ++{ ++ struct pxa25x_udc *dev = m->private; ++ unsigned long flags; ++ int i; ++ u32 tmp; ++ ++ local_irq_save(flags); ++ ++ /* basic device status */ ++ seq_printf(m, DRIVER_DESC "\n" ++ "%s version: %s\nGadget driver: %s\nHost %s\n\n", ++ driver_name, DRIVER_VERSION SIZE_STR "(pio)", ++ dev->driver ? dev->driver->driver.name : "(none)", ++ is_vbus_present() ? "full speed" : "disconnected"); ++ ++ /* registers for device and ep0 */ ++ seq_printf(m, ++ "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", ++ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); ++ ++ tmp = UDCCR; ++ seq_printf(m, ++ "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, ++ (tmp & UDCCR_REM) ? " rem" : "", ++ (tmp & UDCCR_RSTIR) ? " rstir" : "", ++ (tmp & UDCCR_SRM) ? " srm" : "", ++ (tmp & UDCCR_SUSIR) ? " susir" : "", ++ (tmp & UDCCR_RESIR) ? " resir" : "", ++ (tmp & UDCCR_RSM) ? " rsm" : "", ++ (tmp & UDCCR_UDA) ? " uda" : "", ++ (tmp & UDCCR_UDE) ? " ude" : ""); ++ ++ tmp = UDCCS0; ++ seq_printf(m, ++ "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, ++ (tmp & UDCCS0_SA) ? " sa" : "", ++ (tmp & UDCCS0_RNE) ? " rne" : "", ++ (tmp & UDCCS0_FST) ? " fst" : "", ++ (tmp & UDCCS0_SST) ? " sst" : "", ++ (tmp & UDCCS0_DRWF) ? " dwrf" : "", ++ (tmp & UDCCS0_FTF) ? " ftf" : "", ++ (tmp & UDCCS0_IPR) ? " ipr" : "", ++ (tmp & UDCCS0_OPR) ? " opr" : ""); ++ ++ if (dev->has_cfr) { ++ tmp = UDCCFR; ++ seq_printf(m, ++ "udccfr %02X =%s%s\n", tmp, ++ (tmp & UDCCFR_AREN) ? " aren" : "", ++ (tmp & UDCCFR_ACM) ? " acm" : ""); ++ } ++ ++ if (!is_vbus_present() || !dev->driver) ++ goto done; ++ ++ seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", ++ dev->stats.write.bytes, dev->stats.write.ops, ++ dev->stats.read.bytes, dev->stats.read.ops, ++ dev->stats.irqs); ++ ++ /* dump endpoint queues */ ++ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { ++ struct pxa25x_ep *ep = &dev->ep [i]; ++ struct pxa25x_request *req; ++ ++ if (i != 0) { ++ const struct usb_endpoint_descriptor *desc; ++ ++ desc = ep->desc; ++ if (!desc) ++ continue; ++ tmp = *dev->ep [i].reg_udccs; ++ seq_printf(m, ++ "%s max %d %s udccs %02x irqs %lu\n", ++ ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), ++ "pio", tmp, ep->pio_irqs); ++ /* TODO translate all five groups of udccs bits! */ ++ ++ } else /* ep0 should only have one transfer queued */ ++ seq_printf(m, "ep0 max 16 pio irqs %lu\n", ++ ep->pio_irqs); ++ ++ if (list_empty(&ep->queue)) { ++ seq_printf(m, "\t(nothing queued)\n"); ++ continue; ++ } ++ list_for_each_entry(req, &ep->queue, queue) { ++ seq_printf(m, ++ "\treq %p len %d/%d buf %p\n", ++ &req->req, req->req.actual, ++ req->req.length, req->req.buf); ++ } ++ } ++ ++done: ++ local_irq_restore(flags); ++ return 0; ++} ++ ++static int ++udc_debugfs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, udc_seq_show, inode->i_private); ++} ++ ++static const struct file_operations debug_fops = { ++ .open = udc_debugfs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++#define create_debug_files(dev) \ ++ do { \ ++ dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \ ++ S_IRUGO, NULL, dev, &debug_fops); \ ++ } while (0) ++#define remove_debug_files(dev) \ ++ do { \ ++ if (dev->debugfs_udc) \ ++ debugfs_remove(dev->debugfs_udc); \ ++ } while (0) ++ ++#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ ++ ++#define create_debug_files(dev) do {} while (0) ++#define remove_debug_files(dev) do {} while (0) ++ ++#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * udc_disable - disable USB device controller ++ */ ++static void udc_disable(struct pxa25x_udc *dev) ++{ ++ /* block all irqs */ ++ udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); ++ UICR0 = UICR1 = 0xff; ++ UFNRH = UFNRH_SIM; ++ ++ /* if hardware supports it, disconnect from usb */ ++ pullup_off(); ++ ++ udc_clear_mask_UDCCR(UDCCR_UDE); ++ ++ ep0_idle (dev); ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++} ++ ++ ++/* ++ * udc_reinit - initialize software state ++ */ ++static void udc_reinit(struct pxa25x_udc *dev) ++{ ++ u32 i; ++ ++ /* device/ep0 records init */ ++ INIT_LIST_HEAD (&dev->gadget.ep_list); ++ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); ++ dev->ep0state = EP0_IDLE; ++ ++ /* basic endpoint records init */ ++ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { ++ struct pxa25x_ep *ep = &dev->ep[i]; ++ ++ if (i != 0) ++ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); ++ ++ ep->desc = NULL; ++ ep->stopped = 0; ++ INIT_LIST_HEAD (&ep->queue); ++ ep->pio_irqs = 0; ++ } ++ ++ /* the rest was statically initialized, and is read-only */ ++} ++ ++/* until it's enabled, this UDC should be completely invisible ++ * to any USB host. ++ */ ++static void udc_enable (struct pxa25x_udc *dev) ++{ ++ udc_clear_mask_UDCCR(UDCCR_UDE); ++ ++ /* try to clear these bits before we enable the udc */ ++ udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); ++ ++ ep0_idle(dev); ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ dev->stats.irqs = 0; ++ ++ /* ++ * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: ++ * - enable UDC ++ * - if RESET is already in progress, ack interrupt ++ * - unmask reset interrupt ++ */ ++ udc_set_mask_UDCCR(UDCCR_UDE); ++ if (!(UDCCR & UDCCR_UDA)) ++ udc_ack_int_UDCCR(UDCCR_RSTIR); ++ ++ if (dev->has_cfr /* UDC_RES2 is defined */) { ++ /* pxa255 (a0+) can avoid a set_config race that could ++ * prevent gadget drivers from configuring correctly ++ */ ++ UDCCFR = UDCCFR_ACM | UDCCFR_MB1; ++ } else { ++ /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) ++ * which could result in missing packets and interrupts. ++ * supposedly one bit per endpoint, controlling whether it ++ * double buffers or not; ACM/AREN bits fit into the holes. ++ * zero bits (like USIR0_IRx) disable double buffering. ++ */ ++ UDC_RES1 = 0x00; ++ UDC_RES2 = 0x00; ++ } ++ ++ /* enable suspend/resume and reset irqs */ ++ udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); ++ ++ /* enable ep0 irqs */ ++ UICR0 &= ~UICR0_IM0; ++ ++ /* if hardware supports it, pullup D+ and wait for reset */ ++ pullup_on(); ++} ++ ++ ++/* when a driver is successfully registered, it will receive ++ * control requests including set_configuration(), which enables ++ * non-control requests. then usb traffic follows until a ++ * disconnect is reported. then a host may connect again, or ++ * the driver might get unbound. ++ */ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ struct pxa25x_udc *dev = the_controller; ++ int retval; ++ ++ if (!driver ++ || driver->speed < USB_SPEED_FULL ++ || !driver->bind ++ || !driver->disconnect ++ || !driver->setup) ++ return -EINVAL; ++ if (!dev) ++ return -ENODEV; ++ if (dev->driver) ++ return -EBUSY; ++ ++ /* first hook up the driver ... */ ++ dev->driver = driver; ++ dev->gadget.dev.driver = &driver->driver; ++ dev->pullup = 1; ++ ++ retval = device_add (&dev->gadget.dev); ++ if (retval) { ++fail: ++ dev->driver = NULL; ++ dev->gadget.dev.driver = NULL; ++ return retval; ++ } ++ retval = driver->bind(&dev->gadget); ++ if (retval) { ++ DMSG("bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ device_del (&dev->gadget.dev); ++ goto fail; ++ } ++ ++ /* ... then enable host detection and ep0; and we're ready ++ * for set_configuration as well as eventual disconnect. ++ */ ++ DMSG("registered gadget driver '%s'\n", driver->driver.name); ++ pullup(dev); ++ dump_state(dev); ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_register_driver); ++ ++static void ++stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) ++{ ++ int i; ++ ++ /* don't disconnect drivers more than once */ ++ if (dev->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = NULL; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { ++ struct pxa25x_ep *ep = &dev->ep[i]; ++ ++ ep->stopped = 1; ++ nuke(ep, -ESHUTDOWN); ++ } ++ del_timer_sync(&dev->timer); ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (driver) ++ driver->disconnect(&dev->gadget); ++ ++ /* re-init driver-visible data structures */ ++ udc_reinit(dev); ++} ++ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct pxa25x_udc *dev = the_controller; ++ ++ if (!dev) ++ return -ENODEV; ++ if (!driver || driver != dev->driver || !driver->unbind) ++ return -EINVAL; ++ ++ local_irq_disable(); ++ dev->pullup = 0; ++ pullup(dev); ++ stop_activity(dev, driver); ++ local_irq_enable(); ++ ++ driver->unbind(&dev->gadget); ++ dev->gadget.dev.driver = NULL; ++ dev->driver = NULL; ++ ++ device_del (&dev->gadget.dev); ++ ++ DMSG("unregistered gadget driver '%s'\n", driver->driver.name); ++ dump_state(dev); ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_ARCH_LUBBOCK ++ ++/* Lubbock has separate connect and disconnect irqs. More typical designs ++ * use one GPIO as the VBUS IRQ, and another to control the D+ pullup. ++ */ ++ ++static irqreturn_t ++lubbock_vbus_irq(int irq, void *_dev) ++{ ++ struct pxa25x_udc *dev = _dev; ++ int vbus; ++ ++ dev->stats.irqs++; ++ switch (irq) { ++ case LUBBOCK_USB_IRQ: ++ vbus = 1; ++ disable_irq(LUBBOCK_USB_IRQ); ++ enable_irq(LUBBOCK_USB_DISC_IRQ); ++ break; ++ case LUBBOCK_USB_DISC_IRQ: ++ vbus = 0; ++ disable_irq(LUBBOCK_USB_DISC_IRQ); ++ enable_irq(LUBBOCK_USB_IRQ); ++ break; ++ default: ++ return IRQ_NONE; ++ } ++ ++ pxa25x_udc_vbus_session(&dev->gadget, vbus); ++ return IRQ_HANDLED; ++} ++ ++#endif ++ ++static irqreturn_t udc_vbus_irq(int irq, void *_dev) ++{ ++ struct pxa25x_udc *dev = _dev; ++ int vbus = gpio_get_value(dev->mach->gpio_vbus); ++ ++ if (dev->mach->gpio_vbus_inverted) ++ vbus = !vbus; ++ ++ pxa25x_udc_vbus_session(&dev->gadget, vbus); ++ return IRQ_HANDLED; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline void clear_ep_state (struct pxa25x_udc *dev) ++{ ++ unsigned i; ++ ++ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint ++ * fifos, and pending transactions mustn't be continued in any case. ++ */ ++ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) ++ nuke(&dev->ep[i], -ECONNABORTED); ++} ++ ++static void udc_watchdog(unsigned long _dev) ++{ ++ struct pxa25x_udc *dev = (void *)_dev; ++ ++ local_irq_disable(); ++ if (dev->ep0state == EP0_STALL ++ && (UDCCS0 & UDCCS0_FST) == 0 ++ && (UDCCS0 & UDCCS0_SST) == 0) { ++ UDCCS0 = UDCCS0_FST|UDCCS0_FTF; ++ DBG(DBG_VERBOSE, "ep0 re-stall\n"); ++ start_watchdog(dev); ++ } ++ local_irq_enable(); ++} ++ ++static void handle_ep0 (struct pxa25x_udc *dev) ++{ ++ u32 udccs0 = UDCCS0; ++ struct pxa25x_ep *ep = &dev->ep [0]; ++ struct pxa25x_request *req; ++ union { ++ struct usb_ctrlrequest r; ++ u8 raw [8]; ++ u32 word [2]; ++ } u; ++ ++ if (list_empty(&ep->queue)) ++ req = NULL; ++ else ++ req = list_entry(ep->queue.next, struct pxa25x_request, queue); ++ ++ /* clear stall status */ ++ if (udccs0 & UDCCS0_SST) { ++ nuke(ep, -EPIPE); ++ UDCCS0 = UDCCS0_SST; ++ del_timer(&dev->timer); ++ ep0_idle(dev); ++ } ++ ++ /* previous request unfinished? non-error iff back-to-back ... */ ++ if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { ++ nuke(ep, 0); ++ del_timer(&dev->timer); ++ ep0_idle(dev); ++ } ++ ++ switch (dev->ep0state) { ++ case EP0_IDLE: ++ /* late-breaking status? */ ++ udccs0 = UDCCS0; ++ ++ /* start control request? */ ++ if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) ++ == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { ++ int i; ++ ++ nuke (ep, -EPROTO); ++ ++ /* read SETUP packet */ ++ for (i = 0; i < 8; i++) { ++ if (unlikely(!(UDCCS0 & UDCCS0_RNE))) { ++bad_setup: ++ DMSG("SETUP %d!\n", i); ++ goto stall; ++ } ++ u.raw [i] = (u8) UDDR0; ++ } ++ if (unlikely((UDCCS0 & UDCCS0_RNE) != 0)) ++ goto bad_setup; ++ ++got_setup: ++ DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", ++ u.r.bRequestType, u.r.bRequest, ++ le16_to_cpu(u.r.wValue), ++ le16_to_cpu(u.r.wIndex), ++ le16_to_cpu(u.r.wLength)); ++ ++ /* cope with automagic for some standard requests. */ ++ dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) ++ == USB_TYPE_STANDARD; ++ dev->req_config = 0; ++ dev->req_pending = 1; ++ switch (u.r.bRequest) { ++ /* hardware restricts gadget drivers here! */ ++ case USB_REQ_SET_CONFIGURATION: ++ if (u.r.bRequestType == USB_RECIP_DEVICE) { ++ /* reflect hardware's automagic ++ * up to the gadget driver. ++ */ ++config_change: ++ dev->req_config = 1; ++ clear_ep_state(dev); ++ /* if !has_cfr, there's no synch ++ * else use AREN (later) not SA|OPR ++ * USIR0_IR0 acts edge sensitive ++ */ ++ } ++ break; ++ /* ... and here, even more ... */ ++ case USB_REQ_SET_INTERFACE: ++ if (u.r.bRequestType == USB_RECIP_INTERFACE) { ++ /* udc hardware is broken by design: ++ * - altsetting may only be zero; ++ * - hw resets all interfaces' eps; ++ * - ep reset doesn't include halt(?). ++ */ ++ DMSG("broken set_interface (%d/%d)\n", ++ le16_to_cpu(u.r.wIndex), ++ le16_to_cpu(u.r.wValue)); ++ goto config_change; ++ } ++ break; ++ /* hardware was supposed to hide this */ ++ case USB_REQ_SET_ADDRESS: ++ if (u.r.bRequestType == USB_RECIP_DEVICE) { ++ ep0start(dev, 0, "address"); ++ return; ++ } ++ break; ++ } ++ ++ if (u.r.bRequestType & USB_DIR_IN) ++ dev->ep0state = EP0_IN_DATA_PHASE; ++ else ++ dev->ep0state = EP0_OUT_DATA_PHASE; ++ ++ i = dev->driver->setup(&dev->gadget, &u.r); ++ if (i < 0) { ++ /* hardware automagic preventing STALL... */ ++ if (dev->req_config) { ++ /* hardware sometimes neglects to tell ++ * tell us about config change events, ++ * so later ones may fail... ++ */ ++ WARNING("config change %02x fail %d?\n", ++ u.r.bRequest, i); ++ return; ++ /* TODO experiment: if has_cfr, ++ * hardware didn't ACK; maybe we ++ * could actually STALL! ++ */ ++ } ++ DBG(DBG_VERBOSE, "protocol STALL, " ++ "%02x err %d\n", UDCCS0, i); ++stall: ++ /* the watchdog timer helps deal with cases ++ * where udc seems to clear FST wrongly, and ++ * then NAKs instead of STALLing. ++ */ ++ ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); ++ start_watchdog(dev); ++ dev->ep0state = EP0_STALL; ++ ++ /* deferred i/o == no response yet */ ++ } else if (dev->req_pending) { ++ if (likely(dev->ep0state == EP0_IN_DATA_PHASE ++ || dev->req_std || u.r.wLength)) ++ ep0start(dev, 0, "defer"); ++ else ++ ep0start(dev, UDCCS0_IPR, "defer/IPR"); ++ } ++ ++ /* expect at least one data or status stage irq */ ++ return; ++ ++ } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) ++ == (UDCCS0_OPR|UDCCS0_SA))) { ++ unsigned i; ++ ++ /* pxa210/250 erratum 131 for B0/B1 says RNE lies. ++ * still observed on a pxa255 a0. ++ */ ++ DBG(DBG_VERBOSE, "e131\n"); ++ nuke(ep, -EPROTO); ++ ++ /* read SETUP data, but don't trust it too much */ ++ for (i = 0; i < 8; i++) ++ u.raw [i] = (u8) UDDR0; ++ if ((u.r.bRequestType & USB_RECIP_MASK) ++ > USB_RECIP_OTHER) ++ goto stall; ++ if (u.word [0] == 0 && u.word [1] == 0) ++ goto stall; ++ goto got_setup; ++ } else { ++ /* some random early IRQ: ++ * - we acked FST ++ * - IPR cleared ++ * - OPR got set, without SA (likely status stage) ++ */ ++ UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR); ++ } ++ break; ++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ ++ if (udccs0 & UDCCS0_OPR) { ++ UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; ++ DBG(DBG_VERBOSE, "ep0in premature status\n"); ++ if (req) ++ done(ep, req, 0); ++ ep0_idle(dev); ++ } else /* irq was IPR clearing */ { ++ if (req) { ++ /* this IN packet might finish the request */ ++ (void) write_ep0_fifo(ep, req); ++ } /* else IN token before response was written */ ++ } ++ break; ++ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ ++ if (udccs0 & UDCCS0_OPR) { ++ if (req) { ++ /* this OUT packet might finish the request */ ++ if (read_ep0_fifo(ep, req)) ++ done(ep, req, 0); ++ /* else more OUT packets expected */ ++ } /* else OUT token before read was issued */ ++ } else /* irq was IPR clearing */ { ++ DBG(DBG_VERBOSE, "ep0out premature status\n"); ++ if (req) ++ done(ep, req, 0); ++ ep0_idle(dev); ++ } ++ break; ++ case EP0_END_XFER: ++ if (req) ++ done(ep, req, 0); ++ /* ack control-IN status (maybe in-zlp was skipped) ++ * also appears after some config change events. ++ */ ++ if (udccs0 & UDCCS0_OPR) ++ UDCCS0 = UDCCS0_OPR; ++ ep0_idle(dev); ++ break; ++ case EP0_STALL: ++ UDCCS0 = UDCCS0_FST; ++ break; ++ } ++ USIR0 = USIR0_IR0; ++} ++ ++static void handle_ep(struct pxa25x_ep *ep) ++{ ++ struct pxa25x_request *req; ++ int is_in = ep->bEndpointAddress & USB_DIR_IN; ++ int completed; ++ u32 udccs, tmp; ++ ++ do { ++ completed = 0; ++ if (likely (!list_empty(&ep->queue))) ++ req = list_entry(ep->queue.next, ++ struct pxa25x_request, queue); ++ else ++ req = NULL; ++ ++ // TODO check FST handling ++ ++ udccs = *ep->reg_udccs; ++ if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ ++ tmp = UDCCS_BI_TUR; ++ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) ++ tmp |= UDCCS_BI_SST; ++ tmp &= udccs; ++ if (likely (tmp)) ++ *ep->reg_udccs = tmp; ++ if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) ++ completed = write_fifo(ep, req); ++ ++ } else { /* irq from RPC (or for ISO, ROF) */ ++ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) ++ tmp = UDCCS_BO_SST | UDCCS_BO_DME; ++ else ++ tmp = UDCCS_IO_ROF | UDCCS_IO_DME; ++ tmp &= udccs; ++ if (likely(tmp)) ++ *ep->reg_udccs = tmp; ++ ++ /* fifos can hold packets, ready for reading... */ ++ if (likely(req)) { ++ completed = read_fifo(ep, req); ++ } else ++ pio_irq_disable (ep->bEndpointAddress); ++ } ++ ep->pio_irqs++; ++ } while (completed); ++} ++ ++/* ++ * pxa25x_udc_irq - interrupt handler ++ * ++ * avoid delays in ep0 processing. the control handshaking isn't always ++ * under software control (pxa250c0 and the pxa255 are better), and delays ++ * could cause usb protocol errors. ++ */ ++static irqreturn_t ++pxa25x_udc_irq(int irq, void *_dev) ++{ ++ struct pxa25x_udc *dev = _dev; ++ int handled; ++ ++ dev->stats.irqs++; ++ do { ++ u32 udccr = UDCCR; ++ ++ handled = 0; ++ ++ /* SUSpend Interrupt Request */ ++ if (unlikely(udccr & UDCCR_SUSIR)) { ++ udc_ack_int_UDCCR(UDCCR_SUSIR); ++ handled = 1; ++ DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() ++ ? "" : "+disconnect"); ++ ++ if (!is_vbus_present()) ++ stop_activity(dev, dev->driver); ++ else if (dev->gadget.speed != USB_SPEED_UNKNOWN ++ && dev->driver ++ && dev->driver->suspend) ++ dev->driver->suspend(&dev->gadget); ++ ep0_idle (dev); ++ } ++ ++ /* RESume Interrupt Request */ ++ if (unlikely(udccr & UDCCR_RESIR)) { ++ udc_ack_int_UDCCR(UDCCR_RESIR); ++ handled = 1; ++ DBG(DBG_VERBOSE, "USB resume\n"); ++ ++ if (dev->gadget.speed != USB_SPEED_UNKNOWN ++ && dev->driver ++ && dev->driver->resume ++ && is_vbus_present()) ++ dev->driver->resume(&dev->gadget); ++ } ++ ++ /* ReSeT Interrupt Request - USB reset */ ++ if (unlikely(udccr & UDCCR_RSTIR)) { ++ udc_ack_int_UDCCR(UDCCR_RSTIR); ++ handled = 1; ++ ++ if ((UDCCR & UDCCR_UDA) == 0) { ++ DBG(DBG_VERBOSE, "USB reset start\n"); ++ ++ /* reset driver and endpoints, ++ * in case that's not yet done ++ */ ++ stop_activity (dev, dev->driver); ++ ++ } else { ++ DBG(DBG_VERBOSE, "USB reset end\n"); ++ dev->gadget.speed = USB_SPEED_FULL; ++ memset(&dev->stats, 0, sizeof dev->stats); ++ /* driver and endpoints are still reset */ ++ } ++ ++ } else { ++ u32 usir0 = USIR0 & ~UICR0; ++ u32 usir1 = USIR1 & ~UICR1; ++ int i; ++ ++ if (unlikely (!usir0 && !usir1)) ++ continue; ++ ++ DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0); ++ ++ /* control traffic */ ++ if (usir0 & USIR0_IR0) { ++ dev->ep[0].pio_irqs++; ++ handle_ep0(dev); ++ handled = 1; ++ } ++ ++ /* endpoint data transfers */ ++ for (i = 0; i < 8; i++) { ++ u32 tmp = 1 << i; ++ ++ if (i && (usir0 & tmp)) { ++ handle_ep(&dev->ep[i]); ++ USIR0 |= tmp; ++ handled = 1; ++ } ++ if (usir1 & tmp) { ++ handle_ep(&dev->ep[i+8]); ++ USIR1 |= tmp; ++ handled = 1; ++ } ++ } ++ } ++ ++ /* we could also ask for 1 msec SOF (SIR) interrupts */ ++ ++ } while (handled); ++ return IRQ_HANDLED; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void nop_release (struct device *dev) ++{ ++ DMSG("%s %s\n", __func__, dev_name(dev)); ++} ++ ++/* this uses load-time allocation and initialization (instead of ++ * doing it at run-time) to save code, eliminate fault paths, and ++ * be more obviously correct. ++ */ ++static struct pxa25x_udc memory = { ++ .gadget = { ++ .ops = &pxa25x_udc_ops, ++ .ep0 = &memory.ep[0].ep, ++ .name = driver_name, ++ .dev = { ++ .bus_id = "gadget", ++ .release = nop_release, ++ }, ++ }, ++ ++ /* control endpoint */ ++ .ep[0] = { ++ .ep = { ++ .name = ep0name, ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = EP0_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .reg_udccs = &UDCCS0, ++ .reg_uddr = &UDDR0, ++ }, ++ ++ /* first group of endpoints */ ++ .ep[1] = { ++ .ep = { ++ .name = "ep1in-bulk", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS1, ++ .reg_uddr = &UDDR1, ++ }, ++ .ep[2] = { ++ .ep = { ++ .name = "ep2out-bulk", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = 2, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS2, ++ .reg_ubcr = &UBCR2, ++ .reg_uddr = &UDDR2, ++ }, ++#ifndef CONFIG_USB_PXA25X_SMALL ++ .ep[3] = { ++ .ep = { ++ .name = "ep3in-iso", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = ISO_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = ISO_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 3, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ .reg_udccs = &UDCCS3, ++ .reg_uddr = &UDDR3, ++ }, ++ .ep[4] = { ++ .ep = { ++ .name = "ep4out-iso", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = ISO_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = ISO_FIFO_SIZE, ++ .bEndpointAddress = 4, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ .reg_udccs = &UDCCS4, ++ .reg_ubcr = &UBCR4, ++ .reg_uddr = &UDDR4, ++ }, ++ .ep[5] = { ++ .ep = { ++ .name = "ep5in-int", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = INT_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = INT_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 5, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .reg_udccs = &UDCCS5, ++ .reg_uddr = &UDDR5, ++ }, ++ ++ /* second group of endpoints */ ++ .ep[6] = { ++ .ep = { ++ .name = "ep6in-bulk", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 6, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS6, ++ .reg_uddr = &UDDR6, ++ }, ++ .ep[7] = { ++ .ep = { ++ .name = "ep7out-bulk", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = 7, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS7, ++ .reg_ubcr = &UBCR7, ++ .reg_uddr = &UDDR7, ++ }, ++ .ep[8] = { ++ .ep = { ++ .name = "ep8in-iso", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = ISO_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = ISO_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 8, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ .reg_udccs = &UDCCS8, ++ .reg_uddr = &UDDR8, ++ }, ++ .ep[9] = { ++ .ep = { ++ .name = "ep9out-iso", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = ISO_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = ISO_FIFO_SIZE, ++ .bEndpointAddress = 9, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ .reg_udccs = &UDCCS9, ++ .reg_ubcr = &UBCR9, ++ .reg_uddr = &UDDR9, ++ }, ++ .ep[10] = { ++ .ep = { ++ .name = "ep10in-int", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = INT_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = INT_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 10, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .reg_udccs = &UDCCS10, ++ .reg_uddr = &UDDR10, ++ }, ++ ++ /* third group of endpoints */ ++ .ep[11] = { ++ .ep = { ++ .name = "ep11in-bulk", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 11, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS11, ++ .reg_uddr = &UDDR11, ++ }, ++ .ep[12] = { ++ .ep = { ++ .name = "ep12out-bulk", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = 12, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS12, ++ .reg_ubcr = &UBCR12, ++ .reg_uddr = &UDDR12, ++ }, ++ .ep[13] = { ++ .ep = { ++ .name = "ep13in-iso", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = ISO_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = ISO_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 13, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ .reg_udccs = &UDCCS13, ++ .reg_uddr = &UDDR13, ++ }, ++ .ep[14] = { ++ .ep = { ++ .name = "ep14out-iso", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = ISO_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = ISO_FIFO_SIZE, ++ .bEndpointAddress = 14, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ .reg_udccs = &UDCCS14, ++ .reg_ubcr = &UBCR14, ++ .reg_uddr = &UDDR14, ++ }, ++ .ep[15] = { ++ .ep = { ++ .name = "ep15in-int", ++ .ops = &pxa25x_ep_ops, ++ .maxpacket = INT_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ .fifo_size = INT_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 15, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .reg_udccs = &UDCCS15, ++ .reg_uddr = &UDDR15, ++ }, ++#endif /* !CONFIG_USB_PXA25X_SMALL */ ++}; ++ ++#define CP15R0_VENDOR_MASK 0xffffe000 ++ ++#if defined(CONFIG_ARCH_PXA) ++#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */ ++ ++#elif defined(CONFIG_ARCH_IXP4XX) ++#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */ ++ ++#endif ++ ++#define CP15R0_PROD_MASK 0x000003f0 ++#define PXA25x 0x00000100 /* and PXA26x */ ++#define PXA210 0x00000120 ++ ++#define CP15R0_REV_MASK 0x0000000f ++ ++#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK) ++ ++#define PXA255_A0 0x00000106 /* or PXA260_B1 */ ++#define PXA250_C0 0x00000105 /* or PXA26x_B0 */ ++#define PXA250_B2 0x00000104 ++#define PXA250_B1 0x00000103 /* or PXA260_A0 */ ++#define PXA250_B0 0x00000102 ++#define PXA250_A1 0x00000101 ++#define PXA250_A0 0x00000100 ++ ++#define PXA210_C0 0x00000125 ++#define PXA210_B2 0x00000124 ++#define PXA210_B1 0x00000123 ++#define PXA210_B0 0x00000122 ++#define IXP425_A0 0x000001c1 ++#define IXP425_B0 0x000001f1 ++#define IXP465_AD 0x00000200 ++ ++/* ++ * probe - binds to the platform device ++ */ ++static int __init pxa25x_udc_probe(struct platform_device *pdev) ++{ ++ struct pxa25x_udc *dev = &memory; ++ int retval, vbus_irq, irq; ++ u32 chiprev; ++ ++ /* insist on Intel/ARM/XScale */ ++ asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); ++ if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { ++ pr_err("%s: not XScale!\n", driver_name); ++ return -ENODEV; ++ } ++ ++ /* trigger chiprev-specific logic */ ++ switch (chiprev & CP15R0_PRODREV_MASK) { ++#if defined(CONFIG_ARCH_PXA) ++ case PXA255_A0: ++ dev->has_cfr = 1; ++ break; ++ case PXA250_A0: ++ case PXA250_A1: ++ /* A0/A1 "not released"; ep 13, 15 unusable */ ++ /* fall through */ ++ case PXA250_B2: case PXA210_B2: ++ case PXA250_B1: case PXA210_B1: ++ case PXA250_B0: case PXA210_B0: ++ /* OUT-DMA is broken ... */ ++ /* fall through */ ++ case PXA250_C0: case PXA210_C0: ++ break; ++#elif defined(CONFIG_ARCH_IXP4XX) ++ case IXP425_A0: ++ case IXP425_B0: ++ case IXP465_AD: ++ dev->has_cfr = 1; ++ break; ++#endif ++ default: ++ pr_err("%s: unrecognized processor: %08x\n", ++ driver_name, chiprev); ++ /* iop3xx, ixp4xx, ... */ ++ return -ENODEV; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return -ENODEV; ++ ++ dev->clk = clk_get(&pdev->dev, "UDCCLK"); ++ if (IS_ERR(dev->clk)) { ++ retval = PTR_ERR(dev->clk); ++ goto err_clk; ++ } ++ ++ pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, ++ dev->has_cfr ? "" : " (!cfr)", ++ SIZE_STR "(pio)" ++ ); ++ ++ /* other non-static parts of init */ ++ dev->dev = &pdev->dev; ++ dev->mach = pdev->dev.platform_data; ++ ++ if (dev->mach->gpio_vbus) { ++ if ((retval = gpio_request(dev->mach->gpio_vbus, ++ "pxa25x_udc GPIO VBUS"))) { ++ dev_dbg(&pdev->dev, ++ "can't get vbus gpio %d, err: %d\n", ++ dev->mach->gpio_vbus, retval); ++ goto err_gpio_vbus; ++ } ++ gpio_direction_input(dev->mach->gpio_vbus); ++ vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); ++ } else ++ vbus_irq = 0; ++ ++ if (dev->mach->gpio_pullup) { ++ if ((retval = gpio_request(dev->mach->gpio_pullup, ++ "pca25x_udc GPIO PULLUP"))) { ++ dev_dbg(&pdev->dev, ++ "can't get pullup gpio %d, err: %d\n", ++ dev->mach->gpio_pullup, retval); ++ goto err_gpio_pullup; ++ } ++ gpio_direction_output(dev->mach->gpio_pullup, 0); ++ } ++ ++ init_timer(&dev->timer); ++ dev->timer.function = udc_watchdog; ++ dev->timer.data = (unsigned long) dev; ++ ++ device_initialize(&dev->gadget.dev); ++ dev->gadget.dev.parent = &pdev->dev; ++ dev->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ ++ the_controller = dev; ++ platform_set_drvdata(pdev, dev); ++ ++ udc_disable(dev); ++ udc_reinit(dev); ++ ++ dev->vbus = is_vbus_present(); ++ ++ /* irq setup after old hardware state is cleaned up */ ++ retval = request_irq(irq, pxa25x_udc_irq, ++ IRQF_DISABLED, driver_name, dev); ++ if (retval != 0) { ++ pr_err("%s: can't get irq %d, err %d\n", ++ driver_name, irq, retval); ++ goto err_irq1; ++ } ++ dev->got_irq = 1; ++ ++#ifdef CONFIG_ARCH_LUBBOCK ++ if (machine_is_lubbock()) { ++ retval = request_irq(LUBBOCK_USB_DISC_IRQ, ++ lubbock_vbus_irq, ++ IRQF_DISABLED | IRQF_SAMPLE_RANDOM, ++ driver_name, dev); ++ if (retval != 0) { ++ pr_err("%s: can't get irq %i, err %d\n", ++ driver_name, LUBBOCK_USB_DISC_IRQ, retval); ++lubbock_fail0: ++ goto err_irq_lub; ++ } ++ retval = request_irq(LUBBOCK_USB_IRQ, ++ lubbock_vbus_irq, ++ IRQF_DISABLED | IRQF_SAMPLE_RANDOM, ++ driver_name, dev); ++ if (retval != 0) { ++ pr_err("%s: can't get irq %i, err %d\n", ++ driver_name, LUBBOCK_USB_IRQ, retval); ++ free_irq(LUBBOCK_USB_DISC_IRQ, dev); ++ goto lubbock_fail0; ++ } ++ } else ++#endif ++ if (vbus_irq) { ++ retval = request_irq(vbus_irq, udc_vbus_irq, ++ IRQF_DISABLED | IRQF_SAMPLE_RANDOM | ++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ driver_name, dev); ++ if (retval != 0) { ++ pr_err("%s: can't get irq %i, err %d\n", ++ driver_name, vbus_irq, retval); ++ goto err_vbus_irq; ++ } ++ } ++ create_debug_files(dev); ++ ++ return 0; ++ ++ err_vbus_irq: ++#ifdef CONFIG_ARCH_LUBBOCK ++ free_irq(LUBBOCK_USB_DISC_IRQ, dev); ++ err_irq_lub: ++#endif ++ free_irq(irq, dev); ++ err_irq1: ++ if (dev->mach->gpio_pullup) ++ gpio_free(dev->mach->gpio_pullup); ++ err_gpio_pullup: ++ if (dev->mach->gpio_vbus) ++ gpio_free(dev->mach->gpio_vbus); ++ err_gpio_vbus: ++ clk_put(dev->clk); ++ err_clk: ++ return retval; ++} ++ ++static void pxa25x_udc_shutdown(struct platform_device *_dev) ++{ ++ pullup_off(); ++} ++ ++static int __exit pxa25x_udc_remove(struct platform_device *pdev) ++{ ++ struct pxa25x_udc *dev = platform_get_drvdata(pdev); ++ ++ if (dev->driver) ++ return -EBUSY; ++ ++ dev->pullup = 0; ++ pullup(dev); ++ ++ remove_debug_files(dev); ++ ++ if (dev->got_irq) { ++ free_irq(platform_get_irq(pdev, 0), dev); ++ dev->got_irq = 0; ++ } ++#ifdef CONFIG_ARCH_LUBBOCK ++ if (machine_is_lubbock()) { ++ free_irq(LUBBOCK_USB_DISC_IRQ, dev); ++ free_irq(LUBBOCK_USB_IRQ, dev); ++ } ++#endif ++ if (dev->mach->gpio_vbus) { ++ free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); ++ gpio_free(dev->mach->gpio_vbus); ++ } ++ if (dev->mach->gpio_pullup) ++ gpio_free(dev->mach->gpio_pullup); ++ ++ clk_put(dev->clk); ++ ++ platform_set_drvdata(pdev, NULL); ++ the_controller = NULL; ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_PM ++ ++/* USB suspend (controlled by the host) and system suspend (controlled ++ * by the PXA) don't necessarily work well together. If USB is active, ++ * the 48 MHz clock is required; so the system can't enter 33 MHz idle ++ * mode, or any deeper PM saving state. ++ * ++ * For now, we punt and forcibly disconnect from the USB host when PXA ++ * enters any suspend state. While we're disconnected, we always disable ++ * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. ++ * Boards without software pullup control shouldn't use those states. ++ * VBUS IRQs should probably be ignored so that the PXA device just acts ++ * "dead" to USB hosts until system resume. ++ */ ++static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct pxa25x_udc *udc = platform_get_drvdata(dev); ++ unsigned long flags; ++ ++ if (!udc->mach->gpio_pullup && !udc->mach->udc_command) ++ WARNING("USB host won't detect disconnect!\n"); ++ udc->suspended = 1; ++ ++ local_irq_save(flags); ++ pullup(udc); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++static int pxa25x_udc_resume(struct platform_device *dev) ++{ ++ struct pxa25x_udc *udc = platform_get_drvdata(dev); ++ unsigned long flags; ++ ++ udc->suspended = 0; ++ local_irq_save(flags); ++ pullup(udc); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++#else ++#define pxa25x_udc_suspend NULL ++#define pxa25x_udc_resume NULL ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct platform_driver udc_driver = { ++ .shutdown = pxa25x_udc_shutdown, ++ .remove = __exit_p(pxa25x_udc_remove), ++ .suspend = pxa25x_udc_suspend, ++ .resume = pxa25x_udc_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "pxa25x-udc", ++ }, ++}; ++ ++static int __init udc_init(void) ++{ ++ pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); ++ return platform_driver_probe(&udc_driver, pxa25x_udc_probe); ++} ++module_init(udc_init); ++ ++static void __exit udc_exit(void) ++{ ++ platform_driver_unregister(&udc_driver); ++} ++module_exit(udc_exit); ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:pxa25x-udc"); +diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h +new file mode 100644 +index 0000000..1d51aa2 +--- /dev/null ++++ b/drivers/usb/gadget/pxa25x_udc.h +@@ -0,0 +1,266 @@ ++/* ++ * Intel PXA25x on-chip full speed USB device controller ++ * ++ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix ++ * Copyright (C) 2003 David Brownell ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __LINUX_USB_GADGET_PXA25X_H ++#define __LINUX_USB_GADGET_PXA25X_H ++ ++#include <linux/types.h> ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* pxa25x has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ ++#define UFNRH_SIR (1 << 7) /* SOF interrupt request */ ++#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */ ++#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */ ++#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */ ++#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */ ++ ++/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ ++#define UDCCFR UDC_RES2 /* UDC Control Function Register */ ++#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */ ++#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */ ++ ++/* latest pxa255 errata define new "must be one" bits in UDCCFR */ ++#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM)) ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct pxa25x_udc; ++ ++struct pxa25x_ep { ++ struct usb_ep ep; ++ struct pxa25x_udc *dev; ++ ++ const struct usb_endpoint_descriptor *desc; ++ struct list_head queue; ++ unsigned long pio_irqs; ++ ++ unsigned short fifo_size; ++ u8 bEndpointAddress; ++ u8 bmAttributes; ++ ++ unsigned stopped : 1; ++ unsigned dma_fixup : 1; ++ ++ /* UDCCS = UDC Control/Status for this EP ++ * UBCR = UDC Byte Count Remaining (contents of OUT fifo) ++ * UDDR = UDC Endpoint Data Register (the fifo) ++ * DRCM = DMA Request Channel Map ++ */ ++ volatile u32 *reg_udccs; ++ volatile u32 *reg_ubcr; ++ volatile u32 *reg_uddr; ++}; ++ ++struct pxa25x_request { ++ struct usb_request req; ++ struct list_head queue; ++}; ++ ++enum ep0_state { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_END_XFER, ++ EP0_STALL, ++}; ++ ++#define EP0_FIFO_SIZE ((unsigned)16) ++#define BULK_FIFO_SIZE ((unsigned)64) ++#define ISO_FIFO_SIZE ((unsigned)256) ++#define INT_FIFO_SIZE ((unsigned)8) ++ ++struct udc_stats { ++ struct ep0stats { ++ unsigned long ops; ++ unsigned long bytes; ++ } read, write; ++ unsigned long irqs; ++}; ++ ++#ifdef CONFIG_USB_PXA25X_SMALL ++/* when memory's tight, SMALL config saves code+data. */ ++#define PXA_UDC_NUM_ENDPOINTS 3 ++#endif ++ ++#ifndef PXA_UDC_NUM_ENDPOINTS ++#define PXA_UDC_NUM_ENDPOINTS 16 ++#endif ++ ++struct pxa25x_udc { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ ++ enum ep0_state ep0state; ++ struct udc_stats stats; ++ unsigned got_irq : 1, ++ vbus : 1, ++ pullup : 1, ++ has_cfr : 1, ++ req_pending : 1, ++ req_std : 1, ++ req_config : 1, ++ suspended : 1, ++ active : 1; ++ ++#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) ++ struct timer_list timer; ++ ++ struct device *dev; ++ struct clk *clk; ++ struct pxa2xx_udc_mach_info *mach; ++ u64 dma_mask; ++ struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; ++ ++#ifdef CONFIG_USB_GADGET_DEBUG_FS ++ struct dentry *debugfs_udc; ++#endif ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_ARCH_LUBBOCK ++#include <mach/lubbock.h> ++/* lubbock can also report usb connect/disconnect irqs */ ++#endif ++ ++static struct pxa25x_udc *the_controller; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Debugging support vanishes in non-debug builds. DBG_NORMAL should be ++ * mostly silent during normal use/testing, with no timing side-effects. ++ */ ++#define DBG_NORMAL 1 /* error paths, device state transitions */ ++#define DBG_VERBOSE 2 /* add some success path trace info */ ++#define DBG_NOISY 3 /* ... even more: request level */ ++#define DBG_VERY_NOISY 4 /* ... even more: packet level */ ++ ++#define DMSG(stuff...) pr_debug("udc: " stuff) ++ ++#ifdef DEBUG ++ ++static int is_vbus_present(void); ++ ++static const char *state_name[] = { ++ "EP0_IDLE", ++ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", ++ "EP0_END_XFER", "EP0_STALL" ++}; ++ ++#ifdef VERBOSE_DEBUG ++# define UDC_DEBUG DBG_VERBOSE ++#else ++# define UDC_DEBUG DBG_NORMAL ++#endif ++ ++static void __maybe_unused ++dump_udccr(const char *label) ++{ ++ u32 udccr = UDCCR; ++ DMSG("%s %02X =%s%s%s%s%s%s%s%s\n", ++ label, udccr, ++ (udccr & UDCCR_REM) ? " rem" : "", ++ (udccr & UDCCR_RSTIR) ? " rstir" : "", ++ (udccr & UDCCR_SRM) ? " srm" : "", ++ (udccr & UDCCR_SUSIR) ? " susir" : "", ++ (udccr & UDCCR_RESIR) ? " resir" : "", ++ (udccr & UDCCR_RSM) ? " rsm" : "", ++ (udccr & UDCCR_UDA) ? " uda" : "", ++ (udccr & UDCCR_UDE) ? " ude" : ""); ++} ++ ++static void __maybe_unused ++dump_udccs0(const char *label) ++{ ++ u32 udccs0 = UDCCS0; ++ ++ DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n", ++ label, state_name[the_controller->ep0state], udccs0, ++ (udccs0 & UDCCS0_SA) ? " sa" : "", ++ (udccs0 & UDCCS0_RNE) ? " rne" : "", ++ (udccs0 & UDCCS0_FST) ? " fst" : "", ++ (udccs0 & UDCCS0_SST) ? " sst" : "", ++ (udccs0 & UDCCS0_DRWF) ? " dwrf" : "", ++ (udccs0 & UDCCS0_FTF) ? " ftf" : "", ++ (udccs0 & UDCCS0_IPR) ? " ipr" : "", ++ (udccs0 & UDCCS0_OPR) ? " opr" : ""); ++} ++ ++static void __maybe_unused ++dump_state(struct pxa25x_udc *dev) ++{ ++ u32 tmp; ++ unsigned i; ++ ++ DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", ++ is_vbus_present() ? "host " : "disconnected", ++ state_name[dev->ep0state], ++ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); ++ dump_udccr("udccr"); ++ if (dev->has_cfr) { ++ tmp = UDCCFR; ++ DMSG("udccfr %02X =%s%s\n", tmp, ++ (tmp & UDCCFR_AREN) ? " aren" : "", ++ (tmp & UDCCFR_ACM) ? " acm" : ""); ++ } ++ ++ if (!dev->driver) { ++ DMSG("no gadget driver bound\n"); ++ return; ++ } else ++ DMSG("ep0 driver '%s'\n", dev->driver->driver.name); ++ ++ if (!is_vbus_present()) ++ return; ++ ++ dump_udccs0 ("udccs0"); ++ DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", ++ dev->stats.write.bytes, dev->stats.write.ops, ++ dev->stats.read.bytes, dev->stats.read.ops); ++ ++ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { ++ if (dev->ep [i].desc == NULL) ++ continue; ++ DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs); ++ } ++} ++ ++#else ++ ++#define dump_udccr(x) do{}while(0) ++#define dump_udccs0(x) do{}while(0) ++#define dump_state(x) do{}while(0) ++ ++#define UDC_DEBUG ((unsigned)0) ++ ++#endif ++ ++#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) ++ ++#define ERR(stuff...) pr_err("udc: " stuff) ++#define WARNING(stuff...) pr_warning("udc: " stuff) ++#define INFO(stuff...) pr_info("udc: " stuff) ++ ++ ++#endif /* __LINUX_USB_GADGET_PXA25X_H */ +diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c +index e02bfd4..7cbc78a 100644 +--- a/drivers/usb/gadget/pxa27x_udc.c ++++ b/drivers/usb/gadget/pxa27x_udc.c +@@ -33,13 +33,13 @@ + #include <linux/irq.h> + + #include <asm/byteorder.h> +-#include <asm/hardware.h> ++#include <mach/hardware.h> + + #include <linux/usb.h> + #include <linux/usb/ch9.h> + #include <linux/usb/gadget.h> +- +-#include <asm/arch/udc.h> ++#include <mach/pxa2xx-regs.h> /* FIXME: for PSSR */ ++#include <mach/udc.h> + + #include "pxa27x_udc.h" + +@@ -1575,7 +1575,6 @@ static void udc_enable(struct pxa_udc *udc) + { + udc_writel(udc, UDCICR0, 0); + udc_writel(udc, UDCICR1, 0); +- udc_writel(udc, UP2OCR, UP2OCR_HXOE); + udc_clear_mask_UDCCR(udc, UDCCR_UDE); + + clk_enable(udc->clk); +@@ -1623,7 +1622,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) + struct pxa_udc *udc = the_controller; + int retval; + +- if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind ++ if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind + || !driver->disconnect || !driver->setup) + return -EINVAL; + if (!udc) +@@ -2360,18 +2359,19 @@ static int pxa_udc_resume(struct platform_device *_dev) + * Software must configure the USB OTG pad, UDC, and UHC + * to the state they were in before entering sleep mode. + */ +- PSSR |= PSSR_OTGPH; ++ if (cpu_is_pxa27x()) ++ PSSR |= PSSR_OTGPH; + + return 0; + } + #endif + + /* work with hotplug and coldplug */ +-MODULE_ALIAS("platform:pxa2xx-udc"); ++MODULE_ALIAS("platform:pxa27x-udc"); + + static struct platform_driver udc_driver = { + .driver = { +- .name = "pxa2xx-udc", ++ .name = "pxa27x-udc", + .owner = THIS_MODULE, + }, + .remove = __exit_p(pxa_udc_remove), +diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h +index 97453db..1d1b793 100644 +--- a/drivers/usb/gadget/pxa27x_udc.h ++++ b/drivers/usb/gadget/pxa27x_udc.h +@@ -484,12 +484,4 @@ static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget) + #define ep_warn(ep, fmt, arg...) \ + dev_warn(ep->dev->dev, "%s:%s:" fmt, EPNAME(ep), __func__, ## arg) + +-/* +- * Cannot include pxa-regs.h, as register names are similar. +- * So PSSR is redefined here. This should be removed once UDC registers will +- * be gone from pxa-regs.h. +- */ +-#define PSSR __REG(0x40F00004) /* Power Manager Sleep Status */ +-#define PSSR_OTGPH (1 << 6) /* OTG Peripheral Hold */ +- + #endif /* __LINUX_USB_GADGET_PXA27X_H */ +diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c +deleted file mode 100644 +index 08f699b..0000000 +--- a/drivers/usb/gadget/pxa2xx_udc.c ++++ /dev/null +@@ -1,2383 +0,0 @@ +-/* +- * linux/drivers/usb/gadget/pxa2xx_udc.c +- * Intel PXA25x and IXP4xx on-chip full speed USB device controllers +- * +- * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) +- * Copyright (C) 2003 Robert Schwebel, Pengutronix +- * Copyright (C) 2003 Benedikt Spranger, Pengutronix +- * Copyright (C) 2003 David Brownell +- * Copyright (C) 2003 Joshua Wise +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- * +- */ +- +-/* #define VERBOSE_DEBUG */ +- +-#include <linux/device.h> +-#include <linux/module.h> +-#include <linux/kernel.h> +-#include <linux/ioport.h> +-#include <linux/types.h> +-#include <linux/errno.h> +-#include <linux/delay.h> +-#include <linux/slab.h> +-#include <linux/init.h> +-#include <linux/timer.h> +-#include <linux/list.h> +-#include <linux/interrupt.h> +-#include <linux/mm.h> +-#include <linux/platform_device.h> +-#include <linux/dma-mapping.h> +-#include <linux/irq.h> +-#include <linux/clk.h> +-#include <linux/err.h> +-#include <linux/seq_file.h> +-#include <linux/debugfs.h> +- +-#include <asm/byteorder.h> +-#include <asm/dma.h> +-#include <asm/gpio.h> +-#include <asm/io.h> +-#include <asm/system.h> +-#include <asm/mach-types.h> +-#include <asm/unaligned.h> +-#include <asm/hardware.h> +- +-#include <linux/usb/ch9.h> +-#include <linux/usb/gadget.h> +- +-#include <asm/mach/udc_pxa2xx.h> +- +- +-/* +- * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x +- * series processors. The UDC for the IXP 4xx series is very similar. +- * There are fifteen endpoints, in addition to ep0. +- * +- * Such controller drivers work with a gadget driver. The gadget driver +- * returns descriptors, implements configuration and data protocols used +- * by the host to interact with this device, and allocates endpoints to +- * the different protocol interfaces. The controller driver virtualizes +- * usb hardware so that the gadget drivers will be more portable. +- * +- * This UDC hardware wants to implement a bit too much USB protocol, so +- * it constrains the sorts of USB configuration change events that work. +- * The errata for these chips are misleading; some "fixed" bugs from +- * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. +- * +- * Note that the UDC hardware supports DMA (except on IXP) but that's +- * not used here. IN-DMA (to host) is simple enough, when the data is +- * suitably aligned (16 bytes) ... the network stack doesn't do that, +- * other software can. OUT-DMA is buggy in most chip versions, as well +- * as poorly designed (data toggle not automatic). So this driver won't +- * bother using DMA. (Mostly-working IN-DMA support was available in +- * kernels before 2.6.23, but was never enabled or well tested.) +- */ +- +-#define DRIVER_VERSION "30-June-2007" +-#define DRIVER_DESC "PXA 25x USB Device Controller driver" +- +- +-static const char driver_name [] = "pxa2xx_udc"; +- +-static const char ep0name [] = "ep0"; +- +- +-#ifdef CONFIG_ARCH_IXP4XX +- +-/* cpu-specific register addresses are compiled in to this code */ +-#ifdef CONFIG_ARCH_PXA +-#error "Can't configure both IXP and PXA" +-#endif +- +-/* IXP doesn't yet support <linux/clk.h> */ +-#define clk_get(dev,name) NULL +-#define clk_enable(clk) do { } while (0) +-#define clk_disable(clk) do { } while (0) +-#define clk_put(clk) do { } while (0) +- +-#endif +- +-#include "pxa2xx_udc.h" +- +- +-#ifdef CONFIG_USB_PXA2XX_SMALL +-#define SIZE_STR " (small)" +-#else +-#define SIZE_STR "" +-#endif +- +-/* --------------------------------------------------------------------------- +- * endpoint related parts of the api to the usb controller hardware, +- * used by gadget driver; and the inner talker-to-hardware core. +- * --------------------------------------------------------------------------- +- */ +- +-static void pxa2xx_ep_fifo_flush (struct usb_ep *ep); +-static void nuke (struct pxa2xx_ep *, int status); +- +-/* one GPIO should be used to detect VBUS from the host */ +-static int is_vbus_present(void) +-{ +- struct pxa2xx_udc_mach_info *mach = the_controller->mach; +- +- if (mach->gpio_vbus) { +- int value = gpio_get_value(mach->gpio_vbus); +- return mach->gpio_vbus_inverted ? !value : value; +- } +- if (mach->udc_is_connected) +- return mach->udc_is_connected(); +- return 1; +-} +- +-/* one GPIO should control a D+ pullup, so host sees this device (or not) */ +-static void pullup_off(void) +-{ +- struct pxa2xx_udc_mach_info *mach = the_controller->mach; +- +- if (mach->gpio_pullup) +- gpio_set_value(mach->gpio_pullup, 0); +- else if (mach->udc_command) +- mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); +-} +- +-static void pullup_on(void) +-{ +- struct pxa2xx_udc_mach_info *mach = the_controller->mach; +- +- if (mach->gpio_pullup) +- gpio_set_value(mach->gpio_pullup, 1); +- else if (mach->udc_command) +- mach->udc_command(PXA2XX_UDC_CMD_CONNECT); +-} +- +-static void pio_irq_enable(int bEndpointAddress) +-{ +- bEndpointAddress &= 0xf; +- if (bEndpointAddress < 8) +- UICR0 &= ~(1 << bEndpointAddress); +- else { +- bEndpointAddress -= 8; +- UICR1 &= ~(1 << bEndpointAddress); +- } +-} +- +-static void pio_irq_disable(int bEndpointAddress) +-{ +- bEndpointAddress &= 0xf; +- if (bEndpointAddress < 8) +- UICR0 |= 1 << bEndpointAddress; +- else { +- bEndpointAddress -= 8; +- UICR1 |= 1 << bEndpointAddress; +- } +-} +- +-/* The UDCCR reg contains mask and interrupt status bits, +- * so using '|=' isn't safe as it may ack an interrupt. +- */ +-#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE) +- +-static inline void udc_set_mask_UDCCR(int mask) +-{ +- UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); +-} +- +-static inline void udc_clear_mask_UDCCR(int mask) +-{ +- UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); +-} +- +-static inline void udc_ack_int_UDCCR(int mask) +-{ +- /* udccr contains the bits we dont want to change */ +- __u32 udccr = UDCCR & UDCCR_MASK_BITS; +- +- UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); +-} +- +-/* +- * endpoint enable/disable +- * +- * we need to verify the descriptors used to enable endpoints. since pxa2xx +- * endpoint configurations are fixed, and are pretty much always enabled, +- * there's not a lot to manage here. +- * +- * because pxa2xx can't selectively initialize bulk (or interrupt) endpoints, +- * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except +- * for a single interface (with only the default altsetting) and for gadget +- * drivers that don't halt endpoints (not reset by set_interface). that also +- * means that if you use ISO, you must violate the USB spec rule that all +- * iso endpoints must be in non-default altsettings. +- */ +-static int pxa2xx_ep_enable (struct usb_ep *_ep, +- const struct usb_endpoint_descriptor *desc) +-{ +- struct pxa2xx_ep *ep; +- struct pxa2xx_udc *dev; +- +- ep = container_of (_ep, struct pxa2xx_ep, ep); +- if (!_ep || !desc || ep->desc || _ep->name == ep0name +- || desc->bDescriptorType != USB_DT_ENDPOINT +- || ep->bEndpointAddress != desc->bEndpointAddress +- || ep->fifo_size < le16_to_cpu +- (desc->wMaxPacketSize)) { +- DMSG("%s, bad ep or descriptor\n", __func__); +- return -EINVAL; +- } +- +- /* xfer types must match, except that interrupt ~= bulk */ +- if (ep->bmAttributes != desc->bmAttributes +- && ep->bmAttributes != USB_ENDPOINT_XFER_BULK +- && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { +- DMSG("%s, %s type mismatch\n", __func__, _ep->name); +- return -EINVAL; +- } +- +- /* hardware _could_ do smaller, but driver doesn't */ +- if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK +- && le16_to_cpu (desc->wMaxPacketSize) +- != BULK_FIFO_SIZE) +- || !desc->wMaxPacketSize) { +- DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); +- return -ERANGE; +- } +- +- dev = ep->dev; +- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { +- DMSG("%s, bogus device state\n", __func__); +- return -ESHUTDOWN; +- } +- +- ep->desc = desc; +- ep->stopped = 0; +- ep->pio_irqs = 0; +- ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); +- +- /* flush fifo (mostly for OUT buffers) */ +- pxa2xx_ep_fifo_flush (_ep); +- +- /* ... reset halt state too, if we could ... */ +- +- DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); +- return 0; +-} +- +-static int pxa2xx_ep_disable (struct usb_ep *_ep) +-{ +- struct pxa2xx_ep *ep; +- unsigned long flags; +- +- ep = container_of (_ep, struct pxa2xx_ep, ep); +- if (!_ep || !ep->desc) { +- DMSG("%s, %s not enabled\n", __func__, +- _ep ? ep->ep.name : NULL); +- return -EINVAL; +- } +- local_irq_save(flags); +- +- nuke (ep, -ESHUTDOWN); +- +- /* flush fifo (mostly for IN buffers) */ +- pxa2xx_ep_fifo_flush (_ep); +- +- ep->desc = NULL; +- ep->stopped = 1; +- +- local_irq_restore(flags); +- DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); +- return 0; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* for the pxa2xx, these can just wrap kmalloc/kfree. gadget drivers +- * must still pass correctly initialized endpoints, since other controller +- * drivers may care about how it's currently set up (dma issues etc). +- */ +- +-/* +- * pxa2xx_ep_alloc_request - allocate a request data structure +- */ +-static struct usb_request * +-pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) +-{ +- struct pxa2xx_request *req; +- +- req = kzalloc(sizeof(*req), gfp_flags); +- if (!req) +- return NULL; +- +- INIT_LIST_HEAD (&req->queue); +- return &req->req; +-} +- +- +-/* +- * pxa2xx_ep_free_request - deallocate a request data structure +- */ +-static void +-pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) +-{ +- struct pxa2xx_request *req; +- +- req = container_of (_req, struct pxa2xx_request, req); +- WARN_ON (!list_empty (&req->queue)); +- kfree(req); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * done - retire a request; caller blocked irqs +- */ +-static void done(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int status) +-{ +- unsigned stopped = ep->stopped; +- +- list_del_init(&req->queue); +- +- if (likely (req->req.status == -EINPROGRESS)) +- req->req.status = status; +- else +- status = req->req.status; +- +- if (status && status != -ESHUTDOWN) +- DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", +- ep->ep.name, &req->req, status, +- req->req.actual, req->req.length); +- +- /* don't modify queue heads during completion callback */ +- ep->stopped = 1; +- req->req.complete(&ep->ep, &req->req); +- ep->stopped = stopped; +-} +- +- +-static inline void ep0_idle (struct pxa2xx_udc *dev) +-{ +- dev->ep0state = EP0_IDLE; +-} +- +-static int +-write_packet(volatile u32 *uddr, struct pxa2xx_request *req, unsigned max) +-{ +- u8 *buf; +- unsigned length, count; +- +- buf = req->req.buf + req->req.actual; +- prefetch(buf); +- +- /* how big will this packet be? */ +- length = min(req->req.length - req->req.actual, max); +- req->req.actual += length; +- +- count = length; +- while (likely(count--)) +- *uddr = *buf++; +- +- return length; +-} +- +-/* +- * write to an IN endpoint fifo, as many packets as possible. +- * irqs will use this to write the rest later. +- * caller guarantees at least one packet buffer is ready (or a zlp). +- */ +-static int +-write_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) +-{ +- unsigned max; +- +- max = le16_to_cpu(ep->desc->wMaxPacketSize); +- do { +- unsigned count; +- int is_last, is_short; +- +- count = write_packet(ep->reg_uddr, req, max); +- +- /* last packet is usually short (or a zlp) */ +- if (unlikely (count != max)) +- is_last = is_short = 1; +- else { +- if (likely(req->req.length != req->req.actual) +- || req->req.zero) +- is_last = 0; +- else +- is_last = 1; +- /* interrupt/iso maxpacket may not fill the fifo */ +- is_short = unlikely (max < ep->fifo_size); +- } +- +- DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", +- ep->ep.name, count, +- is_last ? "/L" : "", is_short ? "/S" : "", +- req->req.length - req->req.actual, req); +- +- /* let loose that packet. maybe try writing another one, +- * double buffering might work. TSP, TPC, and TFS +- * bit values are the same for all normal IN endpoints. +- */ +- *ep->reg_udccs = UDCCS_BI_TPC; +- if (is_short) +- *ep->reg_udccs = UDCCS_BI_TSP; +- +- /* requests complete when all IN data is in the FIFO */ +- if (is_last) { +- done (ep, req, 0); +- if (list_empty(&ep->queue)) +- pio_irq_disable (ep->bEndpointAddress); +- return 1; +- } +- +- // TODO experiment: how robust can fifo mode tweaking be? +- // double buffering is off in the default fifo mode, which +- // prevents TFS from being set here. +- +- } while (*ep->reg_udccs & UDCCS_BI_TFS); +- return 0; +-} +- +-/* caller asserts req->pending (ep0 irq status nyet cleared); starts +- * ep0 data stage. these chips want very simple state transitions. +- */ +-static inline +-void ep0start(struct pxa2xx_udc *dev, u32 flags, const char *tag) +-{ +- UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR; +- USIR0 = USIR0_IR0; +- dev->req_pending = 0; +- DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", +- __func__, tag, UDCCS0, flags); +-} +- +-static int +-write_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) +-{ +- unsigned count; +- int is_short; +- +- count = write_packet(&UDDR0, req, EP0_FIFO_SIZE); +- ep->dev->stats.write.bytes += count; +- +- /* last packet "must be" short (or a zlp) */ +- is_short = (count != EP0_FIFO_SIZE); +- +- DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, +- req->req.length - req->req.actual, req); +- +- if (unlikely (is_short)) { +- if (ep->dev->req_pending) +- ep0start(ep->dev, UDCCS0_IPR, "short IN"); +- else +- UDCCS0 = UDCCS0_IPR; +- +- count = req->req.length; +- done (ep, req, 0); +- ep0_idle(ep->dev); +-#ifndef CONFIG_ARCH_IXP4XX +-#if 1 +- /* This seems to get rid of lost status irqs in some cases: +- * host responds quickly, or next request involves config +- * change automagic, or should have been hidden, or ... +- * +- * FIXME get rid of all udelays possible... +- */ +- if (count >= EP0_FIFO_SIZE) { +- count = 100; +- do { +- if ((UDCCS0 & UDCCS0_OPR) != 0) { +- /* clear OPR, generate ack */ +- UDCCS0 = UDCCS0_OPR; +- break; +- } +- count--; +- udelay(1); +- } while (count); +- } +-#endif +-#endif +- } else if (ep->dev->req_pending) +- ep0start(ep->dev, 0, "IN"); +- return is_short; +-} +- +- +-/* +- * read_fifo - unload packet(s) from the fifo we use for usb OUT +- * transfers and put them into the request. caller should have made +- * sure there's at least one packet ready. +- * +- * returns true if the request completed because of short packet or the +- * request buffer having filled (and maybe overran till end-of-packet). +- */ +-static int +-read_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) +-{ +- for (;;) { +- u32 udccs; +- u8 *buf; +- unsigned bufferspace, count, is_short; +- +- /* make sure there's a packet in the FIFO. +- * UDCCS_{BO,IO}_RPC are all the same bit value. +- * UDCCS_{BO,IO}_RNE are all the same bit value. +- */ +- udccs = *ep->reg_udccs; +- if (unlikely ((udccs & UDCCS_BO_RPC) == 0)) +- break; +- buf = req->req.buf + req->req.actual; +- prefetchw(buf); +- bufferspace = req->req.length - req->req.actual; +- +- /* read all bytes from this packet */ +- if (likely (udccs & UDCCS_BO_RNE)) { +- count = 1 + (0x0ff & *ep->reg_ubcr); +- req->req.actual += min (count, bufferspace); +- } else /* zlp */ +- count = 0; +- is_short = (count < ep->ep.maxpacket); +- DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", +- ep->ep.name, udccs, count, +- is_short ? "/S" : "", +- req, req->req.actual, req->req.length); +- while (likely (count-- != 0)) { +- u8 byte = (u8) *ep->reg_uddr; +- +- if (unlikely (bufferspace == 0)) { +- /* this happens when the driver's buffer +- * is smaller than what the host sent. +- * discard the extra data. +- */ +- if (req->req.status != -EOVERFLOW) +- DMSG("%s overflow %d\n", +- ep->ep.name, count); +- req->req.status = -EOVERFLOW; +- } else { +- *buf++ = byte; +- bufferspace--; +- } +- } +- *ep->reg_udccs = UDCCS_BO_RPC; +- /* RPC/RSP/RNE could now reflect the other packet buffer */ +- +- /* iso is one request per packet */ +- if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { +- if (udccs & UDCCS_IO_ROF) +- req->req.status = -EHOSTUNREACH; +- /* more like "is_done" */ +- is_short = 1; +- } +- +- /* completion */ +- if (is_short || req->req.actual == req->req.length) { +- done (ep, req, 0); +- if (list_empty(&ep->queue)) +- pio_irq_disable (ep->bEndpointAddress); +- return 1; +- } +- +- /* finished that packet. the next one may be waiting... */ +- } +- return 0; +-} +- +-/* +- * special ep0 version of the above. no UBCR0 or double buffering; status +- * handshaking is magic. most device protocols don't need control-OUT. +- * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other +- * protocols do use them. +- */ +-static int +-read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) +-{ +- u8 *buf, byte; +- unsigned bufferspace; +- +- buf = req->req.buf + req->req.actual; +- bufferspace = req->req.length - req->req.actual; +- +- while (UDCCS0 & UDCCS0_RNE) { +- byte = (u8) UDDR0; +- +- if (unlikely (bufferspace == 0)) { +- /* this happens when the driver's buffer +- * is smaller than what the host sent. +- * discard the extra data. +- */ +- if (req->req.status != -EOVERFLOW) +- DMSG("%s overflow\n", ep->ep.name); +- req->req.status = -EOVERFLOW; +- } else { +- *buf++ = byte; +- req->req.actual++; +- bufferspace--; +- } +- } +- +- UDCCS0 = UDCCS0_OPR | UDCCS0_IPR; +- +- /* completion */ +- if (req->req.actual >= req->req.length) +- return 1; +- +- /* finished that packet. the next one may be waiting... */ +- return 0; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static int +-pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +-{ +- struct pxa2xx_request *req; +- struct pxa2xx_ep *ep; +- struct pxa2xx_udc *dev; +- unsigned long flags; +- +- req = container_of(_req, struct pxa2xx_request, req); +- if (unlikely (!_req || !_req->complete || !_req->buf +- || !list_empty(&req->queue))) { +- DMSG("%s, bad params\n", __func__); +- return -EINVAL; +- } +- +- ep = container_of(_ep, struct pxa2xx_ep, ep); +- if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { +- DMSG("%s, bad ep\n", __func__); +- return -EINVAL; +- } +- +- dev = ep->dev; +- if (unlikely (!dev->driver +- || dev->gadget.speed == USB_SPEED_UNKNOWN)) { +- DMSG("%s, bogus device state\n", __func__); +- return -ESHUTDOWN; +- } +- +- /* iso is always one packet per request, that's the only way +- * we can report per-packet status. that also helps with dma. +- */ +- if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC +- && req->req.length > le16_to_cpu +- (ep->desc->wMaxPacketSize))) +- return -EMSGSIZE; +- +- DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", +- _ep->name, _req, _req->length, _req->buf); +- +- local_irq_save(flags); +- +- _req->status = -EINPROGRESS; +- _req->actual = 0; +- +- /* kickstart this i/o queue? */ +- if (list_empty(&ep->queue) && !ep->stopped) { +- if (ep->desc == NULL/* ep0 */) { +- unsigned length = _req->length; +- +- switch (dev->ep0state) { +- case EP0_IN_DATA_PHASE: +- dev->stats.write.ops++; +- if (write_ep0_fifo(ep, req)) +- req = NULL; +- break; +- +- case EP0_OUT_DATA_PHASE: +- dev->stats.read.ops++; +- /* messy ... */ +- if (dev->req_config) { +- DBG(DBG_VERBOSE, "ep0 config ack%s\n", +- dev->has_cfr ? "" : " raced"); +- if (dev->has_cfr) +- UDCCFR = UDCCFR_AREN|UDCCFR_ACM +- |UDCCFR_MB1; +- done(ep, req, 0); +- dev->ep0state = EP0_END_XFER; +- local_irq_restore (flags); +- return 0; +- } +- if (dev->req_pending) +- ep0start(dev, UDCCS0_IPR, "OUT"); +- if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0 +- && read_ep0_fifo(ep, req))) { +- ep0_idle(dev); +- done(ep, req, 0); +- req = NULL; +- } +- break; +- +- default: +- DMSG("ep0 i/o, odd state %d\n", dev->ep0state); +- local_irq_restore (flags); +- return -EL2HLT; +- } +- /* can the FIFO can satisfy the request immediately? */ +- } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { +- if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 +- && write_fifo(ep, req)) +- req = NULL; +- } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 +- && read_fifo(ep, req)) { +- req = NULL; +- } +- +- if (likely (req && ep->desc)) +- pio_irq_enable(ep->bEndpointAddress); +- } +- +- /* pio or dma irq handler advances the queue. */ +- if (likely(req != NULL)) +- list_add_tail(&req->queue, &ep->queue); +- local_irq_restore(flags); +- +- return 0; +-} +- +- +-/* +- * nuke - dequeue ALL requests +- */ +-static void nuke(struct pxa2xx_ep *ep, int status) +-{ +- struct pxa2xx_request *req; +- +- /* called with irqs blocked */ +- while (!list_empty(&ep->queue)) { +- req = list_entry(ep->queue.next, +- struct pxa2xx_request, +- queue); +- done(ep, req, status); +- } +- if (ep->desc) +- pio_irq_disable (ep->bEndpointAddress); +-} +- +- +-/* dequeue JUST ONE request */ +-static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +-{ +- struct pxa2xx_ep *ep; +- struct pxa2xx_request *req; +- unsigned long flags; +- +- ep = container_of(_ep, struct pxa2xx_ep, ep); +- if (!_ep || ep->ep.name == ep0name) +- return -EINVAL; +- +- local_irq_save(flags); +- +- /* make sure it's actually queued on this endpoint */ +- list_for_each_entry (req, &ep->queue, queue) { +- if (&req->req == _req) +- break; +- } +- if (&req->req != _req) { +- local_irq_restore(flags); +- return -EINVAL; +- } +- +- done(ep, req, -ECONNRESET); +- +- local_irq_restore(flags); +- return 0; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value) +-{ +- struct pxa2xx_ep *ep; +- unsigned long flags; +- +- ep = container_of(_ep, struct pxa2xx_ep, ep); +- if (unlikely (!_ep +- || (!ep->desc && ep->ep.name != ep0name)) +- || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { +- DMSG("%s, bad ep\n", __func__); +- return -EINVAL; +- } +- if (value == 0) { +- /* this path (reset toggle+halt) is needed to implement +- * SET_INTERFACE on normal hardware. but it can't be +- * done from software on the PXA UDC, and the hardware +- * forgets to do it as part of SET_INTERFACE automagic. +- */ +- DMSG("only host can clear %s halt\n", _ep->name); +- return -EROFS; +- } +- +- local_irq_save(flags); +- +- if ((ep->bEndpointAddress & USB_DIR_IN) != 0 +- && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 +- || !list_empty(&ep->queue))) { +- local_irq_restore(flags); +- return -EAGAIN; +- } +- +- /* FST bit is the same for control, bulk in, bulk out, interrupt in */ +- *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; +- +- /* ep0 needs special care */ +- if (!ep->desc) { +- start_watchdog(ep->dev); +- ep->dev->req_pending = 0; +- ep->dev->ep0state = EP0_STALL; +- +- /* and bulk/intr endpoints like dropping stalls too */ +- } else { +- unsigned i; +- for (i = 0; i < 1000; i += 20) { +- if (*ep->reg_udccs & UDCCS_BI_SST) +- break; +- udelay(20); +- } +- } +- local_irq_restore(flags); +- +- DBG(DBG_VERBOSE, "%s halt\n", _ep->name); +- return 0; +-} +- +-static int pxa2xx_ep_fifo_status(struct usb_ep *_ep) +-{ +- struct pxa2xx_ep *ep; +- +- ep = container_of(_ep, struct pxa2xx_ep, ep); +- if (!_ep) { +- DMSG("%s, bad ep\n", __func__); +- return -ENODEV; +- } +- /* pxa can't report unclaimed bytes from IN fifos */ +- if ((ep->bEndpointAddress & USB_DIR_IN) != 0) +- return -EOPNOTSUPP; +- if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN +- || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) +- return 0; +- else +- return (*ep->reg_ubcr & 0xfff) + 1; +-} +- +-static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep) +-{ +- struct pxa2xx_ep *ep; +- +- ep = container_of(_ep, struct pxa2xx_ep, ep); +- if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { +- DMSG("%s, bad ep\n", __func__); +- return; +- } +- +- /* toggle and halt bits stay unchanged */ +- +- /* for OUT, just read and discard the FIFO contents. */ +- if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { +- while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) +- (void) *ep->reg_uddr; +- return; +- } +- +- /* most IN status is the same, but ISO can't stall */ +- *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR +- | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) +- ? 0 : UDCCS_BI_SST; +-} +- +- +-static struct usb_ep_ops pxa2xx_ep_ops = { +- .enable = pxa2xx_ep_enable, +- .disable = pxa2xx_ep_disable, +- +- .alloc_request = pxa2xx_ep_alloc_request, +- .free_request = pxa2xx_ep_free_request, +- +- .queue = pxa2xx_ep_queue, +- .dequeue = pxa2xx_ep_dequeue, +- +- .set_halt = pxa2xx_ep_set_halt, +- .fifo_status = pxa2xx_ep_fifo_status, +- .fifo_flush = pxa2xx_ep_fifo_flush, +-}; +- +- +-/* --------------------------------------------------------------------------- +- * device-scoped parts of the api to the usb controller hardware +- * --------------------------------------------------------------------------- +- */ +- +-static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget) +-{ +- return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff); +-} +- +-static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget) +-{ +- /* host may not have enabled remote wakeup */ +- if ((UDCCS0 & UDCCS0_DRWF) == 0) +- return -EHOSTUNREACH; +- udc_set_mask_UDCCR(UDCCR_RSM); +- return 0; +-} +- +-static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *); +-static void udc_enable (struct pxa2xx_udc *); +-static void udc_disable(struct pxa2xx_udc *); +- +-/* We disable the UDC -- and its 48 MHz clock -- whenever it's not +- * in active use. +- */ +-static int pullup(struct pxa2xx_udc *udc) +-{ +- int is_active = udc->vbus && udc->pullup && !udc->suspended; +- DMSG("%s\n", is_active ? "active" : "inactive"); +- if (is_active) { +- if (!udc->active) { +- udc->active = 1; +- /* Enable clock for USB device */ +- clk_enable(udc->clk); +- udc_enable(udc); +- } +- } else { +- if (udc->active) { +- if (udc->gadget.speed != USB_SPEED_UNKNOWN) { +- DMSG("disconnect %s\n", udc->driver +- ? udc->driver->driver.name +- : "(no driver)"); +- stop_activity(udc, udc->driver); +- } +- udc_disable(udc); +- /* Disable clock for USB device */ +- clk_disable(udc->clk); +- udc->active = 0; +- } +- +- } +- return 0; +-} +- +-/* VBUS reporting logically comes from a transceiver */ +-static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +-{ +- struct pxa2xx_udc *udc; +- +- udc = container_of(_gadget, struct pxa2xx_udc, gadget); +- udc->vbus = (is_active != 0); +- DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); +- pullup(udc); +- return 0; +-} +- +-/* drivers may have software control over D+ pullup */ +-static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active) +-{ +- struct pxa2xx_udc *udc; +- +- udc = container_of(_gadget, struct pxa2xx_udc, gadget); +- +- /* not all boards support pullup control */ +- if (!udc->mach->gpio_pullup && !udc->mach->udc_command) +- return -EOPNOTSUPP; +- +- udc->pullup = (is_active != 0); +- pullup(udc); +- return 0; +-} +- +-static const struct usb_gadget_ops pxa2xx_udc_ops = { +- .get_frame = pxa2xx_udc_get_frame, +- .wakeup = pxa2xx_udc_wakeup, +- .vbus_session = pxa2xx_udc_vbus_session, +- .pullup = pxa2xx_udc_pullup, +- +- // .vbus_draw ... boards may consume current from VBUS, up to +- // 100-500mA based on config. the 500uA suspend ceiling means +- // that exclusively vbus-powered PXA designs violate USB specs. +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-#ifdef CONFIG_USB_GADGET_DEBUG_FS +- +-static int +-udc_seq_show(struct seq_file *m, void *_d) +-{ +- struct pxa2xx_udc *dev = m->private; +- unsigned long flags; +- int i; +- u32 tmp; +- +- local_irq_save(flags); +- +- /* basic device status */ +- seq_printf(m, DRIVER_DESC "\n" +- "%s version: %s\nGadget driver: %s\nHost %s\n\n", +- driver_name, DRIVER_VERSION SIZE_STR "(pio)", +- dev->driver ? dev->driver->driver.name : "(none)", +- is_vbus_present() ? "full speed" : "disconnected"); +- +- /* registers for device and ep0 */ +- seq_printf(m, +- "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", +- UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); +- +- tmp = UDCCR; +- seq_printf(m, +- "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, +- (tmp & UDCCR_REM) ? " rem" : "", +- (tmp & UDCCR_RSTIR) ? " rstir" : "", +- (tmp & UDCCR_SRM) ? " srm" : "", +- (tmp & UDCCR_SUSIR) ? " susir" : "", +- (tmp & UDCCR_RESIR) ? " resir" : "", +- (tmp & UDCCR_RSM) ? " rsm" : "", +- (tmp & UDCCR_UDA) ? " uda" : "", +- (tmp & UDCCR_UDE) ? " ude" : ""); +- +- tmp = UDCCS0; +- seq_printf(m, +- "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, +- (tmp & UDCCS0_SA) ? " sa" : "", +- (tmp & UDCCS0_RNE) ? " rne" : "", +- (tmp & UDCCS0_FST) ? " fst" : "", +- (tmp & UDCCS0_SST) ? " sst" : "", +- (tmp & UDCCS0_DRWF) ? " dwrf" : "", +- (tmp & UDCCS0_FTF) ? " ftf" : "", +- (tmp & UDCCS0_IPR) ? " ipr" : "", +- (tmp & UDCCS0_OPR) ? " opr" : ""); +- +- if (dev->has_cfr) { +- tmp = UDCCFR; +- seq_printf(m, +- "udccfr %02X =%s%s\n", tmp, +- (tmp & UDCCFR_AREN) ? " aren" : "", +- (tmp & UDCCFR_ACM) ? " acm" : ""); +- } +- +- if (!is_vbus_present() || !dev->driver) +- goto done; +- +- seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", +- dev->stats.write.bytes, dev->stats.write.ops, +- dev->stats.read.bytes, dev->stats.read.ops, +- dev->stats.irqs); +- +- /* dump endpoint queues */ +- for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { +- struct pxa2xx_ep *ep = &dev->ep [i]; +- struct pxa2xx_request *req; +- +- if (i != 0) { +- const struct usb_endpoint_descriptor *desc; +- +- desc = ep->desc; +- if (!desc) +- continue; +- tmp = *dev->ep [i].reg_udccs; +- seq_printf(m, +- "%s max %d %s udccs %02x irqs %lu\n", +- ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), +- "pio", tmp, ep->pio_irqs); +- /* TODO translate all five groups of udccs bits! */ +- +- } else /* ep0 should only have one transfer queued */ +- seq_printf(m, "ep0 max 16 pio irqs %lu\n", +- ep->pio_irqs); +- +- if (list_empty(&ep->queue)) { +- seq_printf(m, "\t(nothing queued)\n"); +- continue; +- } +- list_for_each_entry(req, &ep->queue, queue) { +- seq_printf(m, +- "\treq %p len %d/%d buf %p\n", +- &req->req, req->req.actual, +- req->req.length, req->req.buf); +- } +- } +- +-done: +- local_irq_restore(flags); +- return 0; +-} +- +-static int +-udc_debugfs_open(struct inode *inode, struct file *file) +-{ +- return single_open(file, udc_seq_show, inode->i_private); +-} +- +-static const struct file_operations debug_fops = { +- .open = udc_debugfs_open, +- .read = seq_read, +- .llseek = seq_lseek, +- .release = single_release, +- .owner = THIS_MODULE, +-}; +- +-#define create_debug_files(dev) \ +- do { \ +- dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \ +- S_IRUGO, NULL, dev, &debug_fops); \ +- } while (0) +-#define remove_debug_files(dev) \ +- do { \ +- if (dev->debugfs_udc) \ +- debugfs_remove(dev->debugfs_udc); \ +- } while (0) +- +-#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ +- +-#define create_debug_files(dev) do {} while (0) +-#define remove_debug_files(dev) do {} while (0) +- +-#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * udc_disable - disable USB device controller +- */ +-static void udc_disable(struct pxa2xx_udc *dev) +-{ +- /* block all irqs */ +- udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); +- UICR0 = UICR1 = 0xff; +- UFNRH = UFNRH_SIM; +- +- /* if hardware supports it, disconnect from usb */ +- pullup_off(); +- +- udc_clear_mask_UDCCR(UDCCR_UDE); +- +- ep0_idle (dev); +- dev->gadget.speed = USB_SPEED_UNKNOWN; +-} +- +- +-/* +- * udc_reinit - initialize software state +- */ +-static void udc_reinit(struct pxa2xx_udc *dev) +-{ +- u32 i; +- +- /* device/ep0 records init */ +- INIT_LIST_HEAD (&dev->gadget.ep_list); +- INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); +- dev->ep0state = EP0_IDLE; +- +- /* basic endpoint records init */ +- for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { +- struct pxa2xx_ep *ep = &dev->ep[i]; +- +- if (i != 0) +- list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); +- +- ep->desc = NULL; +- ep->stopped = 0; +- INIT_LIST_HEAD (&ep->queue); +- ep->pio_irqs = 0; +- } +- +- /* the rest was statically initialized, and is read-only */ +-} +- +-/* until it's enabled, this UDC should be completely invisible +- * to any USB host. +- */ +-static void udc_enable (struct pxa2xx_udc *dev) +-{ +- udc_clear_mask_UDCCR(UDCCR_UDE); +- +- /* try to clear these bits before we enable the udc */ +- udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); +- +- ep0_idle(dev); +- dev->gadget.speed = USB_SPEED_UNKNOWN; +- dev->stats.irqs = 0; +- +- /* +- * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: +- * - enable UDC +- * - if RESET is already in progress, ack interrupt +- * - unmask reset interrupt +- */ +- udc_set_mask_UDCCR(UDCCR_UDE); +- if (!(UDCCR & UDCCR_UDA)) +- udc_ack_int_UDCCR(UDCCR_RSTIR); +- +- if (dev->has_cfr /* UDC_RES2 is defined */) { +- /* pxa255 (a0+) can avoid a set_config race that could +- * prevent gadget drivers from configuring correctly +- */ +- UDCCFR = UDCCFR_ACM | UDCCFR_MB1; +- } else { +- /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) +- * which could result in missing packets and interrupts. +- * supposedly one bit per endpoint, controlling whether it +- * double buffers or not; ACM/AREN bits fit into the holes. +- * zero bits (like USIR0_IRx) disable double buffering. +- */ +- UDC_RES1 = 0x00; +- UDC_RES2 = 0x00; +- } +- +- /* enable suspend/resume and reset irqs */ +- udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); +- +- /* enable ep0 irqs */ +- UICR0 &= ~UICR0_IM0; +- +- /* if hardware supports it, pullup D+ and wait for reset */ +- pullup_on(); +-} +- +- +-/* when a driver is successfully registered, it will receive +- * control requests including set_configuration(), which enables +- * non-control requests. then usb traffic follows until a +- * disconnect is reported. then a host may connect again, or +- * the driver might get unbound. +- */ +-int usb_gadget_register_driver(struct usb_gadget_driver *driver) +-{ +- struct pxa2xx_udc *dev = the_controller; +- int retval; +- +- if (!driver +- || driver->speed < USB_SPEED_FULL +- || !driver->bind +- || !driver->disconnect +- || !driver->setup) +- return -EINVAL; +- if (!dev) +- return -ENODEV; +- if (dev->driver) +- return -EBUSY; +- +- /* first hook up the driver ... */ +- dev->driver = driver; +- dev->gadget.dev.driver = &driver->driver; +- dev->pullup = 1; +- +- retval = device_add (&dev->gadget.dev); +- if (retval) { +-fail: +- dev->driver = NULL; +- dev->gadget.dev.driver = NULL; +- return retval; +- } +- retval = driver->bind(&dev->gadget); +- if (retval) { +- DMSG("bind to driver %s --> error %d\n", +- driver->driver.name, retval); +- device_del (&dev->gadget.dev); +- goto fail; +- } +- +- /* ... then enable host detection and ep0; and we're ready +- * for set_configuration as well as eventual disconnect. +- */ +- DMSG("registered gadget driver '%s'\n", driver->driver.name); +- pullup(dev); +- dump_state(dev); +- return 0; +-} +-EXPORT_SYMBOL(usb_gadget_register_driver); +- +-static void +-stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver) +-{ +- int i; +- +- /* don't disconnect drivers more than once */ +- if (dev->gadget.speed == USB_SPEED_UNKNOWN) +- driver = NULL; +- dev->gadget.speed = USB_SPEED_UNKNOWN; +- +- /* prevent new request submissions, kill any outstanding requests */ +- for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { +- struct pxa2xx_ep *ep = &dev->ep[i]; +- +- ep->stopped = 1; +- nuke(ep, -ESHUTDOWN); +- } +- del_timer_sync(&dev->timer); +- +- /* report disconnect; the driver is already quiesced */ +- if (driver) +- driver->disconnect(&dev->gadget); +- +- /* re-init driver-visible data structures */ +- udc_reinit(dev); +-} +- +-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +-{ +- struct pxa2xx_udc *dev = the_controller; +- +- if (!dev) +- return -ENODEV; +- if (!driver || driver != dev->driver || !driver->unbind) +- return -EINVAL; +- +- local_irq_disable(); +- dev->pullup = 0; +- pullup(dev); +- stop_activity(dev, driver); +- local_irq_enable(); +- +- driver->unbind(&dev->gadget); +- dev->gadget.dev.driver = NULL; +- dev->driver = NULL; +- +- device_del (&dev->gadget.dev); +- +- DMSG("unregistered gadget driver '%s'\n", driver->driver.name); +- dump_state(dev); +- return 0; +-} +-EXPORT_SYMBOL(usb_gadget_unregister_driver); +- +- +-/*-------------------------------------------------------------------------*/ +- +-#ifdef CONFIG_ARCH_LUBBOCK +- +-/* Lubbock has separate connect and disconnect irqs. More typical designs +- * use one GPIO as the VBUS IRQ, and another to control the D+ pullup. +- */ +- +-static irqreturn_t +-lubbock_vbus_irq(int irq, void *_dev) +-{ +- struct pxa2xx_udc *dev = _dev; +- int vbus; +- +- dev->stats.irqs++; +- switch (irq) { +- case LUBBOCK_USB_IRQ: +- vbus = 1; +- disable_irq(LUBBOCK_USB_IRQ); +- enable_irq(LUBBOCK_USB_DISC_IRQ); +- break; +- case LUBBOCK_USB_DISC_IRQ: +- vbus = 0; +- disable_irq(LUBBOCK_USB_DISC_IRQ); +- enable_irq(LUBBOCK_USB_IRQ); +- break; +- default: +- return IRQ_NONE; +- } +- +- pxa2xx_udc_vbus_session(&dev->gadget, vbus); +- return IRQ_HANDLED; +-} +- +-#endif +- +-static irqreturn_t udc_vbus_irq(int irq, void *_dev) +-{ +- struct pxa2xx_udc *dev = _dev; +- int vbus = gpio_get_value(dev->mach->gpio_vbus); +- +- if (dev->mach->gpio_vbus_inverted) +- vbus = !vbus; +- +- pxa2xx_udc_vbus_session(&dev->gadget, vbus); +- return IRQ_HANDLED; +-} +- +- +-/*-------------------------------------------------------------------------*/ +- +-static inline void clear_ep_state (struct pxa2xx_udc *dev) +-{ +- unsigned i; +- +- /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint +- * fifos, and pending transactions mustn't be continued in any case. +- */ +- for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) +- nuke(&dev->ep[i], -ECONNABORTED); +-} +- +-static void udc_watchdog(unsigned long _dev) +-{ +- struct pxa2xx_udc *dev = (void *)_dev; +- +- local_irq_disable(); +- if (dev->ep0state == EP0_STALL +- && (UDCCS0 & UDCCS0_FST) == 0 +- && (UDCCS0 & UDCCS0_SST) == 0) { +- UDCCS0 = UDCCS0_FST|UDCCS0_FTF; +- DBG(DBG_VERBOSE, "ep0 re-stall\n"); +- start_watchdog(dev); +- } +- local_irq_enable(); +-} +- +-static void handle_ep0 (struct pxa2xx_udc *dev) +-{ +- u32 udccs0 = UDCCS0; +- struct pxa2xx_ep *ep = &dev->ep [0]; +- struct pxa2xx_request *req; +- union { +- struct usb_ctrlrequest r; +- u8 raw [8]; +- u32 word [2]; +- } u; +- +- if (list_empty(&ep->queue)) +- req = NULL; +- else +- req = list_entry(ep->queue.next, struct pxa2xx_request, queue); +- +- /* clear stall status */ +- if (udccs0 & UDCCS0_SST) { +- nuke(ep, -EPIPE); +- UDCCS0 = UDCCS0_SST; +- del_timer(&dev->timer); +- ep0_idle(dev); +- } +- +- /* previous request unfinished? non-error iff back-to-back ... */ +- if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { +- nuke(ep, 0); +- del_timer(&dev->timer); +- ep0_idle(dev); +- } +- +- switch (dev->ep0state) { +- case EP0_IDLE: +- /* late-breaking status? */ +- udccs0 = UDCCS0; +- +- /* start control request? */ +- if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) +- == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { +- int i; +- +- nuke (ep, -EPROTO); +- +- /* read SETUP packet */ +- for (i = 0; i < 8; i++) { +- if (unlikely(!(UDCCS0 & UDCCS0_RNE))) { +-bad_setup: +- DMSG("SETUP %d!\n", i); +- goto stall; +- } +- u.raw [i] = (u8) UDDR0; +- } +- if (unlikely((UDCCS0 & UDCCS0_RNE) != 0)) +- goto bad_setup; +- +-got_setup: +- DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", +- u.r.bRequestType, u.r.bRequest, +- le16_to_cpu(u.r.wValue), +- le16_to_cpu(u.r.wIndex), +- le16_to_cpu(u.r.wLength)); +- +- /* cope with automagic for some standard requests. */ +- dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) +- == USB_TYPE_STANDARD; +- dev->req_config = 0; +- dev->req_pending = 1; +- switch (u.r.bRequest) { +- /* hardware restricts gadget drivers here! */ +- case USB_REQ_SET_CONFIGURATION: +- if (u.r.bRequestType == USB_RECIP_DEVICE) { +- /* reflect hardware's automagic +- * up to the gadget driver. +- */ +-config_change: +- dev->req_config = 1; +- clear_ep_state(dev); +- /* if !has_cfr, there's no synch +- * else use AREN (later) not SA|OPR +- * USIR0_IR0 acts edge sensitive +- */ +- } +- break; +- /* ... and here, even more ... */ +- case USB_REQ_SET_INTERFACE: +- if (u.r.bRequestType == USB_RECIP_INTERFACE) { +- /* udc hardware is broken by design: +- * - altsetting may only be zero; +- * - hw resets all interfaces' eps; +- * - ep reset doesn't include halt(?). +- */ +- DMSG("broken set_interface (%d/%d)\n", +- le16_to_cpu(u.r.wIndex), +- le16_to_cpu(u.r.wValue)); +- goto config_change; +- } +- break; +- /* hardware was supposed to hide this */ +- case USB_REQ_SET_ADDRESS: +- if (u.r.bRequestType == USB_RECIP_DEVICE) { +- ep0start(dev, 0, "address"); +- return; +- } +- break; +- } +- +- if (u.r.bRequestType & USB_DIR_IN) +- dev->ep0state = EP0_IN_DATA_PHASE; +- else +- dev->ep0state = EP0_OUT_DATA_PHASE; +- +- i = dev->driver->setup(&dev->gadget, &u.r); +- if (i < 0) { +- /* hardware automagic preventing STALL... */ +- if (dev->req_config) { +- /* hardware sometimes neglects to tell +- * tell us about config change events, +- * so later ones may fail... +- */ +- WARN("config change %02x fail %d?\n", +- u.r.bRequest, i); +- return; +- /* TODO experiment: if has_cfr, +- * hardware didn't ACK; maybe we +- * could actually STALL! +- */ +- } +- DBG(DBG_VERBOSE, "protocol STALL, " +- "%02x err %d\n", UDCCS0, i); +-stall: +- /* the watchdog timer helps deal with cases +- * where udc seems to clear FST wrongly, and +- * then NAKs instead of STALLing. +- */ +- ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); +- start_watchdog(dev); +- dev->ep0state = EP0_STALL; +- +- /* deferred i/o == no response yet */ +- } else if (dev->req_pending) { +- if (likely(dev->ep0state == EP0_IN_DATA_PHASE +- || dev->req_std || u.r.wLength)) +- ep0start(dev, 0, "defer"); +- else +- ep0start(dev, UDCCS0_IPR, "defer/IPR"); +- } +- +- /* expect at least one data or status stage irq */ +- return; +- +- } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) +- == (UDCCS0_OPR|UDCCS0_SA))) { +- unsigned i; +- +- /* pxa210/250 erratum 131 for B0/B1 says RNE lies. +- * still observed on a pxa255 a0. +- */ +- DBG(DBG_VERBOSE, "e131\n"); +- nuke(ep, -EPROTO); +- +- /* read SETUP data, but don't trust it too much */ +- for (i = 0; i < 8; i++) +- u.raw [i] = (u8) UDDR0; +- if ((u.r.bRequestType & USB_RECIP_MASK) +- > USB_RECIP_OTHER) +- goto stall; +- if (u.word [0] == 0 && u.word [1] == 0) +- goto stall; +- goto got_setup; +- } else { +- /* some random early IRQ: +- * - we acked FST +- * - IPR cleared +- * - OPR got set, without SA (likely status stage) +- */ +- UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR); +- } +- break; +- case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ +- if (udccs0 & UDCCS0_OPR) { +- UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; +- DBG(DBG_VERBOSE, "ep0in premature status\n"); +- if (req) +- done(ep, req, 0); +- ep0_idle(dev); +- } else /* irq was IPR clearing */ { +- if (req) { +- /* this IN packet might finish the request */ +- (void) write_ep0_fifo(ep, req); +- } /* else IN token before response was written */ +- } +- break; +- case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ +- if (udccs0 & UDCCS0_OPR) { +- if (req) { +- /* this OUT packet might finish the request */ +- if (read_ep0_fifo(ep, req)) +- done(ep, req, 0); +- /* else more OUT packets expected */ +- } /* else OUT token before read was issued */ +- } else /* irq was IPR clearing */ { +- DBG(DBG_VERBOSE, "ep0out premature status\n"); +- if (req) +- done(ep, req, 0); +- ep0_idle(dev); +- } +- break; +- case EP0_END_XFER: +- if (req) +- done(ep, req, 0); +- /* ack control-IN status (maybe in-zlp was skipped) +- * also appears after some config change events. +- */ +- if (udccs0 & UDCCS0_OPR) +- UDCCS0 = UDCCS0_OPR; +- ep0_idle(dev); +- break; +- case EP0_STALL: +- UDCCS0 = UDCCS0_FST; +- break; +- } +- USIR0 = USIR0_IR0; +-} +- +-static void handle_ep(struct pxa2xx_ep *ep) +-{ +- struct pxa2xx_request *req; +- int is_in = ep->bEndpointAddress & USB_DIR_IN; +- int completed; +- u32 udccs, tmp; +- +- do { +- completed = 0; +- if (likely (!list_empty(&ep->queue))) +- req = list_entry(ep->queue.next, +- struct pxa2xx_request, queue); +- else +- req = NULL; +- +- // TODO check FST handling +- +- udccs = *ep->reg_udccs; +- if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ +- tmp = UDCCS_BI_TUR; +- if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) +- tmp |= UDCCS_BI_SST; +- tmp &= udccs; +- if (likely (tmp)) +- *ep->reg_udccs = tmp; +- if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) +- completed = write_fifo(ep, req); +- +- } else { /* irq from RPC (or for ISO, ROF) */ +- if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) +- tmp = UDCCS_BO_SST | UDCCS_BO_DME; +- else +- tmp = UDCCS_IO_ROF | UDCCS_IO_DME; +- tmp &= udccs; +- if (likely(tmp)) +- *ep->reg_udccs = tmp; +- +- /* fifos can hold packets, ready for reading... */ +- if (likely(req)) { +- completed = read_fifo(ep, req); +- } else +- pio_irq_disable (ep->bEndpointAddress); +- } +- ep->pio_irqs++; +- } while (completed); +-} +- +-/* +- * pxa2xx_udc_irq - interrupt handler +- * +- * avoid delays in ep0 processing. the control handshaking isn't always +- * under software control (pxa250c0 and the pxa255 are better), and delays +- * could cause usb protocol errors. +- */ +-static irqreturn_t +-pxa2xx_udc_irq(int irq, void *_dev) +-{ +- struct pxa2xx_udc *dev = _dev; +- int handled; +- +- dev->stats.irqs++; +- do { +- u32 udccr = UDCCR; +- +- handled = 0; +- +- /* SUSpend Interrupt Request */ +- if (unlikely(udccr & UDCCR_SUSIR)) { +- udc_ack_int_UDCCR(UDCCR_SUSIR); +- handled = 1; +- DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() +- ? "" : "+disconnect"); +- +- if (!is_vbus_present()) +- stop_activity(dev, dev->driver); +- else if (dev->gadget.speed != USB_SPEED_UNKNOWN +- && dev->driver +- && dev->driver->suspend) +- dev->driver->suspend(&dev->gadget); +- ep0_idle (dev); +- } +- +- /* RESume Interrupt Request */ +- if (unlikely(udccr & UDCCR_RESIR)) { +- udc_ack_int_UDCCR(UDCCR_RESIR); +- handled = 1; +- DBG(DBG_VERBOSE, "USB resume\n"); +- +- if (dev->gadget.speed != USB_SPEED_UNKNOWN +- && dev->driver +- && dev->driver->resume +- && is_vbus_present()) +- dev->driver->resume(&dev->gadget); +- } +- +- /* ReSeT Interrupt Request - USB reset */ +- if (unlikely(udccr & UDCCR_RSTIR)) { +- udc_ack_int_UDCCR(UDCCR_RSTIR); +- handled = 1; +- +- if ((UDCCR & UDCCR_UDA) == 0) { +- DBG(DBG_VERBOSE, "USB reset start\n"); +- +- /* reset driver and endpoints, +- * in case that's not yet done +- */ +- stop_activity (dev, dev->driver); +- +- } else { +- DBG(DBG_VERBOSE, "USB reset end\n"); +- dev->gadget.speed = USB_SPEED_FULL; +- memset(&dev->stats, 0, sizeof dev->stats); +- /* driver and endpoints are still reset */ +- } +- +- } else { +- u32 usir0 = USIR0 & ~UICR0; +- u32 usir1 = USIR1 & ~UICR1; +- int i; +- +- if (unlikely (!usir0 && !usir1)) +- continue; +- +- DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0); +- +- /* control traffic */ +- if (usir0 & USIR0_IR0) { +- dev->ep[0].pio_irqs++; +- handle_ep0(dev); +- handled = 1; +- } +- +- /* endpoint data transfers */ +- for (i = 0; i < 8; i++) { +- u32 tmp = 1 << i; +- +- if (i && (usir0 & tmp)) { +- handle_ep(&dev->ep[i]); +- USIR0 |= tmp; +- handled = 1; +- } +- if (usir1 & tmp) { +- handle_ep(&dev->ep[i+8]); +- USIR1 |= tmp; +- handled = 1; +- } +- } +- } +- +- /* we could also ask for 1 msec SOF (SIR) interrupts */ +- +- } while (handled); +- return IRQ_HANDLED; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void nop_release (struct device *dev) +-{ +- DMSG("%s %s\n", __func__, dev->bus_id); +-} +- +-/* this uses load-time allocation and initialization (instead of +- * doing it at run-time) to save code, eliminate fault paths, and +- * be more obviously correct. +- */ +-static struct pxa2xx_udc memory = { +- .gadget = { +- .ops = &pxa2xx_udc_ops, +- .ep0 = &memory.ep[0].ep, +- .name = driver_name, +- .dev = { +- .bus_id = "gadget", +- .release = nop_release, +- }, +- }, +- +- /* control endpoint */ +- .ep[0] = { +- .ep = { +- .name = ep0name, +- .ops = &pxa2xx_ep_ops, +- .maxpacket = EP0_FIFO_SIZE, +- }, +- .dev = &memory, +- .reg_udccs = &UDCCS0, +- .reg_uddr = &UDDR0, +- }, +- +- /* first group of endpoints */ +- .ep[1] = { +- .ep = { +- .name = "ep1in-bulk", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = BULK_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = BULK_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 1, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .reg_udccs = &UDCCS1, +- .reg_uddr = &UDDR1, +- }, +- .ep[2] = { +- .ep = { +- .name = "ep2out-bulk", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = BULK_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = BULK_FIFO_SIZE, +- .bEndpointAddress = 2, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .reg_udccs = &UDCCS2, +- .reg_ubcr = &UBCR2, +- .reg_uddr = &UDDR2, +- }, +-#ifndef CONFIG_USB_PXA2XX_SMALL +- .ep[3] = { +- .ep = { +- .name = "ep3in-iso", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = ISO_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = ISO_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 3, +- .bmAttributes = USB_ENDPOINT_XFER_ISOC, +- .reg_udccs = &UDCCS3, +- .reg_uddr = &UDDR3, +- }, +- .ep[4] = { +- .ep = { +- .name = "ep4out-iso", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = ISO_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = ISO_FIFO_SIZE, +- .bEndpointAddress = 4, +- .bmAttributes = USB_ENDPOINT_XFER_ISOC, +- .reg_udccs = &UDCCS4, +- .reg_ubcr = &UBCR4, +- .reg_uddr = &UDDR4, +- }, +- .ep[5] = { +- .ep = { +- .name = "ep5in-int", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = INT_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = INT_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 5, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .reg_udccs = &UDCCS5, +- .reg_uddr = &UDDR5, +- }, +- +- /* second group of endpoints */ +- .ep[6] = { +- .ep = { +- .name = "ep6in-bulk", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = BULK_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = BULK_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 6, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .reg_udccs = &UDCCS6, +- .reg_uddr = &UDDR6, +- }, +- .ep[7] = { +- .ep = { +- .name = "ep7out-bulk", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = BULK_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = BULK_FIFO_SIZE, +- .bEndpointAddress = 7, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .reg_udccs = &UDCCS7, +- .reg_ubcr = &UBCR7, +- .reg_uddr = &UDDR7, +- }, +- .ep[8] = { +- .ep = { +- .name = "ep8in-iso", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = ISO_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = ISO_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 8, +- .bmAttributes = USB_ENDPOINT_XFER_ISOC, +- .reg_udccs = &UDCCS8, +- .reg_uddr = &UDDR8, +- }, +- .ep[9] = { +- .ep = { +- .name = "ep9out-iso", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = ISO_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = ISO_FIFO_SIZE, +- .bEndpointAddress = 9, +- .bmAttributes = USB_ENDPOINT_XFER_ISOC, +- .reg_udccs = &UDCCS9, +- .reg_ubcr = &UBCR9, +- .reg_uddr = &UDDR9, +- }, +- .ep[10] = { +- .ep = { +- .name = "ep10in-int", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = INT_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = INT_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 10, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .reg_udccs = &UDCCS10, +- .reg_uddr = &UDDR10, +- }, +- +- /* third group of endpoints */ +- .ep[11] = { +- .ep = { +- .name = "ep11in-bulk", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = BULK_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = BULK_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 11, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .reg_udccs = &UDCCS11, +- .reg_uddr = &UDDR11, +- }, +- .ep[12] = { +- .ep = { +- .name = "ep12out-bulk", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = BULK_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = BULK_FIFO_SIZE, +- .bEndpointAddress = 12, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .reg_udccs = &UDCCS12, +- .reg_ubcr = &UBCR12, +- .reg_uddr = &UDDR12, +- }, +- .ep[13] = { +- .ep = { +- .name = "ep13in-iso", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = ISO_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = ISO_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 13, +- .bmAttributes = USB_ENDPOINT_XFER_ISOC, +- .reg_udccs = &UDCCS13, +- .reg_uddr = &UDDR13, +- }, +- .ep[14] = { +- .ep = { +- .name = "ep14out-iso", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = ISO_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = ISO_FIFO_SIZE, +- .bEndpointAddress = 14, +- .bmAttributes = USB_ENDPOINT_XFER_ISOC, +- .reg_udccs = &UDCCS14, +- .reg_ubcr = &UBCR14, +- .reg_uddr = &UDDR14, +- }, +- .ep[15] = { +- .ep = { +- .name = "ep15in-int", +- .ops = &pxa2xx_ep_ops, +- .maxpacket = INT_FIFO_SIZE, +- }, +- .dev = &memory, +- .fifo_size = INT_FIFO_SIZE, +- .bEndpointAddress = USB_DIR_IN | 15, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .reg_udccs = &UDCCS15, +- .reg_uddr = &UDDR15, +- }, +-#endif /* !CONFIG_USB_PXA2XX_SMALL */ +-}; +- +-#define CP15R0_VENDOR_MASK 0xffffe000 +- +-#if defined(CONFIG_ARCH_PXA) +-#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */ +- +-#elif defined(CONFIG_ARCH_IXP4XX) +-#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */ +- +-#endif +- +-#define CP15R0_PROD_MASK 0x000003f0 +-#define PXA25x 0x00000100 /* and PXA26x */ +-#define PXA210 0x00000120 +- +-#define CP15R0_REV_MASK 0x0000000f +- +-#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK) +- +-#define PXA255_A0 0x00000106 /* or PXA260_B1 */ +-#define PXA250_C0 0x00000105 /* or PXA26x_B0 */ +-#define PXA250_B2 0x00000104 +-#define PXA250_B1 0x00000103 /* or PXA260_A0 */ +-#define PXA250_B0 0x00000102 +-#define PXA250_A1 0x00000101 +-#define PXA250_A0 0x00000100 +- +-#define PXA210_C0 0x00000125 +-#define PXA210_B2 0x00000124 +-#define PXA210_B1 0x00000123 +-#define PXA210_B0 0x00000122 +-#define IXP425_A0 0x000001c1 +-#define IXP425_B0 0x000001f1 +-#define IXP465_AD 0x00000200 +- +-/* +- * probe - binds to the platform device +- */ +-static int __init pxa2xx_udc_probe(struct platform_device *pdev) +-{ +- struct pxa2xx_udc *dev = &memory; +- int retval, vbus_irq, irq; +- u32 chiprev; +- +- /* insist on Intel/ARM/XScale */ +- asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); +- if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { +- pr_err("%s: not XScale!\n", driver_name); +- return -ENODEV; +- } +- +- /* trigger chiprev-specific logic */ +- switch (chiprev & CP15R0_PRODREV_MASK) { +-#if defined(CONFIG_ARCH_PXA) +- case PXA255_A0: +- dev->has_cfr = 1; +- break; +- case PXA250_A0: +- case PXA250_A1: +- /* A0/A1 "not released"; ep 13, 15 unusable */ +- /* fall through */ +- case PXA250_B2: case PXA210_B2: +- case PXA250_B1: case PXA210_B1: +- case PXA250_B0: case PXA210_B0: +- /* OUT-DMA is broken ... */ +- /* fall through */ +- case PXA250_C0: case PXA210_C0: +- break; +-#elif defined(CONFIG_ARCH_IXP4XX) +- case IXP425_A0: +- case IXP425_B0: +- case IXP465_AD: +- dev->has_cfr = 1; +- break; +-#endif +- default: +- pr_err("%s: unrecognized processor: %08x\n", +- driver_name, chiprev); +- /* iop3xx, ixp4xx, ... */ +- return -ENODEV; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) +- return -ENODEV; +- +- dev->clk = clk_get(&pdev->dev, "UDCCLK"); +- if (IS_ERR(dev->clk)) { +- retval = PTR_ERR(dev->clk); +- goto err_clk; +- } +- +- pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, +- dev->has_cfr ? "" : " (!cfr)", +- SIZE_STR "(pio)" +- ); +- +- /* other non-static parts of init */ +- dev->dev = &pdev->dev; +- dev->mach = pdev->dev.platform_data; +- +- if (dev->mach->gpio_vbus) { +- if ((retval = gpio_request(dev->mach->gpio_vbus, +- "pxa2xx_udc GPIO VBUS"))) { +- dev_dbg(&pdev->dev, +- "can't get vbus gpio %d, err: %d\n", +- dev->mach->gpio_vbus, retval); +- goto err_gpio_vbus; +- } +- gpio_direction_input(dev->mach->gpio_vbus); +- vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); +- } else +- vbus_irq = 0; +- +- if (dev->mach->gpio_pullup) { +- if ((retval = gpio_request(dev->mach->gpio_pullup, +- "pca2xx_udc GPIO PULLUP"))) { +- dev_dbg(&pdev->dev, +- "can't get pullup gpio %d, err: %d\n", +- dev->mach->gpio_pullup, retval); +- goto err_gpio_pullup; +- } +- gpio_direction_output(dev->mach->gpio_pullup, 0); +- } +- +- init_timer(&dev->timer); +- dev->timer.function = udc_watchdog; +- dev->timer.data = (unsigned long) dev; +- +- device_initialize(&dev->gadget.dev); +- dev->gadget.dev.parent = &pdev->dev; +- dev->gadget.dev.dma_mask = pdev->dev.dma_mask; +- +- the_controller = dev; +- platform_set_drvdata(pdev, dev); +- +- udc_disable(dev); +- udc_reinit(dev); +- +- dev->vbus = is_vbus_present(); +- +- /* irq setup after old hardware state is cleaned up */ +- retval = request_irq(irq, pxa2xx_udc_irq, +- IRQF_DISABLED, driver_name, dev); +- if (retval != 0) { +- pr_err("%s: can't get irq %d, err %d\n", +- driver_name, irq, retval); +- goto err_irq1; +- } +- dev->got_irq = 1; +- +-#ifdef CONFIG_ARCH_LUBBOCK +- if (machine_is_lubbock()) { +- retval = request_irq(LUBBOCK_USB_DISC_IRQ, +- lubbock_vbus_irq, +- IRQF_DISABLED | IRQF_SAMPLE_RANDOM, +- driver_name, dev); +- if (retval != 0) { +- pr_err("%s: can't get irq %i, err %d\n", +- driver_name, LUBBOCK_USB_DISC_IRQ, retval); +-lubbock_fail0: +- goto err_irq_lub; +- } +- retval = request_irq(LUBBOCK_USB_IRQ, +- lubbock_vbus_irq, +- IRQF_DISABLED | IRQF_SAMPLE_RANDOM, +- driver_name, dev); +- if (retval != 0) { +- pr_err("%s: can't get irq %i, err %d\n", +- driver_name, LUBBOCK_USB_IRQ, retval); +- free_irq(LUBBOCK_USB_DISC_IRQ, dev); +- goto lubbock_fail0; +- } +- } else +-#endif +- if (vbus_irq) { +- retval = request_irq(vbus_irq, udc_vbus_irq, +- IRQF_DISABLED | IRQF_SAMPLE_RANDOM | +- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +- driver_name, dev); +- if (retval != 0) { +- pr_err("%s: can't get irq %i, err %d\n", +- driver_name, vbus_irq, retval); +- goto err_vbus_irq; +- } +- } +- create_debug_files(dev); +- +- return 0; +- +- err_vbus_irq: +-#ifdef CONFIG_ARCH_LUBBOCK +- free_irq(LUBBOCK_USB_DISC_IRQ, dev); +- err_irq_lub: +-#endif +- free_irq(irq, dev); +- err_irq1: +- if (dev->mach->gpio_pullup) +- gpio_free(dev->mach->gpio_pullup); +- err_gpio_pullup: +- if (dev->mach->gpio_vbus) +- gpio_free(dev->mach->gpio_vbus); +- err_gpio_vbus: +- clk_put(dev->clk); +- err_clk: +- return retval; +-} +- +-static void pxa2xx_udc_shutdown(struct platform_device *_dev) +-{ +- pullup_off(); +-} +- +-static int __exit pxa2xx_udc_remove(struct platform_device *pdev) +-{ +- struct pxa2xx_udc *dev = platform_get_drvdata(pdev); +- +- if (dev->driver) +- return -EBUSY; +- +- dev->pullup = 0; +- pullup(dev); +- +- remove_debug_files(dev); +- +- if (dev->got_irq) { +- free_irq(platform_get_irq(pdev, 0), dev); +- dev->got_irq = 0; +- } +-#ifdef CONFIG_ARCH_LUBBOCK +- if (machine_is_lubbock()) { +- free_irq(LUBBOCK_USB_DISC_IRQ, dev); +- free_irq(LUBBOCK_USB_IRQ, dev); +- } +-#endif +- if (dev->mach->gpio_vbus) { +- free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); +- gpio_free(dev->mach->gpio_vbus); +- } +- if (dev->mach->gpio_pullup) +- gpio_free(dev->mach->gpio_pullup); +- +- clk_put(dev->clk); +- +- platform_set_drvdata(pdev, NULL); +- the_controller = NULL; +- return 0; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-#ifdef CONFIG_PM +- +-/* USB suspend (controlled by the host) and system suspend (controlled +- * by the PXA) don't necessarily work well together. If USB is active, +- * the 48 MHz clock is required; so the system can't enter 33 MHz idle +- * mode, or any deeper PM saving state. +- * +- * For now, we punt and forcibly disconnect from the USB host when PXA +- * enters any suspend state. While we're disconnected, we always disable +- * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. +- * Boards without software pullup control shouldn't use those states. +- * VBUS IRQs should probably be ignored so that the PXA device just acts +- * "dead" to USB hosts until system resume. +- */ +-static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state) +-{ +- struct pxa2xx_udc *udc = platform_get_drvdata(dev); +- unsigned long flags; +- +- if (!udc->mach->gpio_pullup && !udc->mach->udc_command) +- WARN("USB host won't detect disconnect!\n"); +- udc->suspended = 1; +- +- local_irq_save(flags); +- pullup(udc); +- local_irq_restore(flags); +- +- return 0; +-} +- +-static int pxa2xx_udc_resume(struct platform_device *dev) +-{ +- struct pxa2xx_udc *udc = platform_get_drvdata(dev); +- unsigned long flags; +- +- udc->suspended = 0; +- local_irq_save(flags); +- pullup(udc); +- local_irq_restore(flags); +- +- return 0; +-} +- +-#else +-#define pxa2xx_udc_suspend NULL +-#define pxa2xx_udc_resume NULL +-#endif +- +-/*-------------------------------------------------------------------------*/ +- +-static struct platform_driver udc_driver = { +- .shutdown = pxa2xx_udc_shutdown, +- .remove = __exit_p(pxa2xx_udc_remove), +- .suspend = pxa2xx_udc_suspend, +- .resume = pxa2xx_udc_resume, +- .driver = { +- .owner = THIS_MODULE, +- .name = "pxa2xx-udc", +- }, +-}; +- +-static int __init udc_init(void) +-{ +- pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); +- return platform_driver_probe(&udc_driver, pxa2xx_udc_probe); +-} +-module_init(udc_init); +- +-static void __exit udc_exit(void) +-{ +- platform_driver_unregister(&udc_driver); +-} +-module_exit(udc_exit); +- +-MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); +-MODULE_LICENSE("GPL"); +-MODULE_ALIAS("platform:pxa2xx-udc"); +diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h +deleted file mode 100644 +index e2c19e8..0000000 +--- a/drivers/usb/gadget/pxa2xx_udc.h ++++ /dev/null +@@ -1,267 +0,0 @@ +-/* +- * linux/drivers/usb/gadget/pxa2xx_udc.h +- * Intel PXA2xx on-chip full speed USB device controller +- * +- * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix +- * Copyright (C) 2003 David Brownell +- * +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +- +-#ifndef __LINUX_USB_GADGET_PXA2XX_H +-#define __LINUX_USB_GADGET_PXA2XX_H +- +-#include <linux/types.h> +- +-/*-------------------------------------------------------------------------*/ +- +-/* pxa2xx has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ +-#define UFNRH_SIR (1 << 7) /* SOF interrupt request */ +-#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */ +-#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */ +-#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */ +-#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */ +- +-/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ +-#define UDCCFR UDC_RES2 /* UDC Control Function Register */ +-#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */ +-#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */ +- +-/* latest pxa255 errata define new "must be one" bits in UDCCFR */ +-#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM)) +- +-/*-------------------------------------------------------------------------*/ +- +-struct pxa2xx_udc; +- +-struct pxa2xx_ep { +- struct usb_ep ep; +- struct pxa2xx_udc *dev; +- +- const struct usb_endpoint_descriptor *desc; +- struct list_head queue; +- unsigned long pio_irqs; +- +- unsigned short fifo_size; +- u8 bEndpointAddress; +- u8 bmAttributes; +- +- unsigned stopped : 1; +- unsigned dma_fixup : 1; +- +- /* UDCCS = UDC Control/Status for this EP +- * UBCR = UDC Byte Count Remaining (contents of OUT fifo) +- * UDDR = UDC Endpoint Data Register (the fifo) +- * DRCM = DMA Request Channel Map +- */ +- volatile u32 *reg_udccs; +- volatile u32 *reg_ubcr; +- volatile u32 *reg_uddr; +-}; +- +-struct pxa2xx_request { +- struct usb_request req; +- struct list_head queue; +-}; +- +-enum ep0_state { +- EP0_IDLE, +- EP0_IN_DATA_PHASE, +- EP0_OUT_DATA_PHASE, +- EP0_END_XFER, +- EP0_STALL, +-}; +- +-#define EP0_FIFO_SIZE ((unsigned)16) +-#define BULK_FIFO_SIZE ((unsigned)64) +-#define ISO_FIFO_SIZE ((unsigned)256) +-#define INT_FIFO_SIZE ((unsigned)8) +- +-struct udc_stats { +- struct ep0stats { +- unsigned long ops; +- unsigned long bytes; +- } read, write; +- unsigned long irqs; +-}; +- +-#ifdef CONFIG_USB_PXA2XX_SMALL +-/* when memory's tight, SMALL config saves code+data. */ +-#define PXA_UDC_NUM_ENDPOINTS 3 +-#endif +- +-#ifndef PXA_UDC_NUM_ENDPOINTS +-#define PXA_UDC_NUM_ENDPOINTS 16 +-#endif +- +-struct pxa2xx_udc { +- struct usb_gadget gadget; +- struct usb_gadget_driver *driver; +- +- enum ep0_state ep0state; +- struct udc_stats stats; +- unsigned got_irq : 1, +- vbus : 1, +- pullup : 1, +- has_cfr : 1, +- req_pending : 1, +- req_std : 1, +- req_config : 1, +- suspended : 1, +- active : 1; +- +-#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) +- struct timer_list timer; +- +- struct device *dev; +- struct clk *clk; +- struct pxa2xx_udc_mach_info *mach; +- u64 dma_mask; +- struct pxa2xx_ep ep [PXA_UDC_NUM_ENDPOINTS]; +- +-#ifdef CONFIG_USB_GADGET_DEBUG_FS +- struct dentry *debugfs_udc; +-#endif +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-#ifdef CONFIG_ARCH_LUBBOCK +-#include <asm/arch/lubbock.h> +-/* lubbock can also report usb connect/disconnect irqs */ +-#endif +- +-static struct pxa2xx_udc *the_controller; +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * Debugging support vanishes in non-debug builds. DBG_NORMAL should be +- * mostly silent during normal use/testing, with no timing side-effects. +- */ +-#define DBG_NORMAL 1 /* error paths, device state transitions */ +-#define DBG_VERBOSE 2 /* add some success path trace info */ +-#define DBG_NOISY 3 /* ... even more: request level */ +-#define DBG_VERY_NOISY 4 /* ... even more: packet level */ +- +-#define DMSG(stuff...) pr_debug("udc: " stuff) +- +-#ifdef DEBUG +- +-static int is_vbus_present(void); +- +-static const char *state_name[] = { +- "EP0_IDLE", +- "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", +- "EP0_END_XFER", "EP0_STALL" +-}; +- +-#ifdef VERBOSE_DEBUG +-# define UDC_DEBUG DBG_VERBOSE +-#else +-# define UDC_DEBUG DBG_NORMAL +-#endif +- +-static void __maybe_unused +-dump_udccr(const char *label) +-{ +- u32 udccr = UDCCR; +- DMSG("%s %02X =%s%s%s%s%s%s%s%s\n", +- label, udccr, +- (udccr & UDCCR_REM) ? " rem" : "", +- (udccr & UDCCR_RSTIR) ? " rstir" : "", +- (udccr & UDCCR_SRM) ? " srm" : "", +- (udccr & UDCCR_SUSIR) ? " susir" : "", +- (udccr & UDCCR_RESIR) ? " resir" : "", +- (udccr & UDCCR_RSM) ? " rsm" : "", +- (udccr & UDCCR_UDA) ? " uda" : "", +- (udccr & UDCCR_UDE) ? " ude" : ""); +-} +- +-static void __maybe_unused +-dump_udccs0(const char *label) +-{ +- u32 udccs0 = UDCCS0; +- +- DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n", +- label, state_name[the_controller->ep0state], udccs0, +- (udccs0 & UDCCS0_SA) ? " sa" : "", +- (udccs0 & UDCCS0_RNE) ? " rne" : "", +- (udccs0 & UDCCS0_FST) ? " fst" : "", +- (udccs0 & UDCCS0_SST) ? " sst" : "", +- (udccs0 & UDCCS0_DRWF) ? " dwrf" : "", +- (udccs0 & UDCCS0_FTF) ? " ftf" : "", +- (udccs0 & UDCCS0_IPR) ? " ipr" : "", +- (udccs0 & UDCCS0_OPR) ? " opr" : ""); +-} +- +-static void __maybe_unused +-dump_state(struct pxa2xx_udc *dev) +-{ +- u32 tmp; +- unsigned i; +- +- DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", +- is_vbus_present() ? "host " : "disconnected", +- state_name[dev->ep0state], +- UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); +- dump_udccr("udccr"); +- if (dev->has_cfr) { +- tmp = UDCCFR; +- DMSG("udccfr %02X =%s%s\n", tmp, +- (tmp & UDCCFR_AREN) ? " aren" : "", +- (tmp & UDCCFR_ACM) ? " acm" : ""); +- } +- +- if (!dev->driver) { +- DMSG("no gadget driver bound\n"); +- return; +- } else +- DMSG("ep0 driver '%s'\n", dev->driver->driver.name); +- +- if (!is_vbus_present()) +- return; +- +- dump_udccs0 ("udccs0"); +- DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", +- dev->stats.write.bytes, dev->stats.write.ops, +- dev->stats.read.bytes, dev->stats.read.ops); +- +- for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { +- if (dev->ep [i].desc == NULL) +- continue; +- DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs); +- } +-} +- +-#else +- +-#define dump_udccr(x) do{}while(0) +-#define dump_udccs0(x) do{}while(0) +-#define dump_state(x) do{}while(0) +- +-#define UDC_DEBUG ((unsigned)0) +- +-#endif +- +-#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) +- +-#define ERR(stuff...) pr_err("udc: " stuff) +-#define WARN(stuff...) pr_warning("udc: " stuff) +-#define INFO(stuff...) pr_info("udc: " stuff) +- +- +-#endif /* __LINUX_USB_GADGET_PXA2XX_H */ +diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c +index d0677f5..7228e85 100644 +--- a/drivers/usb/gadget/rndis.c ++++ b/drivers/usb/gadget/rndis.c +@@ -1,8 +1,6 @@ + /* + * RNDIS MSG parser + * +- * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ +- * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * +@@ -30,6 +28,7 @@ + #include <linux/init.h> + #include <linux/list.h> + #include <linux/proc_fs.h> ++#include <linux/seq_file.h> + #include <linux/netdevice.h> + + #include <asm/io.h> +@@ -38,9 +37,7 @@ + #include <asm/unaligned.h> + + +-#undef RNDIS_PM +-#undef RNDIS_WAKEUP +-#undef VERBOSE ++#undef VERBOSE_DEBUG + + #include "rndis.h" + +@@ -96,9 +93,6 @@ static const u32 oid_supported_list [] = + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_PHYSICAL_MEDIUM, +-#if 0 +- OID_GEN_RNDIS_CONFIG_PARAMETER, +-#endif + + /* the statistical stuff */ + OID_GEN_XMIT_OK, +@@ -146,7 +140,14 @@ static const u32 oid_supported_list [] = + #endif /* RNDIS_OPTIONAL_STATS */ + + #ifdef RNDIS_PM +- /* PM and wakeup are mandatory for USB: */ ++ /* PM and wakeup are "mandatory" for USB, but the RNDIS specs ++ * don't say what they mean ... and the NDIS specs are often ++ * confusing and/or ambiguous in this context. (That is, more ++ * so than their specs for the other OIDs.) ++ * ++ * FIXME someone who knows what these should do, please ++ * implement them! ++ */ + + /* power management */ + OID_PNP_CAPABILITIES, +@@ -173,6 +174,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + __le32 *outbuf; + int i, count; + rndis_query_cmplt_type *resp; ++ struct net_device *net; ++ struct net_device_stats *stats; + + if (!r) return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; +@@ -194,6 +197,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + outbuf = (__le32 *) &resp[1]; + resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + ++ net = rndis_per_dev_params[configNr].dev; ++ if (net->get_stats) ++ stats = net->get_stats(net); ++ else ++ stats = NULL; ++ + switch (OID) { + + /* general oids (table 4-1) */ +@@ -350,11 +359,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + case OID_GEN_XMIT_OK: + if (rndis_debug > 1) + DBG("%s: OID_GEN_XMIT_OK\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- rndis_per_dev_params [configNr].stats->tx_packets - +- rndis_per_dev_params [configNr].stats->tx_errors - +- rndis_per_dev_params [configNr].stats->tx_dropped); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->tx_packets ++ - stats->tx_errors - stats->tx_dropped); + retval = 0; + } + break; +@@ -363,11 +370,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + case OID_GEN_RCV_OK: + if (rndis_debug > 1) + DBG("%s: OID_GEN_RCV_OK\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- rndis_per_dev_params [configNr].stats->rx_packets - +- rndis_per_dev_params [configNr].stats->rx_errors - +- rndis_per_dev_params [configNr].stats->rx_dropped); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_packets ++ - stats->rx_errors - stats->rx_dropped); + retval = 0; + } + break; +@@ -376,9 +381,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + case OID_GEN_XMIT_ERROR: + if (rndis_debug > 1) + DBG("%s: OID_GEN_XMIT_ERROR\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->tx_errors); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->tx_errors); + retval = 0; + } + break; +@@ -387,9 +391,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + case OID_GEN_RCV_ERROR: + if (rndis_debug > 1) + DBG("%s: OID_GEN_RCV_ERROR\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_errors); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_errors); + retval = 0; + } + break; +@@ -397,150 +400,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + /* mandatory */ + case OID_GEN_RCV_NO_BUFFER: + DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_dropped); +- retval = 0; +- } +- break; +- +-#ifdef RNDIS_OPTIONAL_STATS +- case OID_GEN_DIRECTED_BYTES_XMIT: +- DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__); +- /* +- * Aunt Tilly's size of shoes +- * minus antarctica count of penguins +- * divided by weight of Alpha Centauri +- */ +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- (rndis_per_dev_params [configNr] +- .stats->tx_packets - +- rndis_per_dev_params [configNr] +- .stats->tx_errors - +- rndis_per_dev_params [configNr] +- .stats->tx_dropped) +- * 123); +- retval = 0; +- } +- break; +- +- case OID_GEN_DIRECTED_FRAMES_XMIT: +- DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__); +- /* dito */ +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- (rndis_per_dev_params [configNr] +- .stats->tx_packets - +- rndis_per_dev_params [configNr] +- .stats->tx_errors - +- rndis_per_dev_params [configNr] +- .stats->tx_dropped) +- / 123); +- retval = 0; +- } +- break; +- +- case OID_GEN_MULTICAST_BYTES_XMIT: +- DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast*1234); +- retval = 0; +- } +- break; +- +- case OID_GEN_MULTICAST_FRAMES_XMIT: +- DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast); +- retval = 0; +- } +- break; +- +- case OID_GEN_BROADCAST_BYTES_XMIT: +- DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->tx_packets/42*255); +- retval = 0; +- } +- break; +- +- case OID_GEN_BROADCAST_FRAMES_XMIT: +- DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->tx_packets/42); +- retval = 0; +- } +- break; +- +- case OID_GEN_DIRECTED_BYTES_RCV: +- DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__); +- *outbuf = __constant_cpu_to_le32 (0); +- retval = 0; +- break; +- +- case OID_GEN_DIRECTED_FRAMES_RCV: +- DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__); +- *outbuf = __constant_cpu_to_le32 (0); +- retval = 0; +- break; +- +- case OID_GEN_MULTICAST_BYTES_RCV: +- DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast * 1111); +- retval = 0; +- } +- break; +- +- case OID_GEN_MULTICAST_FRAMES_RCV: +- DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast); +- retval = 0; +- } +- break; +- +- case OID_GEN_BROADCAST_BYTES_RCV: +- DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_packets/42*255); +- retval = 0; +- } +- break; +- +- case OID_GEN_BROADCAST_FRAMES_RCV: +- DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_packets/42); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_dropped); + retval = 0; + } + break; + +- case OID_GEN_RCV_CRC_ERROR: +- DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_crc_errors); +- retval = 0; +- } +- break; +- +- case OID_GEN_TRANSMIT_QUEUE_LENGTH: +- DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__); +- *outbuf = __constant_cpu_to_le32 (0); +- retval = 0; +- break; +-#endif /* RNDIS_OPTIONAL_STATS */ +- + /* ieee802.3 OIDs (table 4-3) */ + + /* mandatory */ +@@ -592,9 +457,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + /* mandatory */ + case OID_802_3_RCV_ERROR_ALIGNMENT: + DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_frame_errors); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_frame_errors); + retval = 0; + } + break; +@@ -613,64 +477,6 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + retval = 0; + break; + +-#ifdef RNDIS_OPTIONAL_STATS +- case OID_802_3_XMIT_DEFERRED: +- DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_MAX_COLLISIONS: +- DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_RCV_OVERRUN: +- DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_UNDERRUN: +- DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_HEARTBEAT_FAILURE: +- DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_TIMES_CRS_LOST: +- DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_LATE_COLLISIONS: +- DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__); +- /* TODO */ +- break; +-#endif /* RNDIS_OPTIONAL_STATS */ +- +-#ifdef RNDIS_PM +- /* power management OIDs (table 4-5) */ +- case OID_PNP_CAPABILITIES: +- DBG("%s: OID_PNP_CAPABILITIES\n", __func__); +- +- /* for now, no wakeup capabilities */ +- length = sizeof (struct NDIS_PNP_CAPABILITIES); +- memset(outbuf, 0, length); +- retval = 0; +- break; +- case OID_PNP_QUERY_POWER: +- DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__, +- get_unaligned_le32(buf) - 1); +- /* only suspend is a real power state, and +- * it can't be entered by OID_PNP_SET_POWER... +- */ +- length = 0; +- retval = 0; +- break; +-#endif +- + default: + pr_warning("%s: query unknown OID 0x%08X\n", + __func__, OID); +@@ -726,9 +532,6 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, + * what makes the packet flow start and stop, like + * activating the CDC Ethernet altsetting. + */ +-#ifdef RNDIS_PM +-update_linkstate: +-#endif + retval = 0; + if (*params->filter) { + params->state = RNDIS_DATA_INITIALIZED; +@@ -747,49 +550,6 @@ update_linkstate: + DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); + retval = 0; + break; +-#if 0 +- case OID_GEN_RNDIS_CONFIG_PARAMETER: +- { +- struct rndis_config_parameter *param; +- param = (struct rndis_config_parameter *) buf; +- DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", +- __func__, +- min(cpu_to_le32(param->ParameterNameLength),80), +- buf + param->ParameterNameOffset); +- retval = 0; +- } +- break; +-#endif +- +-#ifdef RNDIS_PM +- case OID_PNP_SET_POWER: +- /* The only real power state is USB suspend, and RNDIS requests +- * can't enter it; this one isn't really about power. After +- * resuming, Windows forces a reset, and then SET_POWER D0. +- * FIXME ... then things go batty; Windows wedges itself. +- */ +- i = get_unaligned_le32(buf); +- DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1); +- switch (i) { +- case NdisDeviceStateD0: +- *params->filter = params->saved_filter; +- goto update_linkstate; +- case NdisDeviceStateD3: +- case NdisDeviceStateD2: +- case NdisDeviceStateD1: +- params->saved_filter = *params->filter; +- retval = 0; +- break; +- } +- break; +- +-#ifdef RNDIS_WAKEUP +- // no wakeup support advertised, so wakeup OIDs always fail: +- // - OID_PNP_ENABLE_WAKE_UP +- // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN +-#endif +- +-#endif /* RNDIS_PM */ + + default: + pr_warning("%s: set unknown OID 0x%08X, size %d\n", +@@ -807,8 +567,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) + { + rndis_init_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + +- if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; ++ if (!params->dev) ++ return -ENOTSUPP; + + r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); + if (!r) +@@ -826,7 +588,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) + resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); + resp->MaxTransferSize = cpu_to_le32 ( +- rndis_per_dev_params [configNr].dev->mtu ++ params->dev->mtu + + sizeof (struct ethhdr) + + sizeof (struct rndis_packet_msg_type) + + 22); +@@ -834,10 +596,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) + resp->AFListOffset = __constant_cpu_to_le32 (0); + resp->AFListSize = __constant_cpu_to_le32 (0); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -845,9 +604,11 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) + { + rndis_query_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); +- if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; ++ if (!params->dev) ++ return -ENOTSUPP; + + /* + * we need more memory: +@@ -878,9 +639,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) + } else + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); ++ params->resp_avail(params->v); + return 0; + } + +@@ -889,6 +648,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) + u32 BufLength, BufOffset; + rndis_set_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); + if (!r) +@@ -898,7 +658,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) + BufLength = le32_to_cpu (buf->InformationBufferLength); + BufOffset = le32_to_cpu (buf->InformationBufferOffset); + +-#ifdef VERBOSE ++#ifdef VERBOSE_DEBUG + DBG("%s: Length: %d\n", __func__, BufLength); + DBG("%s: Offset: %d\n", __func__, BufOffset); + DBG("%s: InfoBuffer: ", __func__); +@@ -919,10 +679,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) + else + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -930,6 +687,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) + { + rndis_reset_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); + if (!r) +@@ -942,10 +700,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) + /* resent information */ + resp->AddressingReset = __constant_cpu_to_le32 (1); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -954,6 +709,7 @@ static int rndis_keepalive_response (int configNr, + { + rndis_keepalive_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + /* host "should" check only in RNDIS_DATA_INITIALIZED state */ + +@@ -968,10 +724,7 @@ static int rndis_keepalive_response (int configNr, + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -983,8 +736,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status) + { + rndis_indicate_status_msg_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + +- if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) ++ if (params->state == RNDIS_UNINITIALIZED) + return -ENOTSUPP; + + r = rndis_add_response (configNr, +@@ -1000,9 +754,7 @@ static int rndis_indicate_status_msg (int configNr, u32 status) + resp->StatusBufferLength = __constant_cpu_to_le32 (0); + resp->StatusBufferOffset = __constant_cpu_to_le32 (0); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); ++ params->resp_avail(params->v); + return 0; + } + +@@ -1029,7 +781,6 @@ void rndis_uninit (int configNr) + + if (configNr >= RNDIS_MAX_CONFIGS) + return; +- rndis_per_dev_params [configNr].used = 0; + rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; + + /* drain the response queue */ +@@ -1142,21 +893,25 @@ int rndis_msg_parser (u8 configNr, u8 *buf) + return -ENOTSUPP; + } + +-int rndis_register (int (* rndis_control_ack) (struct net_device *)) ++int rndis_register(void (*resp_avail)(void *v), void *v) + { + u8 i; + ++ if (!resp_avail) ++ return -EINVAL; ++ + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + if (!rndis_per_dev_params [i].used) { + rndis_per_dev_params [i].used = 1; +- rndis_per_dev_params [i].ack = rndis_control_ack; ++ rndis_per_dev_params [i].resp_avail = resp_avail; ++ rndis_per_dev_params [i].v = v; + DBG("%s: configNr = %d\n", __func__, i); + return i; + } + } + DBG("failed\n"); + +- return -1; ++ return -ENODEV; + } + + void rndis_deregister (int configNr) +@@ -1169,16 +924,14 @@ void rndis_deregister (int configNr) + return; + } + +-int rndis_set_param_dev (u8 configNr, struct net_device *dev, +- struct net_device_stats *stats, +- u16 *cdc_filter) ++int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) + { + DBG("%s:\n", __func__ ); +- if (!dev || !stats) return -1; ++ if (!dev) ++ return -EINVAL; + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].dev = dev; +- rndis_per_dev_params [configNr].stats = stats; + rndis_per_dev_params [configNr].filter = cdc_filter; + + return 0; +@@ -1296,14 +1049,11 @@ int rndis_rm_hdr(struct sk_buff *skb) + + #ifdef CONFIG_USB_GADGET_DEBUG_FILES + +-static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, +- void *data) ++static int rndis_proc_show(struct seq_file *m, void *v) + { +- char *out = page; +- int len; +- rndis_params *param = (rndis_params *) data; ++ rndis_params *param = m->private; + +- out += snprintf (out, count, ++ seq_printf(m, + "Config Nr. %d\n" + "used : %s\n" + "state : %s\n" +@@ -1326,25 +1076,13 @@ static int rndis_proc_read (char *page, char **start, off_t off, int count, int + (param->media_state) ? 0 : param->speed*100, + (param->media_state) ? "disconnected" : "connected", + param->vendorID, param->vendorDescr); +- +- len = out - page; +- len -= off; +- +- if (len < count) { +- *eof = 1; +- if (len <= 0) +- return 0; +- } else +- len = count; +- +- *start = page + off; +- return len; ++ return 0; + } + +-static int rndis_proc_write (struct file *file, const char __user *buffer, +- unsigned long count, void *data) ++static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) + { +- rndis_params *p = data; ++ rndis_params *p = PDE(file->f_path.dentry->d_inode)->data; + u32 speed = 0; + int i, fl_speed = 0; + +@@ -1386,6 +1124,20 @@ static int rndis_proc_write (struct file *file, const char __user *buffer, + return count; + } + ++static int rndis_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, rndis_proc_show, PDE(inode)->data); ++} ++ ++static const struct file_operations rndis_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = rndis_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .write = rndis_proc_write, ++}; ++ + #define NAME_TEMPLATE "driver/rndis-%03d" + + static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; +@@ -1403,7 +1155,9 @@ int __init rndis_init (void) + + sprintf (name, NAME_TEMPLATE, i); + if (!(rndis_connect_state [i] +- = create_proc_entry (name, 0660, NULL))) ++ = proc_create_data(name, 0660, NULL, ++ &rndis_proc_fops, ++ (void *)(rndis_per_dev_params + i)))) + { + DBG("%s :remove entries", __func__); + while (i) { +@@ -1413,11 +1167,6 @@ int __init rndis_init (void) + DBG("\n"); + return -EIO; + } +- +- rndis_connect_state [i]->write_proc = rndis_proc_write; +- rndis_connect_state [i]->read_proc = rndis_proc_read; +- rndis_connect_state [i]->data = (void *) +- (rndis_per_dev_params + i); + #endif + rndis_per_dev_params [i].confignr = i; + rndis_per_dev_params [i].used = 0; +diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h +index 397b149..aac61df 100644 +--- a/drivers/usb/gadget/rndis.h ++++ b/drivers/usb/gadget/rndis.h +@@ -1,8 +1,6 @@ + /* + * RNDIS Definitions for Remote NDIS + * +- * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ +- * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * +@@ -235,20 +233,19 @@ typedef struct rndis_params + const u8 *host_mac; + u16 *filter; + struct net_device *dev; +- struct net_device_stats *stats; + + u32 vendorID; + const char *vendorDescr; +- int (*ack) (struct net_device *); ++ void (*resp_avail)(void *v); ++ void *v; + struct list_head resp_queue; + } rndis_params; + + /* RNDIS Message parser and other useless functions */ + int rndis_msg_parser (u8 configNr, u8 *buf); +-int rndis_register (int (*rndis_control_ack) (struct net_device *)); ++int rndis_register(void (*resp_avail)(void *v), void *v); + void rndis_deregister (int configNr); + int rndis_set_param_dev (u8 configNr, struct net_device *dev, +- struct net_device_stats *stats, + u16 *cdc_filter); + int rndis_set_param_vendor (u8 configNr, u32 vendorID, + const char *vendorDescr); +diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c +index 6b1ef48..29d13eb 100644 +--- a/drivers/usb/gadget/s3c2410_udc.c ++++ b/drivers/usb/gadget/s3c2410_udc.c +@@ -35,7 +35,6 @@ + #include <linux/list.h> + #include <linux/interrupt.h> + #include <linux/platform_device.h> +-#include <linux/version.h> + #include <linux/clk.h> + + #include <linux/debugfs.h> +@@ -49,15 +48,14 @@ + #include <asm/irq.h> + #include <asm/system.h> + #include <asm/unaligned.h> +-#include <asm/arch/irqs.h> ++#include <mach/irqs.h> + +-#include <asm/arch/hardware.h> +-#include <asm/arch/regs-gpio.h> ++#include <mach/hardware.h> ++#include <mach/regs-gpio.h> + + #include <asm/plat-s3c24xx/regs-udc.h> + #include <asm/plat-s3c24xx/udc.h> + +-#include <asm/mach-types.h> + + #include "s3c2410_udc.h" + +@@ -888,7 +886,7 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) + } + } + +-#include <asm/arch/regs-irq.h> ++#include <mach/regs-irq.h> + + /* + * s3c2410_udc_irq - interrupt handler +diff --git a/drivers/usb/gadget/sa1100_udc.c b/drivers/usb/gadget/sa1100_udc.c +new file mode 100644 +index 0000000..3a9f24f +--- /dev/null ++++ b/drivers/usb/gadget/sa1100_udc.c +@@ -0,0 +1,2421 @@ ++/* ++ * SA1100 USB Device Controller (UDC) driver. ++ * ++ * Copyright (C) Compaq Computer Corporation, 1998, 1999 ++ * Copyright (C) Extenex Corporation, 2001 ++ * Copyright (C) David Brownell, 2003 ++ * Copyright (C) Nick Bane, 2005, 2006, 2007 ++ * Many fragments from pxa2xx_udc.c and mach-sa1100 driver with various ++ * GPL Copyright authors incl Russel king and Nicolas Pitre ++ * Working port to 2.6.32-1 by N C Bane ++ * ++ * This file provides interrupt routing and overall coordination for the ++ * sa1100 USB endpoints: ep0, ep1out-bulk, ep2in-bulk, as well as device ++ * initialization and some parts of USB "Chapter 9" device behavior. ++ * ++ * It implements the "USB gadget controller" API, abstracting most hardware ++ * details so that drivers running on top of this API are mostly independent ++ * of hardware. A key exception is that ep0 logic needs to understand which ++ * endpoints a given controller has, and their capabilities. Also, hardware ++ * that doesn't fully support USB (like sa1100) may need workarounds in the ++ * protocols implemented by device functions. ++ * ++ * See linux/Documentation/arm/SA1100/SA1100_USB for more info, or the ++ * kerneldoc for the API exposed to gadget drivers. ++ * ++ */ ++//#define DEBUG 1 ++//#define VERBOSE 1 ++ ++//#define SA1100_USB_DEBUG ++#ifdef SA1100_USB_DEBUG ++static int sa1100_usb_debug=0; ++#endif ++ ++#define NCB_DMA_FIX ++#ifdef NCB_DMA_FIX ++// This is a clunky fix for dma alignemnt issues ++// It should probably be done better by someone more ++// steeped in DMA lore ++#include <linux/slab.h> ++#define SEND_BUFFER_SIZE 4096 /* this is probably a bit big */ ++#define RECEIVE_BUFFER_SIZE 256 /* 64 may be all that is necessary */ ++static char *send_buffer=NULL; ++static char *receive_buffer=NULL; ++#endif ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/smp_lock.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/timer.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/version.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++ ++#include <asm/byteorder.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/dma.h> ++#include <asm/system.h> ++#include <asm/mach-types.h> ++#include <asm/unaligned.h> ++ ++#include <linux/usb.h> ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++ ++#if CONFIG_PROC_FS ++#include <linux/proc_fs.h> ++#endif ++ ++#if defined(CONFIG_SA1100_BALLOON) ++#include <asm/arch/balloon2.h> ++#endif ++ ++#if defined(CONFIG_SA1100_COLLIE) ++#include <asm/hardware/scoop.h> ++#include <asm/arch/collie.h> ++#endif ++ ++#define DRIVER_VERSION __DATE__ ++ ++#define DMA_ADDR_INVALID (~(dma_addr_t)0) ++ ++ ++static const char driver_name [] = "sa1100_udc"; ++static const char driver_desc [] = "SA-1110 USB Device Controller"; ++ ++static const char ep0name [] = "ep0"; ++ ++#ifdef DEBUG ++static char *type_string (u8 bmAttributes) ++{ ++ switch ( (bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: return "bulk"; ++ //case USB_ENDPOINT_XFER_ISOC: return "iso"; ++ case USB_ENDPOINT_XFER_INT: return "intr"; ++ }; ++ return "control"; ++} ++#endif ++ ++#include <linux/dma-mapping.h> ++struct usb_stats_t { ++ unsigned long ep0_fifo_write_failures; ++ unsigned long ep0_bytes_written; ++ unsigned long ep0_fifo_read_failures; ++ unsigned long ep0_bytes_read; ++}; ++ ++struct usb_info_t { ++ dma_regs_t *dmaregs_tx, *dmaregs_rx; ++ int state; ++ unsigned char address; ++ struct usb_stats_t stats; ++}; ++ ++enum { kError=-1, kEvSuspend=0, kEvReset=1, ++ kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 }; ++int usbctl_next_state_on_event( int event ) { ++ return 0; ++} ++static struct usb_info_t usbd_info; ++ ++/* receiver */ ++void ep1_reset(void); ++void ep1_stall(void); ++int sa1100_usb_recv (struct usb_request *req, void (*callback) (int,int)); ++ ++/* xmitter */ ++void ep2_reset(void); ++void ep2_stall(void); ++int sa1100_usb_send (struct usb_request *req, void (*callback) (int,int)); ++ ++/* UDC register utility functions */ ++#define UDC_write(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) = (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: write %#x to %p (%#lx) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while((reg) != (val)); \ ++} ++ ++#define UDC_set(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) |= (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: set %#x of %p (%#lx) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while(!((reg) & (val))); \ ++} ++ ++#define UDC_clear(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) &= ~(val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: clear %#x of %p (%#lx) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while((reg) & (val)); \ ++} ++ ++#define UDC_flip(reg, val) { \ ++ int i = 10000; \ ++ (reg) = (val); \ ++ do { \ ++ (reg) = (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: flip %#x of %p (%#lx) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while(((reg) & (val))); \ ++} ++ ++#include "sa1100_udc.h" ++ ++static struct sa1100_udc *the_controller; ++static void nuke (struct sa1100_ep *, int status); ++static void done (struct sa1100_ep *ep, struct sa1100_request *req, int status); ++static inline void ep0_idle (struct sa1100_udc *dev) ++{ ++ dev->ep0state = EP0_IDLE; ++} ++ ++// ep0 handlers ++ ++// 1 == lots of trace noise, 0 = only "important' stuff ++#define VERBOSITY 0 ++ ++#if 1 && !defined( ASSERT ) ++# define ASSERT(expr) \ ++ if(!(expr)) { \ ++ printk( "Assertion failed! %s,%s,%s,line=%d\n",\ ++ #expr,__FILE__,__FUNCTION__,__LINE__); \ ++ } ++#else ++# define ASSERT(expr) ++#endif ++ ++#if VERBOSITY ++#define PRINTKD(fmt, args...) printk( fmt , ## args) ++#else ++#define PRINTKD(fmt, args...) ++#endif ++ ++/* other subroutines */ ++unsigned int (*wrint)(void); ++void ep0_int_hndlr( void ); ++static void ep0_queue(void *buf, unsigned int req, unsigned int act); ++static void write_fifo( void ); ++static int read_fifo( struct usb_ctrlrequest * p ); ++ ++/* some voodo helpers 01Mar01ww */ ++static void set_cs_bits( __u32 set_bits ); ++static void set_de( void ); ++static void set_ipr( void ); ++static void set_ipr_and_de( void ); ++static bool clear_opr( void ); ++ ++/*************************************************************************** ++Inline Helpers ++***************************************************************************/ ++ ++/* Data extraction from usb_request_t fields */ ++enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 }; ++static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); } ++ ++static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); } ++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); } ++ ++/* following is hook for self-powered flag in GET_STATUS. Some devices ++ .. might like to override and return real info */ ++static inline bool self_powered_hook( void ) { return true; } ++ ++#if VERBOSITY ++/* "pcs" == "print control status" */ ++static inline void pcs( void ) ++{ ++ __u32 foo = Ser0UDCCS0; ++ printk( "%8.8X: %s %s %s %s\n", ++ foo, ++ foo & UDCCS0_SE ? "SE" : "", ++ foo & UDCCS0_OPR ? "OPR" : "", ++ foo & UDCCS0_IPR ? "IPR" : "", ++ foo & UDCCS0_SST ? "SST" : "" ++ ); ++} ++static inline void preq( struct usb_ctrlrequest * pReq ) ++{ ++ static char * tnames[] = { "dev", "intf", "ep", "oth" }; ++ static char * rnames[] = { "std", "class", "vendor", "???" }; ++ char * psz; ++ switch( pReq->bRequest ) { ++ case USB_REQ_GET_STATUS: psz = "get stat"; break; ++ case USB_REQ_CLEAR_FEATURE: psz = "clr feat"; break; ++ case USB_REQ_SET_FEATURE: psz = "set feat"; break; ++ case USB_REQ_SET_ADDRESS: psz = "set addr"; break; ++ case USB_REQ_GET_DESCRIPTOR: psz = "get desc"; break; ++ case USB_REQ_SET_DESCRIPTOR: psz = "set desc"; break; ++ case USB_REQ_GET_CONFIGURATION: psz = "get cfg"; break; ++ case USB_REQ_SET_CONFIGURATION: psz = "set cfg"; break; ++ case USB_REQ_GET_INTERFACE: psz = "get intf"; break; ++ case USB_REQ_SET_INTERFACE: psz = "set intf"; break; ++ default: psz = "unknown"; break; ++ } ++ printk( "- [%s: %s req to %s. dir=%s]\n", psz, ++ rnames[ (pReq->bRequestType >> 5) & 3 ], ++ tnames[ pReq->bRequestType & 3 ], ++ ( pReq->bRequestType & 0x80 ) ? "in" : "out" ); ++} ++ ++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) ++{ ++ printk("%s: bRequestType=0x%02x bRequest=0x%02x " ++ "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n", ++ prefix, req->bRequestType, req->bRequest, ++ le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex), ++ le16_to_cpu(req->wLength)); ++} ++#else ++static inline void pcs( void ){} ++//static inline void preq( void ){} ++static inline void preq( void *x ){} ++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) {} ++#endif ++ ++/*************************************************************************** ++Globals ++***************************************************************************/ ++static const char pszMe[] = "usbep0: "; ++ ++ ++/* global write struct to keep write ++ ..state around across interrupts */ ++static struct { ++ unsigned char *p; ++ int bytes_left; ++} wr; ++ ++/*************************************************************************** ++Public Interface ++***************************************************************************/ ++ ++/* reset received from HUB (or controller just went nuts and reset by itself!) ++ so udc core has been reset, track this state here */ ++void ep0_reset(void) ++{ ++ /* reset state machine */ ++ wr.p = NULL; ++ wr.bytes_left = 0; ++ usbd_info.address=0; ++// needed? ++ Ser0UDCAR = 0; ++} ++ ++ ++/* handle interrupt for endpoint zero */ ++ ++inline void ep0_clear_write(void) { ++ wr.p = NULL; ++ wr.bytes_left = 0; ++} ++ ++/* this is a config packet parser based on that from the updated HH 2.6 udc */ ++static void ep0_read_packet(void) ++{ ++ unsigned char status_buf[2]; /* returned in GET_STATUS */ ++ struct usb_ctrlrequest req; ++ int request_type; ++ int n; ++ __u32 address; ++ __u32 in, out; ++ ++ /* reset previous count */ ++ the_controller->ep0_req_len=-1; ++ ++ /* read the setup request */ ++ n = read_fifo( &req ); ++ usbctl_dump_request("ep0_read_packet",&req); ++ ++ if ( n != sizeof( req ) ) { ++ printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. " ++ " Stalling out...\n", ++ pszMe, sizeof( req ), n ); ++ /* force stall, serviced out */ ++ set_cs_bits( UDCCS0_FST | UDCCS0_SO ); ++ goto sh_sb_end; ++ } ++ ++ /* Is it a standard request? (not vendor or class request) */ ++ request_type = type_code_from_request( req.bRequestType ); ++ if ( request_type != 0 ) { ++ printk( "%ssetup begin: unsupported bRequestType: %d ignored\n", ++ pszMe, request_type ); ++ set_cs_bits( UDCCS0_DE | UDCCS0_SO ); ++ goto sh_sb_end; ++ } ++ ++ /* save requested reply size */ ++ the_controller->ep0_req_len=le16_to_cpu(req.wLength); ++ PRINTKD("%s: request length is %d\n",__FUNCTION__,the_controller->ep0_req_len); ++ ++#if VERBOSITY ++ { ++ unsigned char * pdb = (unsigned char *) &req; ++ PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ", ++ pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7] ++ ); ++ preq( &req ); ++ } ++#endif ++ ++ /* Handle it */ ++ switch( req.bRequest ) { ++ ++ /* This first bunch have no data phase */ ++ ++ case USB_REQ_SET_ADDRESS: ++ address = (__u32) (req.wValue & 0x7F); ++ /* when SO and DE sent, UDC will enter status phase and ack, ++ ..propagating new address to udc core. Next control transfer ++ ..will be on the new address. You can't see the change in a ++ ..read back of CAR until then. (about 250us later, on my box). ++ ..The original Intel driver sets S0 and DE and code to check ++ ..that address has propagated here. I tried this, but it ++ ..would only work sometimes! The rest of the time it would ++ ..never propagate and we'd spin forever. So now I just set ++ ..it and pray... ++ */ ++ Ser0UDCAR = address; ++ usbd_info.address = address; ++ usbctl_next_state_on_event( kEvAddress ); ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ printk( "%sI have been assigned address: %d\n", pszMe, address ); ++ break; ++ ++ ++ case USB_REQ_SET_CONFIGURATION: ++ if ( req.wValue == 1 ) { ++ /* configured */ ++ if (usbctl_next_state_on_event( kEvConfig ) != kError) { ++ /* (re)set the out and in max packet sizes */ ++ PRINTKD( "%s: calling the_controller.driver->setup with SET_CONFIGURATION\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ in = __le16_to_cpu( the_controller->ep[1].ep.maxpacket ); ++ out = __le16_to_cpu( the_controller->ep[2].ep.maxpacket ); ++ Ser0UDCOMP = ( out - 1 ); ++ Ser0UDCIMP = ( in - 1 ); ++ // we are configured ++ usbd_info.state = USB_STATE_CONFIGURED; ++ // enable rx and tx interrupts ++ Ser0UDCCR &= ~(UDCCR_RIM | UDCCR_TIM); ++ ++ printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in ); ++ break; ++ } ++ } else if ( req.wValue == 0 ) { ++ /* de-configured */ ++ if (usbctl_next_state_on_event( kEvDeConfig ) != kError ) ++ printk( "%sDe-Configured\n", pszMe ); ++ usbd_info.state = 0; ++ Ser0UDCCR |= UDCCR_RIM | UDCCR_TIM; ++ ep1_reset (); ++ ep2_reset (); ++ printk("%s: de-configured. Tx and Rx interrupts disabled. ep1 and ep2 reset\n",__FUNCTION__); ++ } else { ++ printk( "%ssetup phase: Unknown " ++ "\"set configuration\" data %d\n", ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ case USB_REQ_CLEAR_FEATURE: ++ /* could check data length, direction...26Jan01ww */ ++ if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */ ++ int ep = windex_to_ep_num( req.wIndex ); ++ if ( ep == 1 ) { ++ printk( "%sclear feature \"endpoint halt\" " ++ " on receiver\n", pszMe ); ++ ep1_reset(); ++ } ++ else if ( ep == 2 ) { ++ printk( "%sclear feature \"endpoint halt\" " ++ "on xmitter\n", pszMe ); ++ ep2_reset(); ++ } else { ++ printk( "%sclear feature \"endpoint halt\" " ++ "on unsupported ep # %d\n", ++ pszMe, ep ); ++ } ++ } else { ++ printk( "%sUnsupported feature selector (%d) " ++ "in clear feature. Ignored.\n" , ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ case USB_REQ_SET_FEATURE: ++ if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */ ++ int ep = windex_to_ep_num( req.wValue ); ++ if ( ep == 1 ) { ++ printk( "%set feature \"endpoint halt\" " ++ "on receiver\n", pszMe ); ++ ep1_stall(); ++ } ++ else if ( ep == 2 ) { ++ printk( "%sset feature \"endpoint halt\" " ++ " on xmitter\n", pszMe ); ++ ep2_stall(); ++ } else { ++ printk( "%sset feature \"endpoint halt\" " ++ "on unsupported ep # %d\n", ++ pszMe, ep ); ++ } ++ } ++ else { ++ printk( "%sUnsupported feature selector " ++ "(%d) in set feature\n", ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ /* The rest have a data phase that writes back to the host */ ++ case USB_REQ_GET_STATUS: ++ /* return status bit flags */ ++ status_buf[0] = status_buf[1] = 0; ++ n = request_target(req.bRequestType); ++ switch( n ) { ++ case kTargetDevice: ++ if ( self_powered_hook() ) ++ status_buf[0] |= 1; ++ break; ++ case kTargetInterface: ++ break; ++ case kTargetEndpoint: ++ /* return stalled bit */ ++ n = windex_to_ep_num( req.wIndex ); ++ if ( n == 1 ) ++ status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4; ++ else if ( n == 2 ) ++ status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5; ++ else { ++ printk( "%sUnknown endpoint (%d) " ++ "in GET_STATUS\n", pszMe, n ); ++ } ++ break; ++ default: ++ printk( "%sUnknown target (%d) in GET_STATUS\n", ++ pszMe, n ); ++ /* fall thru */ ++ break; ++ } ++ PRINTKD("%s: GET_STATUS writing %d\n",__FUNCTION__,req.wLength); ++ ep0_queue( status_buf, req.wLength, sizeof( status_buf )); ++ break; ++ case USB_REQ_GET_DESCRIPTOR: ++ PRINTKD( "%s: calling the_controller.driver->setup with GET_DESCRIPTOR\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ case USB_REQ_GET_CONFIGURATION: ++ PRINTKD( "%s: calling the_controller.driver->setup with GET_CONFIGURATION\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ case USB_REQ_GET_INTERFACE: ++ PRINTKD( "%s: calling the_controller->driver->setup with GET_INTERFACE\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ case USB_REQ_SET_INTERFACE: ++ PRINTKD( "%s: calling the_controller->driver->setup with SET_INTERFACE\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ default : ++ printk("%sunknown request 0x%x\n", pszMe, req.bRequest); ++ break; ++ } /* switch( bRequest ) */ ++ ++sh_sb_end: ++ return; ++ ++} ++ ++void ep0_int_hndlr(void) ++{ ++ u32 cs_reg_in; ++ ++ pcs(); ++ ++ cs_reg_in = Ser0UDCCS0; ++ ++ /* ++ * If "setup end" has been set, the usb controller has terminated ++ * a setup transaction before we set DE. This happens during ++ * enumeration with some hosts. For example, the host will ask for ++ * our device descriptor and specify a return of 64 bytes. When we ++ * hand back the first 8, the host will know our max packet size ++ * and turn around and issue a new setup immediately. This causes ++ * the UDC to auto-ack the new setup and set SE. We must then ++ * "unload" (process) the new setup, which is what will happen ++ * after this preamble is finished executing. ++ */ ++ if (cs_reg_in & UDCCS0_SE) { ++ PRINTKD("UDC: early termination of setup\n"); ++ ++ /* ++ * Clear setup end ++ */ ++ set_cs_bits(UDCCS0_SSE); ++ ++ /* ++ * Clear any pending write. ++ */ ++ ep0_clear_write(); ++ } ++ ++ /* ++ * UDC sent a stall due to a protocol violation. ++ */ ++ if (cs_reg_in & UDCCS0_SST) { ++ PRINTKD("UDC: write_preamble: UDC sent stall\n"); ++ ++ /* ++ * Clear sent stall ++ */ ++ set_cs_bits(UDCCS0_SST); ++ ++ /* ++ * Clear any pending write. ++ */ ++ ep0_clear_write(); ++ } ++ ++ switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) { ++ case UDCCS0_OPR | UDCCS0_IPR: ++ PRINTKD("UDC: write_preamble: see OPR. Stopping write to " ++ "handle new SETUP\n"); ++ ++ /* ++ * very rarely, you can get OPR and ++ * leftover IPR. Try to clear ++ */ ++ UDC_clear(Ser0UDCCS0, UDCCS0_IPR); ++ ++ /* ++ * Clear any pending write. ++ */ ++ ep0_clear_write(); ++ ++ /*FALLTHROUGH*/ ++ case UDCCS0_OPR: ++ /* ++ * A new setup request is pending. Handle ++ * it. Note that we don't try to read a ++ * packet if SE was set and OPR is clear. ++ */ ++ ep0_read_packet(); ++ break; ++ ++ case 0: ++ // if data pending ... ++ if (wr.p) { ++ unsigned int cs_bits = 0; ++ if (wr.bytes_left != 0) { ++ /* ++ * More data to go ++ */ ++ write_fifo(); ++ // packet ready ++ cs_bits |= UDCCS0_IPR; ++ } ++ ++ if (wr.bytes_left == 0) { ++ /* ++ * All data sent. ++ */ ++ cs_bits |= wrint(); ++ // a null packet may be following ++ if (!wrint) ++ ep0_clear_write(); ++ } ++ set_cs_bits(cs_bits); ++ } ++ else ++ PRINTKD("%s: No data - probably an ACK\n",__FUNCTION__); ++ break; ++ ++ case UDCCS0_IPR: ++ PRINTKD("UDC: IPR set, not writing\n"); ++ break; ++ } ++ ++ pcs(); ++ PRINTKD( "-end-\n" ); ++} ++ ++static unsigned int ep0_sh_write_data(void) ++{ ++ /* ++ * If bytes left is zero, we are coming in on the ++ * interrupt after the last packet went out. And ++ * we know we don't have to empty packet this ++ * transfer so just set DE and we are done ++ */ ++ PRINTKD("UDC: normal packet ended\n"); ++ wrint=NULL; ++ return UDCCS0_DE; ++} ++ ++static unsigned int ep0_sh_write_with_empty_packet(void) ++{ ++ /* ++ * If bytes left is zero, we are coming in on the ++ * interrupt after the last packet went out. ++ * We must do short packet suff, so set DE and IPR ++ */ ++ PRINTKD("UDC: short packet sent\n"); ++ wrint=NULL; ++ return UDCCS0_IPR | UDCCS0_DE; ++} ++ ++static unsigned int ep0_sh_write_data_then_empty_packet(void) ++{ ++ PRINTKD("UDC: last packet full. Send empty packet next\n"); ++ wrint=ep0_sh_write_with_empty_packet; ++ return 0; ++} ++ ++static void ep0_queue(void *buf, unsigned int len, unsigned int req_len) ++{ ++ __u32 cs_reg_bits = UDCCS0_IPR; ++ ++ PRINTKD("a=%d r=%d\n", len, req_len); ++ ++ if (len == 0) { ++ // no output packet to wait for ++ PRINTKD("%s: zero byte packet being queued. Setting DE and OPR end exiting\n",__FUNCTION__); ++ set_cs_bits(UDCCS0_DE | UDCCS0_SO); ++ return; ++ } ++ ++ /* ++ * thou shalt not enter data phase until ++ * Out Packet Ready is clear ++ */ ++ if (!clear_opr()) { ++ printk("UDC: SO did not clear OPR\n"); ++ set_cs_bits(UDCCS0_DE | UDCCS0_SO); ++ return; ++ } ++ ++ // note data to xmit stored ++ wr.p=buf; ++ wr.bytes_left=min(len, req_len); ++ ++ // write the first block ++ write_fifo(); ++ ++ // done already? ++ if (wr.bytes_left == 0) { ++ /* ++ * out in one, so data end ++ */ ++ cs_reg_bits |= UDCCS0_DE; ++ ep0_clear_write(); ++ // rest is a shorter than expected reply? ++ } else if (len < req_len) { ++ /* ++ * we are going to short-change host ++ * so need nul to not stall ++ */ ++ if (len % 8) { ++ PRINTKD("%s: %d more to go ending in a short packet.\n",__FUNCTION__,wr.bytes_left); ++ wrint=ep0_sh_write_with_empty_packet; ++ } ++ // unless we are on a packet boundary. Then send full packet plus null packet. ++ else { ++ PRINTKD("%s: %d more to go then add empty packet.\n",__FUNCTION__,wr.bytes_left); ++ wrint=ep0_sh_write_data_then_empty_packet; ++ } ++ } else { ++ /* ++ * we have as much or more than requested ++ */ ++ PRINTKD("%s: %d more to go.\n",__FUNCTION__,wr.bytes_left); ++ wrint=ep0_sh_write_data; ++ } ++ ++ /* ++ * note: IPR was set uncondtionally at start of routine ++ */ ++ set_cs_bits(cs_reg_bits); ++} ++ ++/* ++ * write_fifo() ++ * Stick bytes in the 8 bytes endpoint zero FIFO. ++ * This version uses a variety of tricks to make sure the bytes ++ * are written correctly. 1. The count register is checked to ++ * see if the byte went in, and the write is attempted again ++ * if not. 2. An overall counter is used to break out so we ++ * don't hang in those (rare) cases where the UDC reverses ++ * direction of the FIFO underneath us without notification ++ * (in response to host aborting a setup transaction early). ++ * ++ */ ++static void write_fifo( void ) ++{ ++ int bytes_this_time = min(wr.bytes_left, 8); ++ int bytes_written = 0; ++ ++ PRINTKD( "WF=%d: ", bytes_this_time ); ++ ++ while( bytes_this_time-- ) { ++ unsigned int cwc; ++ int i; ++ PRINTKD( "%2.2X ", *wr.p ); ++ cwc = Ser0UDCWC & 15; ++ i = 10; ++ do { ++ Ser0UDCD0 = *wr.p; ++ udelay( 20 ); /* voodo 28Feb01ww */ ++ } while( (Ser0UDCWC &15) == cwc && --i ); ++ ++ if ( i == 0 ) { ++ printk( "%swrite_fifo: write failure\n", pszMe ); ++ usbd_info.stats.ep0_fifo_write_failures++; ++ } ++ ++ wr.p++; ++ bytes_written++; ++ } ++ wr.bytes_left -= bytes_written; ++ ++ /* following propagation voodo so maybe caller writing IPR in ++ ..a moment might actually get it to stick 28Feb01ww */ ++ udelay( 300 ); ++ ++ usbd_info.stats.ep0_bytes_written += bytes_written; ++ PRINTKD( "L=%d WCR=%8.8lX\n", wr.bytes_left, Ser0UDCWC ); ++} ++/* ++ * read_fifo() ++ * Read 1-8 bytes out of FIFO and put in request. ++ * Called to do the initial read of setup requests ++ * from the host. Return number of bytes read. ++ * ++ * Like write fifo above, this driver uses multiple ++ * reads checked agains the count register with an ++ * overall timeout. ++ * ++ */ ++static int ++read_fifo( struct usb_ctrlrequest * request ) ++{ ++ int bytes_read = 0; ++ int fifo_count; ++ ++ unsigned char * pOut = (unsigned char*) request; ++ ++ fifo_count = ( Ser0UDCWC & 0xFF ); ++ ++ ASSERT( fifo_count <= 8 ); ++ PRINTKD( "RF=%d ", fifo_count ); ++ ++ while( fifo_count-- ) { ++ unsigned int cwc; ++ int i; ++ ++ cwc = Ser0UDCWC & 15; ++ ++ i = 10; ++ do { ++ *pOut = (unsigned char) Ser0UDCD0; ++ udelay( 20 ); ++ } while( ( Ser0UDCWC & 15 ) == cwc && --i ); ++ ++ if ( i == 0 ) { ++ printk( "%sread_fifo(): read failure\n", pszMe ); ++ usbd_info.stats.ep0_fifo_read_failures++; ++ } ++ pOut++; ++ bytes_read++; ++ } ++ ++ PRINTKD( "fc=%d\n", bytes_read ); ++ usbd_info.stats.ep0_bytes_read++; ++ return bytes_read; ++} ++ ++/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */ ++ ++#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE ) ++#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS )) ++#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE) ++ ++static void set_cs_bits( __u32 bits ) ++{ ++ if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST) ) ++ Ser0UDCCS0 = bits; ++ else if ( (bits & BOTH_BITS) == BOTH_BITS ) ++ set_ipr_and_de(); ++ else if ( bits & UDCCS0_IPR ) ++ set_ipr(); ++ else if ( bits & UDCCS0_DE ) ++ set_de(); ++} ++ ++static void set_de( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= UDCCS0_DE; ++ } else { ++ PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( Ser0UDCCS0 & UDCCS0_DE ) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8lX)\n", ++ pszMe, UDCCS0_DE, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static void set_ipr( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= UDCCS0_IPR; ++ } else { ++ PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( Ser0UDCCS0 & UDCCS0_IPR ) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8lX)\n", ++ pszMe, UDCCS0_IPR, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static void set_ipr_and_de( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= BOTH_BITS; ++ } else { ++ PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8lX)\n", ++ pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static bool clear_opr( void ) ++{ ++ int i = 10000; ++ bool is_clear; ++ do { ++ Ser0UDCCS0 = UDCCS0_SO; ++ is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR ); ++ if ( i-- <= 0 ) { ++ printk( "%sclear_opr(): failed\n", pszMe ); ++ break; ++ } ++ } while( ! is_clear ); ++ return is_clear; ++} ++ ++ ++ ++// ep1 handlers ++ ++static char *ep1_buf; ++static int ep1_len; ++static void (*ep1_callback)(int flag, int size); ++static char *ep1_curdmabuf; ++static dma_addr_t ep1_curdmapos; ++static int ep1_curdmalen; ++static int ep1_remain; ++static int ep1_used; ++ ++static dma_regs_t *dmaregs_rx = NULL; ++static int rx_pktsize; ++ ++static int naking; ++ ++static void ++ep1_start(void) ++{ ++ sa1100_reset_dma(dmaregs_rx); ++ if (!ep1_curdmalen) { ++ ep1_curdmalen = rx_pktsize; ++ if (ep1_curdmalen > ep1_remain) ++ ep1_curdmalen = ep1_remain; ++ ep1_curdmapos = dma_map_single(NULL, ep1_curdmabuf, ep1_curdmalen, ++ DMA_FROM_DEVICE); ++ } ++ ++ UDC_write( Ser0UDCOMP, ep1_curdmalen-1 ); ++ ++ sa1100_start_dma(dmaregs_rx, ep1_curdmapos, ep1_curdmalen); ++ ++ if ( naking ) { ++ /* turn off NAK of OUT packets, if set */ ++ UDC_flip( Ser0UDCCS1, UDCCS1_RPC ); ++ naking = 0; ++ } ++} ++ ++static void ++ep1_done(int flag) ++{ ++ int size = ep1_len - ep1_remain; ++ ++ if (!ep1_len) ++ return; ++ if (ep1_curdmalen) ++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, ++ DMA_FROM_DEVICE); ++ ep1_len = ep1_curdmalen = 0; ++ if (ep1_callback) ++ ep1_callback(flag, size); ++} ++ ++void ++ep1_state_change_notify( int new_state ) ++{ ++ ++} ++ ++void ++ep1_stall( void ) ++{ ++ /* SET_FEATURE force stall at UDC */ ++ UDC_set( Ser0UDCCS1, UDCCS1_FST ); ++} ++ ++int ++ep1_init(dma_regs_t *dmaregs) ++{ ++ dmaregs_rx = dmaregs; ++ sa1100_reset_dma(dmaregs_rx); ++ ep1_done(-EAGAIN); ++ return 0; ++} ++ ++void ++ep1_reset(void) ++{ ++ if (dmaregs_rx) ++ sa1100_reset_dma(dmaregs_rx); ++ UDC_clear(Ser0UDCCS1, UDCCS1_FST); ++ ep1_done(-EINTR); ++} ++ ++void ep1_int_hndlr(int udcsr) ++{ ++ dma_addr_t dma_addr; ++ unsigned int len; ++ int status = Ser0UDCCS1; ++ ++ if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking ); ++ ++ if (status & UDCCS1_RPC) { ++ ++ if (!ep1_curdmalen) { ++ printk("usb_recv: RPC for non-existent buffer\n"); ++ naking=1; ++ return; ++ } ++ ++ sa1100_stop_dma(dmaregs_rx); ++ ++ if (status & UDCCS1_SST) { ++ printk("usb_recv: stall sent OMP=%ld\n", Ser0UDCOMP); ++ UDC_flip(Ser0UDCCS1, UDCCS1_SST); ++ ep1_done(-EIO); // UDC aborted current transfer, so we do ++ return; ++ } ++ ++ if (status & UDCCS1_RPE) { ++ printk("usb_recv: RPError %x\n", status); ++ UDC_flip(Ser0UDCCS1, UDCCS1_RPC); ++ ep1_done(-EIO); ++ return; ++ } ++ ++ dma_addr=sa1100_get_dma_pos(dmaregs_rx); ++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, ++ DMA_FROM_DEVICE); ++ len = dma_addr - ep1_curdmapos; ++#ifdef SA1100_USB_DEBUG ++ if (sa1100_usb_debug) { ++ int i; ++ printk("usb rx %d :\n ",len); ++ if (sa1100_usb_debug>1) { ++ for (i=0; i<len; i++) { ++ if ((i % 32)==31) ++ printk("\n "); ++ printk("%2.2x ",((char *)ep1_curdmapos)[i]); ++ } ++ } ++ printk("\n"); ++ } ++#endif ++ if (len < ep1_curdmalen) { ++ char *buf = ep1_curdmabuf + len; ++ while (Ser0UDCCS1 & UDCCS1_RNE) { ++ if (len >= ep1_curdmalen) { ++ printk("usb_recv: too much data in fifo\n"); ++ break; ++ } ++ *buf++ = Ser0UDCDR; ++ len++; ++ } ++ } else if (Ser0UDCCS1 & UDCCS1_RNE) { ++ printk("usb_recv: fifo screwed, shouldn't contain data\n"); ++ len = 0; ++ } ++ ++#if defined(NCB_DMA_FIX) ++// if (len && (ep1_buf != ep1_curdmabuf)) ++// memcpy(ep1_buf,ep1_curdmabuf,len); ++ if (len) ++ memcpy(&(((unsigned char *)ep1_buf)[ep1_used]),ep1_curdmabuf,len); ++#endif ++ ++ ep1_curdmalen = 0; /* dma unmap already done */ ++ ep1_remain -= len; ++ ep1_used += len; ++// ep1_curdmabuf += len; // use same buffer again ++ naking = 1; ++//printk("%s: received %d, %d remaining\n",__FUNCTION__,len,ep1_remain); ++ if (len && (len == rx_pktsize)) ++ ep1_start(); ++ else ++ ep1_done((len) ? 0 : -EPIPE); ++ } ++ /* else, you can get here if we are holding NAK */ ++} ++ ++int ++sa1100_usb_recv(struct usb_request *req, void (*callback)(int flag, int size)) ++{ ++ unsigned long flags; ++ char *buf=req->buf; ++ int len=req->length; ++ ++ if (ep1_len) ++ return -EBUSY; ++ ++ local_irq_save(flags); ++ ep1_buf = buf; ++ ep1_len = len; ++ ep1_callback = callback; ++ ep1_remain = len; ++ ep1_used = 0; ++#ifdef NCB_DMA_FIX ++// if (((size_t)buf)&3) ++ if (1) ++ ep1_curdmabuf = receive_buffer; ++ else ++#else ++ ep1_curdmabuf = buf; ++#endif ++ ep1_curdmalen = 0; ++ ep1_start(); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++// ep2 handlers ++ ++static char *ep2_buf; ++static int ep2_len; ++static void (*ep2_callback)(int status, int size); ++static dma_addr_t ep2_dma; ++static dma_addr_t ep2_curdmapos; ++static int ep2_curdmalen; ++static int ep2_remain; ++static dma_regs_t *dmaregs_tx = NULL; ++static int tx_pktsize; ++ ++/* device state is changing, async */ ++void ++ep2_state_change_notify( int new_state ) ++{ ++} ++ ++/* set feature stall executing, async */ ++void ++ep2_stall( void ) ++{ ++ UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */ ++} ++ ++static void ++ep2_start(void) ++{ ++ if (!ep2_len) ++ return; ++ ++ ep2_curdmalen = tx_pktsize; ++ if (ep2_curdmalen > ep2_remain) ++ ep2_curdmalen = ep2_remain; ++ ++ /* must do this _before_ queue buffer.. */ ++ UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */ ++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); ++ ++ Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug ++ sa1100_start_dma(dmaregs_tx, ep2_curdmapos, ep2_curdmalen); ++} ++ ++static void ++ep2_done(int flag) ++{ ++ int size = ep2_len - ep2_remain; ++ if (ep2_len) { ++ dma_unmap_single(NULL, ep2_dma, ep2_len, DMA_TO_DEVICE); ++ ep2_len = 0; ++ if (ep2_callback) ++ ep2_callback(flag, size); ++ } ++} ++ ++int ep2_init(dma_regs_t *dmaregs) ++{ ++ dmaregs_tx = dmaregs; ++ sa1100_reset_dma(dmaregs_tx); ++ ep2_done(-EAGAIN); ++ return 0; ++} ++ ++void ep2_reset(void) ++{ ++ UDC_clear(Ser0UDCCS2, UDCCS2_FST); ++ if (dmaregs_tx) ++ sa1100_reset_dma(dmaregs_tx); ++ ep2_done(-EINTR); ++} ++ ++void ep2_int_hndlr(int udcsr) ++{ ++ int status = Ser0UDCCS2; ++ ++ if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug. ++ Ser0UDCAR = usbd_info.address; ++ ++ if (status & UDCCS2_TPC) { ++ ++ UDC_flip(Ser0UDCCS2, UDCCS2_SST); ++ ++ sa1100_reset_dma(dmaregs_tx); ++ ++ if (status & (UDCCS2_TPE | UDCCS2_TUR)) { ++ printk("usb_send: transmit error %x\n", status); ++ ep2_done(-EIO); ++ } else { ++ ep2_curdmapos += ep2_curdmalen; ++ ep2_remain -= ep2_curdmalen; ++ ++ if (ep2_remain != 0) ++ ep2_start(); ++ else ++ ep2_done(0); ++ } ++ } else { ++ printk("usb_send: Not TPC: UDCCS2 = %x\n", status); ++ } ++} ++ ++int ++sa1100_usb_send(struct usb_request *req, void (*callback)(int status, int size)) ++{ ++ char *buf=req->buf; ++ int len=req->length; ++ unsigned long flags; ++ ++ if (usbd_info.state != USB_STATE_CONFIGURED) { ++ PRINTKD("%s: return -ENODEV\n",__FUNCTION__); ++ return -ENODEV; ++ } ++ ++ if (ep2_len) { ++ PRINTKD("%s: return -EBUSY\n",__FUNCTION__); ++ return -EBUSY; ++ } ++ ++ local_irq_save(flags); ++#ifdef NCB_DMA_FIX ++ // if misaligned, copy to aligned buffer ++// if (((size_t)buf)&3) { ++ if (1) { ++ PRINTKD("%s: copying %d bytes to send_buffer\n",__FUNCTION__,len); ++ memcpy(send_buffer,buf,len); ++ ep2_buf = send_buffer; ++ } ++ else ++#endif ++ ep2_buf = buf; ++ ++ ep2_len = len; ++ ep2_dma = dma_map_single(NULL, ep2_buf, len,DMA_TO_DEVICE); ++ PRINTKD("%s: mapped dma to buffer(%p0\n",__FUNCTION__,buf); ++ ++ ep2_callback = callback; ++ ep2_remain = len; ++ ep2_curdmapos = ep2_dma; ++ ++ PRINTKD("%s: calling ep2_start\n",__FUNCTION__); ++ ep2_start(); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++/*-------------------------------------------------------------------------*/ ++ ++static int ++sa1100_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) ++{ ++ struct sa1100_udc *dev; ++ struct sa1100_ep *ep; ++ u32 max; ++ int type; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!_ep || !desc || ep->desc || _ep->name == ep0name ++ || desc->bDescriptorType != USB_DT_ENDPOINT) { ++ PRINTKD("%s: _ep = %p, desc = %p\n",__FUNCTION__,_ep,desc); ++ if (_ep && desc) ++ PRINTKD("%s: ep->desc = %p, _ep->name = %s desc->bDescriptorType = %s\n",__FUNCTION__,ep->desc,_ep->name, ++ (desc->bDescriptorType == USB_DT_ENDPOINT) ? "USB_DT_ENDPOINT":"bad!!"); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ max = le16_to_cpu (desc->wMaxPacketSize); ++ switch (max) { ++ case 64: case 32: ++ /* note: maxpacket > 16 means DMA might overrun/underrun */ ++ case 16: case 8: ++ break; ++ default: ++ if (type == USB_ENDPOINT_XFER_INT && max < 64) ++ break; ++ return -EDOM; ++ } ++ ++ switch (type) { ++ case USB_ENDPOINT_XFER_BULK: ++ case USB_ENDPOINT_XFER_INT: ++ if (ep == &dev->ep[2]) { ++ if (desc->bEndpointAddress != (USB_DIR_IN|2)) { ++ PRINTKD("%s: ep[2] has invalid endpoint\n",__FUNCTION__); ++ return -EINVAL; ++ } ++ tx_pktsize = max; ++ Ser0UDCOMP = max - 1; ++ PRINTKD("%s: ep2 max packet size is %d\n",__FUNCTION__,max); ++ break; ++ } else if (ep == &dev->ep[1]) { ++ if (desc->bEndpointAddress != (USB_DIR_OUT|1)) { ++ PRINTKD("%s: ep[1] has invalid endpoint\n",__FUNCTION__); ++ return -EINVAL; ++ } ++ rx_pktsize = max; ++ Ser0UDCIMP = max - 1; ++ PRINTKD("%s: ep1 max packet size is %d\n",__FUNCTION__,max); ++ break; ++ } ++ // FALLTHROUGH ++ default: ++ PRINTKD("%s: Invalid endpoint\n",__FUNCTION__); ++ return -EINVAL; ++ } ++ ++ _ep->maxpacket = max; ++ ep->desc = desc; ++ ep->stopped = 0; ++ ++ DEBUG (dev, "enabled %s %s max %04x\n", _ep->name, ++ type_string (desc->bmAttributes), max); ++ ++ return 0; ++} ++ ++static int sa1100_disable (struct usb_ep *_ep) ++{ ++ struct sa1100_ep *ep; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!_ep || !ep->desc || _ep->name == ep0name) ++ return -EINVAL; ++ ++ nuke (ep, -ESHUTDOWN); ++ ++ DEBUG (ep->dev, "disabled %s\n", _ep->name); ++ ++ ep->desc = NULL; ++ ep->stopped = 1; ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_request * ++sa1100_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) ++{ ++ struct sa1100_request *req; ++ ++ if (!_ep) ++ return 0; ++ ++ req = kzalloc(sizeof *req, gfp_flags); ++ if (!req) ++ return 0; ++ ++ memset (req, 0, sizeof *req); ++ req->req.dma = DMA_ADDR_INVALID; ++ INIT_LIST_HEAD (&req->queue); ++ return &req->req; ++} ++ ++static void sa1100_free_request(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct sa1100_request *req; ++ ++ req = container_of (_req, struct sa1100_request, req); ++ WARN_ON (!list_empty (&req->queue)); ++ kfree(req); //NCB - see pxa2xx_udc ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void done(struct sa1100_ep *ep, struct sa1100_request *req, int status) ++{ ++ unsigned stopped = ep->stopped; ++ ++ list_del_init (&req->queue); ++ ++ if (likely(req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ VDEBUG (ep->dev, "complete %s req %p stat %d len %u/%u\n", ++ ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ req->req.complete (&ep->ep, &req->req); ++ ep->stopped = stopped; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* FIXME move away from the old non-queued api. ++ * - forces extra work on us ++ * - stores request state twice ++ * - doesn't let gadget driver handle dma mapping ++ * - status codes need mapping ++ */ ++ ++static int map_status(int status) ++{ ++ switch (status) { ++ case 0: ++ case -EIO: /* ep[12]_int_handler */ ++ return status; ++ case -EPIPE: /* ep1_int_handler */ ++ return 0; ++ // case -EAGAIN: /* ep[12]_init */ ++ // case -EINTR: /* ep[12]_reset */ ++ default: ++ return -ESHUTDOWN; ++ } ++} ++ ++static void tx_callback(int status, int size) ++{ ++ struct sa1100_ep *ep = &the_controller->ep[2]; ++ struct sa1100_request *req; ++ ++ if (list_empty (&ep->queue)) { ++ if (status != -EAGAIN) ++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n", ++ ep->ep.name, status, size); ++ return; ++ } ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++ req->req.actual = size; ++ done (ep, req, map_status (status)); ++ ++ if (ep->stopped || list_empty (&ep->queue)) ++ return; ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++ sa1100_usb_send (&req->req, tx_callback); ++} ++ ++static void rx_callback (int status, int size) ++{ ++ struct sa1100_ep *ep = &the_controller->ep[1]; ++ struct sa1100_request *req; ++ ++ if (list_empty (&ep->queue)) { ++ if (status != -EAGAIN) ++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n", ++ ep->ep.name, status, size); ++ return; ++ } ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++ req->req.actual = size; ++ done (ep, req, map_status (status)); ++ ++ if (ep->stopped || list_empty (&ep->queue)) ++ return; ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++ sa1100_usb_recv (&req->req, rx_callback); ++} ++ ++ ++static int ++sa1100_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) ++{ ++ struct sa1100_request *req; ++ struct sa1100_ep *ep; ++ struct sa1100_udc *dev; ++ unsigned long flags; ++ ++ req = container_of (_req, struct sa1100_request, req); ++ if (!_req || !_req->complete || !_req->buf ++ || !list_empty (&req->queue)) ++ return -EINVAL; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && _ep->name != ep0name))) ++ return -EINVAL; ++ ++ dev = ep->dev; ++ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) ++ return -ESHUTDOWN; ++ ++ // handle ep0 ++ if (_ep->name == ep0name) { ++ ep0_queue( _req->buf, _req->length, dev->ep0_req_len >=0 ? dev->ep0_req_len: _req->length ); ++ return 0; ++ } ++ ++ /* sa1100 udc can't write zlps */ ++ if (ep == &dev->ep[2] && _req->length == 0) ++ return -ERANGE; ++ ++ /* the old sa1100 api doesn't use 'unsigned' for lengths */ ++ if (_req->length > INT_MAX) ++ return -ERANGE; ++ ++ VDEBUG (dev, "%s queue req %p, len %d buf %p\n", ++ _ep->name, _req, _req->length, _req->buf); ++ ++ local_irq_save (flags); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++ if (list_empty (&ep->queue) && !ep->stopped) { ++ /* FIXME this does DMA mapping wrong. caller is allowed ++ * to provide buffers that don't need mapping, but this ++ * doesn't use them. ++ */ ++ if (ep == &ep->dev->ep[2]) { ++ PRINTKD("%s: sa1100_usb_send buf %p length %d\n",__FUNCTION__,_req->buf,_req->length); ++ sa1100_usb_send (_req, tx_callback); ++ } ++ else if (ep == &ep->dev->ep[1]) { ++ PRINTKD("%s: sa1100_usb_recv buf %p length %d\n",__FUNCTION__,_req->buf,_req->length); ++ sa1100_usb_recv (_req, rx_callback); ++ } ++ /* ep0 rx/tx is handled separately */ ++ } ++ list_add_tail (&req->queue, &ep->queue); ++ ++ local_irq_restore (flags); ++ ++ return 0; ++} ++ ++/* dequeue ALL requests */ ++static void nuke (struct sa1100_ep *ep, int status) ++{ ++ struct sa1100_request *req; ++ ++ /* called with irqs blocked */ ++ while (!list_empty (&ep->queue)) { ++ req = list_entry (ep->queue.next, ++ struct sa1100_request, ++ queue); ++ done (ep, req, status); ++ } ++ if (ep == &ep->dev->ep[1]) ++ ep1_reset (); ++ else if (ep == &ep->dev->ep[2]) ++ ep2_reset (); ++} ++ ++/* dequeue JUST ONE request */ ++static int sa1100_dequeue (struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct sa1100_ep *ep; ++ struct sa1100_request *req; ++ unsigned long flags; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!_ep || (!ep->desc && _ep->name != ep0name) || !_req) ++ return -EINVAL; ++ ++ local_irq_save (flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry (req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ if (&req->req != _req) { ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ ++ done(ep, req, -ECONNRESET); ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++sa1100_set_halt (struct usb_ep *_ep, int value) ++{ ++ struct sa1100_ep *ep; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (unlikely(!_ep ++ || (!ep->desc && _ep->name != ep0name)) ++ || (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC) ++ return -EINVAL; ++ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear"); ++ ++ /* set/clear, then synch memory views with the device */ ++ if (value) { ++ if (ep == &ep->dev->ep[1]) ++ ep1_stall (); ++ else ++ ep2_stall (); ++ } else { ++ if (ep == &ep->dev->ep[1]) ++ ep1_reset (); ++ else ++ ep2_reset (); ++ } ++ ++ return 0; ++} ++ ++static struct usb_ep_ops sa1100_ep_ops = { ++ .enable = sa1100_enable, ++ .disable = sa1100_disable, ++ ++ .alloc_request = sa1100_alloc_request, ++ .free_request = sa1100_free_request, ++ ++ .queue = sa1100_queue, ++ .dequeue = sa1100_dequeue, ++ ++ .set_halt = sa1100_set_halt, ++ // .fifo_status = sa1100_fifo_status, ++ // .fifo_flush = sa1100_fifo_flush, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int sa1100_get_frame (struct usb_gadget *_gadget) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static int sa1100_wakeup (struct usb_gadget *_gadget) ++{ ++ struct sa1100_udc *dev; ++ ++ if (!_gadget) ++ return 0; ++ dev = container_of (_gadget, struct sa1100_udc, gadget); ++ ++ // FIXME ++ ++ return 0; ++} ++ ++static const struct usb_gadget_ops sa1100_ops = { ++ .get_frame = sa1100_get_frame, ++ .wakeup = sa1100_wakeup, ++ ++ // .set_selfpowered = sa1100_set_selfpowered, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline void enable_resume_mask_suspend (void) ++{ ++ int i = 0; ++ ++ while (1) { ++ Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events ++ udelay (i); ++ if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s Could not set SUSIM %8.8lX\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++ ++ i = 0; ++ while (1) { ++ Ser0UDCCR &= ~UDCCR_RESIM; ++ udelay (i); ++ if ( (Ser0UDCCR & UDCCR_RESIM) == 0 ++ || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s Could not clear RESIM %8.8lX\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++} ++ ++static inline void enable_suspend_mask_resume (void) ++{ ++ int i = 0; ++ while (1) { ++ Ser0UDCCR |= UDCCR_RESIM; // mask future resume events ++ udelay (i); ++ if (Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s could not set RESIM %8.8lX\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++ i = 0; ++ while (1) { ++ Ser0UDCCR &= ~UDCCR_SUSIM; ++ udelay (i); ++ if ( (Ser0UDCCR & UDCCR_SUSIM) == 0 ++ || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s Could not clear SUSIM %8.8lX\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++} ++ ++// HACK DEBUG 3Mar01ww ++// Well, maybe not, it really seems to help! 08Mar01ww ++static void core_kicker (void) ++{ ++ u32 car = Ser0UDCAR; ++ u32 imp = Ser0UDCIMP; ++ u32 omp = Ser0UDCOMP; ++ ++ UDC_set (Ser0UDCCR, UDCCR_UDD); ++ udelay (300); ++ UDC_clear (Ser0UDCCR, UDCCR_UDD); ++ ++ Ser0UDCAR = car; ++ Ser0UDCIMP = imp; ++ Ser0UDCOMP = omp; ++} ++ ++static irqreturn_t udc_int_hndlr(int irq, void *_dev) ++{ ++ struct sa1100_udc *dev = _dev; ++ u32 status = Ser0UDCSR; ++ ++ PRINTKD("%s: status = 0x%x and control = 0x%lx\n", __FUNCTION__, ++ status, Ser0UDCCR); ++ /* ReSeT Interrupt Request - UDC has been reset */ ++ if (status & UDCSR_RSTIR) { ++ PRINTKD("%s: processing UDCSR_RSTIR\n", __FUNCTION__); ++ if (usbctl_next_state_on_event(kEvReset) != kError) { ++ /* starting 20ms or so reset sequence now... */ ++ INFO (dev, "Resetting\n"); ++ ep0_reset(); // just set state to idle ++ ep1_reset(); // flush dma, clear false stall ++ ep2_reset(); // flush dma, clear false stall ++ } ++ // mask reset ints, they flood during sequence, enable ++ // suspend and resume ++ UDC_set(Ser0UDCCR, UDCCR_REM); // mask reset ++ UDC_clear(Ser0UDCCR, (UDCCR_SUSIM | UDCCR_RESIM)); // enable suspend and resume ++ UDC_flip(Ser0UDCSR, status); // clear all pending sources ++ PRINTKD("%s: setting USB_FULL_SPEED\n",__FUNCTION__); ++ dev->gadget.speed = USB_SPEED_FULL; ++ return IRQ_HANDLED; // NCB ++ } ++ ++ /* else we have done something other than reset, ++ * so be sure reset enabled ++ */ ++ UDC_clear(Ser0UDCCR, UDCCR_REM); ++ ++ /* RESume Interrupt Request */ ++ if (status & UDCSR_RESIR) { ++ struct usb_gadget_driver *driver = dev->driver; ++ ++ PRINTKD("%s: processing UDCSR_RESIR\n",__FUNCTION__); ++ if (driver->resume) ++ driver->resume (&dev->gadget); ++ core_kicker (); ++ enable_suspend_mask_resume (); ++ } ++ ++ /* SUSpend Interrupt Request */ ++ if (status & UDCSR_SUSIR) { ++ struct usb_gadget_driver *driver = dev->driver; ++ ++ PRINTKD("%s: processing UDCSR_SUSIR\n",__FUNCTION__); ++ if (driver->suspend) ++ driver->suspend (&dev->gadget); ++ enable_resume_mask_suspend (); ++ } ++ ++ UDC_flip(Ser0UDCSR, status); // clear all pending sources ++ ++ if (status & UDCSR_EIR) ++ PRINTKD("%s: processing ep0_int_hndlr\n",__FUNCTION__); ++ ep0_int_hndlr(); ++ ++ if (status & UDCSR_RIR) { ++ PRINTKD("%s: processing ep1_int_hndlr\n",__FUNCTION__); ++ ep1_int_hndlr(status); ++ } ++ if (status & UDCSR_TIR) { ++ PRINTKD("%s: processing ep2_int_hndlr\n",__FUNCTION__); ++ ep2_int_hndlr(status); ++ } ++ ++ return IRQ_HANDLED; // NCB ++} ++ ++/* soft_connect_hook () ++ * Some devices have platform-specific circuitry to make USB ++ * not seem to be plugged in, even when it is. This allows ++ * software to control when a device 'appears' on the USB bus ++ * (after Linux has booted and this driver has loaded, for ++ * example). If you have such a circuit, control it here. ++ */ ++#ifdef CONFIG_SA1100_EXTENEX1 ++static void soft_connect_hook(int enable) ++{ ++ if (machine_is_extenex1 ()) { ++ if (enable) { ++ PPDR |= PPC_USB_SOFT_CON; ++ PPSR |= PPC_USB_SOFT_CON; ++ } else { ++ PPSR &= ~PPC_USB_SOFT_CON; ++ PPDR &= ~PPC_USB_SOFT_CON; ++ } ++ } ++} ++#elif defined(CONFIG_SA1100_BALLOON) ++static void soft_connect_hook(int enable) ++{ ++ if (machine_is_balloon()) { ++ if (enable) ++ balloon_cpld_control(BALLOON_UDC_DISCONNECT, 0); ++ else ++ balloon_cpld_control(BALLOON_UDC_DISCONNECT, 1); ++ } ++} ++#elif defined(CONFIG_SA1100_COLLIE) ++extern struct platform_device colliescoop_device; ++ ++static void soft_connect_hook(int enable) ++{ ++ if(enable) ++ set_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_LB_VOL_CHG); ++ else ++ reset_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_LB_VOL_CHG); ++} ++#else ++#define soft_connect_hook(x) do { } while (0); ++#endif ++ ++/* "function" sysfs attribute */ ++static ssize_t ++show_function(struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ struct sa1100_udc *dev = dev_get_drvdata (_dev); ++ ++ if (!dev->driver ++ || !dev->driver->function ++ || strlen(dev->driver->function) > PAGE_SIZE) ++ return 0; ++ return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); ++} ++static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); ++ ++/* disable the UDC at the source */ ++static void udc_disable(struct sa1100_udc *dev) ++{ ++ soft_connect_hook(0); ++ UDC_set(Ser0UDCCR, UDCCR_UDD); ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ep0_idle(dev); ++} ++ ++static void udc_reinit(struct sa1100_udc *dev) ++{ ++ u32 i; ++ ++ /* Initialize the gadget controller data structure */ ++ INIT_LIST_HEAD(&dev->gadget.ep_list); ++ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); ++ ep0_idle(dev); ++ for ( i = 0 ; i < 3 ; i++) { ++ struct sa1100_ep *ep = &dev->ep[i]; ++ if (i != 0) ++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ++ ep->desc = NULL; ++ ep->stopped = 0; ++ INIT_LIST_HEAD(&ep->queue); ++ } ++} ++ ++/* enable the udc at the source */ ++static void udc_enable(struct sa1100_udc *dev) ++{ ++ UDC_clear (Ser0UDCCR, UDCCR_UDD); ++ ep0_idle(dev); ++} ++ ++static void ep0_start(struct sa1100_udc *dev) ++{ ++ udc_enable(dev); ++ udelay(100); ++ ++ /* clear stall - receiver seems to start stalled? 19Jan01ww */ ++ /* also clear other stuff just to be thurough 22Feb01ww */ ++ UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC ); ++ UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC ); ++ ++ /* mask everything */ ++ Ser0UDCCR = 0xFC; ++ ++ /* flush DMA and fire through some -EAGAINs */ ++ ep1_init(dev->ep[1].dmaregs); ++ ep2_init(dev->ep[2].dmaregs); ++ ++ /* enable any platform specific hardware */ ++ soft_connect_hook(1); ++ ++ /* clear all top-level sources */ ++ Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | ++ UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ; ++ ++ /* EXERIMENT - a short line in the spec says toggling this ++ * bit diddles the internal state machine in the udc to ++ * expect a suspend ++ */ ++ Ser0UDCCR |= UDCCR_RESIM; ++ /* END EXPERIMENT 10Feb01ww */ ++ ++ /* enable any platform specific hardware */ ++ soft_connect_hook(1); ++ ++ /* Enable interrupts. If you are unplugged you will immediately ++ * get a suspend interrupt. If you are plugged and have a soft ++ * connect-circuit, you will get a reset. If you are plugged ++ * without a soft-connect, I think you also get suspend. In short, ++ * start with suspend masked and everything else enabled ++ */ ++ UDC_write(Ser0UDCCR, UDCCR_SUSIM); ++} ++ ++ ++/* when a driver is successfully registered, it will receive ++ * control requests including set_configuration(), which enables ++ * non-control requests. then usb traffic follows until a ++ * disconnect is reported. then a host may connect again, or ++ * the driver might get unbound. ++ */ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ struct sa1100_udc *dev = the_controller; ++ int retval; ++ ++ if (!driver || !driver->bind || !driver->setup) ++ return -EINVAL; ++ if (!dev) ++ return -ENODEV; ++ if (dev->driver) ++ return -EBUSY; ++ ++ /* hook up the driver ... */ ++ dev->driver = driver; ++ dev->gadget.dev.driver = &driver->driver; ++ ++ retval = device_add(&dev->gadget.dev); ++ if (retval != 0) { ++ printk(KERN_ERR "Error in device_add() : %d\n",retval); ++ goto register_error; ++ } ++ ++ retval = driver->bind (&dev->gadget); ++ if (retval != 0) { ++ DEBUG(dev, "bind to driver %s --> %d\n", ++ driver->driver.name, retval); ++ device_del(&dev->gadget.dev); ++ goto register_error; ++ } ++ ++ retval = device_create_file(dev->dev, &dev_attr_function); ++ ++ /* ... then enable host detection and ep0; and we're ready ++ * for set_configuration as well as eventual disconnect. ++ */ ++ ep0_start(dev); ++ ++ DEBUG(dev, "%s ready\n", driver->driver.name); ++ ++ return 0; ++ ++register_error: ++ dev->driver = NULL; ++ dev->gadget.dev.driver = NULL; ++ return retval; ++} ++EXPORT_SYMBOL (usb_gadget_register_driver); ++ ++static void ++stop_activity(struct sa1100_udc *dev, struct usb_gadget_driver *driver) ++{ ++ int i; ++ ++ /* don't disconnect if it's not connected */ ++ if (dev->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = NULL; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* mask everything */ ++ Ser0UDCCR = 0xFC; ++ ++ /* stop hardware; prevent new request submissions; ++ * and kill any outstanding requests. ++ */ ++ for (i = 0; i < 3; i++) { ++ struct sa1100_ep *ep = &dev->ep[i]; ++ ep->stopped = 1; ++ nuke(ep, -ESHUTDOWN); ++ } ++ udc_disable (dev); ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (driver) ++ driver->disconnect(&dev->gadget); ++ ++ /* re-init driver-visible data structures */ ++ udc_reinit(dev); ++} ++ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct sa1100_udc *dev = the_controller; ++ ++ if (!dev) ++ return -ENODEV; ++ if (!driver || driver != dev->driver) ++ return -EINVAL; ++ ++ local_irq_disable(); ++ stop_activity (dev, driver); ++ local_irq_enable(); ++ if (driver->unbind) ++ driver->unbind(&dev->gadget); ++ dev->driver = 0; ++ ++ device_del(&dev->gadget.dev); ++ device_remove_file(dev->dev, &dev_attr_function); ++ ++ DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name); ++ return 0; ++} ++EXPORT_SYMBOL (usb_gadget_unregister_driver); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/*-------------------------------------------------------------------------*/ ++ ++////////////////////////////////////////////////////////////////////////////// ++// Proc Filesystem Support ++////////////////////////////////////////////////////////////////////////////// ++ ++#if CONFIG_PROC_FS ++ ++#define SAY(fmt,args...) p += sprintf (p, fmt, ## args) ++#define SAYV(num) p += sprintf (p, num_fmt, "Value", num) ++#define SAYC(label,yn) p += sprintf (p, yn_fmt, label, yn) ++#define SAYS(label,v) p += sprintf (p, cnt_fmt, label, v) ++ ++static int usbctl_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ const char * num_fmt = "%25.25s: %8.8lX\n"; ++ const char * cnt_fmt = "%25.25s: %lu\n"; ++ const char * yn_fmt = "%25.25s: %s\n"; ++ const char * yes = "YES"; ++ const char * no = "NO"; ++ unsigned long v; ++ char * p = page; ++ int len; ++ ++ SAY ("SA1100 USB Controller Core\n"); ++ ++ SAYS ("ep0 bytes read", usbd_info.stats.ep0_bytes_read); ++ SAYS ("ep0 bytes written", usbd_info.stats.ep0_bytes_written); ++ SAYS ("ep0 FIFO read failures", usbd_info.stats.ep0_fifo_read_failures); ++ SAYS ("ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures); ++ ++ SAY ("\n"); ++ ++ v = Ser0UDCAR; ++ SAY ("%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v); ++ v = Ser0UDCIMP; ++ SAY ("%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v); ++ v = Ser0UDCOMP; ++ SAY ("%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v); ++ ++ v = Ser0UDCCR; ++ SAY ("\nUDC Mask Register\n"); ++ SAYV (v); ++ SAYC ("UDC Active", (v & UDCCR_UDA) ? yes : no); ++ SAYC ("Suspend interrupts masked", (v & UDCCR_SUSIM) ? yes : no); ++ SAYC ("Resume interrupts masked", (v & UDCCR_RESIM) ? yes : no); ++ SAYC ("Reset interrupts masked", (v & UDCCR_REM) ? yes : no); ++ ++ v = Ser0UDCSR; ++ SAY ("\nUDC Interrupt Request Register\n"); ++ SAYV (v); ++ SAYC ("Reset pending", (v & UDCSR_RSTIR) ? yes : no); ++ SAYC ("Suspend pending", (v & UDCSR_SUSIR) ? yes : no); ++ SAYC ("Resume pending", (v & UDCSR_RESIR) ? yes : no); ++ SAYC ("ep0 pending", (v & UDCSR_EIR) ? yes : no); ++ SAYC ("receiver pending", (v & UDCSR_RIR) ? yes : no); ++ SAYC ("tramsitter pending", (v & UDCSR_TIR) ? yes : no); ++ ++#ifdef CONFIG_SA1100_EXTENEX1 ++ SAYC ("\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden"); ++#endif ++ ++#if 1 ++ SAY ("\nDMA Tx registers\n"); ++ { ++ dma_regs_t *r=the_controller->ep[2].dmaregs; ++ SAY (" DDAR"); ++ SAYV(r->DDAR); ++ SAY (" DCSR"); ++ SAYV(r->RdDCSR); ++ SAY (" DBSA (address buf A) "); ++ SAYV(r->DBSA); ++ SAY (" DBTA (transfer count A) "); ++ SAYV(r->DBTA); ++ SAY (" DBSB (address buf B) "); ++ SAYV(r->DBSB); ++ SAY (" DBTB (transfer count B) "); ++ SAYV(r->DBTB); ++ ++ } ++ SAY ("\nDMA Rx registers\n"); ++ { ++ dma_regs_t *r=the_controller->ep[1].dmaregs; ++ SAY (" DDAR"); ++ SAYV(r->DDAR); ++ SAY (" DCSR"); ++ SAYV(r->RdDCSR); ++ SAY (" DBSA (address buf A) "); ++ SAYV(r->DBSA); ++ SAY (" DBTA (transfer count A) "); ++ SAYV(r->DBTA); ++ SAY (" DBSB (address buf B) "); ++ SAYV(r->DBSB); ++ SAY (" DBTB (transfer count B) "); ++ SAYV(r->DBTB); ++ ++ } ++#endif ++#if 1 ++ v = Ser0UDCCS0; ++ SAY ("\nUDC Endpoint Zero Status Register\n"); ++ SAYV (v); ++ SAYC ("Out Packet Ready", (v & UDCCS0_OPR) ? yes : no); ++ SAYC ("In Packet Ready", (v & UDCCS0_IPR) ? yes : no); ++ SAYC ("Sent Stall", (v & UDCCS0_SST) ? yes : no); ++ SAYC ("Force Stall", (v & UDCCS0_FST) ? yes : no); ++ SAYC ("Data End", (v & UDCCS0_DE) ? yes : no); ++ SAYC ("Data Setup End", (v & UDCCS0_SE) ? yes : no); ++ SAYC ("Serviced (SO)", (v & UDCCS0_SO) ? yes : no); ++ ++ v = Ser0UDCCS1; ++ SAY ("\nUDC Receiver Status Register\n"); ++ SAYV (v); ++ SAYC ("Receive Packet Complete", (v & UDCCS1_RPC) ? yes : no); ++ SAYC ("Sent Stall", (v & UDCCS1_SST) ? yes : no); ++ SAYC ("Force Stall", (v & UDCCS1_FST) ? yes : no); ++ SAYC ("Receive Packet Error", (v & UDCCS1_RPE) ? yes : no); ++ SAYC ("Receive FIFO not empty", (v & UDCCS1_RNE) ? yes : no); ++ ++ v = Ser0UDCCS2; ++ SAY ("\nUDC Transmitter Status Register\n"); ++ SAYV (v); ++ SAYC ("FIFO has < 8 of 16 chars", (v & UDCCS2_TFS) ? yes : no); ++ SAYC ("Transmit Packet Complete", (v & UDCCS2_TPC) ? yes : no); ++ SAYC ("Transmit FIFO underrun", (v & UDCCS2_TUR) ? yes : no); ++ SAYC ("Transmit Packet Error", (v & UDCCS2_TPE) ? yes : no); ++ SAYC ("Sent Stall", (v & UDCCS2_SST) ? yes : no); ++ SAYC ("Force Stall", (v & UDCCS2_FST) ? yes : no); ++#endif ++ ++ len = (p - page) - off; ++ if (len < 0) ++ len = 0; ++ *eof = (len <=count) ? 1 : 0; ++ *start = page + off; ++ return len; ++} ++ ++static inline void register_proc_entry (void) ++{ ++ create_proc_read_entry (driver_name, 0, NULL, ++ usbctl_read_proc, NULL); ++} ++ ++static inline void unregister_proc_entry (void) ++{ ++ remove_proc_entry (driver_name, NULL); ++} ++ ++#else ++ ++#define register_proc_entry() do {} while (0) ++#define unregister_proc_entry() do {} while (0) ++ ++#endif /* CONFIG_PROC_FS */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++MODULE_DESCRIPTION ("sa1100_udc"); ++MODULE_AUTHOR ("Various"); ++MODULE_LICENSE ("GPL"); ++ ++static struct sa1100_udc memory = { ++ .gadget = { ++ .ops = &sa1100_ops, ++ .ep0 = &memory.ep[0].ep, ++ .name = driver_name, ++ .dev = { ++ .bus_id = "gadget", ++ }, ++ }, ++ ++ /* control endpoint */ ++ .ep[0] = { ++ .ep = { ++ .name = ep0name, ++ .ops = &sa1100_ep_ops, ++ .maxpacket = EP0_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ }, ++ ++ /* first group of endpoints */ ++ .ep[1] = { ++ .ep = { ++ .name = "ep1out-bulk", ++ .ops = &sa1100_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ }, ++ .ep[2] = { ++ .ep = { ++ .name = "ep2in-bulk", ++ .ops = &sa1100_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++ }, ++ .dev = &memory, ++ } ++}; ++ ++static int __init sa1100_udc_probe(struct device *_dev) ++{ ++ struct sa1100_udc *dev = &memory; ++ int retval = 0; ++ ++ /* setup dev */ ++ dev->dev = _dev; ++// dev->mach = _dev->platform_data; ++ ++ device_initialize(&dev->gadget.dev); ++ dev->gadget.dev.parent = _dev; ++ dev->gadget.dev.dma_mask = _dev->dma_mask; ++ ++ the_controller = dev; ++ dev_set_drvdata(_dev, dev); ++ ++ /* controller stays disabled until gadget driver is bound */ ++ udc_disable(dev); ++ udc_reinit(dev); ++ ++// spin_lock_init(&the_udc.lock); ++ register_proc_entry(); ++ ++ /* setup dma channels and IRQ */ ++ retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive", ++ NULL, NULL, &dev->ep[1].dmaregs); ++ if (retval) { ++ ERROR(dev, "couldn't get rx dma, err %d\n", retval); ++ goto err_rx_dma; ++ } ++ retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit", ++ NULL, NULL, &dev->ep[2].dmaregs); ++ if (retval) { ++ ERROR(dev, "couldn't get tx dma, err %d\n", retval); ++ goto err_tx_dma; ++ } ++ retval = request_irq(IRQ_Ser0UDC, udc_int_hndlr, IRQF_DISABLED, ++ driver_name, dev); ++ if (retval) { ++ ERROR(dev, "couldn't get irq, err %d\n", retval); ++ goto err_irq; ++ } ++ ++ INFO(dev, "initialized, rx %p tx %p irq %d\n", ++ dev->ep[1].dmaregs, dev->ep[2].dmaregs, IRQ_Ser0UDC); ++ return 0; ++ ++err_irq: ++ sa1100_free_dma(dev->ep[2].dmaregs); ++ usbd_info.dmaregs_rx = 0; ++err_tx_dma: ++ sa1100_free_dma(dev->ep[1].dmaregs); ++ usbd_info.dmaregs_tx = 0; ++err_rx_dma: ++ return retval; ++} ++ ++static int __exit sa1100_udc_remove(struct device *_dev) ++{ ++ struct sa1100_udc *dev = dev_get_drvdata(_dev); ++ ++ udc_disable(dev); ++ unregister_proc_entry(); ++ usb_gadget_unregister_driver(dev->driver); ++ sa1100_free_dma(dev->ep[1].dmaregs); ++ sa1100_free_dma(dev->ep[2].dmaregs); ++ free_irq(IRQ_Ser0UDC, dev); ++ dev_set_drvdata(_dev,NULL); ++ the_controller = NULL; ++ return 0; ++} ++ ++static struct device_driver udc_driver = { ++ .name = "sa11x0-udc", ++ .bus = &platform_bus_type, ++ .probe = sa1100_udc_probe, ++ .remove = __exit_p(sa1100_udc_remove), ++// .suspend = sa1100_udc_suspend, ++// .resume = sa1100_udc_resume, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init udc_init(void) ++{ ++ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); ++#ifdef NCB_DMA_FIX ++ send_buffer = (char*) kzalloc(SEND_BUFFER_SIZE, GFP_KERNEL | GFP_DMA ); ++ receive_buffer = (char*) kzalloc(RECEIVE_BUFFER_SIZE, GFP_KERNEL | GFP_DMA ); ++#endif ++ return driver_register(&udc_driver); ++} ++module_init(udc_init); ++ ++static void __exit udc_exit(void) ++{ ++#ifdef NCB_DMA_FIX ++ if (send_buffer) { ++ kfree(send_buffer); ++ send_buffer = NULL; ++ } ++ if (receive_buffer) { ++ kfree(receive_buffer); ++ receive_buffer = NULL; ++ } ++#endif ++ driver_unregister(&udc_driver); ++} ++module_exit(udc_exit); +diff --git a/drivers/usb/gadget/sa1100_udc.h b/drivers/usb/gadget/sa1100_udc.h +new file mode 100644 +index 0000000..acb665a +--- /dev/null ++++ b/drivers/usb/gadget/sa1100_udc.h +@@ -0,0 +1,94 @@ ++/* ++ * internals of "new style" UDC controller ++ * <linux/usb_gadget.h> replaces ARM-specific "sa1100_usb.h". ++ */ ++ ++struct sa1100_ep { ++ struct usb_ep ep; ++ struct sa1100_udc *dev; ++ //unsigned long irqs; ++ ++ const struct usb_endpoint_descriptor *desc; ++ struct list_head queue; ++ dma_regs_t *dmaregs; ++ unsigned stopped : 1; ++}; ++ ++struct sa1100_request { ++ struct usb_request req; ++ struct list_head queue; ++// NCB unsigned mapped : 1; ++}; ++ ++enum ep0_state { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_END_XFER, ++ EP0_STALL, ++}; ++ ++#define EP0_FIFO_SIZE ((unsigned)8) ++#define BULK_FIFO_SIZE ((unsigned)64) ++//#define ISO_FIFO_SIZE ((unsigned)256) ++//#define INT_FIFO_SIZE ((unsigned)8) ++ ++struct udc_stats { ++ struct ep0stats { ++ unsigned long ops; ++ unsigned long bytes; ++ } read, write; ++ unsigned long irqs; ++}; ++ ++struct sa1100_udc { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ struct device *dev; ++ enum ep0_state ep0state; ++ struct udc_stats stats; ++// NCB spinlock_t lock; ++// NCB dma_regs_t *dmaregs_tx, *dmaregs_rx; ++ unsigned got_irq : 1, ++ vbus : 1, ++ pullup : 1, ++ has_cfr : 1, ++ req_pending : 1, ++ req_std : 1, ++ req_config : 1; ++ struct timer_list timer; ++ u64 dma_mask; ++ unsigned char address; ++ struct sa1100_ep ep[3]; ++ int ep0_req_len; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define xprintk(dev,level,fmt,args...) \ ++ printk(level "%s: " fmt , driver_name , ## args) ++ ++#ifdef DEBUG ++#undef DEBUG ++#define DEBUG(dev,fmt,args...) \ ++ xprintk(dev , KERN_DEBUG , fmt , ## args) ++#else ++#define DEBUG(dev,fmt,args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++#ifdef VERBOSE ++#define VDEBUG DEBUG ++#else ++#define VDEBUG(dev,fmt,args...) \ ++ do { } while (0) ++#endif /* VERBOSE */ ++ ++#define ERROR(dev,fmt,args...) \ ++ xprintk(dev , KERN_ERR , fmt , ## args) ++#define WARN(dev,fmt,args...) \ ++ xprintk(dev , KERN_WARNING , fmt , ## args) ++#define INFO(dev,fmt,args...) \ ++ xprintk(dev , KERN_INFO , fmt , ## args)
++ ++/*-------------------------------------------------------------------------*/ +diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c +index fa019fa..b3699af 100644 +--- a/drivers/usb/gadget/serial.c ++++ b/drivers/usb/gadget/serial.c +@@ -1,15 +1,9 @@ + /* +- * g_serial.c -- USB gadget serial driver ++ * serial.c -- USB gadget serial driver + * +- * Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com) +- * +- * This code is based in part on the Gadget Zero driver, which +- * is Copyright (C) 2003 by David Brownell, all rights reserved. +- * +- * This code also borrows from usbserial.c, which is +- * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) +- * Copyright (C) 2000 Peter Berger (pberger@brimson.com) +- * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) ++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) ++ * Copyright (C) 2008 by David Brownell ++ * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, +@@ -22,2254 +16,237 @@ + #include <linux/tty.h> + #include <linux/tty_flip.h> + +-#include <linux/usb/ch9.h> +-#include <linux/usb/cdc.h> +-#include <linux/usb/gadget.h> +- ++#include "u_serial.h" + #include "gadget_chips.h" + + + /* Defines */ + +-#define GS_VERSION_STR "v2.2" +-#define GS_VERSION_NUM 0x2200 ++#define GS_VERSION_STR "v2.4" ++#define GS_VERSION_NUM 0x2400 + + #define GS_LONG_NAME "Gadget Serial" +-#define GS_SHORT_NAME "g_serial" +- +-#define GS_MAJOR 127 +-#define GS_MINOR_START 0 +- +-/* REVISIT only one port is supported for now; +- * see gs_{send,recv}_packet() ... no multiplexing, +- * and no support for multiple ACM devices. +- */ +-#define GS_NUM_PORTS 1 +- +-#define GS_NUM_CONFIGS 1 +-#define GS_NO_CONFIG_ID 0 +-#define GS_BULK_CONFIG_ID 1 +-#define GS_ACM_CONFIG_ID 2 +- +-#define GS_MAX_NUM_INTERFACES 2 +-#define GS_BULK_INTERFACE_ID 0 +-#define GS_CONTROL_INTERFACE_ID 0 +-#define GS_DATA_INTERFACE_ID 1 +- +-#define GS_MAX_DESC_LEN 256 +- +-#define GS_DEFAULT_READ_Q_SIZE 32 +-#define GS_DEFAULT_WRITE_Q_SIZE 32 +- +-#define GS_DEFAULT_WRITE_BUF_SIZE 8192 +-#define GS_TMP_BUF_SIZE 8192 +- +-#define GS_CLOSE_TIMEOUT 15 +- +-#define GS_DEFAULT_USE_ACM 0 +- +-/* 9600-8-N-1 ... matches init_termios.c_cflag and defaults +- * expected by "usbser.sys" on MS-Windows. +- */ +-#define GS_DEFAULT_DTE_RATE 9600 +-#define GS_DEFAULT_DATA_BITS 8 +-#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY +-#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS +- +-/* maxpacket and other transfer characteristics vary by speed. */ +-static inline struct usb_endpoint_descriptor * +-choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, +- struct usb_endpoint_descriptor *fs) +-{ +- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) +- return hs; +- return fs; +-} +- +- +-/* debug settings */ +-#ifdef DEBUG +-static int debug = 1; +-#else +-#define debug 0 +-#endif +- +-#define gs_debug(format, arg...) \ +- do { if (debug) pr_debug(format, ## arg); } while (0) +-#define gs_debug_level(level, format, arg...) \ +- do { if (debug >= level) pr_debug(format, ## arg); } while (0) ++#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR + ++/*-------------------------------------------------------------------------*/ + + /* Thanks to NetChip Technologies for donating this product ID. +- * +- * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! +- * Instead: allocate your own, using normal USB-IF procedures. +- */ ++* ++* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! ++* Instead: allocate your own, using normal USB-IF procedures. ++*/ + #define GS_VENDOR_ID 0x0525 /* NetChip */ + #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ + #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ + +-#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ +-#define GS_NOTIFY_MAXPACKET 8 ++/* string IDs are assigned dynamically */ + ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++#define STRING_DESCRIPTION_IDX 2 + +-/* circular buffer */ +-struct gs_buf { +- unsigned int buf_size; +- char *buf_buf; +- char *buf_get; +- char *buf_put; +-}; ++static char manufacturer[50]; + +-/* the port structure holds info for each port, one for each minor number */ +-struct gs_port { +- struct gs_dev *port_dev; /* pointer to device struct */ +- struct tty_struct *port_tty; /* pointer to tty struct */ +- spinlock_t port_lock; +- int port_num; +- int port_open_count; +- int port_in_use; /* open/close in progress */ +- wait_queue_head_t port_write_wait;/* waiting to write */ +- struct gs_buf *port_write_buf; +- struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ +- u16 port_handshake_bits; +-#define RS232_RTS (1 << 1) +-#define RS232_DTE (1 << 0) ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = GS_VERSION_NAME, ++ [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, ++ { } /* end of list */ + }; + +-/* the device structure holds info for the USB device */ +-struct gs_dev { +- struct usb_gadget *dev_gadget; /* gadget device pointer */ +- spinlock_t dev_lock; /* lock for set/reset config */ +- int dev_config; /* configuration number */ +- struct usb_ep *dev_notify_ep; /* address of notify endpoint */ +- struct usb_ep *dev_in_ep; /* address of in endpoint */ +- struct usb_ep *dev_out_ep; /* address of out endpoint */ +- struct usb_endpoint_descriptor /* descriptor of notify ep */ +- *dev_notify_ep_desc; +- struct usb_endpoint_descriptor /* descriptor of in endpoint */ +- *dev_in_ep_desc; +- struct usb_endpoint_descriptor /* descriptor of out endpoint */ +- *dev_out_ep_desc; +- struct usb_request *dev_ctrl_req; /* control request */ +- struct list_head dev_req_list; /* list of write requests */ +- int dev_sched_port; /* round robin port scheduled */ +- struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, + }; + +- +-/* Functions */ +- +-/* tty driver internals */ +-static int gs_send(struct gs_dev *dev); +-static int gs_send_packet(struct gs_dev *dev, char *packet, +- unsigned int size); +-static int gs_recv_packet(struct gs_dev *dev, char *packet, +- unsigned int size); +-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req); +-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req); +- +-/* gadget driver internals */ +-static int gs_set_config(struct gs_dev *dev, unsigned config); +-static void gs_reset_config(struct gs_dev *dev); +-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, +- u8 type, unsigned int index, int is_otg); +- +-static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, +- gfp_t kmalloc_flags); +-static void gs_free_req(struct usb_ep *ep, struct usb_request *req); +- +-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags); +-static void gs_free_ports(struct gs_dev *dev); +- +-/* circular buffer */ +-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags); +-static void gs_buf_free(struct gs_buf *gb); +-static void gs_buf_clear(struct gs_buf *gb); +-static unsigned int gs_buf_data_avail(struct gs_buf *gb); +-static unsigned int gs_buf_space_avail(struct gs_buf *gb); +-static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, +- unsigned int count); +-static unsigned int gs_buf_get(struct gs_buf *gb, char *buf, +- unsigned int count); +- +- +-/* Globals */ +- +-static struct gs_dev *gs_device; +- +-static struct mutex gs_open_close_lock[GS_NUM_PORTS]; +- +- +-/*-------------------------------------------------------------------------*/ +- +-/* USB descriptors */ +- +-#define GS_MANUFACTURER_STR_ID 1 +-#define GS_PRODUCT_STR_ID 2 +-#define GS_SERIAL_STR_ID 3 +-#define GS_BULK_CONFIG_STR_ID 4 +-#define GS_ACM_CONFIG_STR_ID 5 +-#define GS_CONTROL_STR_ID 6 +-#define GS_DATA_STR_ID 7 +- +-/* static strings, in UTF-8 */ +-static char manufacturer[50]; +-static struct usb_string gs_strings[] = { +- { GS_MANUFACTURER_STR_ID, manufacturer }, +- { GS_PRODUCT_STR_ID, GS_LONG_NAME }, +- { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" }, +- { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" }, +- { GS_CONTROL_STR_ID, "Gadget Serial Control" }, +- { GS_DATA_STR_ID, "Gadget Serial Data" }, +- { } /* end of list */ +-}; +- +-static struct usb_gadget_strings gs_string_table = { +- .language = 0x0409, /* en-us */ +- .strings = gs_strings, ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, + }; + +-static struct usb_device_descriptor gs_device_desc = { ++static struct usb_device_descriptor device_desc = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), ++ /* .bDeviceClass = f(use_acm) */ + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, ++ /* .bMaxPacketSize0 = f(hardware) */ + .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), +- .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), +- .iManufacturer = GS_MANUFACTURER_STR_ID, +- .iProduct = GS_PRODUCT_STR_ID, +- .bNumConfigurations = GS_NUM_CONFIGS, ++ /* .idProduct = f(use_acm) */ ++ /* .bcdDevice = f(hardware) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ .bNumConfigurations = 1, + }; + +-static struct usb_otg_descriptor gs_otg_descriptor = { +- .bLength = sizeof(gs_otg_descriptor), ++static struct usb_otg_descriptor otg_descriptor = { ++ .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, +- .bmAttributes = USB_OTG_SRP, +-}; +- +-static struct usb_config_descriptor gs_bulk_config_desc = { +- .bLength = USB_DT_CONFIG_SIZE, +- .bDescriptorType = USB_DT_CONFIG, +- /* .wTotalLength computed dynamically */ +- .bNumInterfaces = 1, +- .bConfigurationValue = GS_BULK_CONFIG_ID, +- .iConfiguration = GS_BULK_CONFIG_STR_ID, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, +-}; +- +-static struct usb_config_descriptor gs_acm_config_desc = { +- .bLength = USB_DT_CONFIG_SIZE, +- .bDescriptorType = USB_DT_CONFIG, +- /* .wTotalLength computed dynamically */ +- .bNumInterfaces = 2, +- .bConfigurationValue = GS_ACM_CONFIG_ID, +- .iConfiguration = GS_ACM_CONFIG_STR_ID, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, +-}; +- +-static const struct usb_interface_descriptor gs_bulk_interface_desc = { +- .bLength = USB_DT_INTERFACE_SIZE, +- .bDescriptorType = USB_DT_INTERFACE, +- .bInterfaceNumber = GS_BULK_INTERFACE_ID, +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_VENDOR_SPEC, +- .bInterfaceSubClass = 0, +- .bInterfaceProtocol = 0, +- .iInterface = GS_DATA_STR_ID, +-}; +- +-static const struct usb_interface_descriptor gs_control_interface_desc = { +- .bLength = USB_DT_INTERFACE_SIZE, +- .bDescriptorType = USB_DT_INTERFACE, +- .bInterfaceNumber = GS_CONTROL_INTERFACE_ID, +- .bNumEndpoints = 1, +- .bInterfaceClass = USB_CLASS_COMM, +- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, +- .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, +- .iInterface = GS_CONTROL_STR_ID, +-}; +- +-static const struct usb_interface_descriptor gs_data_interface_desc = { +- .bLength = USB_DT_INTERFACE_SIZE, +- .bDescriptorType = USB_DT_INTERFACE, +- .bInterfaceNumber = GS_DATA_INTERFACE_ID, +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_CDC_DATA, +- .bInterfaceSubClass = 0, +- .bInterfaceProtocol = 0, +- .iInterface = GS_DATA_STR_ID, +-}; +- +-static const struct usb_cdc_header_desc gs_header_desc = { +- .bLength = sizeof(gs_header_desc), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_HEADER_TYPE, +- .bcdCDC = __constant_cpu_to_le16(0x0110), +-}; +- +-static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = { +- .bLength = sizeof(gs_call_mgmt_descriptor), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, +- .bmCapabilities = 0, +- .bDataInterface = 1, /* index of data interface */ +-}; +- +-static struct usb_cdc_acm_descriptor gs_acm_descriptor = { +- .bLength = sizeof(gs_acm_descriptor), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_ACM_TYPE, +- .bmCapabilities = (1 << 1), +-}; +- +-static const struct usb_cdc_union_desc gs_union_desc = { +- .bLength = sizeof(gs_union_desc), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_UNION_TYPE, +- .bMasterInterface0 = 0, /* index of control interface */ +- .bSlaveInterface0 = 1, /* index of data interface */ +-}; +- +-static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), +- .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, +-}; +- +-static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_OUT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_bulk_interface_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_in_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_out_desc, +- NULL, +-}; +- +-static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_control_interface_desc, +- (struct usb_descriptor_header *) &gs_header_desc, +- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, +- (struct usb_descriptor_header *) &gs_acm_descriptor, +- (struct usb_descriptor_header *) &gs_union_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_notify_desc, +- (struct usb_descriptor_header *) &gs_data_interface_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_in_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_out_desc, +- NULL, +-}; + +-static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), +- .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, +-}; +- +-static struct usb_endpoint_descriptor gs_highspeed_in_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_endpoint_descriptor gs_highspeed_out_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_qualifier_descriptor gs_qualifier_desc = { +- .bLength = sizeof(struct usb_qualifier_descriptor), +- .bDescriptorType = USB_DT_DEVICE_QUALIFIER, +- .bcdUSB = __constant_cpu_to_le16 (0x0200), +- /* assumes ep0 uses the same value for both speeds ... */ +- .bNumConfigurations = GS_NUM_CONFIGS, +-}; +- +-static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_bulk_interface_desc, +- (struct usb_descriptor_header *) &gs_highspeed_in_desc, +- (struct usb_descriptor_header *) &gs_highspeed_out_desc, +- NULL, ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }; + +-static const struct usb_descriptor_header *gs_acm_highspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_control_interface_desc, +- (struct usb_descriptor_header *) &gs_header_desc, +- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, +- (struct usb_descriptor_header *) &gs_acm_descriptor, +- (struct usb_descriptor_header *) &gs_union_desc, +- (struct usb_descriptor_header *) &gs_highspeed_notify_desc, +- (struct usb_descriptor_header *) &gs_data_interface_desc, +- (struct usb_descriptor_header *) &gs_highspeed_in_desc, +- (struct usb_descriptor_header *) &gs_highspeed_out_desc, ++static const struct usb_descriptor_header *otg_desc[] = { ++ (struct usb_descriptor_header *) &otg_descriptor, + NULL, + }; + +- + /*-------------------------------------------------------------------------*/ + + /* Module */ +-MODULE_DESCRIPTION(GS_LONG_NAME); ++MODULE_DESCRIPTION(GS_VERSION_NAME); + MODULE_AUTHOR("Al Borchers"); ++MODULE_AUTHOR("David Brownell"); + MODULE_LICENSE("GPL"); + +-#ifdef DEBUG +-module_param(debug, int, S_IRUGO|S_IWUSR); +-MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on"); +-#endif +- +-static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE; +-module_param(read_q_size, uint, S_IRUGO); +-MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32"); +- +-static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE; +-module_param(write_q_size, uint, S_IRUGO); +-MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32"); ++static int use_acm = true; ++module_param(use_acm, bool, 0); ++MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); + +-static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE; +-module_param(write_buf_size, uint, S_IRUGO); +-MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192"); +- +-static unsigned int use_acm = GS_DEFAULT_USE_ACM; +-module_param(use_acm, uint, S_IRUGO); +-MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no"); ++static unsigned n_ports = 1; ++module_param(n_ports, uint, 0); ++MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); + + /*-------------------------------------------------------------------------*/ + +-/* TTY Driver */ +- +-/* +- * gs_open +- */ +-static int gs_open(struct tty_struct *tty, struct file *file) +-{ +- int port_num; +- unsigned long flags; +- struct gs_port *port; +- struct gs_dev *dev; +- struct gs_buf *buf; +- struct mutex *mtx; +- int ret; +- +- port_num = tty->index; +- +- gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file); +- +- if (port_num < 0 || port_num >= GS_NUM_PORTS) { +- pr_err("gs_open: (%d,%p,%p) invalid port number\n", +- port_num, tty, file); +- return -ENODEV; +- } +- +- dev = gs_device; +- +- if (dev == NULL) { +- pr_err("gs_open: (%d,%p,%p) NULL device pointer\n", +- port_num, tty, file); +- return -ENODEV; +- } +- +- mtx = &gs_open_close_lock[port_num]; +- if (mutex_lock_interruptible(mtx)) { +- pr_err("gs_open: (%d,%p,%p) interrupted waiting for mutex\n", +- port_num, tty, file); +- return -ERESTARTSYS; +- } +- +- spin_lock_irqsave(&dev->dev_lock, flags); +- +- if (dev->dev_config == GS_NO_CONFIG_ID) { +- pr_err("gs_open: (%d,%p,%p) device is not connected\n", +- port_num, tty, file); +- ret = -ENODEV; +- goto exit_unlock_dev; +- } +- +- port = dev->dev_port[port_num]; +- +- if (port == NULL) { +- pr_err("gs_open: (%d,%p,%p) NULL port pointer\n", +- port_num, tty, file); +- ret = -ENODEV; +- goto exit_unlock_dev; +- } +- +- spin_lock(&port->port_lock); +- spin_unlock(&dev->dev_lock); +- +- if (port->port_dev == NULL) { +- pr_err("gs_open: (%d,%p,%p) port disconnected (1)\n", +- port_num, tty, file); +- ret = -EIO; +- goto exit_unlock_port; +- } +- +- if (port->port_open_count > 0) { +- ++port->port_open_count; +- gs_debug("gs_open: (%d,%p,%p) already open\n", +- port_num, tty, file); +- ret = 0; +- goto exit_unlock_port; +- } +- +- tty->driver_data = NULL; +- +- /* mark port as in use, we can drop port lock and sleep if necessary */ +- port->port_in_use = 1; +- +- /* allocate write buffer on first open */ +- if (port->port_write_buf == NULL) { +- spin_unlock_irqrestore(&port->port_lock, flags); +- buf = gs_buf_alloc(write_buf_size, GFP_KERNEL); +- spin_lock_irqsave(&port->port_lock, flags); +- +- /* might have been disconnected while asleep, check */ +- if (port->port_dev == NULL) { +- pr_err("gs_open: (%d,%p,%p) port disconnected (2)\n", +- port_num, tty, file); +- port->port_in_use = 0; +- ret = -EIO; +- goto exit_unlock_port; +- } +- +- if ((port->port_write_buf=buf) == NULL) { +- pr_err("gs_open: (%d,%p,%p) cannot allocate " +- "port write buffer\n", +- port_num, tty, file); +- port->port_in_use = 0; +- ret = -ENOMEM; +- goto exit_unlock_port; +- } +- +- } +- +- /* wait for carrier detect (not implemented) */ +- +- /* might have been disconnected while asleep, check */ +- if (port->port_dev == NULL) { +- pr_err("gs_open: (%d,%p,%p) port disconnected (3)\n", +- port_num, tty, file); +- port->port_in_use = 0; +- ret = -EIO; +- goto exit_unlock_port; +- } +- +- tty->driver_data = port; +- port->port_tty = tty; +- port->port_open_count = 1; +- port->port_in_use = 0; +- +- gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file); +- +- ret = 0; +- +-exit_unlock_port: +- spin_unlock_irqrestore(&port->port_lock, flags); +- mutex_unlock(mtx); +- return ret; +- +-exit_unlock_dev: +- spin_unlock_irqrestore(&dev->dev_lock, flags); +- mutex_unlock(mtx); +- return ret; +- +-} +- +-/* +- * gs_close +- */ +- +-static int gs_write_finished_event_safely(struct gs_port *p) +-{ +- int cond; +- +- spin_lock_irq(&(p)->port_lock); +- cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf); +- spin_unlock_irq(&(p)->port_lock); +- return cond; +-} +- +-static void gs_close(struct tty_struct *tty, struct file *file) +-{ +- struct gs_port *port = tty->driver_data; +- struct mutex *mtx; +- +- if (port == NULL) { +- pr_err("gs_close: NULL port pointer\n"); +- return; +- } +- +- gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file); +- +- mtx = &gs_open_close_lock[port->port_num]; +- mutex_lock(mtx); +- +- spin_lock_irq(&port->port_lock); +- +- if (port->port_open_count == 0) { +- pr_err("gs_close: (%d,%p,%p) port is already closed\n", +- port->port_num, tty, file); +- goto exit; +- } +- +- if (port->port_open_count > 1) { +- --port->port_open_count; +- goto exit; +- } +- +- /* free disconnected port on final close */ +- if (port->port_dev == NULL) { +- kfree(port); +- goto exit; +- } +- +- /* mark port as closed but in use, we can drop port lock */ +- /* and sleep if necessary */ +- port->port_in_use = 1; +- port->port_open_count = 0; +- +- /* wait for write buffer to drain, or */ +- /* at most GS_CLOSE_TIMEOUT seconds */ +- if (gs_buf_data_avail(port->port_write_buf) > 0) { +- spin_unlock_irq(&port->port_lock); +- wait_event_interruptible_timeout(port->port_write_wait, +- gs_write_finished_event_safely(port), +- GS_CLOSE_TIMEOUT * HZ); +- spin_lock_irq(&port->port_lock); +- } +- +- /* free disconnected port on final close */ +- /* (might have happened during the above sleep) */ +- if (port->port_dev == NULL) { +- kfree(port); +- goto exit; +- } +- +- gs_buf_clear(port->port_write_buf); +- +- tty->driver_data = NULL; +- port->port_tty = NULL; +- port->port_in_use = 0; +- +- gs_debug("gs_close: (%d,%p,%p) completed\n", +- port->port_num, tty, file); +- +-exit: +- spin_unlock_irq(&port->port_lock); +- mutex_unlock(mtx); +-} +- +-/* +- * gs_write +- */ +-static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) ++static int __init serial_bind_config(struct usb_configuration *c) + { +- unsigned long flags; +- struct gs_port *port = tty->driver_data; +- int ret; +- +- if (port == NULL) { +- pr_err("gs_write: NULL port pointer\n"); +- return -EIO; +- } +- +- gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty, +- count); +- +- if (count == 0) +- return 0; +- +- spin_lock_irqsave(&port->port_lock, flags); +- +- if (port->port_dev == NULL) { +- pr_err("gs_write: (%d,%p) port is not connected\n", +- port->port_num, tty); +- ret = -EIO; +- goto exit; +- } +- +- if (port->port_open_count == 0) { +- pr_err("gs_write: (%d,%p) port is closed\n", +- port->port_num, tty); +- ret = -EBADF; +- goto exit; +- } +- +- count = gs_buf_put(port->port_write_buf, buf, count); +- +- spin_unlock_irqrestore(&port->port_lock, flags); +- +- gs_send(gs_device); +- +- gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty, +- count); ++ unsigned i; ++ int status = 0; + +- return count; +- +-exit: +- spin_unlock_irqrestore(&port->port_lock, flags); +- return ret; +-} +- +-/* +- * gs_put_char +- */ +-static int gs_put_char(struct tty_struct *tty, unsigned char ch) +-{ +- unsigned long flags; +- struct gs_port *port = tty->driver_data; +- int ret = 0; +- +- if (port == NULL) { +- pr_err("gs_put_char: NULL port pointer\n"); +- return 0; +- } +- +- gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", +- port->port_num, tty, ch, __builtin_return_address(0)); +- +- spin_lock_irqsave(&port->port_lock, flags); +- +- if (port->port_dev == NULL) { +- pr_err("gs_put_char: (%d,%p) port is not connected\n", +- port->port_num, tty); +- goto exit; +- } +- +- if (port->port_open_count == 0) { +- pr_err("gs_put_char: (%d,%p) port is closed\n", +- port->port_num, tty); +- goto exit; +- } +- +- ret = gs_buf_put(port->port_write_buf, &ch, 1); +- +-exit: +- spin_unlock_irqrestore(&port->port_lock, flags); +- return ret; +-} +- +-/* +- * gs_flush_chars +- */ +-static void gs_flush_chars(struct tty_struct *tty) +-{ +- unsigned long flags; +- struct gs_port *port = tty->driver_data; +- +- if (port == NULL) { +- pr_err("gs_flush_chars: NULL port pointer\n"); +- return; +- } +- +- gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); +- +- spin_lock_irqsave(&port->port_lock, flags); +- +- if (port->port_dev == NULL) { +- pr_err("gs_flush_chars: (%d,%p) port is not connected\n", +- port->port_num, tty); +- goto exit; +- } +- +- if (port->port_open_count == 0) { +- pr_err("gs_flush_chars: (%d,%p) port is closed\n", +- port->port_num, tty); +- goto exit; +- } +- +- spin_unlock_irqrestore(&port->port_lock, flags); +- +- gs_send(gs_device); +- +- return; +- +-exit: +- spin_unlock_irqrestore(&port->port_lock, flags); +-} +- +-/* +- * gs_write_room +- */ +-static int gs_write_room(struct tty_struct *tty) +-{ +- +- int room = 0; +- unsigned long flags; +- struct gs_port *port = tty->driver_data; +- +- +- if (port == NULL) +- return 0; +- +- spin_lock_irqsave(&port->port_lock, flags); +- +- if (port->port_dev != NULL && port->port_open_count > 0 +- && port->port_write_buf != NULL) +- room = gs_buf_space_avail(port->port_write_buf); +- +- spin_unlock_irqrestore(&port->port_lock, flags); +- +- gs_debug("gs_write_room: (%d,%p) room=%d\n", +- port->port_num, tty, room); +- +- return room; +-} +- +-/* +- * gs_chars_in_buffer +- */ +-static int gs_chars_in_buffer(struct tty_struct *tty) +-{ +- int chars = 0; +- unsigned long flags; +- struct gs_port *port = tty->driver_data; +- +- if (port == NULL) +- return 0; +- +- spin_lock_irqsave(&port->port_lock, flags); +- +- if (port->port_dev != NULL && port->port_open_count > 0 +- && port->port_write_buf != NULL) +- chars = gs_buf_data_avail(port->port_write_buf); +- +- spin_unlock_irqrestore(&port->port_lock, flags); +- +- gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n", +- port->port_num, tty, chars); +- +- return chars; +-} +- +-/* +- * gs_throttle +- */ +-static void gs_throttle(struct tty_struct *tty) +-{ +-} +- +-/* +- * gs_unthrottle +- */ +-static void gs_unthrottle(struct tty_struct *tty) +-{ +-} +- +-/* +- * gs_break +- */ +-static void gs_break(struct tty_struct *tty, int break_state) +-{ +-} +- +-/* +- * gs_ioctl +- */ +-static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +-{ +- struct gs_port *port = tty->driver_data; +- +- if (port == NULL) { +- pr_err("gs_ioctl: NULL port pointer\n"); +- return -EIO; ++ for (i = 0; i < n_ports && status == 0; i++) { ++ if (use_acm) ++ status = acm_bind_config(c, i); ++ else ++ status = gser_bind_config(c, i); + } +- +- gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n", +- port->port_num, tty, file, cmd, arg); +- +- /* handle ioctls */ +- +- /* could not handle ioctl */ +- return -ENOIOCTLCMD; +-} +- +-/* +- * gs_set_termios +- */ +-static void gs_set_termios(struct tty_struct *tty, struct ktermios *old) +-{ ++ return status; + } + +-static const struct tty_operations gs_tty_ops = { +- .open = gs_open, +- .close = gs_close, +- .write = gs_write, +- .put_char = gs_put_char, +- .flush_chars = gs_flush_chars, +- .write_room = gs_write_room, +- .ioctl = gs_ioctl, +- .set_termios = gs_set_termios, +- .throttle = gs_throttle, +- .unthrottle = gs_unthrottle, +- .break_ctl = gs_break, +- .chars_in_buffer = gs_chars_in_buffer, ++static struct usb_configuration serial_config_driver = { ++ /* .label = f(use_acm) */ ++ .bind = serial_bind_config, ++ /* .bConfigurationValue = f(use_acm) */ ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ + }; + +-/*-------------------------------------------------------------------------*/ +- +-/* +-* gs_send +-* +-* This function finds available write requests, calls +-* gs_send_packet to fill these packets with data, and +-* continues until either there are no more write requests +-* available or no more data to send. This function is +-* run whenever data arrives or write requests are available. +-*/ +-static int gs_send(struct gs_dev *dev) +-{ +- int ret,len; +- unsigned long flags; +- struct usb_ep *ep; +- struct usb_request *req; +- +- if (dev == NULL) { +- pr_err("gs_send: NULL device pointer\n"); +- return -ENODEV; +- } +- +- spin_lock_irqsave(&dev->dev_lock, flags); +- +- ep = dev->dev_in_ep; +- +- while(!list_empty(&dev->dev_req_list)) { +- +- req = list_entry(dev->dev_req_list.next, +- struct usb_request, list); +- +- len = gs_send_packet(dev, req->buf, ep->maxpacket); +- +- if (len > 0) { +- gs_debug_level(3, "gs_send: len=%d, 0x%2.2x " +- "0x%2.2x 0x%2.2x ...\n", len, +- *((unsigned char *)req->buf), +- *((unsigned char *)req->buf+1), +- *((unsigned char *)req->buf+2)); +- list_del(&req->list); +- req->length = len; +- spin_unlock_irqrestore(&dev->dev_lock, flags); +- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { +- pr_err( +- "gs_send: cannot queue read request, ret=%d\n", +- ret); +- spin_lock_irqsave(&dev->dev_lock, flags); +- break; +- } +- spin_lock_irqsave(&dev->dev_lock, flags); +- } else { +- break; +- } +- +- } +- +- spin_unlock_irqrestore(&dev->dev_lock, flags); +- +- return 0; +-} +- +-/* +- * gs_send_packet +- * +- * If there is data to send, a packet is built in the given +- * buffer and the size is returned. If there is no data to +- * send, 0 is returned. If there is any error a negative +- * error number is returned. +- * +- * Called during USB completion routine, on interrupt time. +- * +- * We assume that disconnect will not happen until all completion +- * routines have completed, so we can assume that the dev_port +- * array does not change during the lifetime of this function. +- */ +-static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size) +-{ +- unsigned int len; +- struct gs_port *port; +- +- /* TEMPORARY -- only port 0 is supported right now */ +- port = dev->dev_port[0]; +- +- if (port == NULL) { +- pr_err("gs_send_packet: port=%d, NULL port pointer\n", 0); +- return -EIO; +- } +- +- spin_lock(&port->port_lock); +- +- len = gs_buf_data_avail(port->port_write_buf); +- if (len < size) +- size = len; +- +- if (size == 0) +- goto exit; +- +- size = gs_buf_get(port->port_write_buf, packet, size); +- +- if (port->port_tty) +- wake_up_interruptible(&port->port_tty->write_wait); +- +-exit: +- spin_unlock(&port->port_lock); +- return size; +-} +- +-/* +- * gs_recv_packet +- * +- * Called for each USB packet received. Reads the packet +- * header and stuffs the data in the appropriate tty buffer. +- * Returns 0 if successful, or a negative error number. +- * +- * Called during USB completion routine, on interrupt time. +- * +- * We assume that disconnect will not happen until all completion +- * routines have completed, so we can assume that the dev_port +- * array does not change during the lifetime of this function. +- */ +-static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size) +-{ +- unsigned int len; +- struct gs_port *port; +- int ret; +- struct tty_struct *tty; +- +- /* TEMPORARY -- only port 0 is supported right now */ +- port = dev->dev_port[0]; +- +- if (port == NULL) { +- pr_err("gs_recv_packet: port=%d, NULL port pointer\n", +- port->port_num); +- return -EIO; +- } +- +- spin_lock(&port->port_lock); +- +- if (port->port_open_count == 0) { +- pr_err("gs_recv_packet: port=%d, port is closed\n", +- port->port_num); +- ret = -EIO; +- goto exit; +- } +- +- +- tty = port->port_tty; +- +- if (tty == NULL) { +- pr_err("gs_recv_packet: port=%d, NULL tty pointer\n", +- port->port_num); +- ret = -EIO; +- goto exit; +- } +- +- if (port->port_tty->magic != TTY_MAGIC) { +- pr_err("gs_recv_packet: port=%d, bad tty magic\n", +- port->port_num); +- ret = -EIO; +- goto exit; +- } +- +- len = tty_buffer_request_room(tty, size); +- if (len > 0) { +- tty_insert_flip_string(tty, packet, len); +- tty_flip_buffer_push(port->port_tty); +- wake_up_interruptible(&port->port_tty->read_wait); +- } +- ret = 0; +-exit: +- spin_unlock(&port->port_lock); +- return ret; +-} +- +-/* +-* gs_read_complete +-*/ +-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) ++static int __init gs_bind(struct usb_composite_dev *cdev) + { +- int ret; +- struct gs_dev *dev = ep->driver_data; +- +- if (dev == NULL) { +- pr_err("gs_read_complete: NULL device pointer\n"); +- return; +- } ++ int gcnum; ++ struct usb_gadget *gadget = cdev->gadget; ++ int status; + +- switch(req->status) { +- case 0: +- /* normal completion */ +- gs_recv_packet(dev, req->buf, req->actual); +-requeue: +- req->length = ep->maxpacket; +- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { +- pr_err( +- "gs_read_complete: cannot queue read request, ret=%d\n", +- ret); +- } +- break; +- +- case -ESHUTDOWN: +- /* disconnect */ +- gs_debug("gs_read_complete: shutdown\n"); +- gs_free_req(ep, req); +- break; +- +- default: +- /* unexpected */ +- pr_err( +- "gs_read_complete: unexpected status error, status=%d\n", +- req->status); +- goto requeue; +- break; +- } +-} +- +-/* +-* gs_write_complete +-*/ +-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- struct gs_dev *dev = ep->driver_data; +- +- if (dev == NULL) { +- pr_err("gs_write_complete: NULL device pointer\n"); +- return; +- } ++ status = gserial_setup(cdev->gadget, n_ports); ++ if (status < 0) ++ return status; + +- switch(req->status) { +- case 0: +- /* normal completion */ +-requeue: +- spin_lock(&dev->dev_lock); +- list_add(&req->list, &dev->dev_req_list); +- spin_unlock(&dev->dev_lock); +- +- gs_send(dev); +- +- break; +- +- case -ESHUTDOWN: +- /* disconnect */ +- gs_debug("gs_write_complete: shutdown\n"); +- gs_free_req(ep, req); +- break; +- +- default: +- pr_err( +- "gs_write_complete: unexpected status error, status=%d\n", +- req->status); +- goto requeue; +- break; +- } +-} ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ + +-/*-------------------------------------------------------------------------*/ ++ /* device description: manufacturer, product */ ++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", ++ init_utsname()->sysname, init_utsname()->release, ++ gadget->name); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail; ++ strings_dev[STRING_MANUFACTURER_IDX].id = status; + +-/* Gadget Driver */ ++ device_desc.iManufacturer = status; + +-/* +- * gs_unbind +- * +- * Called on module unload. Frees the control request and device +- * structure. +- */ +-static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget) +-{ +- struct gs_dev *dev = get_gadget_data(gadget); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail; ++ strings_dev[STRING_PRODUCT_IDX].id = status; + +- gs_device = NULL; ++ device_desc.iProduct = status; + +- /* read/write requests already freed, only control request remains */ +- if (dev != NULL) { +- if (dev->dev_ctrl_req != NULL) { +- gs_free_req(gadget->ep0, dev->dev_ctrl_req); +- dev->dev_ctrl_req = NULL; +- } +- gs_reset_config(dev); +- gs_free_ports(dev); +- kfree(dev); +- set_gadget_data(gadget, NULL); +- } ++ /* config description */ ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail; ++ strings_dev[STRING_DESCRIPTION_IDX].id = status; + +- pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME, +- GS_VERSION_STR); +-} +- +-/* +- * gs_bind +- * +- * Called on module load. Allocates and initializes the device +- * structure and a control request. +- */ +-static int __init gs_bind(struct usb_gadget *gadget) +-{ +- int ret; +- struct usb_ep *ep; +- struct gs_dev *dev; +- int gcnum; +- +- /* Some controllers can't support CDC ACM: +- * - sh doesn't support multiple interfaces or configs; +- * - sa1100 doesn't have a third interrupt endpoint +- */ +- if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget)) +- use_acm = 0; ++ serial_config_driver.iConfiguration = status; + ++ /* set up other descriptors */ + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) +- gs_device_desc.bcdDevice = +- cpu_to_le16(GS_VERSION_NUM | gcnum); ++ device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum); + else { ++ /* this is so simple (for now, no altsettings) that it ++ * SHOULD NOT have problems with bulk-capable hardware. ++ * so warn about unrcognized controllers -- don't panic. ++ * ++ * things like configuration and altsetting numbering ++ * can need hardware-specific attention though. ++ */ + pr_warning("gs_bind: controller '%s' not recognized\n", + gadget->name); +- /* unrecognized, but safe unless bulk is REALLY quirky */ +- gs_device_desc.bcdDevice = +- __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); +- } +- +- dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); +- if (dev == NULL) +- return -ENOMEM; +- +- usb_ep_autoconfig_reset(gadget); +- +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); +- if (!ep) +- goto autoconf_fail; +- dev->dev_in_ep = ep; +- ep->driver_data = dev; /* claim the endpoint */ +- +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); +- if (!ep) +- goto autoconf_fail; +- dev->dev_out_ep = ep; +- ep->driver_data = dev; /* claim the endpoint */ +- +- if (use_acm) { +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); +- if (!ep) { +- pr_err("gs_bind: cannot run ACM on %s\n", gadget->name); +- goto autoconf_fail; +- } +- gs_device_desc.idProduct = __constant_cpu_to_le16( +- GS_CDC_PRODUCT_ID), +- dev->dev_notify_ep = ep; +- ep->driver_data = dev; /* claim the endpoint */ +- } +- +- gs_device_desc.bDeviceClass = use_acm +- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; +- gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; +- +- if (gadget_is_dualspeed(gadget)) { +- gs_qualifier_desc.bDeviceClass = use_acm +- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; +- /* assume ep0 uses the same packet size for both speeds */ +- gs_qualifier_desc.bMaxPacketSize0 = +- gs_device_desc.bMaxPacketSize0; +- /* assume endpoints are dual-speed */ +- gs_highspeed_notify_desc.bEndpointAddress = +- gs_fullspeed_notify_desc.bEndpointAddress; +- gs_highspeed_in_desc.bEndpointAddress = +- gs_fullspeed_in_desc.bEndpointAddress; +- gs_highspeed_out_desc.bEndpointAddress = +- gs_fullspeed_out_desc.bEndpointAddress; +- } +- +- usb_gadget_set_selfpowered(gadget); +- +- if (gadget_is_otg(gadget)) { +- gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, +- gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ device_desc.bcdDevice = ++ __constant_cpu_to_le16(GS_VERSION_NUM | 0x0099); + } + +- gs_device = dev; +- +- snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", +- init_utsname()->sysname, init_utsname()->release, +- gadget->name); +- +- dev->dev_gadget = gadget; +- spin_lock_init(&dev->dev_lock); +- INIT_LIST_HEAD(&dev->dev_req_list); +- set_gadget_data(gadget, dev); +- +- if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) { +- pr_err("gs_bind: cannot allocate ports\n"); +- gs_unbind(gadget); +- return ret; ++ if (gadget_is_otg(cdev->gadget)) { ++ serial_config_driver.descriptors = otg_desc; ++ serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + +- /* preallocate control response and buffer */ +- dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN, +- GFP_KERNEL); +- if (dev->dev_ctrl_req == NULL) { +- gs_unbind(gadget); +- return -ENOMEM; +- } +- gadget->ep0->driver_data = dev; ++ /* register our configuration */ ++ status = usb_add_config(cdev, &serial_config_driver); ++ if (status < 0) ++ goto fail; + +- pr_info("gs_bind: %s %s bound\n", +- GS_LONG_NAME, GS_VERSION_STR); ++ INFO(cdev, "%s\n", GS_VERSION_NAME); + + return 0; + +-autoconf_fail: +- kfree(dev); +- pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name); +- return -ENODEV; +-} +- +-static int gs_setup_standard(struct usb_gadget *gadget, +- const struct usb_ctrlrequest *ctrl) +-{ +- int ret = -EOPNOTSUPP; +- struct gs_dev *dev = get_gadget_data(gadget); +- struct usb_request *req = dev->dev_ctrl_req; +- u16 wIndex = le16_to_cpu(ctrl->wIndex); +- u16 wValue = le16_to_cpu(ctrl->wValue); +- u16 wLength = le16_to_cpu(ctrl->wLength); +- +- switch (ctrl->bRequest) { +- case USB_REQ_GET_DESCRIPTOR: +- if (ctrl->bRequestType != USB_DIR_IN) +- break; +- +- switch (wValue >> 8) { +- case USB_DT_DEVICE: +- ret = min(wLength, +- (u16)sizeof(struct usb_device_descriptor)); +- memcpy(req->buf, &gs_device_desc, ret); +- break; +- +- case USB_DT_DEVICE_QUALIFIER: +- if (!gadget_is_dualspeed(gadget)) +- break; +- ret = min(wLength, +- (u16)sizeof(struct usb_qualifier_descriptor)); +- memcpy(req->buf, &gs_qualifier_desc, ret); +- break; +- +- case USB_DT_OTHER_SPEED_CONFIG: +- if (!gadget_is_dualspeed(gadget)) +- break; +- /* fall through */ +- case USB_DT_CONFIG: +- ret = gs_build_config_buf(req->buf, gadget, +- wValue >> 8, wValue & 0xff, +- gadget_is_otg(gadget)); +- if (ret >= 0) +- ret = min(wLength, (u16)ret); +- break; +- +- case USB_DT_STRING: +- /* wIndex == language code. */ +- ret = usb_gadget_get_string(&gs_string_table, +- wValue & 0xff, req->buf); +- if (ret >= 0) +- ret = min(wLength, (u16)ret); +- break; +- } +- break; +- +- case USB_REQ_SET_CONFIGURATION: +- if (ctrl->bRequestType != 0) +- break; +- spin_lock(&dev->dev_lock); +- ret = gs_set_config(dev, wValue); +- spin_unlock(&dev->dev_lock); +- break; +- +- case USB_REQ_GET_CONFIGURATION: +- if (ctrl->bRequestType != USB_DIR_IN) +- break; +- *(u8 *)req->buf = dev->dev_config; +- ret = min(wLength, (u16)1); +- break; +- +- case USB_REQ_SET_INTERFACE: +- if (ctrl->bRequestType != USB_RECIP_INTERFACE +- || !dev->dev_config +- || wIndex >= GS_MAX_NUM_INTERFACES) +- break; +- if (dev->dev_config == GS_BULK_CONFIG_ID +- && wIndex != GS_BULK_INTERFACE_ID) +- break; +- /* no alternate interface settings */ +- if (wValue != 0) +- break; +- spin_lock(&dev->dev_lock); +- /* PXA hardware partially handles SET_INTERFACE; +- * we need to kluge around that interference. */ +- if (gadget_is_pxa(gadget)) { +- ret = gs_set_config(dev, use_acm ? +- GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID); +- goto set_interface_done; +- } +- if (dev->dev_config != GS_BULK_CONFIG_ID +- && wIndex == GS_CONTROL_INTERFACE_ID) { +- if (dev->dev_notify_ep) { +- usb_ep_disable(dev->dev_notify_ep); +- usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc); +- } +- } else { +- usb_ep_disable(dev->dev_in_ep); +- usb_ep_disable(dev->dev_out_ep); +- usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc); +- usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc); +- } +- ret = 0; +-set_interface_done: +- spin_unlock(&dev->dev_lock); +- break; +- +- case USB_REQ_GET_INTERFACE: +- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) +- || dev->dev_config == GS_NO_CONFIG_ID) +- break; +- if (wIndex >= GS_MAX_NUM_INTERFACES +- || (dev->dev_config == GS_BULK_CONFIG_ID +- && wIndex != GS_BULK_INTERFACE_ID)) { +- ret = -EDOM; +- break; +- } +- /* no alternate interface settings */ +- *(u8 *)req->buf = 0; +- ret = min(wLength, (u16)1); +- break; +- +- default: +- pr_err("gs_setup: unknown standard request, type=%02x, " +- "request=%02x, value=%04x, index=%04x, length=%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- wValue, wIndex, wLength); +- break; +- } +- +- return ret; ++fail: ++ gserial_cleanup(); ++ return status; + } + +-static void gs_setup_complete_set_line_coding(struct usb_ep *ep, +- struct usb_request *req) +-{ +- struct gs_dev *dev = ep->driver_data; +- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ +- +- switch (req->status) { +- case 0: +- /* normal completion */ +- if (req->actual != sizeof(port->port_line_coding)) +- usb_ep_set_halt(ep); +- else if (port) { +- struct usb_cdc_line_coding *value = req->buf; +- +- /* REVISIT: we currently just remember this data. +- * If we change that, (a) validate it first, then +- * (b) update whatever hardware needs updating. +- */ +- spin_lock(&port->port_lock); +- port->port_line_coding = *value; +- spin_unlock(&port->port_lock); +- } +- break; +- +- case -ESHUTDOWN: +- /* disconnect */ +- gs_free_req(ep, req); +- break; +- +- default: +- /* unexpected */ +- break; +- } +- return; +-} +- +-static int gs_setup_class(struct usb_gadget *gadget, +- const struct usb_ctrlrequest *ctrl) +-{ +- int ret = -EOPNOTSUPP; +- struct gs_dev *dev = get_gadget_data(gadget); +- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ +- struct usb_request *req = dev->dev_ctrl_req; +- u16 wIndex = le16_to_cpu(ctrl->wIndex); +- u16 wValue = le16_to_cpu(ctrl->wValue); +- u16 wLength = le16_to_cpu(ctrl->wLength); +- +- switch (ctrl->bRequest) { +- case USB_CDC_REQ_SET_LINE_CODING: +- if (wLength != sizeof(struct usb_cdc_line_coding)) +- break; +- ret = wLength; +- req->complete = gs_setup_complete_set_line_coding; +- break; +- +- case USB_CDC_REQ_GET_LINE_CODING: +- ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding)); +- if (port) { +- spin_lock(&port->port_lock); +- memcpy(req->buf, &port->port_line_coding, ret); +- spin_unlock(&port->port_lock); +- } +- break; +- +- case USB_CDC_REQ_SET_CONTROL_LINE_STATE: +- if (wLength != 0) +- break; +- ret = 0; +- if (port) { +- /* REVISIT: we currently just remember this data. +- * If we change that, update whatever hardware needs +- * updating. +- */ +- spin_lock(&port->port_lock); +- port->port_handshake_bits = wValue; +- spin_unlock(&port->port_lock); +- } +- break; +- +- default: +- /* NOTE: strictly speaking, we should accept AT-commands +- * using SEND_ENCPSULATED_COMMAND/GET_ENCAPSULATED_RESPONSE. +- * But our call management descriptor says we don't handle +- * call management, so we should be able to get by without +- * handling those "required" commands (except by stalling). +- */ +- pr_err("gs_setup: unknown class request, " +- "type=%02x, request=%02x, value=%04x, " +- "index=%04x, length=%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- wValue, wIndex, wLength); +- break; +- } +- +- return ret; +-} +- +-/* +- * gs_setup_complete +- */ +-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- if (req->status || req->actual != req->length) { +- pr_err("gs_setup_complete: status error, status=%d, " +- "actual=%d, length=%d\n", +- req->status, req->actual, req->length); +- } +-} +- +-/* +- * gs_setup +- * +- * Implements all the control endpoint functionality that's not +- * handled in hardware or the hardware driver. +- * +- * Returns the size of the data sent to the host, or a negative +- * error number. +- */ +-static int gs_setup(struct usb_gadget *gadget, +- const struct usb_ctrlrequest *ctrl) +-{ +- int ret = -EOPNOTSUPP; +- struct gs_dev *dev = get_gadget_data(gadget); +- struct usb_request *req = dev->dev_ctrl_req; +- u16 wIndex = le16_to_cpu(ctrl->wIndex); +- u16 wValue = le16_to_cpu(ctrl->wValue); +- u16 wLength = le16_to_cpu(ctrl->wLength); +- +- req->complete = gs_setup_complete; +- +- switch (ctrl->bRequestType & USB_TYPE_MASK) { +- case USB_TYPE_STANDARD: +- ret = gs_setup_standard(gadget, ctrl); +- break; +- +- case USB_TYPE_CLASS: +- ret = gs_setup_class(gadget, ctrl); +- break; +- +- default: +- pr_err("gs_setup: unknown request, type=%02x, request=%02x, " +- "value=%04x, index=%04x, length=%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- wValue, wIndex, wLength); +- break; +- } +- +- /* respond with data transfer before status phase? */ +- if (ret >= 0) { +- req->length = ret; +- req->zero = ret < wLength +- && (ret % gadget->ep0->maxpacket) == 0; +- ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); +- if (ret < 0) { +- pr_err("gs_setup: cannot queue response, ret=%d\n", +- ret); +- req->status = 0; +- gs_setup_complete(gadget->ep0, req); +- } +- } +- +- /* device either stalls (ret < 0) or reports success */ +- return ret; +-} +- +-/* +- * gs_disconnect +- * +- * Called when the device is disconnected. Frees the closed +- * ports and disconnects open ports. Open ports will be freed +- * on close. Then reallocates the ports for the next connection. +- */ +-static void gs_disconnect(struct usb_gadget *gadget) +-{ +- unsigned long flags; +- struct gs_dev *dev = get_gadget_data(gadget); +- +- spin_lock_irqsave(&dev->dev_lock, flags); +- +- gs_reset_config(dev); +- +- /* free closed ports and disconnect open ports */ +- /* (open ports will be freed when closed) */ +- gs_free_ports(dev); +- +- /* re-allocate ports for the next connection */ +- if (gs_alloc_ports(dev, GFP_ATOMIC) != 0) +- pr_err("gs_disconnect: cannot re-allocate ports\n"); +- +- spin_unlock_irqrestore(&dev->dev_lock, flags); +- +- pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME); +-} +- +-static struct usb_gadget_driver gs_gadget_driver = { +-#ifdef CONFIG_USB_GADGET_DUALSPEED +- .speed = USB_SPEED_HIGH, +-#else +- .speed = USB_SPEED_FULL, +-#endif /* CONFIG_USB_GADGET_DUALSPEED */ +- .function = GS_LONG_NAME, +- .bind = gs_bind, +- .unbind = gs_unbind, +- .setup = gs_setup, +- .disconnect = gs_disconnect, +- .driver = { +- .name = GS_SHORT_NAME, +- .owner = THIS_MODULE, +- }, ++static struct usb_composite_driver gserial_driver = { ++ .name = "g_serial", ++ .dev = &device_desc, ++ .strings = dev_strings, ++ .bind = gs_bind, + }; + +-/* +- * gs_set_config +- * +- * Configures the device by enabling device specific +- * optimizations, setting up the endpoints, allocating +- * read and write requests and queuing read requests. +- * +- * The device lock must be held when calling this function. +- */ +-static int gs_set_config(struct gs_dev *dev, unsigned config) ++static int __init init(void) + { +- int i; +- int ret = 0; +- struct usb_gadget *gadget = dev->dev_gadget; +- struct usb_ep *ep; +- struct usb_endpoint_descriptor *out, *in, *notify; +- struct usb_request *req; +- +- if (dev == NULL) { +- pr_err("gs_set_config: NULL device pointer\n"); +- return 0; +- } +- +- if (config == dev->dev_config) +- return 0; +- +- gs_reset_config(dev); +- +- switch (config) { +- case GS_NO_CONFIG_ID: +- return 0; +- case GS_BULK_CONFIG_ID: +- if (use_acm) +- return -EINVAL; +- break; +- case GS_ACM_CONFIG_ID: +- if (!use_acm) +- return -EINVAL; +- break; +- default: +- return -EINVAL; +- } +- +- in = choose_ep_desc(gadget, +- &gs_highspeed_in_desc, +- &gs_fullspeed_in_desc); +- out = choose_ep_desc(gadget, +- &gs_highspeed_out_desc, +- &gs_fullspeed_out_desc); +- notify = dev->dev_notify_ep +- ? choose_ep_desc(gadget, +- &gs_highspeed_notify_desc, +- &gs_fullspeed_notify_desc) +- : NULL; +- +- ret = usb_ep_enable(dev->dev_in_ep, in); +- if (ret == 0) { +- dev->dev_in_ep_desc = in; +- } else { +- pr_debug("%s: cannot enable %s %s, ret=%d\n", +- __func__, "IN", dev->dev_in_ep->name, ret); +- return ret; +- } +- +- ret = usb_ep_enable(dev->dev_out_ep, out); +- if (ret == 0) { +- dev->dev_out_ep_desc = out; +- } else { +- pr_debug("%s: cannot enable %s %s, ret=%d\n", +- __func__, "OUT", dev->dev_out_ep->name, ret); +-fail0: +- usb_ep_disable(dev->dev_in_ep); +- return ret; +- } +- +- if (notify) { +- ret = usb_ep_enable(dev->dev_notify_ep, notify); +- if (ret == 0) { +- dev->dev_notify_ep_desc = notify; +- } else { +- pr_debug("%s: cannot enable %s %s, ret=%d\n", +- __func__, "NOTIFY", +- dev->dev_notify_ep->name, ret); +- usb_ep_disable(dev->dev_out_ep); +- goto fail0; +- } +- } +- +- dev->dev_config = config; +- +- /* allocate and queue read requests */ +- ep = dev->dev_out_ep; +- for (i=0; i<read_q_size && ret == 0; i++) { +- if ((req=gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC))) { +- req->complete = gs_read_complete; +- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { +- pr_err("gs_set_config: cannot queue read " +- "request, ret=%d\n", ret); +- } +- } else { +- pr_err("gs_set_config: cannot allocate " +- "read requests\n"); +- ret = -ENOMEM; +- goto exit_reset_config; +- } +- } +- +- /* allocate write requests, and put on free list */ +- ep = dev->dev_in_ep; +- for (i=0; i<write_q_size; i++) { +- req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); +- if (req) { +- req->complete = gs_write_complete; +- list_add(&req->list, &dev->dev_req_list); +- } else { +- pr_err("gs_set_config: cannot allocate " +- "write requests\n"); +- ret = -ENOMEM; +- goto exit_reset_config; +- } +- } +- +- /* REVISIT the ACM mode should be able to actually *issue* some +- * notifications, for at least serial state change events if +- * not also for network connection; say so in bmCapabilities. ++ /* We *could* export two configs; that'd be much cleaner... ++ * but neither of these product IDs was defined that way. + */ +- +- pr_info("gs_set_config: %s configured, %s speed %s config\n", +- GS_LONG_NAME, +- gadget->speed == USB_SPEED_HIGH ? "high" : "full", +- config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM"); +- +- return 0; +- +-exit_reset_config: +- gs_reset_config(dev); +- return ret; +-} +- +-/* +- * gs_reset_config +- * +- * Mark the device as not configured, disable all endpoints, +- * which forces completion of pending I/O and frees queued +- * requests, and free the remaining write requests on the +- * free list. +- * +- * The device lock must be held when calling this function. +- */ +-static void gs_reset_config(struct gs_dev *dev) +-{ +- struct usb_request *req; +- +- if (dev == NULL) { +- pr_err("gs_reset_config: NULL device pointer\n"); +- return; +- } +- +- if (dev->dev_config == GS_NO_CONFIG_ID) +- return; +- +- dev->dev_config = GS_NO_CONFIG_ID; +- +- /* free write requests on the free list */ +- while(!list_empty(&dev->dev_req_list)) { +- req = list_entry(dev->dev_req_list.next, +- struct usb_request, list); +- list_del(&req->list); +- gs_free_req(dev->dev_in_ep, req); +- } +- +- /* disable endpoints, forcing completion of pending i/o; */ +- /* completion handlers free their requests in this case */ +- if (dev->dev_notify_ep) +- usb_ep_disable(dev->dev_notify_ep); +- usb_ep_disable(dev->dev_in_ep); +- usb_ep_disable(dev->dev_out_ep); +-} +- +-/* +- * gs_build_config_buf +- * +- * Builds the config descriptors in the given buffer and returns the +- * length, or a negative error number. +- */ +-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, +- u8 type, unsigned int index, int is_otg) +-{ +- int len; +- int high_speed = 0; +- const struct usb_config_descriptor *config_desc; +- const struct usb_descriptor_header **function; +- +- if (index >= gs_device_desc.bNumConfigurations) +- return -EINVAL; +- +- /* other speed switches high and full speed */ +- if (gadget_is_dualspeed(g)) { +- high_speed = (g->speed == USB_SPEED_HIGH); +- if (type == USB_DT_OTHER_SPEED_CONFIG) +- high_speed = !high_speed; +- } +- + if (use_acm) { +- config_desc = &gs_acm_config_desc; +- function = high_speed +- ? gs_acm_highspeed_function +- : gs_acm_fullspeed_function; ++ serial_config_driver.label = "CDC ACM config"; ++ serial_config_driver.bConfigurationValue = 2; ++ device_desc.bDeviceClass = USB_CLASS_COMM; ++ device_desc.idProduct = ++ __constant_cpu_to_le16(GS_CDC_PRODUCT_ID); + } else { +- config_desc = &gs_bulk_config_desc; +- function = high_speed +- ? gs_bulk_highspeed_function +- : gs_bulk_fullspeed_function; +- } +- +- /* for now, don't advertise srp-only devices */ +- if (!is_otg) +- function++; +- +- len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function); +- if (len < 0) +- return len; +- +- ((struct usb_config_descriptor *)buf)->bDescriptorType = type; +- +- return len; +-} +- +-/* +- * gs_alloc_req +- * +- * Allocate a usb_request and its buffer. Returns a pointer to the +- * usb_request or NULL if there is an error. +- */ +-static struct usb_request * +-gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags) +-{ +- struct usb_request *req; +- +- if (ep == NULL) +- return NULL; +- +- req = usb_ep_alloc_request(ep, kmalloc_flags); +- +- if (req != NULL) { +- req->length = len; +- req->buf = kmalloc(len, kmalloc_flags); +- if (req->buf == NULL) { +- usb_ep_free_request(ep, req); +- return NULL; +- } ++ serial_config_driver.label = "Generic Serial config"; ++ serial_config_driver.bConfigurationValue = 1; ++ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; ++ device_desc.idProduct = ++ __constant_cpu_to_le16(GS_PRODUCT_ID); + } ++ strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; + +- return req; ++ return usb_composite_register(&gserial_driver); + } ++module_init(init); + +-/* +- * gs_free_req +- * +- * Free a usb_request and its buffer. +- */ +-static void gs_free_req(struct usb_ep *ep, struct usb_request *req) ++static void __exit cleanup(void) + { +- if (ep != NULL && req != NULL) { +- kfree(req->buf); +- usb_ep_free_request(ep, req); +- } +-} +- +-/* +- * gs_alloc_ports +- * +- * Allocate all ports and set the gs_dev struct to point to them. +- * Return 0 if successful, or a negative error number. +- * +- * The device lock is normally held when calling this function. +- */ +-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags) +-{ +- int i; +- struct gs_port *port; +- +- if (dev == NULL) +- return -EIO; +- +- for (i=0; i<GS_NUM_PORTS; i++) { +- if ((port=kzalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL) +- return -ENOMEM; +- +- port->port_dev = dev; +- port->port_num = i; +- port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE); +- port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT; +- port->port_line_coding.bParityType = GS_DEFAULT_PARITY; +- port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS; +- spin_lock_init(&port->port_lock); +- init_waitqueue_head(&port->port_write_wait); +- +- dev->dev_port[i] = port; +- } +- +- return 0; +-} +- +-/* +- * gs_free_ports +- * +- * Free all closed ports. Open ports are disconnected by +- * freeing their write buffers, setting their device pointers +- * and the pointers to them in the device to NULL. These +- * ports will be freed when closed. +- * +- * The device lock is normally held when calling this function. +- */ +-static void gs_free_ports(struct gs_dev *dev) +-{ +- int i; +- unsigned long flags; +- struct gs_port *port; +- +- if (dev == NULL) +- return; +- +- for (i=0; i<GS_NUM_PORTS; i++) { +- if ((port=dev->dev_port[i]) != NULL) { +- dev->dev_port[i] = NULL; +- +- spin_lock_irqsave(&port->port_lock, flags); +- +- if (port->port_write_buf != NULL) { +- gs_buf_free(port->port_write_buf); +- port->port_write_buf = NULL; +- } +- +- if (port->port_open_count > 0 || port->port_in_use) { +- port->port_dev = NULL; +- wake_up_interruptible(&port->port_write_wait); +- if (port->port_tty) { +- tty_hangup(port->port_tty); +- } +- spin_unlock_irqrestore(&port->port_lock, flags); +- } else { +- spin_unlock_irqrestore(&port->port_lock, flags); +- kfree(port); +- } +- +- } +- } +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* Circular Buffer */ +- +-/* +- * gs_buf_alloc +- * +- * Allocate a circular buffer and all associated memory. +- */ +-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags) +-{ +- struct gs_buf *gb; +- +- if (size == 0) +- return NULL; +- +- gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags); +- if (gb == NULL) +- return NULL; +- +- gb->buf_buf = kmalloc(size, kmalloc_flags); +- if (gb->buf_buf == NULL) { +- kfree(gb); +- return NULL; +- } +- +- gb->buf_size = size; +- gb->buf_get = gb->buf_put = gb->buf_buf; +- +- return gb; +-} +- +-/* +- * gs_buf_free +- * +- * Free the buffer and all associated memory. +- */ +-static void gs_buf_free(struct gs_buf *gb) +-{ +- if (gb) { +- kfree(gb->buf_buf); +- kfree(gb); +- } +-} +- +-/* +- * gs_buf_clear +- * +- * Clear out all data in the circular buffer. +- */ +-static void gs_buf_clear(struct gs_buf *gb) +-{ +- if (gb != NULL) +- gb->buf_get = gb->buf_put; +- /* equivalent to a get of all data available */ +-} +- +-/* +- * gs_buf_data_avail +- * +- * Return the number of bytes of data available in the circular +- * buffer. +- */ +-static unsigned int gs_buf_data_avail(struct gs_buf *gb) +-{ +- if (gb != NULL) +- return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; +- else +- return 0; +-} +- +-/* +- * gs_buf_space_avail +- * +- * Return the number of bytes of space available in the circular +- * buffer. +- */ +-static unsigned int gs_buf_space_avail(struct gs_buf *gb) +-{ +- if (gb != NULL) +- return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; +- else +- return 0; +-} +- +-/* +- * gs_buf_put +- * +- * Copy data data from a user buffer and put it into the circular buffer. +- * Restrict to the amount of space available. +- * +- * Return the number of bytes copied. +- */ +-static unsigned int +-gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) +-{ +- unsigned int len; +- +- if (gb == NULL) +- return 0; +- +- len = gs_buf_space_avail(gb); +- if (count > len) +- count = len; +- +- if (count == 0) +- return 0; +- +- len = gb->buf_buf + gb->buf_size - gb->buf_put; +- if (count > len) { +- memcpy(gb->buf_put, buf, len); +- memcpy(gb->buf_buf, buf+len, count - len); +- gb->buf_put = gb->buf_buf + count - len; +- } else { +- memcpy(gb->buf_put, buf, count); +- if (count < len) +- gb->buf_put += count; +- else /* count == len */ +- gb->buf_put = gb->buf_buf; +- } +- +- return count; +-} +- +-/* +- * gs_buf_get +- * +- * Get data from the circular buffer and copy to the given buffer. +- * Restrict to the amount of data available. +- * +- * Return the number of bytes copied. +- */ +-static unsigned int +-gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) +-{ +- unsigned int len; +- +- if (gb == NULL) +- return 0; +- +- len = gs_buf_data_avail(gb); +- if (count > len) +- count = len; +- +- if (count == 0) +- return 0; +- +- len = gb->buf_buf + gb->buf_size - gb->buf_get; +- if (count > len) { +- memcpy(buf, gb->buf_get, len); +- memcpy(buf+len, gb->buf_buf, count - len); +- gb->buf_get = gb->buf_buf + count - len; +- } else { +- memcpy(buf, gb->buf_get, count); +- if (count < len) +- gb->buf_get += count; +- else /* count == len */ +- gb->buf_get = gb->buf_buf; +- } +- +- return count; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static struct tty_driver *gs_tty_driver; +- +-/* +- * gs_module_init +- * +- * Register as a USB gadget driver and a tty driver. +- */ +-static int __init gs_module_init(void) +-{ +- int i; +- int retval; +- +- retval = usb_gadget_register_driver(&gs_gadget_driver); +- if (retval) { +- pr_err("gs_module_init: cannot register gadget driver, " +- "ret=%d\n", retval); +- return retval; +- } +- +- gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS); +- if (!gs_tty_driver) +- return -ENOMEM; +- gs_tty_driver->owner = THIS_MODULE; +- gs_tty_driver->driver_name = GS_SHORT_NAME; +- gs_tty_driver->name = "ttygs"; +- gs_tty_driver->major = GS_MAJOR; +- gs_tty_driver->minor_start = GS_MINOR_START; +- gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; +- gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; +- gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; +- gs_tty_driver->init_termios = tty_std_termios; +- /* must match GS_DEFAULT_DTE_RATE and friends */ +- gs_tty_driver->init_termios.c_cflag = +- B9600 | CS8 | CREAD | HUPCL | CLOCAL; +- gs_tty_driver->init_termios.c_ispeed = GS_DEFAULT_DTE_RATE; +- gs_tty_driver->init_termios.c_ospeed = GS_DEFAULT_DTE_RATE; +- tty_set_operations(gs_tty_driver, &gs_tty_ops); +- +- for (i = 0; i < GS_NUM_PORTS; i++) +- mutex_init(&gs_open_close_lock[i]); +- +- retval = tty_register_driver(gs_tty_driver); +- if (retval) { +- usb_gadget_unregister_driver(&gs_gadget_driver); +- put_tty_driver(gs_tty_driver); +- pr_err("gs_module_init: cannot register tty driver, " +- "ret=%d\n", retval); +- return retval; +- } +- +- pr_info("gs_module_init: %s %s loaded\n", +- GS_LONG_NAME, GS_VERSION_STR); +- return 0; +-} +-module_init(gs_module_init); +- +-/* +- * gs_module_exit +- * +- * Unregister as a tty driver and a USB gadget driver. +- */ +-static void __exit gs_module_exit(void) +-{ +- tty_unregister_driver(gs_tty_driver); +- put_tty_driver(gs_tty_driver); +- usb_gadget_unregister_driver(&gs_gadget_driver); +- +- pr_info("gs_module_exit: %s %s unloaded\n", +- GS_LONG_NAME, GS_VERSION_STR); ++ usb_composite_unregister(&gserial_driver); ++ gserial_cleanup(); + } +-module_exit(gs_module_exit); ++module_exit(cleanup); +diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c +new file mode 100644 +index 0000000..3791e62 +--- /dev/null ++++ b/drivers/usb/gadget/u_ether.c +@@ -0,0 +1,964 @@ ++/* ++ * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/device.h> ++#include <linux/ctype.h> ++#include <linux/etherdevice.h> ++#include <linux/ethtool.h> ++ ++#include "u_ether.h" ++ ++ ++/* ++ * This component encapsulates the Ethernet link glue needed to provide ++ * one (!) network link through the USB gadget stack, normally "usb0". ++ * ++ * The control and data models are handled by the function driver which ++ * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. ++ * That includes all descriptor and endpoint management. ++ * ++ * Link level addressing is handled by this component using module ++ * parameters; if no such parameters are provided, random link level ++ * addresses are used. Each end of the link uses one address. The ++ * host end address is exported in various ways, and is often recorded ++ * in configuration databases. ++ * ++ * The driver which assembles each configuration using such a link is ++ * responsible for ensuring that each configuration includes at most one ++ * instance of is network link. (The network layer provides ways for ++ * this single "physical" link to be used by multiple virtual links.) ++ */ ++ ++#define DRIVER_VERSION "29-May-2008" ++ ++struct eth_dev { ++ /* lock is held while accessing port_usb ++ * or updating its backlink port_usb->ioport ++ */ ++ spinlock_t lock; ++ struct gether *port_usb; ++ ++ struct net_device *net; ++ struct usb_gadget *gadget; ++ ++ spinlock_t req_lock; /* guard {rx,tx}_reqs */ ++ struct list_head tx_reqs, rx_reqs; ++ atomic_t tx_qlen; ++ ++ unsigned header_len; ++ struct sk_buff *(*wrap)(struct sk_buff *skb); ++ int (*unwrap)(struct sk_buff *skb); ++ ++ struct work_struct work; ++ ++ unsigned long todo; ++#define WORK_RX_MEMORY 0 ++ ++ bool zlp; ++ u8 host_mac[ETH_ALEN]; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define RX_EXTRA 20 /* bytes guarding against rx overflows */ ++ ++#define DEFAULT_QLEN 2 /* double buffering by default */ ++ ++ ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ ++static unsigned qmult = 5; ++module_param(qmult, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(qmult, "queue length multiplier at high speed"); ++ ++#else /* full speed (low speed doesn't do bulk) */ ++#define qmult 1 ++#endif ++ ++/* for dual-speed hardware, use deeper queues at highspeed */ ++static inline int qlen(struct usb_gadget *gadget) ++{ ++ if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) ++ return qmult * DEFAULT_QLEN; ++ else ++ return DEFAULT_QLEN; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* REVISIT there must be a better way than having two sets ++ * of debug calls ... ++ */ ++ ++#undef DBG ++#undef VDBG ++#undef ERROR ++#undef INFO ++ ++#define xprintk(d, level, fmt, args...) \ ++ printk(level "%s: " fmt , (d)->net->name , ## args) ++ ++#ifdef DEBUG ++#undef DEBUG ++#define DBG(dev, fmt, args...) \ ++ xprintk(dev , KERN_DEBUG , fmt , ## args) ++#else ++#define DBG(dev, fmt, args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++#ifdef VERBOSE_DEBUG ++#define VDBG DBG ++#else ++#define VDBG(dev, fmt, args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++#define ERROR(dev, fmt, args...) \ ++ xprintk(dev , KERN_ERR , fmt , ## args) ++#define INFO(dev, fmt, args...) \ ++ xprintk(dev , KERN_INFO , fmt , ## args) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* NETWORK DRIVER HOOKUP (to the layer above this driver) */ ++ ++static int eth_change_mtu(struct net_device *net, int new_mtu) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ unsigned long flags; ++ int status = 0; ++ ++ /* don't change MTU on "live" link (peer won't know) */ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) ++ status = -EBUSY; ++ else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) ++ status = -ERANGE; ++ else ++ net->mtu = new_mtu; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return status; ++} ++ ++static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ ++ strlcpy(p->driver, "g_ether", sizeof p->driver); ++ strlcpy(p->version, DRIVER_VERSION, sizeof p->version); ++ strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); ++ strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info); ++} ++ ++static u32 eth_get_link(struct net_device *net) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ return dev->gadget->speed != USB_SPEED_UNKNOWN; ++} ++ ++/* REVISIT can also support: ++ * - WOL (by tracking suspends and issuing remote wakeup) ++ * - msglevel (implies updated messaging) ++ * - ... probably more ethtool ops ++ */ ++ ++static struct ethtool_ops ops = { ++ .get_drvinfo = eth_get_drvinfo, ++ .get_link = eth_get_link ++}; ++ ++static void defer_kevent(struct eth_dev *dev, int flag) ++{ ++ if (test_and_set_bit(flag, &dev->todo)) ++ return; ++ if (!schedule_work(&dev->work)) ++ ERROR(dev, "kevent %d may have been dropped\n", flag); ++ else ++ DBG(dev, "kevent %d scheduled\n", flag); ++} ++ ++static void rx_complete(struct usb_ep *ep, struct usb_request *req); ++ ++static int ++rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) ++{ ++ struct sk_buff *skb; ++ int retval = -ENOMEM; ++ size_t size = 0; ++ struct usb_ep *out; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) ++ out = dev->port_usb->out_ep; ++ else ++ out = NULL; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ if (!out) ++ return -ENOTCONN; ++ ++ ++ /* Padding up to RX_EXTRA handles minor disagreements with host. ++ * Normally we use the USB "terminate on short read" convention; ++ * so allow up to (N*maxpacket), since that memory is normally ++ * already allocated. Some hardware doesn't deal well with short ++ * reads (e.g. DMA must be N*maxpacket), so for now don't trim a ++ * byte off the end (to force hardware errors on overflow). ++ * ++ * RNDIS uses internal framing, and explicitly allows senders to ++ * pad to end-of-packet. That's potentially nice for speed, but ++ * means receivers can't recover lost synch on their own (because ++ * new packets don't only start after a short RX). ++ */ ++ size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA; ++ size += dev->port_usb->header_len; ++ size += out->maxpacket - 1; ++ size -= size % out->maxpacket; ++ ++ skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); ++ if (skb == NULL) { ++ DBG(dev, "no rx skb\n"); ++ goto enomem; ++ } ++ ++ /* Some platforms perform better when IP packets are aligned, ++ * but on at least one, checksumming fails otherwise. Note: ++ * RNDIS headers involve variable numbers of LE32 values. ++ */ ++ skb_reserve(skb, NET_IP_ALIGN); ++ ++ req->buf = skb->data; ++ req->length = size; ++ req->complete = rx_complete; ++ req->context = skb; ++ ++ retval = usb_ep_queue(out, req, gfp_flags); ++ if (retval == -ENOMEM) ++enomem: ++ defer_kevent(dev, WORK_RX_MEMORY); ++ if (retval) { ++ DBG(dev, "rx submit --> %d\n", retval); ++ if (skb) ++ dev_kfree_skb_any(skb); ++ spin_lock_irqsave(&dev->req_lock, flags); ++ list_add(&req->list, &dev->rx_reqs); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ } ++ return retval; ++} ++ ++static void rx_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct sk_buff *skb = req->context; ++ struct eth_dev *dev = ep->driver_data; ++ int status = req->status; ++ ++ switch (status) { ++ ++ /* normal completion */ ++ case 0: ++ skb_put(skb, req->actual); ++ if (dev->unwrap) ++ status = dev->unwrap(skb); ++ if (status < 0 ++ || ETH_HLEN > skb->len ++ || skb->len > ETH_FRAME_LEN) { ++ dev->net->stats.rx_errors++; ++ dev->net->stats.rx_length_errors++; ++ DBG(dev, "rx length %d\n", skb->len); ++ break; ++ } ++ ++ skb->protocol = eth_type_trans(skb, dev->net); ++ dev->net->stats.rx_packets++; ++ dev->net->stats.rx_bytes += skb->len; ++ ++ /* no buffer copies needed, unless hardware can't ++ * use skb buffers. ++ */ ++ status = netif_rx(skb); ++ skb = NULL; ++ break; ++ ++ /* software-driven interface shutdown */ ++ case -ECONNRESET: /* unlink */ ++ case -ESHUTDOWN: /* disconnect etc */ ++ VDBG(dev, "rx shutdown, code %d\n", status); ++ goto quiesce; ++ ++ /* for hardware automagic (such as pxa) */ ++ case -ECONNABORTED: /* endpoint reset */ ++ DBG(dev, "rx %s reset\n", ep->name); ++ defer_kevent(dev, WORK_RX_MEMORY); ++quiesce: ++ dev_kfree_skb_any(skb); ++ goto clean; ++ ++ /* data overrun */ ++ case -EOVERFLOW: ++ dev->net->stats.rx_over_errors++; ++ /* FALLTHROUGH */ ++ ++ default: ++ dev->net->stats.rx_errors++; ++ DBG(dev, "rx status %d\n", status); ++ break; ++ } ++ ++ if (skb) ++ dev_kfree_skb_any(skb); ++ if (!netif_running(dev->net)) { ++clean: ++ spin_lock(&dev->req_lock); ++ list_add(&req->list, &dev->rx_reqs); ++ spin_unlock(&dev->req_lock); ++ req = NULL; ++ } ++ if (req) ++ rx_submit(dev, req, GFP_ATOMIC); ++} ++ ++static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) ++{ ++ unsigned i; ++ struct usb_request *req; ++ ++ if (!n) ++ return -ENOMEM; ++ ++ /* queue/recycle up to N requests */ ++ i = n; ++ list_for_each_entry(req, list, list) { ++ if (i-- == 0) ++ goto extra; ++ } ++ while (i--) { ++ req = usb_ep_alloc_request(ep, GFP_ATOMIC); ++ if (!req) ++ return list_empty(list) ? -ENOMEM : 0; ++ list_add(&req->list, list); ++ } ++ return 0; ++ ++extra: ++ /* free extras */ ++ for (;;) { ++ struct list_head *next; ++ ++ next = req->list.next; ++ list_del(&req->list); ++ usb_ep_free_request(ep, req); ++ ++ if (next == list) ++ break; ++ ++ req = container_of(next, struct usb_request, list); ++ } ++ return 0; ++} ++ ++static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n) ++{ ++ int status; ++ ++ spin_lock(&dev->req_lock); ++ status = prealloc(&dev->tx_reqs, link->in_ep, n); ++ if (status < 0) ++ goto fail; ++ status = prealloc(&dev->rx_reqs, link->out_ep, n); ++ if (status < 0) ++ goto fail; ++ goto done; ++fail: ++ DBG(dev, "can't alloc requests\n"); ++done: ++ spin_unlock(&dev->req_lock); ++ return status; ++} ++ ++static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) ++{ ++ struct usb_request *req; ++ unsigned long flags; ++ ++ /* fill unused rxq slots with some skb */ ++ spin_lock_irqsave(&dev->req_lock, flags); ++ while (!list_empty(&dev->rx_reqs)) { ++ req = container_of(dev->rx_reqs.next, ++ struct usb_request, list); ++ list_del_init(&req->list); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ ++ if (rx_submit(dev, req, gfp_flags) < 0) { ++ defer_kevent(dev, WORK_RX_MEMORY); ++ return; ++ } ++ ++ spin_lock_irqsave(&dev->req_lock, flags); ++ } ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++} ++ ++static void eth_work(struct work_struct *work) ++{ ++ struct eth_dev *dev = container_of(work, struct eth_dev, work); ++ ++ if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) { ++ if (netif_running(dev->net)) ++ rx_fill(dev, GFP_KERNEL); ++ } ++ ++ if (dev->todo) ++ DBG(dev, "work done, flags = 0x%lx\n", dev->todo); ++} ++ ++static void tx_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct sk_buff *skb = req->context; ++ struct eth_dev *dev = ep->driver_data; ++ ++ switch (req->status) { ++ default: ++ dev->net->stats.tx_errors++; ++ VDBG(dev, "tx err %d\n", req->status); ++ /* FALLTHROUGH */ ++ case -ECONNRESET: /* unlink */ ++ case -ESHUTDOWN: /* disconnect etc */ ++ break; ++ case 0: ++ dev->net->stats.tx_bytes += skb->len; ++ } ++ dev->net->stats.tx_packets++; ++ ++ spin_lock(&dev->req_lock); ++ list_add(&req->list, &dev->tx_reqs); ++ spin_unlock(&dev->req_lock); ++ dev_kfree_skb_any(skb); ++ ++ atomic_dec(&dev->tx_qlen); ++ if (netif_carrier_ok(dev->net)) ++ netif_wake_queue(dev->net); ++} ++ ++static inline int is_promisc(u16 cdc_filter) ++{ ++ return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; ++} ++ ++static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ int length = skb->len; ++ int retval; ++ struct usb_request *req = NULL; ++ unsigned long flags; ++ struct usb_ep *in; ++ u16 cdc_filter; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) { ++ in = dev->port_usb->in_ep; ++ cdc_filter = dev->port_usb->cdc_filter; ++ } else { ++ in = NULL; ++ cdc_filter = 0; ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ if (!in) { ++ dev_kfree_skb_any(skb); ++ return 0; ++ } ++ ++ /* apply outgoing CDC or RNDIS filters */ ++ if (!is_promisc(cdc_filter)) { ++ u8 *dest = skb->data; ++ ++ if (is_multicast_ether_addr(dest)) { ++ u16 type; ++ ++ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host ++ * SET_ETHERNET_MULTICAST_FILTERS requests ++ */ ++ if (is_broadcast_ether_addr(dest)) ++ type = USB_CDC_PACKET_TYPE_BROADCAST; ++ else ++ type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; ++ if (!(cdc_filter & type)) { ++ dev_kfree_skb_any(skb); ++ return 0; ++ } ++ } ++ /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ ++ } ++ ++ spin_lock_irqsave(&dev->req_lock, flags); ++ /* ++ * this freelist can be empty if an interrupt triggered disconnect() ++ * and reconfigured the gadget (shutting down this queue) after the ++ * network stack decided to xmit but before we got the spinlock. ++ */ ++ if (list_empty(&dev->tx_reqs)) { ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ return 1; ++ } ++ ++ req = container_of(dev->tx_reqs.next, struct usb_request, list); ++ list_del(&req->list); ++ ++ /* temporarily stop TX queue when the freelist empties */ ++ if (list_empty(&dev->tx_reqs)) ++ netif_stop_queue(net); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ ++ /* no buffer copies needed, unless the network stack did it ++ * or the hardware can't use skb buffers. ++ * or there's not enough space for extra headers we need ++ */ ++ if (dev->wrap) { ++ struct sk_buff *skb_new; ++ ++ skb_new = dev->wrap(skb); ++ if (!skb_new) ++ goto drop; ++ ++ dev_kfree_skb_any(skb); ++ skb = skb_new; ++ length = skb->len; ++ } ++ req->buf = skb->data; ++ req->context = skb; ++ req->complete = tx_complete; ++ ++ /* use zlp framing on tx for strict CDC-Ether conformance, ++ * though any robust network rx path ignores extra padding. ++ * and some hardware doesn't like to write zlps. ++ */ ++ req->zero = 1; ++ if (!dev->zlp && (length % in->maxpacket) == 0) ++ length++; ++ ++ req->length = length; ++ ++ /* throttle highspeed IRQ rate back slightly */ ++ if (gadget_is_dualspeed(dev->gadget)) ++ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) ++ ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) ++ : 0; ++ ++ retval = usb_ep_queue(in, req, GFP_ATOMIC); ++ switch (retval) { ++ default: ++ DBG(dev, "tx queue err %d\n", retval); ++ break; ++ case 0: ++ net->trans_start = jiffies; ++ atomic_inc(&dev->tx_qlen); ++ } ++ ++ if (retval) { ++drop: ++ dev->net->stats.tx_dropped++; ++ dev_kfree_skb_any(skb); ++ spin_lock_irqsave(&dev->req_lock, flags); ++ if (list_empty(&dev->tx_reqs)) ++ netif_start_queue(net); ++ list_add(&req->list, &dev->tx_reqs); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) ++{ ++ DBG(dev, "%s\n", __func__); ++ ++ /* fill the rx queue */ ++ rx_fill(dev, gfp_flags); ++ ++ /* and open the tx floodgates */ ++ atomic_set(&dev->tx_qlen, 0); ++ netif_wake_queue(dev->net); ++} ++ ++static int eth_open(struct net_device *net) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ struct gether *link; ++ ++ DBG(dev, "%s\n", __func__); ++ if (netif_carrier_ok(dev->net)) ++ eth_start(dev, GFP_KERNEL); ++ ++ spin_lock_irq(&dev->lock); ++ link = dev->port_usb; ++ if (link && link->open) ++ link->open(link); ++ spin_unlock_irq(&dev->lock); ++ ++ return 0; ++} ++ ++static int eth_stop(struct net_device *net) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ unsigned long flags; ++ ++ VDBG(dev, "%s\n", __func__); ++ netif_stop_queue(net); ++ ++ DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", ++ dev->net->stats.rx_packets, dev->net->stats.tx_packets, ++ dev->net->stats.rx_errors, dev->net->stats.tx_errors ++ ); ++ ++ /* ensure there are no more active requests */ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) { ++ struct gether *link = dev->port_usb; ++ ++ if (link->close) ++ link->close(link); ++ ++ /* NOTE: we have no abort-queue primitive we could use ++ * to cancel all pending I/O. Instead, we disable then ++ * reenable the endpoints ... this idiom may leave toggle ++ * wrong, but that's a self-correcting error. ++ * ++ * REVISIT: we *COULD* just let the transfers complete at ++ * their own pace; the network stack can handle old packets. ++ * For the moment we leave this here, since it works. ++ */ ++ usb_ep_disable(link->in_ep); ++ usb_ep_disable(link->out_ep); ++ if (netif_carrier_ok(net)) { ++ DBG(dev, "host still using in/out endpoints\n"); ++ usb_ep_enable(link->in_ep, link->in); ++ usb_ep_enable(link->out_ep, link->out); ++ } ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ ++static char *dev_addr; ++module_param(dev_addr, charp, S_IRUGO); ++MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); ++ ++/* this address is invisible to ifconfig */ ++static char *host_addr; ++module_param(host_addr, charp, S_IRUGO); ++MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); ++ ++ ++static u8 __init nibble(unsigned char c) ++{ ++ if (isdigit(c)) ++ return c - '0'; ++ c = toupper(c); ++ if (isxdigit(c)) ++ return 10 + c - 'A'; ++ return 0; ++} ++ ++static int __init get_ether_addr(const char *str, u8 *dev_addr) ++{ ++ if (str) { ++ unsigned i; ++ ++ for (i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if ((*str == '.') || (*str == ':')) ++ str++; ++ num = nibble(*str++) << 4; ++ num |= (nibble(*str++)); ++ dev_addr [i] = num; ++ } ++ if (is_valid_ether_addr(dev_addr)) ++ return 0; ++ } ++ random_ether_addr(dev_addr); ++ return 1; ++} ++ ++static struct eth_dev *the_dev; ++ ++ ++/** ++ * gether_setup - initialize one ethernet-over-usb link ++ * @g: gadget to associated with these links ++ * @ethaddr: NULL, or a buffer in which the ethernet address of the ++ * host side of the link is recorded ++ * Context: may sleep ++ * ++ * This sets up the single network link that may be exported by a ++ * gadget driver using this framework. The link layer addresses are ++ * set up using module parameters. ++ * ++ * Returns negative errno, or zero on success ++ */ ++int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) ++{ ++ struct eth_dev *dev; ++ struct net_device *net; ++ int status; ++ ++ if (the_dev) ++ return -EBUSY; ++ ++ net = alloc_etherdev(sizeof *dev); ++ if (!net) ++ return -ENOMEM; ++ ++ dev = netdev_priv(net); ++ spin_lock_init(&dev->lock); ++ spin_lock_init(&dev->req_lock); ++ INIT_WORK(&dev->work, eth_work); ++ INIT_LIST_HEAD(&dev->tx_reqs); ++ INIT_LIST_HEAD(&dev->rx_reqs); ++ ++ /* network device setup */ ++ dev->net = net; ++ strcpy(net->name, "usb%d"); ++ ++ if (get_ether_addr(dev_addr, net->dev_addr)) ++ dev_warn(&g->dev, ++ "using random %s ethernet address\n", "self"); ++ if (get_ether_addr(host_addr, dev->host_mac)) ++ dev_warn(&g->dev, ++ "using random %s ethernet address\n", "host"); ++ ++ if (ethaddr) ++ memcpy(ethaddr, dev->host_mac, ETH_ALEN); ++ ++ net->change_mtu = eth_change_mtu; ++ net->hard_start_xmit = eth_start_xmit; ++ net->open = eth_open; ++ net->stop = eth_stop; ++ /* watchdog_timeo, tx_timeout ... */ ++ /* set_multicast_list */ ++ SET_ETHTOOL_OPS(net, &ops); ++ ++ /* two kinds of host-initiated state changes: ++ * - iff DATA transfer is active, carrier is "on" ++ * - tx queueing enabled if open *and* carrier is "on" ++ */ ++ netif_stop_queue(net); ++ netif_carrier_off(net); ++ ++ dev->gadget = g; ++ SET_NETDEV_DEV(net, &g->dev); ++ ++ status = register_netdev(net); ++ if (status < 0) { ++ dev_dbg(&g->dev, "register_netdev failed, %d\n", status); ++ free_netdev(net); ++ } else { ++ DECLARE_MAC_BUF(tmp); ++ ++ INFO(dev, "MAC %s\n", print_mac(tmp, net->dev_addr)); ++ INFO(dev, "HOST MAC %s\n", print_mac(tmp, dev->host_mac)); ++ ++ the_dev = dev; ++ } ++ ++ return status; ++} ++ ++/** ++ * gether_cleanup - remove Ethernet-over-USB device ++ * Context: may sleep ++ * ++ * This is called to free all resources allocated by @gether_setup(). ++ */ ++void gether_cleanup(void) ++{ ++ if (!the_dev) ++ return; ++ ++ unregister_netdev(the_dev->net); ++ free_netdev(the_dev->net); ++ ++ /* assuming we used keventd, it must quiesce too */ ++ flush_scheduled_work(); ++ ++ the_dev = NULL; ++} ++ ++ ++/** ++ * gether_connect - notify network layer that USB link is active ++ * @link: the USB link, set up with endpoints, descriptors matching ++ * current device speed, and any framing wrapper(s) set up. ++ * Context: irqs blocked ++ * ++ * This is called to activate endpoints and let the network layer know ++ * the connection is active ("carrier detect"). It may cause the I/O ++ * queues to open and start letting network packets flow, but will in ++ * any case activate the endpoints so that they respond properly to the ++ * USB host. ++ * ++ * Verify net_device pointer returned using IS_ERR(). If it doesn't ++ * indicate some error code (negative errno), ep->driver_data values ++ * have been overwritten. ++ */ ++struct net_device *gether_connect(struct gether *link) ++{ ++ struct eth_dev *dev = the_dev; ++ int result = 0; ++ ++ if (!dev) ++ return ERR_PTR(-EINVAL); ++ ++ link->in_ep->driver_data = dev; ++ result = usb_ep_enable(link->in_ep, link->in); ++ if (result != 0) { ++ DBG(dev, "enable %s --> %d\n", ++ link->in_ep->name, result); ++ goto fail0; ++ } ++ ++ link->out_ep->driver_data = dev; ++ result = usb_ep_enable(link->out_ep, link->out); ++ if (result != 0) { ++ DBG(dev, "enable %s --> %d\n", ++ link->out_ep->name, result); ++ goto fail1; ++ } ++ ++ if (result == 0) ++ result = alloc_requests(dev, link, qlen(dev->gadget)); ++ ++ if (result == 0) { ++ dev->zlp = link->is_zlp_ok; ++ DBG(dev, "qlen %d\n", qlen(dev->gadget)); ++ ++ dev->header_len = link->header_len; ++ dev->unwrap = link->unwrap; ++ dev->wrap = link->wrap; ++ ++ spin_lock(&dev->lock); ++ dev->port_usb = link; ++ link->ioport = dev; ++ spin_unlock(&dev->lock); ++ ++ netif_carrier_on(dev->net); ++ if (netif_running(dev->net)) ++ eth_start(dev, GFP_ATOMIC); ++ ++ /* on error, disable any endpoints */ ++ } else { ++ (void) usb_ep_disable(link->out_ep); ++fail1: ++ (void) usb_ep_disable(link->in_ep); ++ } ++fail0: ++ /* caller is responsible for cleanup on error */ ++ if (result < 0) ++ return ERR_PTR(result); ++ return dev->net; ++} ++ ++/** ++ * gether_disconnect - notify network layer that USB link is inactive ++ * @link: the USB link, on which gether_connect() was called ++ * Context: irqs blocked ++ * ++ * This is called to deactivate endpoints and let the network layer know ++ * the connection went inactive ("no carrier"). ++ * ++ * On return, the state is as if gether_connect() had never been called. ++ * The endpoints are inactive, and accordingly without active USB I/O. ++ * Pointers to endpoint descriptors and endpoint private data are nulled. ++ */ ++void gether_disconnect(struct gether *link) ++{ ++ struct eth_dev *dev = link->ioport; ++ struct usb_request *req; ++ ++ WARN_ON(!dev); ++ if (!dev) ++ return; ++ ++ DBG(dev, "%s\n", __func__); ++ ++ netif_stop_queue(dev->net); ++ netif_carrier_off(dev->net); ++ ++ /* disable endpoints, forcing (synchronous) completion ++ * of all pending i/o. then free the request objects ++ * and forget about the endpoints. ++ */ ++ usb_ep_disable(link->in_ep); ++ spin_lock(&dev->req_lock); ++ while (!list_empty(&dev->tx_reqs)) { ++ req = container_of(dev->tx_reqs.next, ++ struct usb_request, list); ++ list_del(&req->list); ++ ++ spin_unlock(&dev->req_lock); ++ usb_ep_free_request(link->in_ep, req); ++ spin_lock(&dev->req_lock); ++ } ++ spin_unlock(&dev->req_lock); ++ link->in_ep->driver_data = NULL; ++ link->in = NULL; ++ ++ usb_ep_disable(link->out_ep); ++ spin_lock(&dev->req_lock); ++ while (!list_empty(&dev->rx_reqs)) { ++ req = container_of(dev->rx_reqs.next, ++ struct usb_request, list); ++ list_del(&req->list); ++ ++ spin_unlock(&dev->req_lock); ++ usb_ep_free_request(link->out_ep, req); ++ spin_lock(&dev->req_lock); ++ } ++ spin_unlock(&dev->req_lock); ++ link->out_ep->driver_data = NULL; ++ link->out = NULL; ++ ++ /* finish forgetting about this USB link episode */ ++ dev->header_len = 0; ++ dev->unwrap = NULL; ++ dev->wrap = NULL; ++ ++ spin_lock(&dev->lock); ++ dev->port_usb = NULL; ++ link->ioport = NULL; ++ spin_unlock(&dev->lock); ++} +diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h +new file mode 100644 +index 0000000..0d1f7ae +--- /dev/null ++++ b/drivers/usb/gadget/u_ether.h +@@ -0,0 +1,127 @@ ++/* ++ * u_ether.h -- interface to USB gadget "ethernet link" utilities ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __U_ETHER_H ++#define __U_ETHER_H ++ ++#include <linux/err.h> ++#include <linux/if_ether.h> ++#include <linux/usb/composite.h> ++#include <linux/usb/cdc.h> ++ ++#include "gadget_chips.h" ++ ++ ++/* ++ * This represents the USB side of an "ethernet" link, managed by a USB ++ * function which provides control and (maybe) framing. Two functions ++ * in different configurations could share the same ethernet link/netdev, ++ * using different host interaction models. ++ * ++ * There is a current limitation that only one instance of this link may ++ * be present in any given configuration. When that's a problem, network ++ * layer facilities can be used to package multiple logical links on this ++ * single "physical" one. ++ */ ++struct gether { ++ struct usb_function func; ++ ++ /* updated by gether_{connect,disconnect} */ ++ struct eth_dev *ioport; ++ ++ /* endpoints handle full and/or high speeds */ ++ struct usb_ep *in_ep; ++ struct usb_ep *out_ep; ++ ++ /* descriptors match device speed at gether_connect() time */ ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ ++ bool is_zlp_ok; ++ ++ u16 cdc_filter; ++ ++ /* hooks for added framing, as needed for RNDIS and EEM. ++ * we currently don't support multiple frames per SKB. ++ */ ++ u32 header_len; ++ struct sk_buff *(*wrap)(struct sk_buff *skb); ++ int (*unwrap)(struct sk_buff *skb); ++ ++ /* called on network open/close */ ++ void (*open)(struct gether *); ++ void (*close)(struct gether *); ++}; ++ ++#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ ++ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ ++ |USB_CDC_PACKET_TYPE_PROMISCUOUS \ ++ |USB_CDC_PACKET_TYPE_DIRECTED) ++ ++ ++/* netdev setup/teardown as directed by the gadget driver */ ++int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]); ++void gether_cleanup(void); ++ ++/* connect/disconnect is handled by individual functions */ ++struct net_device *gether_connect(struct gether *); ++void gether_disconnect(struct gether *); ++ ++/* Some controllers can't support CDC Ethernet (ECM) ... */ ++static inline bool can_support_ecm(struct usb_gadget *gadget) ++{ ++ if (!gadget_supports_altsettings(gadget)) ++ return false; ++ ++ /* SA1100 can do ECM, *without* status endpoint ... but we'll ++ * only use it in non-ECM mode for backwards compatibility ++ * (and since we currently require a status endpoint) ++ */ ++ if (gadget_is_sa1100(gadget)) ++ return false; ++ ++ /* Everything else is *presumably* fine ... but this is a bit ++ * chancy, so be **CERTAIN** there are no hardware issues with ++ * your controller. Add it above if it can't handle CDC. ++ */ ++ return true; ++} ++ ++/* each configuration may bind one instance of an ethernet link */ ++int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); ++int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); ++ ++#ifdef CONFIG_USB_ETH_RNDIS ++ ++int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); ++ ++#else ++ ++static inline int ++rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ return 0; ++} ++ ++#endif ++ ++#endif /* __U_ETHER_H */ +diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c +new file mode 100644 +index 0000000..53d5928 +--- /dev/null ++++ b/drivers/usb/gadget/u_serial.c +@@ -0,0 +1,1330 @@ ++/* ++ * u_serial.c - utilities for USB gadget "serial port"/TTY support ++ * ++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) ++ * Copyright (C) 2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation ++ * ++ * This code also borrows from usbserial.c, which is ++ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) ++ * Copyright (C) 2000 Peter Berger (pberger@brimson.com) ++ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) ++ * ++ * This software is distributed under the terms of the GNU General ++ * Public License ("GPL") as published by the Free Software Foundation, ++ * either version 2 of that License or (at your option) any later version. ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/interrupt.h> ++#include <linux/device.h> ++#include <linux/delay.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++ ++#include "u_serial.h" ++ ++ ++/* ++ * This component encapsulates the TTY layer glue needed to provide basic ++ * "serial port" functionality through the USB gadget stack. Each such ++ * port is exposed through a /dev/ttyGS* node. ++ * ++ * After initialization (gserial_setup), these TTY port devices stay ++ * available until they are removed (gserial_cleanup). Each one may be ++ * connected to a USB function (gserial_connect), or disconnected (with ++ * gserial_disconnect) when the USB host issues a config change event. ++ * Data can only flow when the port is connected to the host. ++ * ++ * A given TTY port can be made available in multiple configurations. ++ * For example, each one might expose a ttyGS0 node which provides a ++ * login application. In one case that might use CDC ACM interface 0, ++ * while another configuration might use interface 3 for that. The ++ * work to handle that (including descriptor management) is not part ++ * of this component. ++ * ++ * Configurations may expose more than one TTY port. For example, if ++ * ttyGS0 provides login service, then ttyGS1 might provide dialer access ++ * for a telephone or fax link. And ttyGS2 might be something that just ++ * needs a simple byte stream interface for some messaging protocol that ++ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned. ++ */ ++ ++#define PREFIX "ttyGS" ++ ++/* ++ * gserial is the lifecycle interface, used by USB functions ++ * gs_port is the I/O nexus, used by the tty driver ++ * tty_struct links to the tty/filesystem framework ++ * ++ * gserial <---> gs_port ... links will be null when the USB link is ++ * inactive; managed by gserial_{connect,disconnect}(). each gserial ++ * instance can wrap its own USB control protocol. ++ * gserial->ioport == usb_ep->driver_data ... gs_port ++ * gs_port->port_usb ... gserial ++ * ++ * gs_port <---> tty_struct ... links will be null when the TTY file ++ * isn't opened; managed by gs_open()/gs_close() ++ * gserial->port_tty ... tty_struct ++ * tty_struct->driver_data ... gserial ++ */ ++ ++/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the ++ * next layer of buffering. For TX that's a circular buffer; for RX ++ * consider it a NOP. A third layer is provided by the TTY code. ++ */ ++#define QUEUE_SIZE 16 ++#define WRITE_BUF_SIZE 8192 /* TX only */ ++ ++/* circular buffer */ ++struct gs_buf { ++ unsigned buf_size; ++ char *buf_buf; ++ char *buf_get; ++ char *buf_put; ++}; ++ ++/* ++ * The port structure holds info for each port, one for each minor number ++ * (and thus for each /dev/ node). ++ */ ++struct gs_port { ++ spinlock_t port_lock; /* guard port_* access */ ++ ++ struct gserial *port_usb; ++ struct tty_struct *port_tty; ++ ++ unsigned open_count; ++ bool openclose; /* open/close in progress */ ++ u8 port_num; ++ ++ wait_queue_head_t close_wait; /* wait for last close */ ++ ++ struct list_head read_pool; ++ struct list_head read_queue; ++ unsigned n_read; ++ struct tasklet_struct push; ++ ++ struct list_head write_pool; ++ struct gs_buf port_write_buf; ++ wait_queue_head_t drain_wait; /* wait while writes drain */ ++ ++ /* REVISIT this state ... */ ++ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ ++}; ++ ++/* increase N_PORTS if you need more */ ++#define N_PORTS 4 ++static struct portmaster { ++ struct mutex lock; /* protect open/close */ ++ struct gs_port *port; ++} ports[N_PORTS]; ++static unsigned n_ports; ++ ++#define GS_CLOSE_TIMEOUT 15 /* seconds */ ++ ++ ++ ++#ifdef VERBOSE_DEBUG ++#define pr_vdebug(fmt, arg...) \ ++ pr_debug(fmt, ##arg) ++#else ++#define pr_vdebug(fmt, arg...) \ ++ ({ if (0) pr_debug(fmt, ##arg); }) ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Circular Buffer */ ++ ++/* ++ * gs_buf_alloc ++ * ++ * Allocate a circular buffer and all associated memory. ++ */ ++static int gs_buf_alloc(struct gs_buf *gb, unsigned size) ++{ ++ gb->buf_buf = kmalloc(size, GFP_KERNEL); ++ if (gb->buf_buf == NULL) ++ return -ENOMEM; ++ ++ gb->buf_size = size; ++ gb->buf_put = gb->buf_buf; ++ gb->buf_get = gb->buf_buf; ++ ++ return 0; ++} ++ ++/* ++ * gs_buf_free ++ * ++ * Free the buffer and all associated memory. ++ */ ++static void gs_buf_free(struct gs_buf *gb) ++{ ++ kfree(gb->buf_buf); ++ gb->buf_buf = NULL; ++} ++ ++/* ++ * gs_buf_clear ++ * ++ * Clear out all data in the circular buffer. ++ */ ++static void gs_buf_clear(struct gs_buf *gb) ++{ ++ gb->buf_get = gb->buf_put; ++ /* equivalent to a get of all data available */ ++} ++ ++/* ++ * gs_buf_data_avail ++ * ++ * Return the number of bytes of data written into the circular ++ * buffer. ++ */ ++static unsigned gs_buf_data_avail(struct gs_buf *gb) ++{ ++ return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; ++} ++ ++/* ++ * gs_buf_space_avail ++ * ++ * Return the number of bytes of space available in the circular ++ * buffer. ++ */ ++static unsigned gs_buf_space_avail(struct gs_buf *gb) ++{ ++ return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; ++} ++ ++/* ++ * gs_buf_put ++ * ++ * Copy data data from a user buffer and put it into the circular buffer. ++ * Restrict to the amount of space available. ++ * ++ * Return the number of bytes copied. ++ */ ++static unsigned ++gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count) ++{ ++ unsigned len; ++ ++ len = gs_buf_space_avail(gb); ++ if (count > len) ++ count = len; ++ ++ if (count == 0) ++ return 0; ++ ++ len = gb->buf_buf + gb->buf_size - gb->buf_put; ++ if (count > len) { ++ memcpy(gb->buf_put, buf, len); ++ memcpy(gb->buf_buf, buf+len, count - len); ++ gb->buf_put = gb->buf_buf + count - len; ++ } else { ++ memcpy(gb->buf_put, buf, count); ++ if (count < len) ++ gb->buf_put += count; ++ else /* count == len */ ++ gb->buf_put = gb->buf_buf; ++ } ++ ++ return count; ++} ++ ++/* ++ * gs_buf_get ++ * ++ * Get data from the circular buffer and copy to the given buffer. ++ * Restrict to the amount of data available. ++ * ++ * Return the number of bytes copied. ++ */ ++static unsigned ++gs_buf_get(struct gs_buf *gb, char *buf, unsigned count) ++{ ++ unsigned len; ++ ++ len = gs_buf_data_avail(gb); ++ if (count > len) ++ count = len; ++ ++ if (count == 0) ++ return 0; ++ ++ len = gb->buf_buf + gb->buf_size - gb->buf_get; ++ if (count > len) { ++ memcpy(buf, gb->buf_get, len); ++ memcpy(buf+len, gb->buf_buf, count - len); ++ gb->buf_get = gb->buf_buf + count - len; ++ } else { ++ memcpy(buf, gb->buf_get, count); ++ if (count < len) ++ gb->buf_get += count; ++ else /* count == len */ ++ gb->buf_get = gb->buf_buf; ++ } ++ ++ return count; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* I/O glue between TTY (upper) and USB function (lower) driver layers */ ++ ++/* ++ * gs_alloc_req ++ * ++ * Allocate a usb_request and its buffer. Returns a pointer to the ++ * usb_request or NULL if there is an error. ++ */ ++struct usb_request * ++gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) ++{ ++ struct usb_request *req; ++ ++ req = usb_ep_alloc_request(ep, kmalloc_flags); ++ ++ if (req != NULL) { ++ req->length = len; ++ req->buf = kmalloc(len, kmalloc_flags); ++ if (req->buf == NULL) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ } ++ ++ return req; ++} ++ ++/* ++ * gs_free_req ++ * ++ * Free a usb_request and its buffer. ++ */ ++void gs_free_req(struct usb_ep *ep, struct usb_request *req) ++{ ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++} ++ ++/* ++ * gs_send_packet ++ * ++ * If there is data to send, a packet is built in the given ++ * buffer and the size is returned. If there is no data to ++ * send, 0 is returned. ++ * ++ * Called with port_lock held. ++ */ ++static unsigned ++gs_send_packet(struct gs_port *port, char *packet, unsigned size) ++{ ++ unsigned len; ++ ++ len = gs_buf_data_avail(&port->port_write_buf); ++ if (len < size) ++ size = len; ++ if (size != 0) ++ size = gs_buf_get(&port->port_write_buf, packet, size); ++ return size; ++} ++ ++/* ++ * gs_start_tx ++ * ++ * This function finds available write requests, calls ++ * gs_send_packet to fill these packets with data, and ++ * continues until either there are no more write requests ++ * available or no more data to send. This function is ++ * run whenever data arrives or write requests are available. ++ * ++ * Context: caller owns port_lock; port_usb is non-null. ++ */ ++static int gs_start_tx(struct gs_port *port) ++/* ++__releases(&port->port_lock) ++__acquires(&port->port_lock) ++*/ ++{ ++ struct list_head *pool = &port->write_pool; ++ struct usb_ep *in = port->port_usb->in; ++ int status = 0; ++ bool do_tty_wake = false; ++ ++ while (!list_empty(pool)) { ++ struct usb_request *req; ++ int len; ++ ++ req = list_entry(pool->next, struct usb_request, list); ++ len = gs_send_packet(port, req->buf, in->maxpacket); ++ if (len == 0) { ++ wake_up_interruptible(&port->drain_wait); ++ break; ++ } ++ do_tty_wake = true; ++ ++ req->length = len; ++ list_del(&req->list); ++ ++ pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", ++ port->port_num, len, *((u8 *)req->buf), ++ *((u8 *)req->buf+1), *((u8 *)req->buf+2)); ++ ++ /* Drop lock while we call out of driver; completions ++ * could be issued while we do so. Disconnection may ++ * happen too; maybe immediately before we queue this! ++ * ++ * NOTE that we may keep sending data for a while after ++ * the TTY closed (dev->ioport->port_tty is NULL). ++ */ ++ spin_unlock(&port->port_lock); ++ status = usb_ep_queue(in, req, GFP_ATOMIC); ++ spin_lock(&port->port_lock); ++ ++ if (status) { ++ pr_debug("%s: %s %s err %d\n", ++ __func__, "queue", in->name, status); ++ list_add(&req->list, pool); ++ break; ++ } ++ ++ /* abort immediately after disconnect */ ++ if (!port->port_usb) ++ break; ++ } ++ ++ if (do_tty_wake && port->port_tty) ++ tty_wakeup(port->port_tty); ++ return status; ++} ++ ++/* ++ * Context: caller owns port_lock, and port_usb is set ++ */ ++static unsigned gs_start_rx(struct gs_port *port) ++/* ++__releases(&port->port_lock) ++__acquires(&port->port_lock) ++*/ ++{ ++ struct list_head *pool = &port->read_pool; ++ struct usb_ep *out = port->port_usb->out; ++ unsigned started = 0; ++ ++ while (!list_empty(pool)) { ++ struct usb_request *req; ++ int status; ++ struct tty_struct *tty; ++ ++ /* no more rx if closed */ ++ tty = port->port_tty; ++ if (!tty) ++ break; ++ ++ req = list_entry(pool->next, struct usb_request, list); ++ list_del(&req->list); ++ req->length = out->maxpacket; ++ ++ /* drop lock while we call out; the controller driver ++ * may need to call us back (e.g. for disconnect) ++ */ ++ spin_unlock(&port->port_lock); ++ status = usb_ep_queue(out, req, GFP_ATOMIC); ++ spin_lock(&port->port_lock); ++ ++ if (status) { ++ pr_debug("%s: %s %s err %d\n", ++ __func__, "queue", out->name, status); ++ list_add(&req->list, pool); ++ break; ++ } ++ started++; ++ ++ /* abort immediately after disconnect */ ++ if (!port->port_usb) ++ break; ++ } ++ return started; ++} ++ ++/* ++ * RX tasklet takes data out of the RX queue and hands it up to the TTY ++ * layer until it refuses to take any more data (or is throttled back). ++ * Then it issues reads for any further data. ++ * ++ * If the RX queue becomes full enough that no usb_request is queued, ++ * the OUT endpoint may begin NAKing as soon as its FIFO fills up. ++ * So QUEUE_SIZE packets plus however many the FIFO holds (usually two) ++ * can be buffered before the TTY layer's buffers (currently 64 KB). ++ */ ++static void gs_rx_push(unsigned long _port) ++{ ++ struct gs_port *port = (void *)_port; ++ struct tty_struct *tty; ++ struct list_head *queue = &port->read_queue; ++ bool disconnect = false; ++ bool do_push = false; ++ ++ /* hand any queued data to the tty */ ++ spin_lock_irq(&port->port_lock); ++ tty = port->port_tty; ++ while (!list_empty(queue)) { ++ struct usb_request *req; ++ ++ req = list_first_entry(queue, struct usb_request, list); ++ ++ /* discard data if tty was closed */ ++ if (!tty) ++ goto recycle; ++ ++ /* leave data queued if tty was rx throttled */ ++ if (test_bit(TTY_THROTTLED, &tty->flags)) ++ break; ++ ++ switch (req->status) { ++ case -ESHUTDOWN: ++ disconnect = true; ++ pr_vdebug(PREFIX "%d: shutdown\n", port->port_num); ++ break; ++ ++ default: ++ /* presumably a transient fault */ ++ pr_warning(PREFIX "%d: unexpected RX status %d\n", ++ port->port_num, req->status); ++ /* FALLTHROUGH */ ++ case 0: ++ /* normal completion */ ++ break; ++ } ++ ++ /* push data to (open) tty */ ++ if (req->actual) { ++ char *packet = req->buf; ++ unsigned size = req->actual; ++ unsigned n; ++ int count; ++ ++ /* we may have pushed part of this packet already... */ ++ n = port->n_read; ++ if (n) { ++ packet += n; ++ size -= n; ++ } ++ ++ count = tty_insert_flip_string(tty, packet, size); ++ if (count) ++ do_push = true; ++ if (count != size) { ++ /* stop pushing; TTY layer can't handle more */ ++ port->n_read += count; ++ pr_vdebug(PREFIX "%d: rx block %d/%d\n", ++ port->port_num, ++ count, req->actual); ++ break; ++ } ++ port->n_read = 0; ++ } ++recycle: ++ list_move(&req->list, &port->read_pool); ++ } ++ ++ /* Push from tty to ldisc; this is immediate with low_latency, and ++ * may trigger callbacks to this driver ... so drop the spinlock. ++ */ ++ if (tty && do_push) { ++ spin_unlock_irq(&port->port_lock); ++ tty_flip_buffer_push(tty); ++ wake_up_interruptible(&tty->read_wait); ++ spin_lock_irq(&port->port_lock); ++ ++ /* tty may have been closed */ ++ tty = port->port_tty; ++ } ++ ++ ++ /* We want our data queue to become empty ASAP, keeping data ++ * in the tty and ldisc (not here). If we couldn't push any ++ * this time around, there may be trouble unless there's an ++ * implicit tty_unthrottle() call on its way... ++ * ++ * REVISIT we should probably add a timer to keep the tasklet ++ * from starving ... but it's not clear that case ever happens. ++ */ ++ if (!list_empty(queue) && tty) { ++ if (!test_bit(TTY_THROTTLED, &tty->flags)) { ++ if (do_push) ++ tasklet_schedule(&port->push); ++ else ++ pr_warning(PREFIX "%d: RX not scheduled?\n", ++ port->port_num); ++ } ++ } ++ ++ /* If we're still connected, refill the USB RX queue. */ ++ if (!disconnect && port->port_usb) ++ gs_start_rx(port); ++ ++ spin_unlock_irq(&port->port_lock); ++} ++ ++static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct gs_port *port = ep->driver_data; ++ ++ /* Queue all received data until the tty layer is ready for it. */ ++ spin_lock(&port->port_lock); ++ list_add_tail(&req->list, &port->read_queue); ++ tasklet_schedule(&port->push); ++ spin_unlock(&port->port_lock); ++} ++ ++static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct gs_port *port = ep->driver_data; ++ ++ spin_lock(&port->port_lock); ++ list_add(&req->list, &port->write_pool); ++ ++ switch (req->status) { ++ default: ++ /* presumably a transient fault */ ++ pr_warning("%s: unexpected %s status %d\n", ++ __func__, ep->name, req->status); ++ /* FALL THROUGH */ ++ case 0: ++ /* normal completion */ ++ gs_start_tx(port); ++ break; ++ ++ case -ESHUTDOWN: ++ /* disconnect */ ++ pr_vdebug("%s: %s shutdown\n", __func__, ep->name); ++ break; ++ } ++ ++ spin_unlock(&port->port_lock); ++} ++ ++static void gs_free_requests(struct usb_ep *ep, struct list_head *head) ++{ ++ struct usb_request *req; ++ ++ while (!list_empty(head)) { ++ req = list_entry(head->next, struct usb_request, list); ++ list_del(&req->list); ++ gs_free_req(ep, req); ++ } ++} ++ ++static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, ++ void (*fn)(struct usb_ep *, struct usb_request *)) ++{ ++ int i; ++ struct usb_request *req; ++ ++ /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't ++ * do quite that many this time, don't fail ... we just won't ++ * be as speedy as we might otherwise be. ++ */ ++ for (i = 0; i < QUEUE_SIZE; i++) { ++ req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); ++ if (!req) ++ return list_empty(head) ? -ENOMEM : 0; ++ req->complete = fn; ++ list_add_tail(&req->list, head); ++ } ++ return 0; ++} ++ ++/** ++ * gs_start_io - start USB I/O streams ++ * @dev: encapsulates endpoints to use ++ * Context: holding port_lock; port_tty and port_usb are non-null ++ * ++ * We only start I/O when something is connected to both sides of ++ * this port. If nothing is listening on the host side, we may ++ * be pointlessly filling up our TX buffers and FIFO. ++ */ ++static int gs_start_io(struct gs_port *port) ++{ ++ struct list_head *head = &port->read_pool; ++ struct usb_ep *ep = port->port_usb->out; ++ int status; ++ unsigned started; ++ ++ /* Allocate RX and TX I/O buffers. We can't easily do this much ++ * earlier (with GFP_KERNEL) because the requests are coupled to ++ * endpoints, as are the packet sizes we'll be using. Different ++ * configurations may use different endpoints with a given port; ++ * and high speed vs full speed changes packet sizes too. ++ */ ++ status = gs_alloc_requests(ep, head, gs_read_complete); ++ if (status) ++ return status; ++ ++ status = gs_alloc_requests(port->port_usb->in, &port->write_pool, ++ gs_write_complete); ++ if (status) { ++ gs_free_requests(ep, head); ++ return status; ++ } ++ ++ /* queue read requests */ ++ port->n_read = 0; ++ started = gs_start_rx(port); ++ ++ /* unblock any pending writes into our circular buffer */ ++ if (started) { ++ tty_wakeup(port->port_tty); ++ } else { ++ gs_free_requests(ep, head); ++ gs_free_requests(port->port_usb->in, &port->write_pool); ++ status = -EIO; ++ } ++ ++ return status; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* TTY Driver */ ++ ++/* ++ * gs_open sets up the link between a gs_port and its associated TTY. ++ * That link is broken *only* by TTY close(), and all driver methods ++ * know that. ++ */ ++static int gs_open(struct tty_struct *tty, struct file *file) ++{ ++ int port_num = tty->index; ++ struct gs_port *port; ++ int status; ++ ++ if (port_num < 0 || port_num >= n_ports) ++ return -ENXIO; ++ ++ do { ++ mutex_lock(&ports[port_num].lock); ++ port = ports[port_num].port; ++ if (!port) ++ status = -ENODEV; ++ else { ++ spin_lock_irq(&port->port_lock); ++ ++ /* already open? Great. */ ++ if (port->open_count) { ++ status = 0; ++ port->open_count++; ++ ++ /* currently opening/closing? wait ... */ ++ } else if (port->openclose) { ++ status = -EBUSY; ++ ++ /* ... else we do the work */ ++ } else { ++ status = -EAGAIN; ++ port->openclose = true; ++ } ++ spin_unlock_irq(&port->port_lock); ++ } ++ mutex_unlock(&ports[port_num].lock); ++ ++ switch (status) { ++ default: ++ /* fully handled */ ++ return status; ++ case -EAGAIN: ++ /* must do the work */ ++ break; ++ case -EBUSY: ++ /* wait for EAGAIN task to finish */ ++ msleep(1); ++ /* REVISIT could have a waitchannel here, if ++ * concurrent open performance is important ++ */ ++ break; ++ } ++ } while (status != -EAGAIN); ++ ++ /* Do the "real open" */ ++ spin_lock_irq(&port->port_lock); ++ ++ /* allocate circular buffer on first open */ ++ if (port->port_write_buf.buf_buf == NULL) { ++ ++ spin_unlock_irq(&port->port_lock); ++ status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE); ++ spin_lock_irq(&port->port_lock); ++ ++ if (status) { ++ pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", ++ port->port_num, tty, file); ++ port->openclose = false; ++ goto exit_unlock_port; ++ } ++ } ++ ++ /* REVISIT if REMOVED (ports[].port NULL), abort the open ++ * to let rmmod work faster (but this way isn't wrong). ++ */ ++ ++ /* REVISIT maybe wait for "carrier detect" */ ++ ++ tty->driver_data = port; ++ port->port_tty = tty; ++ ++ port->open_count = 1; ++ port->openclose = false; ++ ++ /* low_latency means ldiscs work in tasklet context, without ++ * needing a workqueue schedule ... easier to keep up. ++ */ ++ tty->low_latency = 1; ++ ++ /* if connected, start the I/O stream */ ++ if (port->port_usb) { ++ struct gserial *gser = port->port_usb; ++ ++ pr_debug("gs_open: start ttyGS%d\n", port->port_num); ++ gs_start_io(port); ++ ++ if (gser->connect) ++ gser->connect(gser); ++ } ++ ++ pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); ++ ++ status = 0; ++ ++exit_unlock_port: ++ spin_unlock_irq(&port->port_lock); ++ return status; ++} ++ ++static int gs_writes_finished(struct gs_port *p) ++{ ++ int cond; ++ ++ /* return true on disconnect or empty buffer */ ++ spin_lock_irq(&p->port_lock); ++ cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf); ++ spin_unlock_irq(&p->port_lock); ++ ++ return cond; ++} ++ ++static void gs_close(struct tty_struct *tty, struct file *file) ++{ ++ struct gs_port *port = tty->driver_data; ++ struct gserial *gser; ++ ++ spin_lock_irq(&port->port_lock); ++ ++ if (port->open_count != 1) { ++ if (port->open_count == 0) ++ WARN_ON(1); ++ else ++ --port->open_count; ++ goto exit; ++ } ++ ++ pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file); ++ ++ /* mark port as closing but in use; we can drop port lock ++ * and sleep if necessary ++ */ ++ port->openclose = true; ++ port->open_count = 0; ++ ++ gser = port->port_usb; ++ if (gser && gser->disconnect) ++ gser->disconnect(gser); ++ ++ /* wait for circular write buffer to drain, disconnect, or at ++ * most GS_CLOSE_TIMEOUT seconds; then discard the rest ++ */ ++ if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) { ++ spin_unlock_irq(&port->port_lock); ++ wait_event_interruptible_timeout(port->drain_wait, ++ gs_writes_finished(port), ++ GS_CLOSE_TIMEOUT * HZ); ++ spin_lock_irq(&port->port_lock); ++ gser = port->port_usb; ++ } ++ ++ /* Iff we're disconnected, there can be no I/O in flight so it's ++ * ok to free the circular buffer; else just scrub it. And don't ++ * let the push tasklet fire again until we're re-opened. ++ */ ++ if (gser == NULL) ++ gs_buf_free(&port->port_write_buf); ++ else ++ gs_buf_clear(&port->port_write_buf); ++ ++ tty->driver_data = NULL; ++ port->port_tty = NULL; ++ ++ port->openclose = false; ++ ++ pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", ++ port->port_num, tty, file); ++ ++ wake_up_interruptible(&port->close_wait); ++exit: ++ spin_unlock_irq(&port->port_lock); ++} ++ ++static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) ++{ ++ struct gs_port *port = tty->driver_data; ++ unsigned long flags; ++ int status; ++ ++ pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n", ++ port->port_num, tty, count); ++ ++ spin_lock_irqsave(&port->port_lock, flags); ++ if (count) ++ count = gs_buf_put(&port->port_write_buf, buf, count); ++ /* treat count == 0 as flush_chars() */ ++ if (port->port_usb) ++ status = gs_start_tx(port); ++ spin_unlock_irqrestore(&port->port_lock, flags); ++ ++ return count; ++} ++ ++static int gs_put_char(struct tty_struct *tty, unsigned char ch) ++{ ++ struct gs_port *port = tty->driver_data; ++ unsigned long flags; ++ int status; ++ ++ pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", ++ port->port_num, tty, ch, __builtin_return_address(0)); ++ ++ spin_lock_irqsave(&port->port_lock, flags); ++ status = gs_buf_put(&port->port_write_buf, &ch, 1); ++ spin_unlock_irqrestore(&port->port_lock, flags); ++ ++ return status; ++} ++ ++static void gs_flush_chars(struct tty_struct *tty) ++{ ++ struct gs_port *port = tty->driver_data; ++ unsigned long flags; ++ ++ pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); ++ ++ spin_lock_irqsave(&port->port_lock, flags); ++ if (port->port_usb) ++ gs_start_tx(port); ++ spin_unlock_irqrestore(&port->port_lock, flags); ++} ++ ++static int gs_write_room(struct tty_struct *tty) ++{ ++ struct gs_port *port = tty->driver_data; ++ unsigned long flags; ++ int room = 0; ++ ++ spin_lock_irqsave(&port->port_lock, flags); ++ if (port->port_usb) ++ room = gs_buf_space_avail(&port->port_write_buf); ++ spin_unlock_irqrestore(&port->port_lock, flags); ++ ++ pr_vdebug("gs_write_room: (%d,%p) room=%d\n", ++ port->port_num, tty, room); ++ ++ return room; ++} ++ ++static int gs_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct gs_port *port = tty->driver_data; ++ unsigned long flags; ++ int chars = 0; ++ ++ spin_lock_irqsave(&port->port_lock, flags); ++ chars = gs_buf_data_avail(&port->port_write_buf); ++ spin_unlock_irqrestore(&port->port_lock, flags); ++ ++ pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n", ++ port->port_num, tty, chars); ++ ++ return chars; ++} ++ ++/* undo side effects of setting TTY_THROTTLED */ ++static void gs_unthrottle(struct tty_struct *tty) ++{ ++ struct gs_port *port = tty->driver_data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&port->port_lock, flags); ++ if (port->port_usb) { ++ /* Kickstart read queue processing. We don't do xon/xoff, ++ * rts/cts, or other handshaking with the host, but if the ++ * read queue backs up enough we'll be NAKing OUT packets. ++ */ ++ tasklet_schedule(&port->push); ++ pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num); ++ } ++ spin_unlock_irqrestore(&port->port_lock, flags); ++} ++ ++static int gs_break_ctl(struct tty_struct *tty, int duration) ++{ ++ struct gs_port *port = tty->driver_data; ++ int status = 0; ++ struct gserial *gser; ++ ++ pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n", ++ port->port_num, duration); ++ ++ spin_lock_irq(&port->port_lock); ++ gser = port->port_usb; ++ if (gser && gser->send_break) ++ status = gser->send_break(gser, duration); ++ spin_unlock_irq(&port->port_lock); ++ ++ return status; ++} ++ ++static const struct tty_operations gs_tty_ops = { ++ .open = gs_open, ++ .close = gs_close, ++ .write = gs_write, ++ .put_char = gs_put_char, ++ .flush_chars = gs_flush_chars, ++ .write_room = gs_write_room, ++ .chars_in_buffer = gs_chars_in_buffer, ++ .unthrottle = gs_unthrottle, ++ .break_ctl = gs_break_ctl, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct tty_driver *gs_tty_driver; ++ ++static int __init ++gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) ++{ ++ struct gs_port *port; ++ ++ port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); ++ if (port == NULL) ++ return -ENOMEM; ++ ++ spin_lock_init(&port->port_lock); ++ init_waitqueue_head(&port->close_wait); ++ init_waitqueue_head(&port->drain_wait); ++ ++ tasklet_init(&port->push, gs_rx_push, (unsigned long) port); ++ ++ INIT_LIST_HEAD(&port->read_pool); ++ INIT_LIST_HEAD(&port->read_queue); ++ INIT_LIST_HEAD(&port->write_pool); ++ ++ port->port_num = port_num; ++ port->port_line_coding = *coding; ++ ++ ports[port_num].port = port; ++ ++ return 0; ++} ++ ++/** ++ * gserial_setup - initialize TTY driver for one or more ports ++ * @g: gadget to associate with these ports ++ * @count: how many ports to support ++ * Context: may sleep ++ * ++ * The TTY stack needs to know in advance how many devices it should ++ * plan to manage. Use this call to set up the ports you will be ++ * exporting through USB. Later, connect them to functions based ++ * on what configuration is activated by the USB host; and disconnect ++ * them as appropriate. ++ * ++ * An example would be a two-configuration device in which both ++ * configurations expose port 0, but through different functions. ++ * One configuration could even expose port 1 while the other ++ * one doesn't. ++ * ++ * Returns negative errno or zero. ++ */ ++int __init gserial_setup(struct usb_gadget *g, unsigned count) ++{ ++ unsigned i; ++ struct usb_cdc_line_coding coding; ++ int status; ++ ++ if (count == 0 || count > N_PORTS) ++ return -EINVAL; ++ ++ gs_tty_driver = alloc_tty_driver(count); ++ if (!gs_tty_driver) ++ return -ENOMEM; ++ ++ gs_tty_driver->owner = THIS_MODULE; ++ gs_tty_driver->driver_name = "g_serial"; ++ gs_tty_driver->name = PREFIX; ++ /* uses dynamically assigned dev_t values */ ++ ++ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; ++ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ++ gs_tty_driver->init_termios = tty_std_termios; ++ ++ /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on ++ * MS-Windows. Otherwise, most of these flags shouldn't affect ++ * anything unless we were to actually hook up to a serial line. ++ */ ++ gs_tty_driver->init_termios.c_cflag = ++ B9600 | CS8 | CREAD | HUPCL | CLOCAL; ++ gs_tty_driver->init_termios.c_ispeed = 9600; ++ gs_tty_driver->init_termios.c_ospeed = 9600; ++ ++ coding.dwDTERate = __constant_cpu_to_le32(9600); ++ coding.bCharFormat = 8; ++ coding.bParityType = USB_CDC_NO_PARITY; ++ coding.bDataBits = USB_CDC_1_STOP_BITS; ++ ++ tty_set_operations(gs_tty_driver, &gs_tty_ops); ++ ++ /* make devices be openable */ ++ for (i = 0; i < count; i++) { ++ mutex_init(&ports[i].lock); ++ status = gs_port_alloc(i, &coding); ++ if (status) { ++ count = i; ++ goto fail; ++ } ++ } ++ n_ports = count; ++ ++ /* export the driver ... */ ++ status = tty_register_driver(gs_tty_driver); ++ if (status) { ++ put_tty_driver(gs_tty_driver); ++ pr_err("%s: cannot register, err %d\n", ++ __func__, status); ++ goto fail; ++ } ++ ++ /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ ++ for (i = 0; i < count; i++) { ++ struct device *tty_dev; ++ ++ tty_dev = tty_register_device(gs_tty_driver, i, &g->dev); ++ if (IS_ERR(tty_dev)) ++ pr_warning("%s: no classdev for port %d, err %ld\n", ++ __func__, i, PTR_ERR(tty_dev)); ++ } ++ ++ pr_debug("%s: registered %d ttyGS* device%s\n", __func__, ++ count, (count == 1) ? "" : "s"); ++ ++ return status; ++fail: ++ while (count--) ++ kfree(ports[count].port); ++ put_tty_driver(gs_tty_driver); ++ gs_tty_driver = NULL; ++ return status; ++} ++ ++static int gs_closed(struct gs_port *port) ++{ ++ int cond; ++ ++ spin_lock_irq(&port->port_lock); ++ cond = (port->open_count == 0) && !port->openclose; ++ spin_unlock_irq(&port->port_lock); ++ return cond; ++} ++ ++/** ++ * gserial_cleanup - remove TTY-over-USB driver and devices ++ * Context: may sleep ++ * ++ * This is called to free all resources allocated by @gserial_setup(). ++ * Accordingly, it may need to wait until some open /dev/ files have ++ * closed. ++ * ++ * The caller must have issued @gserial_disconnect() for any ports ++ * that had previously been connected, so that there is never any ++ * I/O pending when it's called. ++ */ ++void gserial_cleanup(void) ++{ ++ unsigned i; ++ struct gs_port *port; ++ ++ if (!gs_tty_driver) ++ return; ++ ++ /* start sysfs and /dev/ttyGS* node removal */ ++ for (i = 0; i < n_ports; i++) ++ tty_unregister_device(gs_tty_driver, i); ++ ++ for (i = 0; i < n_ports; i++) { ++ /* prevent new opens */ ++ mutex_lock(&ports[i].lock); ++ port = ports[i].port; ++ ports[i].port = NULL; ++ mutex_unlock(&ports[i].lock); ++ ++ tasklet_kill(&port->push); ++ ++ /* wait for old opens to finish */ ++ wait_event(port->close_wait, gs_closed(port)); ++ ++ WARN_ON(port->port_usb != NULL); ++ ++ kfree(port); ++ } ++ n_ports = 0; ++ ++ tty_unregister_driver(gs_tty_driver); ++ gs_tty_driver = NULL; ++ ++ pr_debug("%s: cleaned up ttyGS* support\n", __func__); ++} ++ ++/** ++ * gserial_connect - notify TTY I/O glue that USB link is active ++ * @gser: the function, set up with endpoints and descriptors ++ * @port_num: which port is active ++ * Context: any (usually from irq) ++ * ++ * This is called activate endpoints and let the TTY layer know that ++ * the connection is active ... not unlike "carrier detect". It won't ++ * necessarily start I/O queues; unless the TTY is held open by any ++ * task, there would be no point. However, the endpoints will be ++ * activated so the USB host can perform I/O, subject to basic USB ++ * hardware flow control. ++ * ++ * Caller needs to have set up the endpoints and USB function in @dev ++ * before calling this, as well as the appropriate (speed-specific) ++ * endpoint descriptors, and also have set up the TTY driver by calling ++ * @gserial_setup(). ++ * ++ * Returns negative errno or zero. ++ * On success, ep->driver_data will be overwritten. ++ */ ++int gserial_connect(struct gserial *gser, u8 port_num) ++{ ++ struct gs_port *port; ++ unsigned long flags; ++ int status; ++ ++ if (!gs_tty_driver || port_num >= n_ports) ++ return -ENXIO; ++ ++ /* we "know" gserial_cleanup() hasn't been called */ ++ port = ports[port_num].port; ++ ++ /* activate the endpoints */ ++ status = usb_ep_enable(gser->in, gser->in_desc); ++ if (status < 0) ++ return status; ++ gser->in->driver_data = port; ++ ++ status = usb_ep_enable(gser->out, gser->out_desc); ++ if (status < 0) ++ goto fail_out; ++ gser->out->driver_data = port; ++ ++ /* then tell the tty glue that I/O can work */ ++ spin_lock_irqsave(&port->port_lock, flags); ++ gser->ioport = port; ++ port->port_usb = gser; ++ ++ /* REVISIT unclear how best to handle this state... ++ * we don't really couple it with the Linux TTY. ++ */ ++ gser->port_line_coding = port->port_line_coding; ++ ++ /* REVISIT if waiting on "carrier detect", signal. */ ++ ++ /* if it's already open, start I/O ... and notify the serial ++ * protocol about open/close status (connect/disconnect). ++ */ ++ if (port->open_count) { ++ pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); ++ gs_start_io(port); ++ if (gser->connect) ++ gser->connect(gser); ++ } else { ++ if (gser->disconnect) ++ gser->disconnect(gser); ++ } ++ ++ spin_unlock_irqrestore(&port->port_lock, flags); ++ ++ return status; ++ ++fail_out: ++ usb_ep_disable(gser->in); ++ gser->in->driver_data = NULL; ++ return status; ++} ++ ++/** ++ * gserial_disconnect - notify TTY I/O glue that USB link is inactive ++ * @gser: the function, on which gserial_connect() was called ++ * Context: any (usually from irq) ++ * ++ * This is called to deactivate endpoints and let the TTY layer know ++ * that the connection went inactive ... not unlike "hangup". ++ * ++ * On return, the state is as if gserial_connect() had never been called; ++ * there is no active USB I/O on these endpoints. ++ */ ++void gserial_disconnect(struct gserial *gser) ++{ ++ struct gs_port *port = gser->ioport; ++ unsigned long flags; ++ ++ if (!port) ++ return; ++ ++ /* tell the TTY glue not to do I/O here any more */ ++ spin_lock_irqsave(&port->port_lock, flags); ++ ++ /* REVISIT as above: how best to track this? */ ++ port->port_line_coding = gser->port_line_coding; ++ ++ port->port_usb = NULL; ++ gser->ioport = NULL; ++ if (port->open_count > 0 || port->openclose) { ++ wake_up_interruptible(&port->drain_wait); ++ if (port->port_tty) ++ tty_hangup(port->port_tty); ++ } ++ spin_unlock_irqrestore(&port->port_lock, flags); ++ ++ /* disable endpoints, aborting down any active I/O */ ++ usb_ep_disable(gser->out); ++ gser->out->driver_data = NULL; ++ ++ usb_ep_disable(gser->in); ++ gser->in->driver_data = NULL; ++ ++ /* finally, free any unused/unusable I/O buffers */ ++ spin_lock_irqsave(&port->port_lock, flags); ++ if (port->open_count == 0 && !port->openclose) ++ gs_buf_free(&port->port_write_buf); ++ gs_free_requests(gser->out, &port->read_pool); ++ gs_free_requests(gser->out, &port->read_queue); ++ gs_free_requests(gser->in, &port->write_pool); ++ spin_unlock_irqrestore(&port->port_lock, flags); ++} +diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h +new file mode 100644 +index 0000000..af3910d +--- /dev/null ++++ b/drivers/usb/gadget/u_serial.h +@@ -0,0 +1,66 @@ ++/* ++ * u_serial.h - interface to USB gadget "serial port"/TTY utilities ++ * ++ * Copyright (C) 2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation ++ * ++ * This software is distributed under the terms of the GNU General ++ * Public License ("GPL") as published by the Free Software Foundation, ++ * either version 2 of that License or (at your option) any later version. ++ */ ++ ++#ifndef __U_SERIAL_H ++#define __U_SERIAL_H ++ ++#include <linux/usb/composite.h> ++#include <linux/usb/cdc.h> ++ ++/* ++ * One non-multiplexed "serial" I/O port ... there can be several of these ++ * on any given USB peripheral device, if it provides enough endpoints. ++ * ++ * The "u_serial" utility component exists to do one thing: manage TTY ++ * style I/O using the USB peripheral endpoints listed here, including ++ * hookups to sysfs and /dev for each logical "tty" device. ++ * ++ * REVISIT at least ACM could support tiocmget() if needed. ++ * ++ * REVISIT someday, allow multiplexing several TTYs over these endpoints. ++ */ ++struct gserial { ++ struct usb_function func; ++ ++ /* port is managed by gserial_{connect,disconnect} */ ++ struct gs_port *ioport; ++ ++ struct usb_ep *in; ++ struct usb_ep *out; ++ struct usb_endpoint_descriptor *in_desc; ++ struct usb_endpoint_descriptor *out_desc; ++ ++ /* REVISIT avoid this CDC-ACM support harder ... */ ++ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */ ++ ++ /* notification callbacks */ ++ void (*connect)(struct gserial *p); ++ void (*disconnect)(struct gserial *p); ++ int (*send_break)(struct gserial *p, int duration); ++}; ++ ++/* utilities to allocate/free request and buffer */ ++struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags); ++void gs_free_req(struct usb_ep *, struct usb_request *req); ++ ++/* port setup/teardown is handled by gadget driver */ ++int gserial_setup(struct usb_gadget *g, unsigned n_ports); ++void gserial_cleanup(void); ++ ++/* connect/disconnect is handled by individual functions */ ++int gserial_connect(struct gserial *, u8 port_num); ++void gserial_disconnect(struct gserial *); ++ ++/* functions are bound to configurations by a config or gadget driver */ ++int acm_bind_config(struct usb_configuration *c, u8 port_num); ++int gser_bind_config(struct usb_configuration *c, u8 port_num); ++ ++#endif /* __U_SERIAL_H */ +diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c +index fce4924..aa0bd4f 100644 +--- a/drivers/usb/gadget/zero.c ++++ b/drivers/usb/gadget/zero.c +@@ -1,8 +1,8 @@ + /* + * zero.c -- Gadget Zero, for USB development + * +- * Copyright (C) 2003-2007 David Brownell +- * All rights reserved. ++ * Copyright (C) 2003-2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -30,12 +30,7 @@ + * + * It supports two similar configurations. One sinks whatever the usb host + * writes, and in return sources zeroes. The other loops whatever the host +- * writes back, so the host can read it. Module options include: +- * +- * buflen=N default N=4096, buffer size used +- * qlen=N default N=32, how many buffers in the loopback queue +- * loopdefault default false, list loopback config first +- * autoresume=N default N=0, seconds before triggering remote wakeup ++ * writes back, so the host can read it. + * + * Many drivers will only have one configuration, letting them be much + * simpler if they also don't support high speed operation (like this +@@ -47,94 +42,35 @@ + * work with low capability USB controllers without four bulk endpoints. + */ + ++/* ++ * driver assumes self-powered hardware, and ++ * has no way for users to trigger remote wakeup. ++ */ ++ + /* #define VERBOSE_DEBUG */ + + #include <linux/kernel.h> + #include <linux/utsname.h> + #include <linux/device.h> + +-#include <linux/usb/ch9.h> +-#include <linux/usb/gadget.h> +- ++#include "g_zero.h" + #include "gadget_chips.h" + + + /*-------------------------------------------------------------------------*/ + +-#define DRIVER_VERSION "Earth Day 2008" ++#define DRIVER_VERSION "Cinco de Mayo 2008" + +-static const char shortname[] = "zero"; + static const char longname[] = "Gadget Zero"; + +-static const char source_sink[] = "source and sink data"; +-static const char loopback[] = "loop input to output"; +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * driver assumes self-powered hardware, and +- * has no way for users to trigger remote wakeup. +- * +- * this version autoconfigures as much as possible, +- * which is reasonable for most "bulk-only" drivers. +- */ +-static const char *EP_IN_NAME; /* source */ +-static const char *EP_OUT_NAME; /* sink */ +- +-/*-------------------------------------------------------------------------*/ +- +-/* big enough to hold our biggest descriptor */ +-#define USB_BUFSIZ 256 +- +-struct zero_dev { +- spinlock_t lock; +- struct usb_gadget *gadget; +- struct usb_request *req; /* for control responses */ +- +- /* when configured, we have one of two configs: +- * - source data (in to host) and sink it (out from host) +- * - or loop it back (out from host back in to host) +- */ +- u8 config; +- struct usb_ep *in_ep, *out_ep; +- +- /* autoresume timer */ +- struct timer_list resume; +-}; +- +-#define DBG(d, fmt, args...) \ +- dev_dbg(&(d)->gadget->dev , fmt , ## args) +-#define VDBG(d, fmt, args...) \ +- dev_vdbg(&(d)->gadget->dev , fmt , ## args) +-#define ERROR(d, fmt, args...) \ +- dev_err(&(d)->gadget->dev , fmt , ## args) +-#define WARN(d, fmt, args...) \ +- dev_warn(&(d)->gadget->dev , fmt , ## args) +-#define INFO(d, fmt, args...) \ +- dev_info(&(d)->gadget->dev , fmt , ## args) +- +-/*-------------------------------------------------------------------------*/ +- +-static unsigned buflen = 4096; +-static unsigned qlen = 32; +-static unsigned pattern = 0; +- +-module_param(buflen, uint, S_IRUGO); +-module_param(qlen, uint, S_IRUGO); +-module_param(pattern, uint, S_IRUGO|S_IWUSR); +- +-/* +- * if it's nonzero, autoresume says how many seconds to wait +- * before trying to wake up the host after suspend. +- */ +-static unsigned autoresume = 0; +-module_param(autoresume, uint, 0); ++unsigned buflen = 4096; ++module_param(buflen, uint, 0); + + /* + * Normally the "loopback" configuration is second (index 1) so + * it's not the default. Here's where to change that order, to +- * work better with hosts where config changes are problematic. +- * Or controllers (like superh) that only support one config. ++ * work better with hosts where config changes are problematic or ++ * controllers (like original superh) that only support one config. + */ + static int loopdefault = 0; + module_param(loopdefault, bool, S_IRUGO|S_IWUSR); +@@ -156,24 +92,6 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR); + + /*-------------------------------------------------------------------------*/ + +-/* +- * DESCRIPTORS ... most are static, but strings and (full) +- * configuration descriptors are built on demand. +- */ +- +-#define STRING_MANUFACTURER 25 +-#define STRING_PRODUCT 42 +-#define STRING_SERIAL 101 +-#define STRING_SOURCE_SINK 250 +-#define STRING_LOOPBACK 251 +- +-/* +- * This device advertises two configurations; these numbers work +- * on a pxa250 as well as more flexible hardware. +- */ +-#define CONFIG_SOURCE_SINK 3 +-#define CONFIG_LOOPBACK 2 +- + static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, +@@ -183,248 +101,64 @@ static struct usb_device_descriptor device_desc = { + + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), +- .iManufacturer = STRING_MANUFACTURER, +- .iProduct = STRING_PRODUCT, +- .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 2, + }; + +-static struct usb_config_descriptor source_sink_config = { +- .bLength = sizeof source_sink_config, +- .bDescriptorType = USB_DT_CONFIG, +- +- /* compute wTotalLength on the fly */ +- .bNumInterfaces = 1, +- .bConfigurationValue = CONFIG_SOURCE_SINK, +- .iConfiguration = STRING_SOURCE_SINK, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, /* self-powered */ +-}; +- +-static struct usb_config_descriptor loopback_config = { +- .bLength = sizeof loopback_config, +- .bDescriptorType = USB_DT_CONFIG, +- +- /* compute wTotalLength on the fly */ +- .bNumInterfaces = 1, +- .bConfigurationValue = CONFIG_LOOPBACK, +- .iConfiguration = STRING_LOOPBACK, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, /* self-powered */ +-}; +- ++#ifdef CONFIG_USB_OTG + static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + +- .bmAttributes = USB_OTG_SRP, +-}; +- +-/* one interface in each configuration */ +- +-static const struct usb_interface_descriptor source_sink_intf = { +- .bLength = sizeof source_sink_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_VENDOR_SPEC, +- .iInterface = STRING_SOURCE_SINK, +-}; +- +-static const struct usb_interface_descriptor loopback_intf = { +- .bLength = sizeof loopback_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_VENDOR_SPEC, +- .iInterface = STRING_LOOPBACK, +-}; +- +-/* two full speed bulk endpoints; their use is config-dependent */ +- +-static struct usb_endpoint_descriptor fs_source_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static struct usb_endpoint_descriptor fs_sink_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bEndpointAddress = USB_DIR_OUT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static const struct usb_descriptor_header *fs_source_sink_function[] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &source_sink_intf, +- (struct usb_descriptor_header *) &fs_sink_desc, +- (struct usb_descriptor_header *) &fs_source_desc, +- NULL, +-}; +- +-static const struct usb_descriptor_header *fs_loopback_function[] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &loopback_intf, +- (struct usb_descriptor_header *) &fs_sink_desc, +- (struct usb_descriptor_header *) &fs_source_desc, +- NULL, +-}; +- +-/* +- * usb 2.0 devices need to expose both high speed and full speed +- * descriptors, unless they only run at full speed. +- * +- * that means alternate endpoint descriptors (bigger packets) +- * and a "device qualifier" ... plus more construction options +- * for the config descriptor. +- */ +- +-static struct usb_endpoint_descriptor hs_source_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_endpoint_descriptor hs_sink_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_qualifier_descriptor dev_qualifier = { +- .bLength = sizeof dev_qualifier, +- .bDescriptorType = USB_DT_DEVICE_QUALIFIER, +- +- .bcdUSB = __constant_cpu_to_le16(0x0200), +- .bDeviceClass = USB_CLASS_VENDOR_SPEC, +- +- .bNumConfigurations = 2, ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }; + +-static const struct usb_descriptor_header *hs_source_sink_function[] = { ++const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &source_sink_intf, +- (struct usb_descriptor_header *) &hs_source_desc, +- (struct usb_descriptor_header *) &hs_sink_desc, + NULL, + }; ++#endif + +-static const struct usb_descriptor_header *hs_loopback_function[] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &loopback_intf, +- (struct usb_descriptor_header *) &hs_source_desc, +- (struct usb_descriptor_header *) &hs_sink_desc, +- NULL, +-}; ++/* string IDs are assigned dynamically */ + +-/* maxpacket and other transfer characteristics vary by speed. */ +-static inline struct usb_endpoint_descriptor * +-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, +- struct usb_endpoint_descriptor *fs) +-{ +- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) +- return hs; +- return fs; +-} ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++#define STRING_SERIAL_IDX 2 + + static char manufacturer[50]; + + /* default serial number takes at least two packets */ + static char serial[] = "0123456789.0123456789.0123456789"; + +- +-/* static strings, in UTF-8 */ +-static struct usb_string strings[] = { +- { STRING_MANUFACTURER, manufacturer, }, +- { STRING_PRODUCT, longname, }, +- { STRING_SERIAL, serial, }, +- { STRING_LOOPBACK, loopback, }, +- { STRING_SOURCE_SINK, source_sink, }, ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = longname, ++ [STRING_SERIAL_IDX].s = serial, + { } /* end of list */ + }; + +-static struct usb_gadget_strings stringtab = { ++static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ +- .strings = strings, ++ .strings = strings_dev, + }; + +-/* +- * config descriptors are also handcrafted. these must agree with code +- * that sets configurations, and with code managing interfaces and their +- * altsettings. other complexity may come from: +- * +- * - high speed support, including "other speed config" rules +- * - multiple configurations +- * - interfaces with alternate settings +- * - embedded class or vendor-specific descriptors +- * +- * this handles high speed, and has a second config that could as easily +- * have been an alternate interface setting (on most hardware). +- * +- * NOTE: to demonstrate (and test) more USB capabilities, this driver +- * should include an altsetting to test interrupt transfers, including +- * high bandwidth modes at high speed. (Maybe work like Intel's test +- * device?) +- */ +-static int config_buf(struct usb_gadget *gadget, +- u8 *buf, u8 type, unsigned index) +-{ +- int is_source_sink; +- int len; +- const struct usb_descriptor_header **function; +- int hs = 0; +- +- /* two configurations will always be index 0 and index 1 */ +- if (index > 1) +- return -EINVAL; +- is_source_sink = loopdefault ? (index == 1) : (index == 0); +- +- if (gadget_is_dualspeed(gadget)) { +- hs = (gadget->speed == USB_SPEED_HIGH); +- if (type == USB_DT_OTHER_SPEED_CONFIG) +- hs = !hs; +- } +- if (hs) +- function = is_source_sink +- ? hs_source_sink_function +- : hs_loopback_function; +- else +- function = is_source_sink +- ? fs_source_sink_function +- : fs_loopback_function; +- +- /* for now, don't advertise srp-only devices */ +- if (!gadget_is_otg(gadget)) +- function++; +- +- len = usb_gadget_config_buf(is_source_sink +- ? &source_sink_config +- : &loopback_config, +- buf, USB_BUFSIZ, function); +- if (len < 0) +- return len; +- ((struct usb_config_descriptor *) buf)->bDescriptorType = type; +- return len; +-} ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; + + /*-------------------------------------------------------------------------*/ + +-static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) ++struct usb_request *alloc_ep_req(struct usb_ep *ep) + { + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { +- req->length = length; +- req->buf = kmalloc(length, GFP_ATOMIC); ++ req->length = buflen; ++ req->buf = kmalloc(buflen, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; +@@ -433,681 +167,73 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) + return req; + } + +-static void free_ep_req(struct usb_ep *ep, struct usb_request *req) ++void free_ep_req(struct usb_ep *ep, struct usb_request *req) + { + kfree(req->buf); + usb_ep_free_request(ep, req); + } + +-/*-------------------------------------------------------------------------*/ +- +-/* +- * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals, +- * this just sinks bulk packets OUT to the peripheral and sources them IN +- * to the host, optionally with specific data patterns. +- * +- * In terms of control messaging, this supports all the standard requests +- * plus two that support control-OUT tests. +- * +- * Note that because this doesn't queue more than one request at a time, +- * some other function must be used to test queueing logic. The network +- * link (g_ether) is probably the best option for that. +- */ +- +-/* optionally require specific source/sink data patterns */ +- +-static int +-check_read_data( +- struct zero_dev *dev, +- struct usb_ep *ep, +- struct usb_request *req +-) ++static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) + { +- unsigned i; +- u8 *buf = req->buf; +- +- for (i = 0; i < req->actual; i++, buf++) { +- switch (pattern) { +- /* all-zeroes has no synchronization issues */ +- case 0: +- if (*buf == 0) +- continue; +- break; +- /* mod63 stays in sync with short-terminated transfers, +- * or otherwise when host and gadget agree on how large +- * each usb transfer request should be. resync is done +- * with set_interface or set_config. +- */ +- case 1: +- if (*buf == (u8)(i % 63)) +- continue; +- break; +- } +- ERROR(dev, "bad OUT byte, buf[%d] = %d\n", i, *buf); +- usb_ep_set_halt(ep); +- return -EINVAL; ++ int value; ++ ++ if (ep->driver_data) { ++ value = usb_ep_disable(ep); ++ if (value < 0) ++ DBG(cdev, "disable %s --> %d\n", ++ ep->name, value); ++ ep->driver_data = NULL; + } +- return 0; + } + +-static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) ++void disable_endpoints(struct usb_composite_dev *cdev, ++ struct usb_ep *in, struct usb_ep *out) + { +- unsigned i; +- u8 *buf = req->buf; +- +- switch (pattern) { +- case 0: +- memset(req->buf, 0, req->length); +- break; +- case 1: +- for (i = 0; i < req->length; i++) +- *buf++ = (u8) (i % 63); +- break; +- } +-} +- +-/* if there is only one request in the queue, there'll always be an +- * irq delay between end of one request and start of the next. +- * that prevents using hardware dma queues. +- */ +-static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- struct zero_dev *dev = ep->driver_data; +- int status = req->status; +- +- switch (status) { +- +- case 0: /* normal completion? */ +- if (ep == dev->out_ep) { +- check_read_data(dev, ep, req); +- memset(req->buf, 0x55, req->length); +- } else +- reinit_write_data(ep, req); +- break; +- +- /* this endpoint is normally active while we're configured */ +- case -ECONNABORTED: /* hardware forced ep reset */ +- case -ECONNRESET: /* request dequeued */ +- case -ESHUTDOWN: /* disconnect from host */ +- VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, +- req->actual, req->length); +- if (ep == dev->out_ep) +- check_read_data(dev, ep, req); +- free_ep_req(ep, req); +- return; +- +- case -EOVERFLOW: /* buffer overrun on read means that +- * we didn't provide a big enough +- * buffer. +- */ +- default: +-#if 1 +- DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, +- status, req->actual, req->length); +-#endif +- case -EREMOTEIO: /* short read */ +- break; +- } +- +- status = usb_ep_queue(ep, req, GFP_ATOMIC); +- if (status) { +- ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", +- ep->name, req->length, status); +- usb_ep_set_halt(ep); +- /* FIXME recover later ... somehow */ +- } +-} +- +-static struct usb_request *source_sink_start_ep(struct usb_ep *ep) +-{ +- struct usb_request *req; +- int status; +- +- req = alloc_ep_req(ep, buflen); +- if (!req) +- return NULL; +- +- memset(req->buf, 0, req->length); +- req->complete = source_sink_complete; +- +- if (strcmp(ep->name, EP_IN_NAME) == 0) +- reinit_write_data(ep, req); +- else +- memset(req->buf, 0x55, req->length); +- +- status = usb_ep_queue(ep, req, GFP_ATOMIC); +- if (status) { +- struct zero_dev *dev = ep->driver_data; +- +- ERROR(dev, "start %s --> %d\n", ep->name, status); +- free_ep_req(ep, req); +- req = NULL; +- } +- +- return req; +-} +- +-static int set_source_sink_config(struct zero_dev *dev) +-{ +- int result = 0; +- struct usb_ep *ep; +- struct usb_gadget *gadget = dev->gadget; +- +- gadget_for_each_ep(ep, gadget) { +- const struct usb_endpoint_descriptor *d; +- +- /* one endpoint writes (sources) zeroes in (to the host) */ +- if (strcmp(ep->name, EP_IN_NAME) == 0) { +- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- if (source_sink_start_ep(ep) != NULL) { +- dev->in_ep = ep; +- continue; +- } +- usb_ep_disable(ep); +- result = -EIO; +- } +- +- /* one endpoint reads (sinks) anything out (from the host) */ +- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { +- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- if (source_sink_start_ep(ep) != NULL) { +- dev->out_ep = ep; +- continue; +- } +- usb_ep_disable(ep); +- result = -EIO; +- } +- +- /* ignore any other endpoints */ +- } else +- continue; +- +- /* stop on error */ +- ERROR(dev, "can't start %s, result %d\n", ep->name, result); +- break; +- } +- if (result == 0) +- DBG(dev, "buflen %d\n", buflen); +- +- /* caller is responsible for cleanup on error */ +- return result; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void loopback_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- struct zero_dev *dev = ep->driver_data; +- int status = req->status; +- +- switch (status) { +- +- case 0: /* normal completion? */ +- if (ep == dev->out_ep) { +- /* loop this OUT packet back IN to the host */ +- req->zero = (req->actual < req->length); +- req->length = req->actual; +- status = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC); +- if (status == 0) +- return; +- +- /* "should never get here" */ +- ERROR(dev, "can't loop %s to %s: %d\n", +- ep->name, dev->in_ep->name, +- status); +- } +- +- /* queue the buffer for some later OUT packet */ +- req->length = buflen; +- status = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); +- if (status == 0) +- return; +- +- /* "should never get here" */ +- /* FALLTHROUGH */ +- +- default: +- ERROR(dev, "%s loop complete --> %d, %d/%d\n", ep->name, +- status, req->actual, req->length); +- /* FALLTHROUGH */ +- +- /* NOTE: since this driver doesn't maintain an explicit record +- * of requests it submitted (just maintains qlen count), we +- * rely on the hardware driver to clean up on disconnect or +- * endpoint disable. +- */ +- case -ECONNABORTED: /* hardware forced ep reset */ +- case -ECONNRESET: /* request dequeued */ +- case -ESHUTDOWN: /* disconnect from host */ +- free_ep_req(ep, req); +- return; +- } +-} +- +-static int set_loopback_config(struct zero_dev *dev) +-{ +- int result = 0; +- struct usb_ep *ep; +- struct usb_gadget *gadget = dev->gadget; +- +- gadget_for_each_ep(ep, gadget) { +- const struct usb_endpoint_descriptor *d; +- +- /* one endpoint writes data back IN to the host */ +- if (strcmp(ep->name, EP_IN_NAME) == 0) { +- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- dev->in_ep = ep; +- continue; +- } +- +- /* one endpoint just reads OUT packets */ +- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { +- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- dev->out_ep = ep; +- continue; +- } +- +- /* ignore any other endpoints */ +- } else +- continue; +- +- /* stop on error */ +- ERROR(dev, "can't enable %s, result %d\n", ep->name, result); +- break; +- } +- +- /* allocate a bunch of read buffers and queue them all at once. +- * we buffer at most 'qlen' transfers; fewer if any need more +- * than 'buflen' bytes each. +- */ +- if (result == 0) { +- struct usb_request *req; +- unsigned i; +- +- ep = dev->out_ep; +- for (i = 0; i < qlen && result == 0; i++) { +- req = alloc_ep_req(ep, buflen); +- if (req) { +- req->complete = loopback_complete; +- result = usb_ep_queue(ep, req, GFP_ATOMIC); +- if (result) +- DBG(dev, "%s queue req --> %d\n", +- ep->name, result); +- } else +- result = -ENOMEM; +- } +- } +- if (result == 0) +- DBG(dev, "qlen %d, buflen %d\n", qlen, buflen); +- +- /* caller is responsible for cleanup on error */ +- return result; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void zero_reset_config(struct zero_dev *dev) +-{ +- if (dev->config == 0) +- return; +- +- DBG(dev, "reset config\n"); +- +- /* just disable endpoints, forcing completion of pending i/o. +- * all our completion handlers free their requests in this case. +- */ +- if (dev->in_ep) { +- usb_ep_disable(dev->in_ep); +- dev->in_ep = NULL; +- } +- if (dev->out_ep) { +- usb_ep_disable(dev->out_ep); +- dev->out_ep = NULL; +- } +- dev->config = 0; +- del_timer(&dev->resume); +-} +- +-/* change our operational config. this code must agree with the code +- * that returns config descriptors, and altsetting code. +- * +- * it's also responsible for power management interactions. some +- * configurations might not work with our current power sources. +- * +- * note that some device controller hardware will constrain what this +- * code can do, perhaps by disallowing more than one configuration or +- * by limiting configuration choices (like the pxa2xx). +- */ +-static int zero_set_config(struct zero_dev *dev, unsigned number) +-{ +- int result = 0; +- struct usb_gadget *gadget = dev->gadget; +- +- if (number == dev->config) +- return 0; +- +- if (gadget_is_sa1100(gadget) && dev->config) { +- /* tx fifo is full, but we can't clear it...*/ +- ERROR(dev, "can't change configurations\n"); +- return -ESPIPE; +- } +- zero_reset_config(dev); +- +- switch (number) { +- case CONFIG_SOURCE_SINK: +- result = set_source_sink_config(dev); +- break; +- case CONFIG_LOOPBACK: +- result = set_loopback_config(dev); +- break; +- default: +- result = -EINVAL; +- /* FALL THROUGH */ +- case 0: +- return result; +- } +- +- if (!result && (!dev->in_ep || !dev->out_ep)) +- result = -ENODEV; +- if (result) +- zero_reset_config(dev); +- else { +- char *speed; +- +- switch (gadget->speed) { +- case USB_SPEED_LOW: speed = "low"; break; +- case USB_SPEED_FULL: speed = "full"; break; +- case USB_SPEED_HIGH: speed = "high"; break; +- default: speed = "?"; break; +- } +- +- dev->config = number; +- INFO(dev, "%s speed config #%d: %s\n", speed, number, +- (number == CONFIG_SOURCE_SINK) +- ? source_sink : loopback); +- } +- return result; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- if (req->status || req->actual != req->length) +- DBG((struct zero_dev *) ep->driver_data, +- "setup complete --> %d, %d/%d\n", +- req->status, req->actual, req->length); +-} +- +-/* +- * The setup() callback implements all the ep0 functionality that's +- * not handled lower down, in hardware or the hardware driver (like +- * device and endpoint feature flags, and their status). It's all +- * housekeeping for the gadget function we're implementing. Most of +- * the work is in config-specific setup. +- */ +-static int +-zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- struct usb_request *req = dev->req; +- int value = -EOPNOTSUPP; +- u16 w_index = le16_to_cpu(ctrl->wIndex); +- u16 w_value = le16_to_cpu(ctrl->wValue); +- u16 w_length = le16_to_cpu(ctrl->wLength); +- +- /* usually this stores reply data in the pre-allocated ep0 buffer, +- * but config change events will reconfigure hardware. +- */ +- req->zero = 0; +- switch (ctrl->bRequest) { +- +- case USB_REQ_GET_DESCRIPTOR: +- if (ctrl->bRequestType != USB_DIR_IN) +- goto unknown; +- switch (w_value >> 8) { +- +- case USB_DT_DEVICE: +- value = min(w_length, (u16) sizeof device_desc); +- memcpy(req->buf, &device_desc, value); +- break; +- case USB_DT_DEVICE_QUALIFIER: +- if (!gadget_is_dualspeed(gadget)) +- break; +- value = min(w_length, (u16) sizeof dev_qualifier); +- memcpy(req->buf, &dev_qualifier, value); +- break; +- +- case USB_DT_OTHER_SPEED_CONFIG: +- if (!gadget_is_dualspeed(gadget)) +- break; +- // FALLTHROUGH +- case USB_DT_CONFIG: +- value = config_buf(gadget, req->buf, +- w_value >> 8, +- w_value & 0xff); +- if (value >= 0) +- value = min(w_length, (u16) value); +- break; +- +- case USB_DT_STRING: +- /* wIndex == language code. +- * this driver only handles one language, you can +- * add string tables for other languages, using +- * any UTF-8 characters +- */ +- value = usb_gadget_get_string(&stringtab, +- w_value & 0xff, req->buf); +- if (value >= 0) +- value = min(w_length, (u16) value); +- break; +- } +- break; +- +- /* currently two configs, two speeds */ +- case USB_REQ_SET_CONFIGURATION: +- if (ctrl->bRequestType != 0) +- goto unknown; +- if (gadget->a_hnp_support) +- DBG(dev, "HNP available\n"); +- else if (gadget->a_alt_hnp_support) +- DBG(dev, "HNP needs a different root port\n"); +- else +- VDBG(dev, "HNP inactive\n"); +- spin_lock(&dev->lock); +- value = zero_set_config(dev, w_value); +- spin_unlock(&dev->lock); +- break; +- case USB_REQ_GET_CONFIGURATION: +- if (ctrl->bRequestType != USB_DIR_IN) +- goto unknown; +- *(u8 *)req->buf = dev->config; +- value = min(w_length, (u16) 1); +- break; +- +- /* until we add altsetting support, or other interfaces, +- * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) +- * and already killed pending endpoint I/O. +- */ +- case USB_REQ_SET_INTERFACE: +- if (ctrl->bRequestType != USB_RECIP_INTERFACE) +- goto unknown; +- spin_lock(&dev->lock); +- if (dev->config && w_index == 0 && w_value == 0) { +- u8 config = dev->config; +- +- /* resets interface configuration, forgets about +- * previous transaction state (queued bufs, etc) +- * and re-inits endpoint state (toggle etc) +- * no response queued, just zero status == success. +- * if we had more than one interface we couldn't +- * use this "reset the config" shortcut. +- */ +- zero_reset_config(dev); +- zero_set_config(dev, config); +- value = 0; +- } +- spin_unlock(&dev->lock); +- break; +- case USB_REQ_GET_INTERFACE: +- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) +- goto unknown; +- if (!dev->config) +- break; +- if (w_index != 0) { +- value = -EDOM; +- break; +- } +- *(u8 *)req->buf = 0; +- value = min(w_length, (u16) 1); +- break; +- +- /* +- * These are the same vendor-specific requests supported by +- * Intel's USB 2.0 compliance test devices. We exceed that +- * device spec by allowing multiple-packet requests. +- */ +- case 0x5b: /* control WRITE test -- fill the buffer */ +- if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) +- goto unknown; +- if (w_value || w_index) +- break; +- /* just read that many bytes into the buffer */ +- if (w_length > USB_BUFSIZ) +- break; +- value = w_length; +- break; +- case 0x5c: /* control READ test -- return the buffer */ +- if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) +- goto unknown; +- if (w_value || w_index) +- break; +- /* expect those bytes are still in the buffer; send back */ +- if (w_length > USB_BUFSIZ +- || w_length != req->length) +- break; +- value = w_length; +- break; +- +- default: +-unknown: +- VDBG(dev, +- "unknown control req%02x.%02x v%04x i%04x l%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- w_value, w_index, w_length); +- } +- +- /* respond with data transfer before status phase? */ +- if (value >= 0) { +- req->length = value; +- req->zero = value < w_length; +- value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); +- if (value < 0) { +- DBG(dev, "ep_queue --> %d\n", value); +- req->status = 0; +- zero_setup_complete(gadget->ep0, req); +- } +- } +- +- /* device either stalls (value < 0) or reports success */ +- return value; +-} +- +-static void zero_disconnect(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- unsigned long flags; +- +- spin_lock_irqsave(&dev->lock, flags); +- zero_reset_config(dev); +- +- /* a more significant application might have some non-usb +- * activities to quiesce here, saving resources like power +- * or pushing the notification up a network stack. +- */ +- spin_unlock_irqrestore(&dev->lock, flags); +- +- /* next we may get setup() calls to enumerate new connections; +- * or an unbind() during shutdown (including removing module). +- */ +-} +- +-static void zero_autoresume(unsigned long _dev) +-{ +- struct zero_dev *dev = (struct zero_dev *) _dev; +- int status; +- +- /* normally the host would be woken up for something +- * more significant than just a timer firing... +- */ +- if (dev->gadget->speed != USB_SPEED_UNKNOWN) { +- status = usb_gadget_wakeup(dev->gadget); +- DBG(dev, "wakeup --> %d\n", status); +- } ++ disable_ep(cdev, in); ++ disable_ep(cdev, out); + } + + /*-------------------------------------------------------------------------*/ + +-static void zero_unbind(struct usb_gadget *gadget) ++static int __init zero_bind(struct usb_composite_dev *cdev) + { +- struct zero_dev *dev = get_gadget_data(gadget); +- +- DBG(dev, "unbind\n"); +- +- /* we've already been disconnected ... no i/o is active */ +- if (dev->req) { +- dev->req->length = USB_BUFSIZ; +- free_ep_req(gadget->ep0, dev->req); +- } +- del_timer_sync(&dev->resume); +- kfree(dev); +- set_gadget_data(gadget, NULL); +-} +- +-static int __init zero_bind(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev; +- struct usb_ep *ep; + int gcnum; ++ struct usb_gadget *gadget = cdev->gadget; ++ int id; + +- /* FIXME this can't yet work right with SH ... it has only +- * one configuration, numbered one. ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. + */ +- if (gadget_is_sh(gadget)) +- return -ENODEV; +- +- /* Bulk-only drivers like this one SHOULD be able to +- * autoconfigure on any sane usb controller driver, +- * but there may also be important quirks to address. ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_MANUFACTURER_IDX].id = id; ++ device_desc.iManufacturer = id; ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_PRODUCT_IDX].id = id; ++ device_desc.iProduct = id; ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_SERIAL_IDX].id = id; ++ device_desc.iSerialNumber = id; ++ ++ /* Register primary, then secondary configuration. Note that ++ * SH3 only allows one config... + */ +- usb_ep_autoconfig_reset(gadget); +- ep = usb_ep_autoconfig(gadget, &fs_source_desc); +- if (!ep) { +-autoconf_fail: +- pr_err("%s: can't autoconfigure on %s\n", +- shortname, gadget->name); +- return -ENODEV; ++ if (loopdefault) { ++ loopback_add(cdev); ++ if (!gadget_is_sh(gadget)) ++ sourcesink_add(cdev); ++ } else { ++ sourcesink_add(cdev); ++ if (!gadget_is_sh(gadget)) ++ loopback_add(cdev); + } +- EP_IN_NAME = ep->name; +- ep->driver_data = ep; /* claim */ +- +- ep = usb_ep_autoconfig(gadget, &fs_sink_desc); +- if (!ep) +- goto autoconf_fail; +- EP_OUT_NAME = ep->name; +- ep->driver_data = ep; /* claim */ + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) +@@ -1115,144 +241,44 @@ autoconf_fail: + else { + /* gadget zero is so simple (for now, no altsettings) that + * it SHOULD NOT have problems with bulk-capable hardware. +- * so warn about unrcognized controllers, don't panic. ++ * so just warn about unrcognized controllers -- don't panic. + * + * things like configuration and altsetting numbering + * can need hardware-specific attention though. + */ + pr_warning("%s: controller '%s' not recognized\n", +- shortname, gadget->name); ++ longname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + +- /* ok, we made sense of the hardware ... */ +- dev = kzalloc(sizeof(*dev), GFP_KERNEL); +- if (!dev) +- return -ENOMEM; +- spin_lock_init(&dev->lock); +- dev->gadget = gadget; +- set_gadget_data(gadget, dev); +- +- init_timer(&dev->resume); +- dev->resume.function = zero_autoresume; +- dev->resume.data = (unsigned long) dev; +- +- /* preallocate control response and buffer */ +- dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); +- if (!dev->req) +- goto enomem; +- dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); +- if (!dev->req->buf) +- goto enomem; +- +- dev->req->complete = zero_setup_complete; +- +- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; +- +- if (gadget_is_dualspeed(gadget)) { +- /* assume ep0 uses the same value for both speeds ... */ +- dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; +- +- /* and that all endpoints are dual-speed */ +- hs_source_desc.bEndpointAddress = +- fs_source_desc.bEndpointAddress; +- hs_sink_desc.bEndpointAddress = +- fs_sink_desc.bEndpointAddress; +- } +- +- if (gadget_is_otg(gadget)) { +- otg_descriptor.bmAttributes |= USB_OTG_HNP, +- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- } +- +- usb_gadget_set_selfpowered(gadget); +- +- if (autoresume) { +- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- } +- +- gadget->ep0->driver_data = dev; +- +- INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); +- INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, +- EP_OUT_NAME, EP_IN_NAME); ++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); + + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + + return 0; +- +-enomem: +- zero_unbind(gadget); +- return -ENOMEM; + } + +-/*-------------------------------------------------------------------------*/ +- +-static void zero_suspend(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- +- if (gadget->speed == USB_SPEED_UNKNOWN) +- return; +- +- if (autoresume) { +- mod_timer(&dev->resume, jiffies + (HZ * autoresume)); +- DBG(dev, "suspend, wakeup in %d seconds\n", autoresume); +- } else +- DBG(dev, "suspend\n"); +-} +- +-static void zero_resume(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- +- DBG(dev, "resume\n"); +- del_timer(&dev->resume); +-} +- +- +-/*-------------------------------------------------------------------------*/ +- +-static struct usb_gadget_driver zero_driver = { +-#ifdef CONFIG_USB_GADGET_DUALSPEED +- .speed = USB_SPEED_HIGH, +-#else +- .speed = USB_SPEED_FULL, +-#endif +- .function = (char *) longname, ++static struct usb_composite_driver zero_driver = { ++ .name = "zero", ++ .dev = &device_desc, ++ .strings = dev_strings, + .bind = zero_bind, +- .unbind = __exit_p(zero_unbind), +- +- .setup = zero_setup, +- .disconnect = zero_disconnect, +- +- .suspend = zero_suspend, +- .resume = zero_resume, +- +- .driver = { +- .name = (char *) shortname, +- .owner = THIS_MODULE, +- }, + }; + + MODULE_AUTHOR("David Brownell"); + MODULE_LICENSE("GPL"); + +- + static int __init init(void) + { +- return usb_gadget_register_driver(&zero_driver); ++ return usb_composite_register(&zero_driver); + } + module_init(init); + + static void __exit cleanup(void) + { +- usb_gadget_unregister_driver(&zero_driver); ++ usb_composite_unregister(&zero_driver); + } + module_exit(cleanup); +- +diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h +new file mode 100644 +index 0000000..c932390 +--- /dev/null ++++ b/include/linux/usb/composite.h +@@ -0,0 +1,338 @@ ++/* ++ * composite.h -- framework for usb gadgets which are composite devices ++ * ++ * Copyright (C) 2006-2008 David Brownell ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef __LINUX_USB_COMPOSITE_H ++#define __LINUX_USB_COMPOSITE_H ++ ++/* ++ * This framework is an optional layer on top of the USB Gadget interface, ++ * making it easier to build (a) Composite devices, supporting multiple ++ * functions within any single configuration, and (b) Multi-configuration ++ * devices, also supporting multiple functions but without necessarily ++ * having more than one function per configuration. ++ * ++ * Example: a device with a single configuration supporting both network ++ * link and mass storage functions is a composite device. Those functions ++ * might alternatively be packaged in individual configurations, but in ++ * the composite model the host can use both functions at the same time. ++ */ ++ ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++ ++ ++struct usb_configuration; ++ ++/** ++ * struct usb_function - describes one function of a configuration ++ * @name: For diagnostics, identifies the function. ++ * @strings: tables of strings, keyed by identifiers assigned during bind() ++ * and by language IDs provided in control requests ++ * @descriptors: Table of full (or low) speed descriptors, using interface and ++ * string identifiers assigned during @bind(). If this pointer is null, ++ * the function will not be available at full speed (or at low speed). ++ * @hs_descriptors: Table of high speed descriptors, using interface and ++ * string identifiers assigned during @bind(). If this pointer is null, ++ * the function will not be available at high speed. ++ * @config: assigned when @usb_add_function() is called; this is the ++ * configuration with which this function is associated. ++ * @bind: Before the gadget can register, all of its functions bind() to the ++ * available resources including string and interface identifiers used ++ * in interface or class descriptors; endpoints; I/O buffers; and so on. ++ * @unbind: Reverses @bind; called as a side effect of unregistering the ++ * driver which added this function. ++ * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may ++ * initialize usb_ep.driver data at this time (when it is used). ++ * Note that setting an interface to its current altsetting resets ++ * interface state, and that all interfaces have a disabled state. ++ * @get_alt: Returns the active altsetting. If this is not provided, ++ * then only altsetting zero is supported. ++ * @disable: (REQUIRED) Indicates the function should be disabled. Reasons ++ * include host resetting or reconfiguring the gadget, and disconnection. ++ * @setup: Used for interface-specific control requests. ++ * @suspend: Notifies functions when the host stops sending USB traffic. ++ * @resume: Notifies functions when the host restarts USB traffic. ++ * ++ * A single USB function uses one or more interfaces, and should in most ++ * cases support operation at both full and high speeds. Each function is ++ * associated by @usb_add_function() with a one configuration; that function ++ * causes @bind() to be called so resources can be allocated as part of ++ * setting up a gadget driver. Those resources include endpoints, which ++ * should be allocated using @usb_ep_autoconfig(). ++ * ++ * To support dual speed operation, a function driver provides descriptors ++ * for both high and full speed operation. Except in rare cases that don't ++ * involve bulk endpoints, each speed needs different endpoint descriptors. ++ * ++ * Function drivers choose their own strategies for managing instance data. ++ * The simplest strategy just declares it "static', which means the function ++ * can only be activated once. If the function needs to be exposed in more ++ * than one configuration at a given speed, it needs to support multiple ++ * usb_function structures (one for each configuration). ++ * ++ * A more complex strategy might encapsulate a @usb_function structure inside ++ * a driver-specific instance structure to allows multiple activations. An ++ * example of multiple activations might be a CDC ACM function that supports ++ * two or more distinct instances within the same configuration, providing ++ * several independent logical data links to a USB host. ++ */ ++struct usb_function { ++ const char *name; ++ struct usb_gadget_strings **strings; ++ struct usb_descriptor_header **descriptors; ++ struct usb_descriptor_header **hs_descriptors; ++ ++ struct usb_configuration *config; ++ ++ /* REVISIT: bind() functions can be marked __init, which ++ * makes trouble for section mismatch analysis. See if ++ * we can't restructure things to avoid mismatching. ++ * Related: unbind() may kfree() but bind() won't... ++ */ ++ ++ /* configuration management: bind/unbind */ ++ int (*bind)(struct usb_configuration *, ++ struct usb_function *); ++ void (*unbind)(struct usb_configuration *, ++ struct usb_function *); ++ ++ /* runtime state management */ ++ int (*set_alt)(struct usb_function *, ++ unsigned interface, unsigned alt); ++ int (*get_alt)(struct usb_function *, ++ unsigned interface); ++ void (*disable)(struct usb_function *); ++ int (*setup)(struct usb_function *, ++ const struct usb_ctrlrequest *); ++ void (*suspend)(struct usb_function *); ++ void (*resume)(struct usb_function *); ++ ++ /* internals */ ++ struct list_head list; ++}; ++ ++int usb_add_function(struct usb_configuration *, struct usb_function *); ++ ++int usb_interface_id(struct usb_configuration *, struct usb_function *); ++ ++/** ++ * ep_choose - select descriptor endpoint at current device speed ++ * @g: gadget, connected and running at some speed ++ * @hs: descriptor to use for high speed operation ++ * @fs: descriptor to use for full or low speed operation ++ */ ++static inline struct usb_endpoint_descriptor * ++ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, ++ struct usb_endpoint_descriptor *fs) ++{ ++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) ++ return hs; ++ return fs; ++} ++ ++#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ ++ ++/** ++ * struct usb_configuration - represents one gadget configuration ++ * @label: For diagnostics, describes the configuration. ++ * @strings: Tables of strings, keyed by identifiers assigned during @bind() ++ * and by language IDs provided in control requests. ++ * @descriptors: Table of descriptors preceding all function descriptors. ++ * Examples include OTG and vendor-specific descriptors. ++ * @bind: Called from @usb_add_config() to allocate resources unique to this ++ * configuration and to call @usb_add_function() for each function used. ++ * @unbind: Reverses @bind; called as a side effect of unregistering the ++ * driver which added this configuration. ++ * @setup: Used to delegate control requests that aren't handled by standard ++ * device infrastructure or directed at a specific interface. ++ * @bConfigurationValue: Copied into configuration descriptor. ++ * @iConfiguration: Copied into configuration descriptor. ++ * @bmAttributes: Copied into configuration descriptor. ++ * @bMaxPower: Copied into configuration descriptor. ++ * @cdev: assigned by @usb_add_config() before calling @bind(); this is ++ * the device associated with this configuration. ++ * ++ * Configurations are building blocks for gadget drivers structured around ++ * function drivers. Simple USB gadgets require only one function and one ++ * configuration, and handle dual-speed hardware by always providing the same ++ * functionality. Slightly more complex gadgets may have more than one ++ * single-function configuration at a given speed; or have configurations ++ * that only work at one speed. ++ * ++ * Composite devices are, by definition, ones with configurations which ++ * include more than one function. ++ * ++ * The lifecycle of a usb_configuration includes allocation, initialization ++ * of the fields described above, and calling @usb_add_config() to set up ++ * internal data and bind it to a specific device. The configuration's ++ * @bind() method is then used to initialize all the functions and then ++ * call @usb_add_function() for them. ++ * ++ * Those functions would normally be independant of each other, but that's ++ * not mandatory. CDC WMC devices are an example where functions often ++ * depend on other functions, with some functions subsidiary to others. ++ * Such interdependency may be managed in any way, so long as all of the ++ * descriptors complete by the time the composite driver returns from ++ * its bind() routine. ++ */ ++struct usb_configuration { ++ const char *label; ++ struct usb_gadget_strings **strings; ++ const struct usb_descriptor_header **descriptors; ++ ++ /* REVISIT: bind() functions can be marked __init, which ++ * makes trouble for section mismatch analysis. See if ++ * we can't restructure things to avoid mismatching... ++ */ ++ ++ /* configuration management: bind/unbind */ ++ int (*bind)(struct usb_configuration *); ++ void (*unbind)(struct usb_configuration *); ++ int (*setup)(struct usb_configuration *, ++ const struct usb_ctrlrequest *); ++ ++ /* fields in the config descriptor */ ++ u8 bConfigurationValue; ++ u8 iConfiguration; ++ u8 bmAttributes; ++ u8 bMaxPower; ++ ++ struct usb_composite_dev *cdev; ++ ++ /* internals */ ++ struct list_head list; ++ struct list_head functions; ++ u8 next_interface_id; ++ unsigned highspeed:1; ++ unsigned fullspeed:1; ++ struct usb_function *interface[MAX_CONFIG_INTERFACES]; ++}; ++ ++int usb_add_config(struct usb_composite_dev *, ++ struct usb_configuration *); ++ ++/** ++ * struct usb_composite_driver - groups configurations into a gadget ++ * @name: For diagnostics, identifies the driver. ++ * @dev: Template descriptor for the device, including default device ++ * identifiers. ++ * @strings: tables of strings, keyed by identifiers assigned during bind() ++ * and language IDs provided in control requests ++ * @bind: (REQUIRED) Used to allocate resources that are shared across the ++ * whole device, such as string IDs, and add its configurations using ++ * @usb_add_config(). This may fail by returning a negative errno ++ * value; it should return zero on successful initialization. ++ * @unbind: Reverses @bind(); called as a side effect of unregistering ++ * this driver. ++ * ++ * Devices default to reporting self powered operation. Devices which rely ++ * on bus powered operation should report this in their @bind() method. ++ * ++ * Before returning from @bind, various fields in the template descriptor ++ * may be overridden. These include the idVendor/idProduct/bcdDevice values ++ * normally to bind the appropriate host side driver, and the three strings ++ * (iManufacturer, iProduct, iSerialNumber) normally used to provide user ++ * meaningful device identifiers. (The strings will not be defined unless ++ * they are defined in @dev and @strings.) The correct ep0 maxpacket size ++ * is also reported, as defined by the underlying controller driver. ++ */ ++struct usb_composite_driver { ++ const char *name; ++ const struct usb_device_descriptor *dev; ++ struct usb_gadget_strings **strings; ++ ++ /* REVISIT: bind() functions can be marked __init, which ++ * makes trouble for section mismatch analysis. See if ++ * we can't restructure things to avoid mismatching... ++ */ ++ ++ int (*bind)(struct usb_composite_dev *); ++ int (*unbind)(struct usb_composite_dev *); ++}; ++ ++extern int usb_composite_register(struct usb_composite_driver *); ++extern void usb_composite_unregister(struct usb_composite_driver *); ++ ++ ++/** ++ * struct usb_composite_device - represents one composite usb gadget ++ * @gadget: read-only, abstracts the gadget's usb peripheral controller ++ * @req: used for control responses; buffer is pre-allocated ++ * @bufsiz: size of buffer pre-allocated in @req ++ * @config: the currently active configuration ++ * ++ * One of these devices is allocated and initialized before the ++ * associated device driver's bind() is called. ++ * ++ * OPEN ISSUE: it appears that some WUSB devices will need to be ++ * built by combining a normal (wired) gadget with a wireless one. ++ * This revision of the gadget framework should probably try to make ++ * sure doing that won't hurt too much. ++ * ++ * One notion for how to handle Wireless USB devices involves: ++ * (a) a second gadget here, discovery mechanism TBD, but likely ++ * needing separate "register/unregister WUSB gadget" calls; ++ * (b) updates to usb_gadget to include flags "is it wireless", ++ * "is it wired", plus (presumably in a wrapper structure) ++ * bandgroup and PHY info; ++ * (c) presumably a wireless_ep wrapping a usb_ep, and reporting ++ * wireless-specific parameters like maxburst and maxsequence; ++ * (d) configurations that are specific to wireless links; ++ * (e) function drivers that understand wireless configs and will ++ * support wireless for (additional) function instances; ++ * (f) a function to support association setup (like CBAF), not ++ * necessarily requiring a wireless adapter; ++ * (g) composite device setup that can create one or more wireless ++ * configs, including appropriate association setup support; ++ * (h) more, TBD. ++ */ ++struct usb_composite_dev { ++ struct usb_gadget *gadget; ++ struct usb_request *req; ++ unsigned bufsiz; ++ ++ struct usb_configuration *config; ++ ++ /* internals */ ++ struct usb_device_descriptor desc; ++ struct list_head configs; ++ struct usb_composite_driver *driver; ++ u8 next_string_id; ++ ++ spinlock_t lock; ++ ++ /* REVISIT use and existence of lock ... */ ++}; ++ ++extern int usb_string_id(struct usb_composite_dev *c); ++ ++/* messaging utils */ ++#define DBG(d, fmt, args...) \ ++ dev_dbg(&(d)->gadget->dev , fmt , ## args) ++#define VDBG(d, fmt, args...) \ ++ dev_vdbg(&(d)->gadget->dev , fmt , ## args) ++#define ERROR(d, fmt, args...) \ ++ dev_err(&(d)->gadget->dev , fmt , ## args) ++#define WARNING(d, fmt, args...) \ ++ dev_warn(&(d)->gadget->dev , fmt , ## args) ++#define INFO(d, fmt, args...) \ ++ dev_info(&(d)->gadget->dev , fmt , ## args) ++ ++#endif /* __LINUX_USB_COMPOSITE_H */ +diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h +new file mode 100644 +index 0000000..5b88e36 +--- /dev/null ++++ b/include/linux/usb/ehci_def.h +@@ -0,0 +1,160 @@ ++/* ++ * Copyright (c) 2001-2002 by David Brownell ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __LINUX_USB_EHCI_DEF_H ++#define __LINUX_USB_EHCI_DEF_H ++ ++/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ ++ ++/* Section 2.2 Host Controller Capability Registers */ ++struct ehci_caps { ++ /* these fields are specified as 8 and 16 bit registers, ++ * but some hosts can't perform 8 or 16 bit PCI accesses. ++ */ ++ u32 hc_capbase; ++#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ ++#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ ++ u32 hcs_params; /* HCSPARAMS - offset 0x4 */ ++#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ ++#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ ++#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ ++#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ ++#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ ++#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ ++#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ ++ ++ u32 hcc_params; /* HCCPARAMS - offset 0x8 */ ++#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ ++#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ ++#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ ++#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ ++#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ ++#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ ++ u8 portroute [8]; /* nibbles for routing - offset 0xC */ ++} __attribute__ ((packed)); ++ ++ ++/* Section 2.3 Host Controller Operational Registers */ ++struct ehci_regs { ++ ++ /* USBCMD: offset 0x00 */ ++ u32 command; ++/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ ++#define CMD_PARK (1<<11) /* enable "park" on async qh */ ++#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ ++#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ ++#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ ++#define CMD_ASE (1<<5) /* async schedule enable */ ++#define CMD_PSE (1<<4) /* periodic schedule enable */ ++/* 3:2 is periodic frame list size */ ++#define CMD_RESET (1<<1) /* reset HC not bus */ ++#define CMD_RUN (1<<0) /* start/stop HC */ ++ ++ /* USBSTS: offset 0x04 */ ++ u32 status; ++#define STS_ASS (1<<15) /* Async Schedule Status */ ++#define STS_PSS (1<<14) /* Periodic Schedule Status */ ++#define STS_RECL (1<<13) /* Reclamation */ ++#define STS_HALT (1<<12) /* Not running (any reason) */ ++/* some bits reserved */ ++ /* these STS_* flags are also intr_enable bits (USBINTR) */ ++#define STS_IAA (1<<5) /* Interrupted on async advance */ ++#define STS_FATAL (1<<4) /* such as some PCI access errors */ ++#define STS_FLR (1<<3) /* frame list rolled over */ ++#define STS_PCD (1<<2) /* port change detect */ ++#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ ++#define STS_INT (1<<0) /* "normal" completion (short, ...) */ ++ ++ /* USBINTR: offset 0x08 */ ++ u32 intr_enable; ++ ++ /* FRINDEX: offset 0x0C */ ++ u32 frame_index; /* current microframe number */ ++ /* CTRLDSSEGMENT: offset 0x10 */ ++ u32 segment; /* address bits 63:32 if needed */ ++ /* PERIODICLISTBASE: offset 0x14 */ ++ u32 frame_list; /* points to periodic list */ ++ /* ASYNCLISTADDR: offset 0x18 */ ++ u32 async_next; /* address of next async queue head */ ++ ++ u32 reserved [9]; ++ ++ /* CONFIGFLAG: offset 0x40 */ ++ u32 configured_flag; ++#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ ++ ++ /* PORTSC: offset 0x44 */ ++ u32 port_status [0]; /* up to N_PORTS */ ++/* 31:23 reserved */ ++#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ ++#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ ++#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ ++/* 19:16 for port testing */ ++#define PORT_LED_OFF (0<<14) ++#define PORT_LED_AMBER (1<<14) ++#define PORT_LED_GREEN (2<<14) ++#define PORT_LED_MASK (3<<14) ++#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ ++#define PORT_POWER (1<<12) /* true: has power (see PPC) */ ++#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ ++/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ ++/* 9 reserved */ ++#define PORT_RESET (1<<8) /* reset port */ ++#define PORT_SUSPEND (1<<7) /* suspend port */ ++#define PORT_RESUME (1<<6) /* resume it */ ++#define PORT_OCC (1<<5) /* over current change */ ++#define PORT_OC (1<<4) /* over current active */ ++#define PORT_PEC (1<<3) /* port enable change */ ++#define PORT_PE (1<<2) /* port enable */ ++#define PORT_CSC (1<<1) /* connect status change */ ++#define PORT_CONNECT (1<<0) /* device connected */ ++#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) ++} __attribute__ ((packed)); ++ ++#define USBMODE 0x68 /* USB Device mode */ ++#define USBMODE_SDIS (1<<3) /* Stream disable */ ++#define USBMODE_BE (1<<2) /* BE/LE endianness select */ ++#define USBMODE_CM_HC (3<<0) /* host controller mode */ ++#define USBMODE_CM_IDLE (0<<0) /* idle state */ ++ ++/* Appendix C, Debug port ... intended for use with special "debug devices" ++ * that can help if there's no serial console. (nonstandard enumeration.) ++ */ ++struct ehci_dbg_port { ++ u32 control; ++#define DBGP_OWNER (1<<30) ++#define DBGP_ENABLED (1<<28) ++#define DBGP_DONE (1<<16) ++#define DBGP_INUSE (1<<10) ++#define DBGP_ERRCODE(x) (((x)>>7)&0x07) ++# define DBGP_ERR_BAD 1 ++# define DBGP_ERR_SIGNAL 2 ++#define DBGP_ERROR (1<<6) ++#define DBGP_GO (1<<5) ++#define DBGP_OUT (1<<4) ++#define DBGP_LEN(x) (((x)>>0)&0x0f) ++ u32 pids; ++#define DBGP_PID_GET(x) (((x)>>16)&0xff) ++#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) ++ u32 data03; ++ u32 data47; ++ u32 address; ++#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) ++} __attribute__ ((packed)); ++ ++#endif /* __LINUX_USB_EHCI_DEF_H */ +diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h +index cf468fb..0460a74 100644 +--- a/include/linux/usb/gadget.h ++++ b/include/linux/usb/gadget.h +@@ -33,7 +33,8 @@ struct usb_ep; + * @short_not_ok: When reading data, makes short packets be + * treated as errors (queue stops advancing till cleanup). + * @complete: Function called when request completes, so this request and +- * its buffer may be re-used. ++ * its buffer may be re-used. The function will always be called with ++ * interrupts disabled, and it must not sleep. + * Reads terminate with a short packet, or when the buffer fills, + * whichever comes first. When writes terminate, some data bytes + * will usually still be in flight (often in a hardware fifo). +@@ -271,7 +272,10 @@ static inline void usb_ep_free_request(struct usb_ep *ep, + * (Note that some USB device controllers disallow protocol stall responses + * in some cases.) When control responses are deferred (the response is + * written after the setup callback returns), then usb_ep_set_halt() may be +- * used on ep0 to trigger protocol stalls. ++ * used on ep0 to trigger protocol stalls. Depending on the controller, ++ * it may not be possible to trigger a status-stage protocol stall when the ++ * data stage is over, that is, from within the response's completion ++ * routine. + * + * For periodic endpoints, like interrupt or isochronous ones, the usb host + * arranges to poll once per interval, and the gadget driver usually will +@@ -858,6 +862,25 @@ int usb_descriptor_fillbuf(void *, unsigned, + int usb_gadget_config_buf(const struct usb_config_descriptor *config, + void *buf, unsigned buflen, const struct usb_descriptor_header **desc); + ++/* copy a NULL-terminated vector of descriptors */ ++struct usb_descriptor_header **usb_copy_descriptors( ++ struct usb_descriptor_header **); ++ ++/* return copy of endpoint descriptor given original descriptor set */ ++struct usb_endpoint_descriptor *usb_find_endpoint( ++ struct usb_descriptor_header **src, ++ struct usb_descriptor_header **copy, ++ struct usb_endpoint_descriptor *match); ++ ++/** ++ * usb_free_descriptors - free descriptors returned by usb_copy_descriptors() ++ * @v: vector of descriptors ++ */ ++static inline void usb_free_descriptors(struct usb_descriptor_header **v) ++{ ++ kfree(v); ++} ++ + /*-------------------------------------------------------------------------*/ + + /* utility wrapping a simple endpoint selection policy */ +diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h +new file mode 100644 +index 0000000..e345cea +--- /dev/null ++++ b/include/linux/usb/irda.h +@@ -0,0 +1,151 @@ ++/* ++ * USB IrDA Bridge Device Definition ++ */ ++ ++#ifndef __LINUX_USB_IRDA_H ++#define __LINUX_USB_IRDA_H ++ ++/* This device should use Application-specific class */ ++ ++#define USB_SUBCLASS_IRDA 0x02 ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Class-Specific requests (bRequest field) */ ++ ++#define USB_REQ_CS_IRDA_RECEIVING 1 ++#define USB_REQ_CS_IRDA_CHECK_MEDIA_BUSY 3 ++#define USB_REQ_CS_IRDA_RATE_SNIFF 4 ++#define USB_REQ_CS_IRDA_UNICAST_LIST 5 ++#define USB_REQ_CS_IRDA_GET_CLASS_DESC 6 ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Class-Specific descriptor */ ++ ++#define USB_DT_CS_IRDA 0x21 ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Data sizes */ ++ ++#define USB_IRDA_DS_2048 (1 << 5) ++#define USB_IRDA_DS_1024 (1 << 4) ++#define USB_IRDA_DS_512 (1 << 3) ++#define USB_IRDA_DS_256 (1 << 2) ++#define USB_IRDA_DS_128 (1 << 1) ++#define USB_IRDA_DS_64 (1 << 0) ++ ++/* Window sizes */ ++ ++#define USB_IRDA_WS_7 (1 << 6) ++#define USB_IRDA_WS_6 (1 << 5) ++#define USB_IRDA_WS_5 (1 << 4) ++#define USB_IRDA_WS_4 (1 << 3) ++#define USB_IRDA_WS_3 (1 << 2) ++#define USB_IRDA_WS_2 (1 << 1) ++#define USB_IRDA_WS_1 (1 << 0) ++ ++/* Min turnaround times in usecs */ ++ ++#define USB_IRDA_MTT_0 (1 << 7) ++#define USB_IRDA_MTT_10 (1 << 6) ++#define USB_IRDA_MTT_50 (1 << 5) ++#define USB_IRDA_MTT_100 (1 << 4) ++#define USB_IRDA_MTT_500 (1 << 3) ++#define USB_IRDA_MTT_1000 (1 << 2) ++#define USB_IRDA_MTT_5000 (1 << 1) ++#define USB_IRDA_MTT_10000 (1 << 0) ++ ++/* Baud rates */ ++ ++#define USB_IRDA_BR_4000000 (1 << 8) ++#define USB_IRDA_BR_1152000 (1 << 7) ++#define USB_IRDA_BR_576000 (1 << 6) ++#define USB_IRDA_BR_115200 (1 << 5) ++#define USB_IRDA_BR_57600 (1 << 4) ++#define USB_IRDA_BR_38400 (1 << 3) ++#define USB_IRDA_BR_19200 (1 << 2) ++#define USB_IRDA_BR_9600 (1 << 1) ++#define USB_IRDA_BR_2400 (1 << 0) ++ ++/* Additional BOFs */ ++ ++#define USB_IRDA_AB_0 (1 << 7) ++#define USB_IRDA_AB_1 (1 << 6) ++#define USB_IRDA_AB_2 (1 << 5) ++#define USB_IRDA_AB_3 (1 << 4) ++#define USB_IRDA_AB_6 (1 << 3) ++#define USB_IRDA_AB_12 (1 << 2) ++#define USB_IRDA_AB_24 (1 << 1) ++#define USB_IRDA_AB_48 (1 << 0) ++ ++/* IRDA Rate Sniff */ ++ ++#define USB_IRDA_RATE_SNIFF 1 ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct usb_irda_cs_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __le16 bcdSpecRevision; ++ __u8 bmDataSize; ++ __u8 bmWindowSize; ++ __u8 bmMinTurnaroundTime; ++ __le16 wBaudRate; ++ __u8 bmAdditionalBOFs; ++ __u8 bIrdaRateSniff; ++ __u8 bMaxUnicastList; ++} __attribute__ ((packed)); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Data Format */ ++ ++#define USB_IRDA_STATUS_MEDIA_BUSY (1 << 7) ++ ++/* The following is a 4-bit value used for both ++ * inbound and outbound headers: ++ * ++ * 0 - speed ignored ++ * 1 - 2400 bps ++ * 2 - 9600 bps ++ * 3 - 19200 bps ++ * 4 - 38400 bps ++ * 5 - 57600 bps ++ * 6 - 115200 bps ++ * 7 - 576000 bps ++ * 8 - 1.152 Mbps ++ * 9 - 5 mbps ++ * 10..15 - Reserved ++ */ ++#define USB_IRDA_STATUS_LINK_SPEED 0x0f ++ ++/* The following is a 4-bit value used only for ++ * outbound header: ++ * ++ * 0 - No change (BOF ignored) ++ * 1 - 48 BOFs ++ * 2 - 24 BOFs ++ * 3 - 12 BOFs ++ * 4 - 6 BOFs ++ * 5 - 3 BOFs ++ * 6 - 2 BOFs ++ * 7 - 1 BOFs ++ * 8 - 0 BOFs ++ * 9..15 - Reserved ++ */ ++#define USB_IRDA_EXTRA_BOFS 0xf0 ++ ++struct usb_irda_inbound_header { ++ __u8 bmStatus; ++}; ++ ++struct usb_irda_outbound_header { ++ __u8 bmChange; ++}; ++ ++#endif /* __LINUX_USB_IRDA_H */ ++ +diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h +new file mode 100644 +index 0000000..630962c +--- /dev/null ++++ b/include/linux/usb/musb.h +@@ -0,0 +1,98 @@ ++/* ++ * This is used to for host and peripheral modes of the driver for ++ * Inventra (Multidrop) Highspeed Dual-Role Controllers: (M)HDRC. ++ * ++ * Board initialization should put one of these into dev->platform_data, ++ * probably on some platform_device named "musb_hdrc". It encapsulates ++ * key configuration differences between boards. ++ */ ++ ++/* The USB role is defined by the connector used on the board, so long as ++ * standards are being followed. (Developer boards sometimes won't.) ++ */ ++enum musb_mode { ++ MUSB_UNDEFINED = 0, ++ MUSB_HOST, /* A or Mini-A connector */ ++ MUSB_PERIPHERAL, /* B or Mini-B connector */ ++ MUSB_OTG /* Mini-AB connector */ ++}; ++ ++struct clk; ++ ++struct musb_hdrc_eps_bits { ++ const char name[16]; ++ u8 bits; ++}; ++ ++struct musb_hdrc_config { ++ /* MUSB configuration-specific details */ ++ unsigned multipoint:1; /* multipoint device */ ++ unsigned dyn_fifo:1; /* supports dynamic fifo sizing */ ++ unsigned soft_con:1; /* soft connect required */ ++ unsigned utm_16:1; /* utm data witdh is 16 bits */ ++ unsigned big_endian:1; /* true if CPU uses big-endian */ ++ unsigned mult_bulk_tx:1; /* Tx ep required for multbulk pkts */ ++ unsigned mult_bulk_rx:1; /* Rx ep required for multbulk pkts */ ++ unsigned high_iso_tx:1; /* Tx ep required for HB iso */ ++ unsigned high_iso_rx:1; /* Rx ep required for HD iso */ ++ unsigned dma:1; /* supports DMA */ ++ unsigned vendor_req:1; /* vendor registers required */ ++ ++ u8 num_eps; /* number of endpoints _with_ ep0 */ ++ u8 dma_channels; /* number of dma channels */ ++ u8 dyn_fifo_size; /* dynamic size in bytes */ ++ u8 vendor_ctrl; /* vendor control reg width */ ++ u8 vendor_stat; /* vendor status reg witdh */ ++ u8 dma_req_chan; /* bitmask for required dma channels */ ++ u8 ram_bits; /* ram address size */ ++ ++ struct musb_hdrc_eps_bits *eps_bits; ++}; ++ ++struct musb_hdrc_platform_data { ++ /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */ ++ u8 mode; ++ ++ /* for clk_get() */ ++ const char *clock; ++ ++ /* (HOST or OTG) switch VBUS on/off */ ++ int (*set_vbus)(struct device *dev, int is_on); ++ ++ /* (HOST or OTG) mA/2 power supplied on (default = 8mA) */ ++ u8 power; ++ ++ /* (PERIPHERAL) mA/2 max power consumed (default = 100mA) */ ++ u8 min_power; ++ ++ /* (HOST or OTG) msec/2 after VBUS on till power good */ ++ u8 potpgt; ++ ++ /* Power the device on or off */ ++ int (*set_power)(int state); ++ ++ /* Turn device clock on or off */ ++ int (*set_clock)(struct clk *clock, int is_on); ++ ++ /* MUSB configuration-specific details */ ++ struct musb_hdrc_config *config; ++}; ++ ++ ++/* TUSB 6010 support */ ++ ++#define TUSB6010_OSCCLK_60 16667 /* psec/clk @ 60.0 MHz */ ++#define TUSB6010_REFCLK_24 41667 /* psec/clk @ 24.0 MHz XI */ ++#define TUSB6010_REFCLK_19 52083 /* psec/clk @ 19.2 MHz CLKIN */ ++ ++#ifdef CONFIG_ARCH_OMAP2 ++ ++extern int __init tusb6010_setup_interface( ++ struct musb_hdrc_platform_data *data, ++ unsigned ps_refclk, unsigned waitpin, ++ unsigned async_cs, unsigned sync_cs, ++ unsigned irq, unsigned dmachan); ++ ++extern int tusb6010_platform_retime(unsigned is_refclk); ++ ++#endif /* OMAP2 */ +diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h +index 29d6458..0a6e6d4 100644 +--- a/include/linux/usb/rndis_host.h ++++ b/include/linux/usb/rndis_host.h +@@ -260,7 +260,8 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */ + + + extern void rndis_status(struct usbnet *dev, struct urb *urb); +-extern int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf); ++extern int ++rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen); + extern int + generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags); + extern void rndis_unbind(struct usbnet *dev, struct usb_interface *intf); +diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h +index 8f891cb..655341d 100644 +--- a/include/linux/usb/serial.h ++++ b/include/linux/usb/serial.h +@@ -17,7 +17,8 @@ + #include <linux/mutex.h> + + #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ +-#define SERIAL_TTY_MINORS 255 /* loads of devices :) */ ++#define SERIAL_TTY_MINORS 254 /* loads of devices :) */ ++#define SERIAL_TTY_NO_MINOR 255 /* No minor was assigned */ + + /* The maximum number of ports one device can grab at once */ + #define MAX_NUM_PORTS 8 +@@ -62,7 +63,7 @@ + */ + struct usb_serial_port { + struct usb_serial *serial; +- struct tty_struct *tty; ++ struct tty_port port; + spinlock_t lock; + struct mutex mutex; + unsigned char number; +@@ -89,7 +90,6 @@ struct usb_serial_port { + + wait_queue_head_t write_wait; + struct work_struct work; +- int open_count; + char throttled; + char throttle_req; + char console; +@@ -217,22 +217,27 @@ struct usb_serial_driver { + int (*resume)(struct usb_serial *serial); + + /* serial function calls */ +- int (*open)(struct usb_serial_port *port, struct file *filp); +- void (*close)(struct usb_serial_port *port, struct file *filp); +- int (*write)(struct usb_serial_port *port, const unsigned char *buf, +- int count); +- int (*write_room)(struct usb_serial_port *port); +- int (*ioctl)(struct usb_serial_port *port, struct file *file, ++ /* Called by console with tty = NULL and by tty */ ++ int (*open)(struct tty_struct *tty, ++ struct usb_serial_port *port, struct file *filp); ++ void (*close)(struct tty_struct *tty, ++ struct usb_serial_port *port, struct file *filp); ++ int (*write)(struct tty_struct *tty, struct usb_serial_port *port, ++ const unsigned char *buf, int count); ++ /* Called only by the tty layer */ ++ int (*write_room)(struct tty_struct *tty); ++ int (*ioctl)(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +- void (*set_termios)(struct usb_serial_port *port, struct ktermios *old); +- void (*break_ctl)(struct usb_serial_port *port, int break_state); +- int (*chars_in_buffer)(struct usb_serial_port *port); +- void (*throttle)(struct usb_serial_port *port); +- void (*unthrottle)(struct usb_serial_port *port); +- int (*tiocmget)(struct usb_serial_port *port, struct file *file); +- int (*tiocmset)(struct usb_serial_port *port, struct file *file, ++ void (*set_termios)(struct tty_struct *tty, ++ struct usb_serial_port *port, struct ktermios *old); ++ void (*break_ctl)(struct tty_struct *tty, int break_state); ++ int (*chars_in_buffer)(struct tty_struct *tty); ++ void (*throttle)(struct tty_struct *tty); ++ void (*unthrottle)(struct tty_struct *tty); ++ int (*tiocmget)(struct tty_struct *tty, struct file *file); ++ int (*tiocmset)(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +- ++ /* USB events */ + void (*read_int_callback)(struct urb *urb); + void (*write_int_callback)(struct urb *urb); + void (*read_bulk_callback)(struct urb *urb); +@@ -270,19 +275,19 @@ static inline void usb_serial_console_disconnect(struct usb_serial *serial) {} + /* Functions needed by other parts of the usbserial core */ + extern struct usb_serial *usb_serial_get_by_index(unsigned int minor); + extern void usb_serial_put(struct usb_serial *serial); +-extern int usb_serial_generic_open(struct usb_serial_port *port, +- struct file *filp); +-extern int usb_serial_generic_write(struct usb_serial_port *port, +- const unsigned char *buf, int count); +-extern void usb_serial_generic_close(struct usb_serial_port *port, +- struct file *filp); ++extern int usb_serial_generic_open(struct tty_struct *tty, ++ struct usb_serial_port *port, struct file *filp); ++extern int usb_serial_generic_write(struct tty_struct *tty, ++ struct usb_serial_port *port, const unsigned char *buf, int count); ++extern void usb_serial_generic_close(struct tty_struct *tty, ++ struct usb_serial_port *port, struct file *filp); + extern int usb_serial_generic_resume(struct usb_serial *serial); +-extern int usb_serial_generic_write_room(struct usb_serial_port *port); +-extern int usb_serial_generic_chars_in_buffer(struct usb_serial_port *port); ++extern int usb_serial_generic_write_room(struct tty_struct *tty); ++extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty); + extern void usb_serial_generic_read_bulk_callback(struct urb *urb); + extern void usb_serial_generic_write_bulk_callback(struct urb *urb); +-extern void usb_serial_generic_throttle(struct usb_serial_port *port); +-extern void usb_serial_generic_unthrottle(struct usb_serial_port *port); ++extern void usb_serial_generic_throttle(struct tty_struct *tty); ++extern void usb_serial_generic_unthrottle(struct tty_struct *tty); + extern void usb_serial_generic_shutdown(struct usb_serial *serial); + extern int usb_serial_generic_register(int debug); + extern void usb_serial_generic_deregister(void); diff --git a/packages/linux/linux-rp_2.6.25+2.6.26-rc4.bb b/packages/linux/linux-rp_2.6.25+2.6.26-rc4.bb index a027a59380..3f9eac86a5 100644 --- a/packages/linux/linux-rp_2.6.25+2.6.26-rc4.bb +++ b/packages/linux/linux-rp_2.6.25+2.6.26-rc4.bb @@ -19,7 +19,7 @@ DEFAULT_PREFERENCE_spitz = "1" # Patches submitted upstream are towards top of this list # Hacks should clearly named and at the bottom SRC_URI = "${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/linux-2.6.25.tar.bz2 \ - ${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/testing/patch-2.6.26-rc4.bz2;patch=1 \ + ${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/testing/v2.6.26/patch-2.6.26-rc4.bz2;patch=1 \ ${RPSRC}/lzo_jffs2_sysfs-r1.patch;patch=1 \ ${RPSRC}/hx2750_base-r34.patch;patch=1 \ ${RPSRC}/hx2750_bl-r9.patch;patch=1 \ diff --git a/packages/linux/linux-rp_2.6.26.bb b/packages/linux/linux-rp_2.6.26.bb index 00e39a77a3..dd126285ea 100644 --- a/packages/linux/linux-rp_2.6.26.bb +++ b/packages/linux/linux-rp_2.6.26.bb @@ -1,6 +1,6 @@ require linux-rp.inc -PR = "r4" +PR = "r5" DEFAULT_PREFERENCE = "-1" DEFAULT_PREFERENCE_qemuarm = "-1" @@ -82,6 +82,7 @@ SRC_URI_append_collie = "\ file://collie.patch;patch=1 \ file://collie_keymap.patch;patch=1 \ file://collie-ucbfix.patch;patch=1 \ + file://usb-gadget27bp.patch;patch=1 \ " SRC_URI_append_poodle = "\ diff --git a/packages/meta/slugos-packages.bb b/packages/meta/slugos-packages.bb index dd19abccaf..b01c96b189 100644 --- a/packages/meta/slugos-packages.bb +++ b/packages/meta/slugos-packages.bb @@ -92,6 +92,7 @@ SLUGOS_PACKAGES = "\ fuse \ gawk \ gcc \ + gdb \ gdbm \ glib-2.0 \ gnu-config \ @@ -254,7 +255,6 @@ SLUGOS_BROKEN_PACKAGES = "\ ctorrent \ dsniff \ eciadsl \ - gdb \ gspcav1 \ linphone \ lirc-modules lirc \ diff --git a/packages/mozilla/fennec_hg.bb b/packages/mozilla/fennec_hg.bb index 0760768e09..8c6f326f73 100644 --- a/packages/mozilla/fennec_hg.bb +++ b/packages/mozilla/fennec_hg.bb @@ -1,13 +1,13 @@ DESCRIPTION = "Mozilla Mobile browser" DEPENDS += "cairo alsa-lib " -PV = "0.8+0.9pre" -MOZPV = "0.9pre" -PR = "r10" +PV = "0.9+1.0a1" +MOZPV = "1.0a1" +PR = "r0" PE = "1" -SRC_URI = "hg://hg.mozilla.org/;module=mozilla-central;rev=3a9a64e5bedc \ - hg://hg.mozilla.org/;module=mobile-browser;rev=53d19b4b249a \ +SRC_URI = "hg://hg.mozilla.org/;module=mozilla-central;rev=6c2f8bd79cbc \ + hg://hg.mozilla.org/;module=mobile-browser;rev=8f96b58057ad \ file://jsautocfg.h \ file://jsautocfg-dontoverwrite.patch;patch=1 \ " @@ -28,6 +28,7 @@ do_configure_prepend() { if [ -e ${WORKDIR}/mobile-browser ] ; then mv ${WORKDIR}/mobile-browser ${S}/mobile fi + sed -i -e 's:head\ -1:head\ -n1:g' client.mk oe_runmake -f client.mk CONFIGURE_ARGS="${EXTRA_OECONF}" configure } diff --git a/packages/nfs-utils/nfs-utils_1.1.2.bb b/packages/nfs-utils/nfs-utils_1.1.2.bb index bcdabd59e5..872a9bf8ff 100644 --- a/packages/nfs-utils/nfs-utils_1.1.2.bb +++ b/packages/nfs-utils/nfs-utils_1.1.2.bb @@ -5,7 +5,7 @@ LICENSE = "GPL" PR = "2" -DEPENDS = "tcp-wrappers libevent" +DEPENDS = "e2fsprogs tcp-wrappers libevent" SRC_URI = "${SOURCEFORGE_MIRROR}/nfs/nfs-utils-${PV}.tar.gz \ file://nfsserver \ diff --git a/packages/openchrome/configure-dri.patch b/packages/openchrome/configure-dri.patch new file mode 100644 index 0000000000..605078ec2b --- /dev/null +++ b/packages/openchrome/configure-dri.patch @@ -0,0 +1,11 @@ +--- s/configure.ac~ 2008-07-27 10:50:51.000000000 +0100 ++++ s/configure.ac 2008-07-27 10:57:25.000000000 +0100 +@@ -70,7 +70,7 @@ + XORG_DRIVER_CHECK_EXT(DPMSExtension, xextproto) + + # Checks for pkg-config packages +-PKG_CHECK_MODULES(XORG, [xorg-server xproto xvmc fontsproto libdrm $REQUIRED_MODULES]) ++PKG_CHECK_MODULES(XORG, [xorg-server xproto xvmc fontsproto libdrm xf86driproto $REQUIRED_MODULES]) + sdkdir=$(pkg-config --variable=sdkdir xorg-server) + + # Checks for libraries. diff --git a/packages/pcsc-lite/files/pcscd.init b/packages/pcsc-lite/files/pcscd.init new file mode 100644 index 0000000000..92385ab196 --- /dev/null +++ b/packages/pcsc-lite/files/pcscd.init @@ -0,0 +1,32 @@ +#!/bin/sh +DAEMON=/usr/sbin/pcscd +NAME=pcscd +DESC="PCSC Daemon" +PIDFILE=/var/run/pcscd/pcscd.pid +ARGS="" + +test -f $DAEMON || exit 0 + +case "$1" in + start) + echo -n "Starting $DESC: $NAME" + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $ARGS + echo "." + ;; + stop) + echo -n "Stopping $DESC: $NAME" + start-stop-daemon --stop --quiet --pidfile $PIDFILE --exec $DAEMON + echo "." + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac + +exit 0 diff --git a/packages/pcsc-lite/pcsc-lite_1.4.102.bb b/packages/pcsc-lite/pcsc-lite_1.4.102.bb new file mode 100644 index 0000000000..0455ae47a8 --- /dev/null +++ b/packages/pcsc-lite/pcsc-lite_1.4.102.bb @@ -0,0 +1,35 @@ +DESCRIPTION = "PC/SC Lite smart card framework and applications" +HOMEPAGE = "http://pcsclite.alioth.debian.org/" +LICENSE = "BSD" +PR = "r0" + +DEPENDS = "hal" +RDEPENDS_${PN} = "hal" + +SRC_URI = "http://alioth.debian.org/download.php/2479/pcsc-lite-${PV}.tar.bz2 \ + file://pcscd.init " + +inherit autotools update-rc.d + +INITSCRIPT_NAME = "pcscd" +INITSCRIPT_PARAMS = "defaults" + +EXTRA_OECONF = " \ + --enable-libhal \ + --disable-libusb \ + --enable-usbdropdir=${libdir}/pcsc/drivers \ + " + +do_stage() { + autotools_stage_all +} + +do_install() { + oe_runmake DESTDIR="${D}" install + install -d "${D}/etc/init.d" + install -m 755 "${WORKDIR}/pcscd.init" "${D}/etc/init.d/pcscd" +} + +PACKAGES =+ "libpcsclite" + +FILES_libpcsclite = "${libdir}/libpcsclite.so.*" diff --git a/packages/perl/perl-native_5.8.8.bb b/packages/perl/perl-native_5.8.8.bb index c1f2585658..c58f82822c 100644 --- a/packages/perl/perl-native_5.8.8.bb +++ b/packages/perl/perl-native_5.8.8.bb @@ -55,8 +55,10 @@ do_configure () { -Ud_csh \ -Uusesfio \ -Uusenm -des - sed 's!${STAGING_DIR}/bin!${STAGING_BINDIR}!; - s!${STAGING_DIR}/lib!${STAGING_LIBDIR}!' < config.sh > config.sh.new + sed "s!${STAGING_DIR}/bin!${STAGING_BINDIR}!; + s!${STAGING_DIR}/lib!${STAGING_LIBDIR}!; + s!^installbin=.*!installbin=\'${STAGING_BINDIR}\'!; + s!^installsitebin=.*!installsitebin=\'${STAGING_BINDIR}\'!" < config.sh > config.sh.new mv config.sh.new config.sh } do_stage_append() { diff --git a/packages/python/python-2.5-manifest.inc b/packages/python/python-2.5-manifest.inc index 827105ce69..4142a4b620 100644 --- a/packages/python/python-2.5-manifest.inc +++ b/packages/python/python-2.5-manifest.inc @@ -1,12 +1,12 @@ # WARNING: This file is AUTO GENERATED: Manual edits will be lost next time I regenerate the file. -# Generator: 'generate-manifest-2.5.py' Version 20080722 (C) 2002-2008 Michael 'Mickey' Lauer <mlauer@vanille-media.de> +# Generator: './generate-manifest-2.5.py' Version 20080722 (C) 2002-2008 Michael 'Mickey' Lauer <mlauer@vanille-media.de> # Visit the Python for Embedded Systems Site => http://www.Vanille.de/projects/python.spy -PROVIDES+="python-profile python-threading python-distutils python-doctest python-codecs python-ctypes python-pickle python-bzip2 python-datetime python-core python-io python-compiler python-compression python-re python-xmlrpc python-terminal python-email python-image python-tests python-core-dbg python-resource python-devel python-difflib python-math python-syslog python-hotshot python-unixadmin python-textutils python-tkinter python-gdbm python-elementtree python-fcntl python-netclient python-pprint python-netserver python-curses python-smtpd python-html python-readline python-subprocess python-pydoc python-logging python-mailbox python-xml python-mime python-sqlite3 python-sqlite3-tests python-unittest python-stringold python-robotparser python-lib-old-and-deprecated python-compile python-debugger python-pkgutil python-shell python-bsddb python-mmap python-zlib python-db python-crypt python-idle python-lang python-audio " +PROVIDES+="python-profile python-threading python-distutils python-doctest python-codecs python-ctypes python-pickle python-bzip2 python-datetime python-core python-io python-compiler python-compression python-re python-xmlrpc python-terminal python-email python-image python-tests python-core-dbg python-resource python-devel python-difflib python-math python-syslog python-hotshot python-unixadmin python-textutils python-tkinter python-gdbm python-elementtree python-fcntl python-netclient python-pprint python-netserver python-curses python-smtpd python-html python-readline python-subprocess python-pydoc python-logging python-mailbox python-xml python-mime python-sqlite3 python-sqlite3-tests python-unittest python-stringold python-robotparser python-compile python-debugger python-pkgutil python-shell python-bsddb python-mmap python-zlib python-db python-crypt python-idle python-lang python-audio " -PACKAGES="python-profile python-threading python-distutils python-doctest python-codecs python-ctypes python-pickle python-bzip2 python-datetime python-core python-io python-compiler python-compression python-re python-xmlrpc python-terminal python-email python-image python-tests python-core-dbg python-resource python-devel python-difflib python-math python-syslog python-hotshot python-unixadmin python-textutils python-tkinter python-gdbm python-elementtree python-fcntl python-netclient python-pprint python-netserver python-curses python-smtpd python-html python-readline python-subprocess python-pydoc python-logging python-mailbox python-xml python-mime python-sqlite3 python-sqlite3-tests python-unittest python-stringold python-robotparser python-lib-old-and-deprecated python-compile python-debugger python-pkgutil python-shell python-bsddb python-mmap python-zlib python-db python-crypt python-idle python-lang python-audio " +PACKAGES="python-profile python-threading python-distutils python-doctest python-codecs python-ctypes python-pickle python-bzip2 python-datetime python-core python-io python-compiler python-compression python-re python-xmlrpc python-terminal python-email python-image python-tests python-core-dbg python-resource python-devel python-difflib python-math python-syslog python-hotshot python-unixadmin python-textutils python-tkinter python-gdbm python-elementtree python-fcntl python-netclient python-pprint python-netserver python-curses python-smtpd python-html python-readline python-subprocess python-pydoc python-logging python-mailbox python-xml python-mime python-sqlite3 python-sqlite3-tests python-unittest python-stringold python-robotparser python-compile python-debugger python-pkgutil python-shell python-bsddb python-mmap python-zlib python-db python-crypt python-idle python-lang python-audio " DESCRIPTION_python-profile="Python Basic Profiling Support" PR_python-profile="ml0" @@ -69,7 +69,7 @@ RDEPENDS_python-compiler="python-core" FILES_python-compiler="${libdir}/python2.5/compiler " DESCRIPTION_python-compression="Python High Level Compression Support" -PR_python-compression="ml1" +PR_python-compression="ml0" RDEPENDS_python-compression="python-core python-zlib" FILES_python-compression="${libdir}/python2.5/gzip.* ${libdir}/python2.5/zipfile.* ${libdir}/python2.5/tarfile.* " @@ -214,7 +214,7 @@ RDEPENDS_python-pydoc="python-core python-lang python-stringold python-re" FILES_python-pydoc="${bindir}/pydoc ${libdir}/python2.5/pydoc.* " DESCRIPTION_python-logging="Python Logging Support" -PR_python-logging="ml1" +PR_python-logging="ml0" RDEPENDS_python-logging="python-core python-io python-lang python-pickle python-stringold" FILES_python-logging="${libdir}/python2.5/logging " @@ -224,7 +224,7 @@ RDEPENDS_python-mailbox="python-core python-mime" FILES_python-mailbox="${libdir}/python2.5/mailbox.* " DESCRIPTION_python-xml="Python basic XML support." -PR_python-xml="ml1" +PR_python-xml="ml0" RDEPENDS_python-xml="python-core python-re" FILES_python-xml="${libdir}/python2.5/lib-dynload/pyexpat.so ${libdir}/python2.5/xml ${libdir}/python2.5/xmllib.* " @@ -258,18 +258,13 @@ PR_python-robotparser="ml0" RDEPENDS_python-robotparser="python-core python-netclient" FILES_python-robotparser="${libdir}/python2.5/robotparser.* " -DESCRIPTION_python-lib-old-and-deprecated="Python Deprecated Libraries" -PR_python-lib-old-and-deprecated="ml0" -RDEPENDS_python-lib-old-and-deprecated="python-core" -FILES_python-lib-old-and-deprecated="${libdir}/python2.5/lib-old " - DESCRIPTION_python-compile="Python Bytecode Compilation Support" PR_python-compile="ml0" RDEPENDS_python-compile="python-core" FILES_python-compile="${libdir}/python2.5/py_compile.* ${libdir}/python2.5/compileall.* " DESCRIPTION_python-debugger="Python Debugger" -PR_python-debugger="ml1" +PR_python-debugger="ml0" RDEPENDS_python-debugger="python-core python-io python-lang python-re python-stringold python-shell python-pprint" FILES_python-debugger="${libdir}/python2.5/bdb.* ${libdir}/python2.5/pdb.* " @@ -279,12 +274,12 @@ RDEPENDS_python-pkgutil="python-core" FILES_python-pkgutil="${libdir}/python2.5/pkgutil.* " DESCRIPTION_python-shell="Python Shell-Like Functionality" -PR_python-shell="ml1" +PR_python-shell="ml0" RDEPENDS_python-shell="python-core python-re" FILES_python-shell="${libdir}/python2.5/cmd.* ${libdir}/python2.5/commands.* ${libdir}/python2.5/dircache.* ${libdir}/python2.5/fnmatch.* ${libdir}/python2.5/glob.* ${libdir}/python2.5/popen2.* ${libdir}/python2.5/shlex.* ${libdir}/python2.5/shutil.* " DESCRIPTION_python-bsddb="Python Berkeley Database Bindings" -PR_python-bsddb="ml1" +PR_python-bsddb="ml0" RDEPENDS_python-bsddb="python-core" FILES_python-bsddb="${libdir}/python2.5/bsddb ${libdir}/python2.5/lib-dynload/_bsddb.so " @@ -304,7 +299,7 @@ RDEPENDS_python-db="python-core" FILES_python-db="${libdir}/python2.5/anydbm.* ${libdir}/python2.5/dumbdbm.* ${libdir}/python2.5/whichdb.* " DESCRIPTION_python-crypt="Python Basic Cryptographic and Hashing Support" -PR_python-crypt="ml1" +PR_python-crypt="ml0" RDEPENDS_python-crypt="python-core" FILES_python-crypt="${libdir}/python2.5/hashlib.* ${libdir}/python2.5/md5.* ${libdir}/python2.5/sha.* ${libdir}/python2.5/lib-dynload/crypt.so ${libdir}/python2.5/lib-dynload/_hashlib.so ${libdir}/python2.5/lib-dynload/_sha256.so ${libdir}/python2.5/lib-dynload/_sha512.so " @@ -314,7 +309,7 @@ RDEPENDS_python-idle="python-core python-tkinter" FILES_python-idle="${bindir}/idle ${libdir}/python2.5/idlelib " DESCRIPTION_python-lang="Python Low-Level Language Support" -PR_python-lang="ml1" +PR_python-lang="ml0" RDEPENDS_python-lang="python-core" FILES_python-lang="${libdir}/python2.5/lib-dynload/array.so ${libdir}/python2.5/lib-dynload/parser.so ${libdir}/python2.5/lib-dynload/operator.so ${libdir}/python2.5/lib-dynload/_weakref.so ${libdir}/python2.5/lib-dynload/itertools.so ${libdir}/python2.5/lib-dynload/collections.so ${libdir}/python2.5/lib-dynload/_bisect.so ${libdir}/python2.5/lib-dynload/_heapq.so ${libdir}/python2.5/atexit.* ${libdir}/python2.5/bisect.* ${libdir}/python2.5/code.* ${libdir}/python2.5/codeop.* ${libdir}/python2.5/dis.* ${libdir}/python2.5/heapq.* ${libdir}/python2.5/inspect.* ${libdir}/python2.5/keyword.* ${libdir}/python2.5/opcode.* ${libdir}/python2.5/symbol.* ${libdir}/python2.5/repr.* ${libdir}/python2.5/token.* ${libdir}/python2.5/tokenize.* ${libdir}/python2.5/traceback.* ${libdir}/python2.5/linecache.* ${libdir}/python2.5/weakref.* " diff --git a/packages/python/python-2.5.2/05-install.patch b/packages/python/python-2.5.2/05-install.patch new file mode 100644 index 0000000000..c3e249db1c --- /dev/null +++ b/packages/python/python-2.5.2/05-install.patch @@ -0,0 +1,13 @@ +Index: python/Lib/distutils/command/install.py +=================================================================== +--- python.orig/Lib/distutils/command/install.py 2007-03-06 17:15:43.000000000 -0300 ++++ python/Lib/distutils/command/install.py 2007-03-06 17:16:04.000000000 -0300 +@@ -601,7 +601,7 @@ + ('install_headers', has_headers), + ('install_scripts', has_scripts), + ('install_data', has_data), +- ('install_egg_info', lambda self:True), ++ ('install_egg_info', lambda self:False), + ] + + # class install diff --git a/packages/python/python-2.5.2/06-fix-urllib-exception.patch b/packages/python/python-2.5.2/06-fix-urllib-exception.patch new file mode 100644 index 0000000000..d096ee9402 --- /dev/null +++ b/packages/python/python-2.5.2/06-fix-urllib-exception.patch @@ -0,0 +1,13 @@ +Index: python/Lib/urllib.py +=================================================================== +--- python.orig/Lib/urllib.py 2007-03-06 17:16:49.000000000 -0300 ++++ python/Lib/urllib.py 2007-03-06 17:17:05.000000000 -0300 +@@ -358,7 +358,7 @@ + """Default error handler: close the connection and raise IOError.""" + void = fp.read() + fp.close() +- raise IOError, ('http error', errcode, errmsg, headers) ++ raise IOError, ('http error', errcode, errmsg) + + if hasattr(socket, "ssl"): + def open_https(self, url, data=None): diff --git a/packages/python/python-2.5.2/13-set-wakeup-fix.patch b/packages/python/python-2.5.2/13-set-wakeup-fix.patch new file mode 100644 index 0000000000..807014b7a7 --- /dev/null +++ b/packages/python/python-2.5.2/13-set-wakeup-fix.patch @@ -0,0 +1,87 @@ +Index: python-2.5.2/Modules/signalmodule.c +=================================================================== +--- python-2.5.2.orig/Modules/signalmodule.c 2008-02-23 13:10:12.000000000 -0300 ++++ python-2.5.2/Modules/signalmodule.c 2008-02-23 13:10:48.000000000 -0300 +@@ -12,6 +12,8 @@ + + #include <signal.h> + ++#include <sys/stat.h> ++ + #ifndef SIG_ERR + #define SIG_ERR ((PyOS_sighandler_t)(-1)) + #endif +@@ -75,6 +77,8 @@ + PyObject *func; + } Handlers[NSIG]; + ++static sig_atomic_t wakeup_fd = -1; ++ + /* Speed up sigcheck() when none tripped */ + static volatile sig_atomic_t is_tripped = 0; + +@@ -113,6 +117,7 @@ + static void + signal_handler(int sig_num) + { ++ const char dummy_byte = '\0'; + #ifdef WITH_THREAD + #ifdef WITH_PTH + if (PyThread_get_thread_ident() != main_thread) { +@@ -128,6 +133,8 @@ + cleared in PyErr_CheckSignals() before .tripped. */ + is_tripped = 1; + Py_AddPendingCall(checksignals_witharg, NULL); ++ if (wakeup_fd != -1) ++ write(wakeup_fd, &dummy_byte, 1); + #ifdef WITH_THREAD + } + #endif +@@ -267,6 +274,39 @@ + anything else -- the callable Python object used as a handler"); + + ++static PyObject * ++signal_set_wakeup_fd(PyObject *self, PyObject *args) ++{ ++ struct stat buf; ++ int fd, old_fd; ++ if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd)) ++ return NULL; ++#ifdef WITH_THREAD ++ if (PyThread_get_thread_ident() != main_thread) { ++ PyErr_SetString(PyExc_ValueError, ++ "set_wakeup_fd only works in main thread"); ++ return NULL; ++ } ++#endif ++ if (fd != -1 && fstat(fd, &buf) != 0) { ++ PyErr_SetString(PyExc_ValueError, "invalid fd"); ++ return NULL; ++ } ++ old_fd = wakeup_fd; ++ wakeup_fd = fd; ++ return PyLong_FromLong(old_fd); ++} ++ ++PyDoc_STRVAR(set_wakeup_fd_doc, ++"set_wakeup_fd(fd) -> fd\n\ ++\n\ ++Sets the fd to be written to (with '\\0') when a signal\n\ ++comes in. A library can use this to wakeup select or poll.\n\ ++The previous fd is returned.\n\ ++\n\ ++The fd must be non-blocking."); ++ ++ + /* List of functions defined in the module */ + static PyMethodDef signal_methods[] = { + #ifdef HAVE_ALARM +@@ -274,6 +314,7 @@ + #endif + {"signal", signal_signal, METH_VARARGS, signal_doc}, + {"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc}, ++ {"set_wakeup_fd", signal_set_wakeup_fd, METH_VARARGS, set_wakeup_fd_doc}, + #ifdef HAVE_PAUSE + {"pause", (PyCFunction)signal_pause, + METH_NOARGS,pause_doc}, diff --git a/packages/python/python-2.5.2/14-encodings-oriental.patch b/packages/python/python-2.5.2/14-encodings-oriental.patch new file mode 100644 index 0000000000..4c942f1b75 --- /dev/null +++ b/packages/python/python-2.5.2/14-encodings-oriental.patch @@ -0,0 +1,64 @@ +Index: python-2.5.2/debian/rules +=================================================================== +--- python-2.5.2.orig/debian/rules 2008-02-26 14:15:36.000000000 -0300 ++++ python-2.5.2/debian/rules 2008-02-26 14:17:42.000000000 -0300 +@@ -134,6 +134,8 @@ + ) + find $(d_dev)/$(scriptdir) -name "*.pyo" -type f | grep -v -f $(only_dev_list) | xargs rm -f + ++ find $(d_dev)/$(scriptdir)/encodings | grep -f $(only_dev_list) | xargs -i mv '{}' $(d_dev)/$(scriptdir)/encodings_orient ++ + # move the interpreter + mv $(d_dev)/usr/bin/python2.5 $(d_base)/usr/bin/python2.5 + +Index: python-2.5.2/Lib/encodings/__init__.py +=================================================================== +--- python-2.5.2.orig/Lib/encodings/__init__.py 2008-02-26 14:15:07.000000000 -0300 ++++ python-2.5.2/Lib/encodings/__init__.py 2008-02-26 14:17:42.000000000 -0300 +@@ -99,6 +99,14 @@ + pass + else: + break ++ ++ try: ++ mod = __import__('encodings_orient.' + modname, ++ globals(), locals(), _import_tail) ++ except ImportError: ++ pass ++ else: ++ break + else: + mod = None + +Index: python-2.5.2/Makefile.pre.in +=================================================================== +--- python-2.5.2.orig/Makefile.pre.in 2008-02-26 14:15:07.000000000 -0300 ++++ python-2.5.2/Makefile.pre.in 2008-02-26 14:17:42.000000000 -0300 +@@ -717,7 +717,7 @@ + PLATMACPATH=:plat-mac:plat-mac/lib-scriptpackages + LIBSUBDIRS= lib-tk site-packages test test/output test/data \ + test/decimaltestdata \ +- encodings compiler hotshot \ ++ encodings encodings_orient compiler hotshot \ + email email/mime email/test email/test/data \ + sqlite3 sqlite3/test \ + logging bsddb bsddb/test csv wsgiref \ +Index: python-2.5.2/debian/onlysdk +=================================================================== +--- python-2.5.2.orig/debian/onlysdk 2008-02-26 14:15:07.000000000 -0300 ++++ python-2.5.2/debian/onlysdk 2008-02-26 14:17:42.000000000 -0300 +@@ -1,7 +1,6 @@ + distutils + compile +-encodings/cp +-encodings/mac ++encodings_orient + doctest + unittest + hotshot +Index: python-2.5.2/Lib/encodings_orient/__init__.py +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ python-2.5.2/Lib/encodings_orient/__init__.py 2008-02-26 14:17:42.000000000 -0300 +@@ -0,0 +1 @@ ++#Dummy diff --git a/packages/python/python-2.5.2/16-bug1179-imageop.patch b/packages/python/python-2.5.2/16-bug1179-imageop.patch new file mode 100644 index 0000000000..895d4e0a17 --- /dev/null +++ b/packages/python/python-2.5.2/16-bug1179-imageop.patch @@ -0,0 +1,219 @@ +Index: python-2.5.2/Modules/imageop.c +=================================================================== +--- python-2.5.2.orig/Modules/imageop.c 2006-01-19 03:09:39.000000000 -0300 ++++ python-2.5.2/Modules/imageop.c 2008-04-07 16:29:09.000000000 -0300 +@@ -78,7 +78,7 @@ + char *cp, *ncp; + short *nsp; + Py_Int32 *nlp; +- int len, size, x, y, newx1, newx2, newy1, newy2; ++ int len, size, x, y, newx1, newx2, newy1, newy2, nlen; + int ix, iy, xstep, ystep; + PyObject *rv; + +@@ -90,13 +90,19 @@ + PyErr_SetString(ImageopError, "Size should be 1, 2 or 4"); + return 0; + } +- if ( len != size*x*y ) { ++ if (( len != size*x*y ) || ++ ( size != ((len / x) / y) )) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + xstep = (newx1 < newx2)? 1 : -1; + ystep = (newy1 < newy2)? 1 : -1; + ++ nlen = (abs(newx2-newx1)+1)*(abs(newy2-newy1)+1)*size; ++ if ( size != ((nlen / (abs(newx2-newx1)+1)) / (abs(newy2-newy1)+1)) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + rv = PyString_FromStringAndSize(NULL, + (abs(newx2-newx1)+1)*(abs(newy2-newy1)+1)*size); + if ( rv == 0 ) +@@ -132,7 +138,7 @@ + char *cp, *ncp; + short *nsp; + Py_Int32 *nlp; +- int len, size, x, y, newx, newy; ++ int len, size, x, y, newx, newy, nlen; + int ix, iy; + int oix, oiy; + PyObject *rv; +@@ -145,12 +151,18 @@ + PyErr_SetString(ImageopError, "Size should be 1, 2 or 4"); + return 0; + } +- if ( len != size*x*y ) { ++ if ( ( len != size*x*y ) || ++ ( size != ((len / x) / y) ) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } ++ nlen = newx*newy*size; ++ if ( size != ((nlen / newx) / newy) ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + +- rv = PyString_FromStringAndSize(NULL, newx*newy*size); ++ rv = PyString_FromStringAndSize(NULL, nlen); + if ( rv == 0 ) + return 0; + ncp = (char *)PyString_AsString(rv); +@@ -190,7 +202,8 @@ + PyErr_SetString(ImageopError, "Size should be 1 or 4"); + return 0; + } +- if ( maxx*maxy*width != len ) { ++ if ( ( maxx*maxy*width != len ) || ++ ( maxx != ((len / maxy) / width) ) ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } +@@ -240,7 +253,8 @@ + if ( !PyArg_ParseTuple(args, "s#iii", &cp, &len, &x, &y, &tres) ) + return 0; + +- if ( x*y != len ) { ++ if ( ( x*y != len ) || ++ ( x != len / y ) ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } +@@ -281,7 +295,8 @@ + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + +- if ( x*y != len ) { ++ if ( ( x*y != len ) || ++ ( x != len / y ) ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } +@@ -320,7 +335,8 @@ + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + +- if ( x*y != len ) { ++ if ( ( x*y != len ) || ++ ( x != len / y ) ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } +@@ -358,7 +374,8 @@ + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + +- if ( x*y != len ) { ++ if ( ( x*y != len ) || ++ ( x != len / y ) ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } +@@ -404,7 +421,8 @@ + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + +- if ( x*y != len ) { ++ if ( ( x*y != len ) || ++ ( x != len / y ) ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } +@@ -443,7 +461,11 @@ + if ( !PyArg_ParseTuple(args, "s#iiii", &cp, &len, &x, &y, &v0, &v1) ) + return 0; + +- nlen = x*y; ++ nlen = x*y; ++ if ( x != (nlen / y) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + if ( (nlen+7)/8 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; +@@ -481,6 +503,10 @@ + return 0; + + nlen = x*y; ++ if ( x != (nlen / y) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + if ( (nlen+3)/4 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; +@@ -517,6 +543,10 @@ + return 0; + + nlen = x*y; ++ if ( x != (nlen / y) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + if ( (nlen+1)/2 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; +@@ -554,6 +584,10 @@ + return 0; + + nlen = x*y; ++ if ( x != (nlen / y) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + if ( nlen*4 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; +@@ -598,6 +632,10 @@ + return 0; + + nlen = x*y; ++ if ( x != (nlen / y) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + if ( nlen != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; +@@ -648,6 +686,10 @@ + return 0; + + nlen = x*y; ++ if ( x != (nlen / y) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + if ( nlen*4 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; +@@ -693,6 +735,10 @@ + return 0; + + nlen = x*y; ++ if ( x != (nlen / y) ) { ++ PyErr_SetString(ImageopError, "String has incorrect length"); ++ return 0; ++ } + if ( nlen != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; +Index: python-2.5.2/Modules/rgbimgmodule.c +=================================================================== +--- python-2.5.2.orig/Modules/rgbimgmodule.c 2008-02-14 08:26:18.000000000 -0300 ++++ python-2.5.2/Modules/rgbimgmodule.c 2008-04-07 16:29:10.000000000 -0300 +@@ -299,6 +299,11 @@ + xsize = image.xsize; + ysize = image.ysize; + zsize = image.zsize; ++ tablen = xsize * ysize * zsize * sizeof(Py_Int32); ++ if (xsize != (((tablen / ysize) / zsize) / sizeof(Py_Int32))) { ++ PyErr_NoMemory(); ++ goto finally; ++ } + if (rle) { + tablen = ysize * zsize * sizeof(Py_Int32); + rlebuflen = (int) (1.05 * xsize +10); diff --git a/packages/python/python_2.5.2.bb b/packages/python/python_2.5.2.bb index 3ba35544a8..35bcf858e6 100644 --- a/packages/python/python_2.5.2.bb +++ b/packages/python/python_2.5.2.bb @@ -6,19 +6,27 @@ PRIORITY = "optional" DEPENDS = "python-native db gdbm openssl readline sqlite3 tcl tk zlib" DEPENDS_sharprom = "python-native db readline zlib gdbm openssl" # bump this on every change in contrib/python/generate-manifest-2.5.py -PR = "ml11" +PR = "ml13" PYTHON_MAJMIN = "2.5" -SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.bz2 \ - file://bindir-libdir.patch;patch=1 \ - file://crosscompile.patch;patch=1 \ - file://fix-tkinter-detection.patch;patch=1 \ - file://autohell.patch;patch=1 \ - file://sitebranding.patch;patch=1 \ - file://enable-ctypes-module.patch;patch=1 \ - file://default-is-optimized.patch;patch=1 \ - file://sitecustomize.py" +SRC_URI = "\ + http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.bz2 \ + file://bindir-libdir.patch;patch=1 \ + file://crosscompile.patch;patch=1 \ + file://fix-tkinter-detection.patch;patch=1 \ + file://autohell.patch;patch=1 \ + file://sitebranding.patch;patch=1 \ + file://enable-ctypes-module.patch;patch=1 \ + file://default-is-optimized.patch;patch=1 \ + \ + file://05-install.patch;patch=1 \ + file://06-fix-urllib-exception.patch;patch=1 \ + file://16-bug1179-imageop.patch;patch=1 \ + file://13-set-wakeup-fix.patch;patch=1 \ + \ + file://sitecustomize.py \ +" S = "${WORKDIR}/Python-${PV}" inherit autotools diff --git a/packages/tasks/task-xfce-base.bb b/packages/tasks/task-xfce-base.bb index c846b22de5..239725feaa 100644 --- a/packages/tasks/task-xfce-base.bb +++ b/packages/tasks/task-xfce-base.bb @@ -4,12 +4,31 @@ DESCRIPTION = "All packages required for an base XFCE installation" LICENSE = "MIT" -PR = "r3" +PR = "r4" inherit task -RDEPENDS_${PN} = "xfce-mcs-manager xfwm4 xfwm4-theme-default xfce-utils xfdesktop \ - xfce4-panel xfce4-panel-plugins xfce-mcs-plugins xfwm4-mcs-plugins \ - xfce4-panel-mcs-plugins \ - xfdesktop-mcs-plugins" +RDEPENDS_${PN} = " \ + xfce-mcs-manager \ + xfwm4 \ + xfwm4-theme-default \ + xfce-utils \ + xfdesktop \ + xfce4-panel \ + xfce4-panel-plugin-actions \ + xfce4-panel-plugin-clock \ + xfce4-panel-plugin-iconbox \ + xfce4-panel-plugin-launcher \ + xfce4-panel-plugin-pager \ + xfce4-panel-plugin-separator \ + xfce4-panel-plugin-showdesktop \ + xfce4-panel-plugin-systray \ + xfce4-panel-plugin-tasklist \ + xfce4-panel-plugin-windowlist \ + xfce-mcs-plugins \ + xfwm4-mcs-plugins \ + xfce4-panel-mcs-plugins \ + xfdesktop-mcs-plugins \ +" + RRECOMMENDS_${PN} = "xfce-utils-mcs-plugins" diff --git a/packages/ttf-fonts/ttf-wqy-zenhei_0.6.26.bb b/packages/ttf-fonts/ttf-wqy-zenhei_0.6.26.bb new file mode 100644 index 0000000000..873179d6a6 --- /dev/null +++ b/packages/ttf-fonts/ttf-wqy-zenhei_0.6.26.bb @@ -0,0 +1,19 @@ +require ttf.inc + +DESCRIPTION = "WenQuanYi Zen Hei - A Hei-Ti Style Chinese font" +AUTHOR = "Qianqian Fang and The WenQuanYi Project Contributors" +HOMEPAGE = "http://wqy.sourceforge.net/en/" +LICENSE = "GPLv2" + +SRC_URI = "${SOURCEFORGE_MIRROR}/wqy/wqy-zenhei-${PV}-0.tar.gz" +S = "${WORKDIR}/wqy-zenhei" + +do_install_append () { + install -d ${D}${sysconfdir}/fonts/conf.d/ + install -m 0644 ${S}/44-wqy-zenhei.conf ${D}${sysconfdir}/fonts/conf.d/ + install -m 0644 ${S}/66-wqy-zenhei-sharp.conf ${D}${sysconfdir}/fonts/conf.d/ +} + +PACKAGES = "${PN}" + +FILES_${PN} = "${datadir}/fonts ${sysconfdir}" diff --git a/packages/unicap/unicap_0.9.3.bb b/packages/unicap/unicap_0.9.3.bb index c527c80819..dcc0897aa8 100644 --- a/packages/unicap/unicap_0.9.3.bb +++ b/packages/unicap/unicap_0.9.3.bb @@ -2,7 +2,7 @@ DESCRIPTION = "A uniform interface to video capture devices." SECTION = "graphics" LICENSE = "GPL" DEPENDS = "intltool-native gtk+ libpng libxv" -PR = "r0" +PR = "r1" SRC_URI = "http://www.unicap-imaging.org/downloads/unicap-${PV}.tar.gz \ file://pkgconfig.patch;patch=1" @@ -13,10 +13,10 @@ do_stage () { autotools_stage_all } -PACKAGES += "libucil libunicapgtk" +PACKAGES += "libucil unicapgtk" FILES_${PN} = "${libdir}/libunicap.*so.* ${libdir}/unicap2/cpi/lib*.*so.*" FILES_${PN}-dev += " ${libdir}/unicap2/cpi/lib*.*so ${libdir}/unicap2/cpi/lib*.*a" FILES_${PN}-dbg += " ${libdir}/unicap2/cpi/.debug" FILES_libucil = "${libdir}/libucil*so.*" -FILES_libunicapgtk = "${libdir}/libunicapgtk*so.*" +FILES_unicapgtk = "${libdir}/libunicapgtk*so.*" diff --git a/packages/xorg-driver/xf86-video-omapfb_git.bb b/packages/xorg-driver/xf86-video-omapfb_git.bb index d14c1fd396..9be61695ae 100644 --- a/packages/xorg-driver/xf86-video-omapfb_git.bb +++ b/packages/xorg-driver/xf86-video-omapfb_git.bb @@ -2,9 +2,9 @@ require xorg-driver-video.inc DESCRIPTION = "X.Org X server -- OMAP display driver" -PR ="r8" +PR ="r10" -SRCREV = "9a4fe691d60ac29e510dfa5180bd799ead86d1d5" +SRCREV = "7bf64be8e809d00c10c6bdae6933bdc71c642ea4" PV = "0.0.1+${PR}+git${SRCREV}" SRC_URI = "git://git.pingu.fi/xf86-video-omapfb.git;protocol=http" diff --git a/packages/xorg-xserver/xserver-kdrive-1.4.0.90/xorg-1.4-kdrive-rotation.patch b/packages/xorg-xserver/xserver-kdrive-1.4.0.90/xorg-1.4-kdrive-rotation.patch new file mode 100644 index 0000000000..5e3eb8ee14 --- /dev/null +++ b/packages/xorg-xserver/xserver-kdrive-1.4.0.90/xorg-1.4-kdrive-rotation.patch @@ -0,0 +1,31 @@ +diff -rup a/hw/kdrive/src/kinput.c b/hw/kdrive/src/kinput.c +--- a/hw/kdrive/src/kinput.c 2007-08-23 21:04:53.000000000 +0200 ++++ b/hw/kdrive/src/kinput.c 2007-12-22 12:53:27.679853402 +0100 +@@ -2075,19 +2075,25 @@ KdEnqueuePointerEvent(KdPointerInfo *pi, + + /* we don't need to transform z, so we don't. */ + if (flags & KD_MOUSE_DELTA) { ++/* does it really make sense to transform relative coordinates depending on screen rotation?? + if (pi->transformCoordinates) { + x = matrix[0][0] * rx + matrix[0][1] * ry; + y = matrix[1][0] * rx + matrix[1][1] * ry; + } +- else { ++ else {*/ + x = rx; + y = ry; +- } ++/* }*/ + } + else { + if (pi->transformCoordinates) { + x = matrix[0][0] * rx + matrix[0][1] * ry; + y = matrix[1][0] * rx + matrix[1][1] * ry; ++/* negative absolute values indicate calculation from the oposite end of the axis */ ++ if (x < 0) ++ x += matrix[0][2]; ++ if (y < 0) ++ y += matrix[1][2]; + } + else { + x = rx; diff --git a/packages/xorg-xserver/xserver-kdrive_1.4.0.90.bb b/packages/xorg-xserver/xserver-kdrive_1.4.0.90.bb index 922d8f58f3..48ff0ea6fa 100644 --- a/packages/xorg-xserver/xserver-kdrive_1.4.0.90.bb +++ b/packages/xorg-xserver/xserver-kdrive_1.4.0.90.bb @@ -3,7 +3,7 @@ require xserver-kdrive-common.inc DEPENDS += "hal libxkbfile libxcalibrate pixman" PE = "1" -PR = "r2" +PR = "r3" SRC_URI = "${XORG_MIRROR}/individual/xserver/xorg-server-${PV}.tar.bz2 \ ${KDRIVE_COMMON_PATCHES} \ @@ -24,6 +24,7 @@ SRC_URI = "${XORG_MIRROR}/individual/xserver/xorg-server-${PV}.tar.bz2 \ file://xorg-avr32-support.diff;patch=1 \ file://pkgconfig_fix.patch;patch=1 \ file://no_xkb.patch;patch=1;pnum=0 \ + file://xorg-1.4-kdrive-rotation.patch;patch=1 \ " S = "${WORKDIR}/xorg-server-${PV}" |