aboutsummaryrefslogtreecommitdiffstats
path: root/packages/linux/linux-mtx-2-2.4.27/46-otg.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-mtx-2-2.4.27/46-otg.patch')
-rw-r--r--packages/linux/linux-mtx-2-2.4.27/46-otg.patch56853
1 files changed, 56853 insertions, 0 deletions
diff --git a/packages/linux/linux-mtx-2-2.4.27/46-otg.patch b/packages/linux/linux-mtx-2-2.4.27/46-otg.patch
new file mode 100644
index 0000000000..2004894b3e
--- /dev/null
+++ b/packages/linux/linux-mtx-2-2.4.27/46-otg.patch
@@ -0,0 +1,56853 @@
+--- linux/Makefile-otgorig 2006-09-20 16:02:57.347146612 +0200
++++ linux/Makefile 2006-09-20 16:03:35.280797278 +0200
+@@ -181,6 +181,7 @@
+ DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
+ DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
+ DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
++DRIVERS-$(CONFIG_OTG) += drivers/otg/otg_drv.o
+ DRIVERS-$(CONFIG_USB_GADGET) += drivers/usb/gadget/built-in.o
+ DRIVERS-y +=drivers/media/media.o
+ DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o
+--- linux/arch/mips/config-shared.in-otgorig 2006-09-20 16:09:20.671834564 +0200
++++ linux/arch/mips/config-shared.in 2006-09-20 16:09:28.068156725 +0200
+@@ -1009,6 +1009,7 @@
+ endmenu
+
+ source drivers/usb/Config.in
++source drivers/otg/Config.in
+
+ source net/bluetooth/Config.in
+
+--- linux/drivers/Makefile-otgorig 2006-09-20 16:09:47.100985766 +0200
++++ linux/drivers/Makefile 2006-09-20 16:10:25.730668535 +0200
+@@ -6,7 +6,7 @@
+ #
+
+
+-mod-subdirs := dio hil mtd sbus video macintosh usb input telephony ide \
++mod-subdirs := dio hil mtd sbus video macintosh usb otg input telephony ide \
+ message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
+ fc4 net/hamradio i2c acpi bluetooth usb/gadget sensors
+
+@@ -28,6 +28,7 @@
+ subdir-$(CONFIG_MAC) += macintosh
+ subdir-$(CONFIG_PPC32) += macintosh
+ subdir-$(CONFIG_USB) += usb
++subdir-$(CONFIG_OTG) += otg
+ subdir-$(CONFIG_USB_GADGET) += usb/gadget
+ subdir-$(CONFIG_INPUT) += input
+ subdir-$(CONFIG_PHONE) += telephony
+diff -uNr linux/drivers/no-otg/Config.in linux/drivers/otg/Config.in
+--- linux/drivers/no-otg/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/Config.in 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,103 @@
++#
++# USBOTG - Top level configuration of a USB Peripheral or USB On-The-Go Peripheral Device
++#
++# Copyright (c) 2004 Belcarra
++#
++#
++
++mainmenu_option next_comment
++
++ #
++ # Enable/Disable OTG Support
++ #
++ comment 'On-The-Go and USB Peripheral Support'
++ tristate 'Support for On-The-Go and USB Peripherals ' CONFIG_OTG
++
++ if [ "$CONFIG_OTG" != "n" ]; then
++ #
++ # Select appropriate hardware platform, each config file
++ # will only offer options if the kernel is configured for
++ # that specific architecture or platform. They should define
++ # the following to enable the further configuration in this
++ # file:
++ # CONFIG_OTG_PLATFORM_OTG offer OTG configuration
++ #
++ # CONFIG_OTG_PLATFORM_USBD offer USBD configuration and
++ # function selection
++ # CONFIG_OTG_PLATFORM_HOST offer HOST configuration
++ #
++ mainmenu_option next_comment
++ comment 'Select Hardware'
++
++ # platform oriented configurations
++# source drivers/otg/config/Config.in-mainstone
++# source drivers/otg/config/Config.in-pcs-b780
++# source drivers/otg/config/Config.in-pcs-p1
++# source drivers/otg/config/Config.in-mx2ads
++# source drivers/otg/config/Config.in-omap-h2
++ source drivers/otg/config/Config.in-db1550
++# source drivers/otg/config/Config.in-mordor
++
++ # architecture specific configurations
++ source drivers/otg/config/Config.in-au1x00
++# source drivers/otg/config/Config.in-dbmx1
++# source drivers/otg/config/Config.in-lh7a400
++# source drivers/otg/config/Config.in-lubbock
++# source drivers/otg/config/Config.in-smdk2500
++# source drivers/otg/config/Config.in-strongarm
++# source drivers/otg/config/Config.in-superh
++
++ # generic drivers
++ source drivers/otg/config/Config.in-isp1301
++ source drivers/otg/config/Config.in-max3353e
++ endmenu
++
++ if [ "$CONFIG_OTG_PLATFORM_OTG" = "y" -o "$CONFIG_OTG_PLATFORM_USBD" = "y" ]; then
++
++ #
++ # Generic Options
++ #
++ mainmenu_option next_comment
++ comment 'General Support Options'
++ bool 'Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEED
++ # bool 'Enable Root HUB Function' CONFIG_OTG_ROOT_HUB
++ bool 'OTG Fast Tracing' CONFIG_OTG_TRACE
++ tristate 'USB FUNCTION FS Module' CONFIG_OTG_PROCFSM
++ bool 'Disable C99 initializers' CONFIG_OTG_NOC99
++ endmenu
++
++ #
++ # Select USB Peripheral Function Drivers
++ #
++ mainmenu_option next_comment
++ comment 'Targeted Peripherals (USB Peripheral Function Drivers)'
++ source drivers/otg/functions/acm/Config.in
++ source drivers/otg/functions/mouse/Config.in
++ source drivers/otg/functions/network/Config.in
++ source drivers/otg/functions/msc/Config.in
++# source drivers/otg/functions/test/Config.in
++ endmenu
++
++ mainmenu_option next_comment
++ comment 'Traditional Device Options'
++ bool 'Built-in Minimal USB Device' CONFIG_OTG_FW_MN
++ dep_bool 'Enable Auto-Start' CONFIG_OTG_TR_AUTO $CONFIG_OTG_MN
++ endmenu
++ fi
++ #dep_tristate 'Build OTG minihost core' CONFIG_OTG_HOSTCORE $CONFIG_OTG
++ if [ "$CONFIG_OTG_PLATFORM_HOST" != "n" ]; then
++ #
++ # Host configuration
++ #
++ mainmenu_option next_comment
++ comment 'Host configuration (OTG minihost core and HCD)'
++# source drivers/otg/core/Config.in
++ source drivers/otg/ocd/Config.in
++# source drivers/otg/classes/usblan/Config.in
++ endmenu
++ fi
++
++ fi
++
++endmenu
++
+diff -uNr linux/drivers/no-otg/Config.in-orig linux/drivers/otg/Config.in-orig
+--- linux/drivers/no-otg/Config.in-orig 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/Config.in-orig 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,73 @@
++#
++# OTG - configuration of a USB On-The-Go Device
++#
++# Copyright (c) 2004 Belcarra
++#
++# The otg_export script will delete all comments marked "(Testing)"
++#
++
++mainmenu_option next_comment
++
++comment 'OTG devices'
++
++tristate 'Support for USB On-The-Go Devices ' CONFIG_OTG
++
++if [ "$CONFIG_OTG" = "y" -o "$CONFIG_OTG" = "m" ]; then
++ comment ''
++ bool ' Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEED
++ bool ' Enable Root HUB Function' CONFIG_OTG_ROOT_HUB
++ comment ''
++
++ bool ' Disable PCD' CONFIG_OTG_DISABLE_PCD
++ bool ' Disable HCD' CONFIG_OTG_DISABLE_HCD
++
++ comment ''
++
++ bool ' OTG Proc FS' CONFIG_OTG_PROCFS
++ tristate ' OTG Proc FS Module' CONFIG_OTG_PROCFSM $CONFIG_OTG
++
++ comment 'On-The-Go Functions'
++
++ source drivers/otg/functions/network/Config.in
++ source drivers/otg/functions/acm/Config.in
++ source drivers/otg/functions/msc/Config.in
++
++ source drivers/otg/functions/isotest/Config.in
++ source drivers/otg/functions/mouse/Config.in
++
++ comment ''
++ comment 'On-The-Go Transceiver Controller Drivers (TCD)'
++
++ source drivers/otg/tcd/isp1301/Config.in
++
++ comment ''
++ comment 'On-The-Go Peripheral Controller Drivers (PCD)'
++
++ source drivers/otg/pcd/au1x00/Config.in
++ source drivers/otg/pcd/mx1/Config.in
++ source drivers/otg/pcd/mx2/Config.in
++ source drivers/otg/pcd/pxa/Config.in
++ source drivers/otg/pcd/sa1100/Config.in
++ source drivers/otg/pcd/lh7a400/Config.in
++ source drivers/otg/pcd/omap/Config.in
++ source drivers/otg/pcd/smdk2500/Config.in
++ source drivers/otg/pcd/superh/Config.in
++ source drivers/otg/pcd/sx2/Config.in
++ source drivers/otg/pcd/wmmx/Config.in
++
++ comment ''
++ comment 'On-The-Go Host Controller Drivers (HCD)'
++
++ source drivers/otg/hcd/omap/Config.in
++
++ comment ''
++ tristate ' OTG Fast Tracing' CONFIG_OTG_TRACE
++ comment ''
++
++ comment 'Non Current Bus Drivers (Testing)'
++ source drivers/otg/functions/pst/Config.in
++ source drivers/otg/functions/datalog/Config.in
++
++fi
++
++endmenu
+diff -uNr linux/drivers/no-otg/FIX-MAKEFILES linux/drivers/otg/FIX-MAKEFILES
+--- linux/drivers/no-otg/FIX-MAKEFILES 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/FIX-MAKEFILES 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,23 @@
++#!/bin/sh
++
++ARG=$1
++shift
++
++[ -z "${ARG}" ] && echo Bad args && exit 1
++
++#find . -name Makefile-${ARG} | while read i
++#do
++# m=`expr $i : "\(.*\)-${ARG}"`
++# ln -sfv $i $m
++#done
++
++find . -type d | while read d
++do
++ pushd $d > /dev/null
++ if [ -s Makefile-${ARG} ] ; then
++ echo -n `pwd` ": "
++ ln -sfv Makefile-${ARG} Makefile
++ fi
++ popd >/dev/null
++done
++
+diff -uNr linux/drivers/no-otg/Kconfig linux/drivers/otg/Kconfig
+--- linux/drivers/no-otg/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/Kconfig 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,115 @@
++menu "On-The-Go and USB Peripheral Support"
++
++ config OTG
++ tristate "Support for On-The-Go and USB Peripheral Support"
++ ---help---
++ Configure all or part of the Belcarra OTG Stack
++
++
++ menu "On-The-Go Support"
++ depends on OTG
++
++ source "drivers/otg/config/Kconfig-scma11-evb"
++ #source "drivers/otg/config/Kconfig-omap-h2"
++
++ endmenu
++
++ menu "On-The-Go Support Configuration"
++ depends on OTG_PLATFORM_OTG
++
++ choice
++ depends on OTG && OTG_PLATFORM_OTG
++ prompt "On-The-Go Device Configuration"
++ config OTG_CFG_TR
++ bool "Traditional USB Peripheral"
++ ---help---
++ Compile as a Traditional USB Peripheral.
++ On-The-Go support is enabled.
++ config OTG_CFG_HO
++ bool "Host Only"
++ ---help---
++ Compile the USB Host support without the USB Peripheral
++ support. This is generally only useful for testing the
++ USB Host support and Host Controller drivers.
++ config OTG_CFG_PO
++ bool "Peripheral Only"
++ ---help---
++ Compile as a On-The-Go Peripheral-Only device. This
++ is similiar to a Traditional USB Peripheral but enables
++ On-The-Go features such as SRP.
++ config OTG_CFG_DR
++ bool "Dual Role"
++ ---help---
++ Compile as an On-The-Go Dual-Role device.
++
++ endchoice
++ endmenu
++
++ menu "Targeted Peripheral List (USB Host Class Drivers)"
++ depends on OTG_PLATFORM_OTG
++ # souce "drivers/otg/xxxx"
++ #
++ #---help---
++ #A list of USB peripherals that this device
++ #can support when it is acting as a host.
++ endmenu
++
++ menu "General Support Options"
++
++ depends on OTG_PLATFORM_OTG|| OTG_PLATFORM_USBD
++
++ # This needs to be a specific defined variable that comes
++ # from Kconfig-platform file
++ #
++ #config usb
++ # tristate 'OTG host core support (separate from native Linux host support)'
++
++ config OTG_HIGH_SPEED
++ bool 'Enable high speed descriptors'
++ depends on OTG!=n
++
++ config OTG_TRACE
++ bool 'OTG Fast Tracing'
++ depends on OTG!=n
++ ---help---
++ This option implements register trace to support
++ driver debugging.
++
++ #config OTG_ROOT_HUB
++ # bool 'Enable Root HUB Function'
++ # depends on OTG!=n
++
++ config OTG_PROCFS
++ bool 'OTG Proc FS'
++ depends on OTG!=n
++ ---help---
++ This option enables /proc/ support in various modules
++ Note: Some information previously exposed via the /proc
++ interface is now exposed via a different mechanism
++
++ config OTG_PROCFSM
++ tristate 'OTG Proc FS Module'
++ depends on OTG != n
++ ---help---
++ Build in extra support to perform various operations
++ through the /proc filesystem. Note: this module is
++ held over from the Device stack and its functions are
++ gradually being transferred to the OTG Admin API.
++
++ endmenu
++
++
++ menu "Targeted Peripherals List (USB Peripheral Function Drivers)"
++ depends on OTG_PLATFORM_OTG || OTG_PLATFORM_USBD
++ #---help---
++ #A list of USB peripheral types that this device
++ #can emulate when it is acting as a peripheral.
++ source "drivers/otg/functions/acm/Kconfig"
++ source "drivers/otg/functions/mouse/Kconfig"
++ source "drivers/otg/functions/msc/Kconfig"
++ source "drivers/otg/functions/network/Kconfig"
++ endmenu
++
++
++endmenu
++
+diff -uNr linux/drivers/no-otg/Makefile linux/drivers/otg/Makefile
+--- linux/drivers/no-otg/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/Makefile 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,132 @@
++#
++# Belcarra OTG - On-The-Go
++#
++# Copyright (c) 2004 Belcarra Technologies Corp
++
++TOPDIR ?= ../../..
++
++# order here may be important (determines linking order, thus module init order)
++subdir-y := otgcore functions ocd
++subdir-m := otgcore functions ocd
++subdir-n :=
++subdir- :=
++# The target object and module list name.
++
++O_TARGET := otg_drv.o
++
++# Objects that export symbols.
++
++#export-objs := usbd.o usbd-bops.o usbd-fops.o usbd-pcd.o ep0.o hub.o
++
++# Multipart objects. (core layer)
++
++
++# Optional parts of multipart objects.
++
++# Object file lists.
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj- :=
++
++# Each configuration option enables a list of files.
++
++ifeq ($(CONFIG_OTG),y)
++obj-y += otgcore/otgcore.o
++endif
++
++
++# Object files in subdirectories (There has to be a better way to do this)
++
++#=== Function drivers
++f-obj-y :=
++f-obj-m :=
++f-obj-n :=
++f-obj- :=
++
++f-obj-$(CONFIG_OTG_ACM) += functions/function_target.o
++f-obj-$(CONFIG_OTG_ISOTEST) += functions/function_target.o
++f-obj-$(CONFIG_OTG_MSC) += functions/function_target.o
++f-obj-$(CONFIG_OTG_MOUSE) += functions/function_target.o
++f-obj-$(CONFIG_OTG_NETWORK) += functions/function_target.o
++f-obj-$(CONFIG_OTG_PST) += functions/function_target.o
++
++# Remove any duplicate entries in the list by sorting (since that drops dups)
++obj-y += $(sort $(f-obj-y))
++#obj-m += $(sort $(f-obj-m))
++obj-n += $(sort $(f-obj-n))
++obj- += $(sort $(f-obj-))
++
++#=== Peripheral controller drivers
++p-obj-y :=
++p-obj-m :=
++p-obj-n :=
++p-obj- :=
++
++p-obj-$(CONFIG_OTG_AU1X00) += ocd/ocd_target.o
++p-obj-$(CONFIG_OTG_AU1550_DB1550_TR) += ocd/ocd_target.o
++
++# Remove any duplicate entries in the list by sorting (since that drops dups)
++obj-y += $(sort $(p-obj-y))
++#obj-m += $(sort $(p-obj-m))
++obj-n += $(sort $(p-obj-n))
++obj- += $(sort $(p-obj-))
++
++#=== Host controller drivers
++h-obj-y :=
++h-obj-m :=
++h-obj-n :=
++h-obj- :=
++
++
++# Remove any duplicate entries in the list by sorting (since that drops dups)
++obj-y += $(sort $(h-obj-y))
++#obj-m += $(sort $(h-obj-m))
++obj-n += $(sort $(h-obj-n))
++obj- += $(sort $(h-obj-))
++
++
++#=== Class drivers
++class-obj-y :=
++class-obj-m :=
++class-obj-n :=
++class-obj- :=
++
++#class-obj-$(CONFIG_OTG_CLASS_USBLAN) += classes/class_target.o
++
++# Remove any duplicate entries in the list by sorting (since that drops dups)
++obj-y += $(sort $(class-obj-y))
++#obj-m += $(sort $(f-obj-m))
++#obj-n += $(sort $(f-obj-n))
++#obj- += $(sort $(f-obj-))
++
++
++
++# Extract lists of the multi-part drivers.
++# The 'int-*' lists are the intermediate files used to build the multi's.
++
++multi-y := $(filter $(list-multi), $(obj-y))
++multi-m := $(filter $(list-multi), $(obj-m))
++int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
++int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
++
++# Files that are both resident and modular: remove from modular.
++
++obj-m := $(filter-out $(obj-y), $(obj-m))
++int-m := $(filter-out $(int-y), $(int-m))
++
++# Translate to Rules.make lists.
++
++O_OBJS := $(filter-out $(export-objs), $(obj-y))
++OX_OBJS := $(filter $(export-objs), $(obj-y))
++M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
++MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
++MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
++MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
++
++# The global Rules.make.
++
++include $(TOPDIR)/Rules.make
++EXTRA_CFLAGS += -Wno-format -Wall
++
+diff -uNr linux/drivers/no-otg/Makefile-l26 linux/drivers/otg/Makefile-l26
+--- linux/drivers/no-otg/Makefile-l26 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/Makefile-l26 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,7 @@
++#
++# Belcarra OTG - On-The-Go
++#
++# Copyright (c) 2004 Belcarra Technologies Corp
++
++obj-y +=
++obj-m += functions/ ocd/ otgcore/ core/
+diff -uNr linux/drivers/no-otg/OWNER linux/drivers/otg/OWNER
+--- linux/drivers/no-otg/OWNER 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/OWNER 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,20 @@
++OWNER: Belcarra Technologies Corp
++LICENSEE: Freescale
++
++THIS SOURCE CODE KIT IS SUPPLIED UNDER LICENSE
++
++Unless expressly modified elsewhere, the author of
++each source file included herein retains ownership
++of the identified file notwithstanding release of
++a specific version under a Public License such as
++the GNU General Public License.
++
++The line OWNER(entity) in a comment line at or near
++the top of a source file expressly asserts ownership
++of the file by that entity or person.
++
++In addition the presence of an OWNER.TXT or OWNER
++file in a directory asserts ownership of the contents
++of that directory and of the compilation thereof into
++the present and derivative kits.
++
+diff -uNr linux/drivers/no-otg/README linux/drivers/otg/README
+--- linux/drivers/no-otg/README 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/README 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,11 @@
++otg/README
++
++This is the top level of the OTG toolkit source tree.
++
++ classes Host Class drivers
++ functions Peripheral Function drivers
++ ocd OTG Controller drivers
++ otgcore OTG State Machine and USB Device stack
++ otghw Hardware related include files
++ otg Include files
++
+diff -uNr linux/drivers/no-otg/config/Config.in-au1x00 linux/drivers/otg/config/Config.in-au1x00
+--- linux/drivers/no-otg/config/Config.in-au1x00 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/Config.in-au1x00 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,29 @@
++#
++# Copyright (c) 2004 Belcarra
++#
++
++# Au1x00 on DB1100, PB1100 and PB1500
++
++if [ "$CONFIG_SOC_AU1X00" = "y" -o \
++ "$CONFIG_MIPS_AU1X00" = "y" -o \
++ "$CONFIG_CPU_AU1X00" = "y" -o \
++ "$CONFIG_MIPS_AU1500" = "y" -o \
++ "$CONFIG_MIPS_AU1100" = "y" -o \
++ "$CONFIG_MIPS_AU1000" = "y" ]
++then
++ mainmenu_option next_comment
++ comment 'AMD AU1X00 Bus Interface'
++
++ dep_tristate 'DB1100/PB1100/PB1500 Development Boards Support' CONFIG_OTG_AU1X00 $CONFIG_OTG
++
++ if [ "$CONFIG_OTG_AU1X00" != "n" ]; then
++ int 'AU1X00 System Clock' CONFIG_OTG_AU1X00_SCLOCK 400
++ define_bool CONFIG_AU1000_USB_DEVICE n
++ define_bool CONFIG_AU1X00_USB_DEVICE y
++ define_bool CONFIG_OTG_PLATFORM_USBD y
++ #else
++ # define_bool CONFIG_OTG_PLATFORM_USBD n
++ fi
++ endmenu
++fi
++
+diff -uNr linux/drivers/no-otg/config/Config.in-db1550 linux/drivers/otg/config/Config.in-db1550
+--- linux/drivers/no-otg/config/Config.in-db1550 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/Config.in-db1550 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,48 @@
++#
++# Copyright (c) 2004 Belcarra
++#
++
++if [ "$CONFIG_MIPS_DB1550" = "y" -o "$CONFIG_MIPS_MTX2" ]
++then
++ mainmenu_option next_comment
++ comment 'DB1550 Development Board'
++
++ dep_tristate 'DB1550 Development Boards Support' CONFIG_OTG_DB1550 $CONFIG_OTG
++
++ #define_tristate CONFIG_OTG_AU1550
++
++ if [ "$CONFIG_OTG_DB1550" != "n" ]; then
++ int 'AU1X00 System Clock' CONFIG_OTG_AU1X00_SCLOCK 400
++ define_bool CONFIG_AU1000_USB_DEVICE n
++ define_bool CONFIG_AU1X00_USB_DEVICE n
++ define_bool CONFIG_OTG_PLATFORM_USBD y
++ define_tristate CONFIG_OTG_AU1550 $CONFIG_OTG_DB1550
++
++ choice 'Select DB1550 Standard B or Mini A-B Port' \
++ "Mini-B-J14 CONFIG_OTG_DB1550_J14 \
++ Mini-A-B-J15 CONFIG_OTG_DB1550_J15" Mini-B-J14
++
++ if [ "$CONFIG_OTG_DB1550_J14" = "y" ]; then
++ define_bool CONFIG_OTG_PLATFORM_OTG n
++ define_bool CONFIG_OTG_PLATFORM_USBD y
++ define_bool CONFIG_OTG_MAX3353E n
++ define_tristate CONFIG_OTG_AU1550_DB1550_TR $CONFIG_OTG_DB1550
++
++ else
++ if [ "$CONFIG_OTG_DB1550_J15" = "y" ]; then
++ define_bool CONFIG_OTG_PLATFORM_OTG y
++ define_bool CONFIG_OTG_PLATFORM_USBD n
++ define_bool CONFIG_OTG_MAX3353E y
++ define_tristate CONFIG_OTG_MAX3353E_DB1550 $CONFIG_OTG_DB1550
++ define_tristate CONFIG_OTG_AU1550_DB1550_DR $CONFIG_OTG_DB1550
++
++ # J15 is external OTG Transceiver Client
++ define_bool CONFIG_OTG_DB1550_HXOE n
++ define_bool CONFIG_OTG_DB1550_HXS y
++ define_int CONFIG_OTG_DB1550_SEOS 4
++ fi
++ fi
++ fi
++ endmenu
++fi
++
+diff -uNr linux/drivers/no-otg/config/Config.in-dbmx1 linux/drivers/otg/config/Config.in-dbmx1
+--- linux/drivers/no-otg/config/Config.in-dbmx1 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/Config.in-dbmx1 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,19 @@
++#
++# Copyright (c) 2004 Belcarra
++#
++
++# MX1ADS - Motorola MX1
++
++if [ "$CONFIG_ARCH_MX1ADS" = "y" ]; then
++
++ mainmenu_option next_comment
++ comment 'Motorola MX1 Bus Interface'
++ dep_tristate 'DBMX1 Developement Board Support' CONFIG_OTG_DBMX1 $CONFIG_OTG
++
++ if [ "$CONFIG_OTG_DBMX1" != "n" ]; then
++ define_bool CONFIG_OTG_PLATFORM_USBD y
++ else
++ define_bool CONFIG_OTG_PLATFORM_USBD n
++ fi
++ endmenu
++fi
+diff -uNr linux/drivers/no-otg/config/Config.in-isp1301 linux/drivers/otg/config/Config.in-isp1301
+--- linux/drivers/no-otg/config/Config.in-isp1301 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/Config.in-isp1301 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,25 @@
++
++#
++# Copyright (c) 2004 Belcarra
++#
++# ISP 1301 TCD
++
++#if [ "$CONFIG_OTG_ISP1301" = "y" ]; then
++# mainmenu_option next_comment
++# comment 'ISP 1301'
++#
++# #bool 'Proc FS debug' CONFIG_OTG_ISP1301_PROCFSX
++# #bool 'Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEEDX
++# define_bool CONFIG_OTG_TEST y
++#
++# endmenu
++#fi
++if [ "$CONFIG_OTG_ISP1301" = "y" ]; then
++
++ mainmenu_option next_comment
++ comment 'ISP 1301'
++ bool 'Proc FS debug' CONFIG_OTG_ISP1301_PROCFS
++
++ endmenu
++fi
++
+diff -uNr linux/drivers/no-otg/config/Config.in-max3353e linux/drivers/otg/config/Config.in-max3353e
+--- linux/drivers/no-otg/config/Config.in-max3353e 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/Config.in-max3353e 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,25 @@
++
++#
++# Copyright (c) 2004 Belcarra
++#
++# MAX 3353E TCD
++
++#if [ "$CONFIG_OTG_MAX3353E" = "y" ]; then
++# mainmenu_option next_comment
++# comment 'MAX 3353E'
++#
++# #bool 'Proc FS debug' CONFIG_OTG_MAX3353E_PROCFSX
++# #bool 'Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEEDX
++# define_bool CONFIG_OTG_TEST y
++#
++# endmenu
++#fi
++if [ "$CONFIG_OTG_MAX3353E" = "y" ]; then
++
++ mainmenu_option next_comment
++ comment 'MAX 3353E'
++ bool 'Proc FS debug' CONFIG_OTG_MAX3353E_PROCFS
++
++ endmenu
++fi
++
+diff -uNr linux/drivers/no-otg/config/Config.in-mordor linux/drivers/otg/config/Config.in-mordor
+--- linux/drivers/no-otg/config/Config.in-mordor 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/Config.in-mordor 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,28 @@
++#
++# Copyright (c) 2004 Belcarra
++#
++
++# Au1x00 on DB1100, PB1100 and PB1500
++
++if [ "$CONFIG_AMX_MORDOR" = "y" ]
++then
++ mainmenu_option next_comment
++ comment 'AMX Mordor Board'
++
++ dep_tristate 'AMX Mordor Board Support' CONFIG_OTG_AU1550 $CONFIG_OTG
++ define_tristate CONFIG_OTG_BVD $CONFIG_OTG_AU1550
++
++ if [ "$CONFIG_OTG_AU1550" != "n" ]; then
++ int 'AU1X00 System Clock' CONFIG_OTG_AU1X00_SCLOCK 400
++ define_bool CONFIG_AU1000_USB_DEVICE n
++ define_bool CONFIG_AU1X00_USB_DEVICE n
++ define_bool CONFIG_OTG_PLATFORM_USBD y
++
++ define_bool CONFIG_OTG_PLATFORM_OTG n
++ define_bool CONFIG_OTG_PLATFORM_USBD y
++
++ else
++ define_bool CONFIG_OTG_PLATFORM_USBD n
++ fi
++ endmenu
++fi
+diff -uNr linux/drivers/no-otg/config/Kconfig-omap-h2 linux/drivers/otg/config/Kconfig-omap-h2
+--- linux/drivers/no-otg/config/Kconfig-omap-h2 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/Kconfig-omap-h2 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,87 @@
++#
++# Copyright (c) 2004 Belcarra
++#
++
++# OMAP - TI OMAP 1610
++#
++
++# CONFIG_OTG_PLATFORM_OTG # Offer OTG Configuration
++
++# CONFIG_OTG_OMAP # Make OMAP driver
++# CONFIG_OTG_ISP1301 # Make ISP1301 driver
++
++# CONFIG_OTG_ISP1301_OMAP_H2 # Compile ISP1301 OMAP H2 driver
++
++# CONFIG_OTG_OMAP_H2 # OMAP Helen 2 board
++# CONFIG_OTG_OMAP_H2_3_WIRE # Use 3 Wire OTG Configuration
++# CONFIG_OTG_OMAP_H2_4_WIRE # Use 4 Wire OTG Configuration
++# CONFIG_OTG_OMAP_H2_DR # Compile as OTG Dual-role Device
++# CONFIG_OTG_OMAP_H2_TR # Compile as Traditional USB Peripheral
++
++config OTG_OMAP_H2
++ tristate "OMAP 1610 H2 Development Board"
++ depends on OTG && ARCH_OMAP
++ ---help---
++ This implements On-The-Go USB Support for the Helen 2 OMAP
++ Development Board.
++
++choice
++ prompt "Select Transceiver wire configuration"
++ depends on OTG && ARCH_OMAP && OTG_OMAP_H2
++ config OTG_OMAP_H2_3_WIRE
++ bool 'Enable Pin Group 1, 3 Wire Configuration'
++ depends on OMAP_H2 !=n
++ ---help---
++ The H2 board ISP1301 can be configured in either
++ the 3 wire or 4 wire configuration.
++ This enables the ISP1301 on the H2 board on Pin Group 1
++ using the 3-Wire OTG Transceiver Configuration. This
++ requires that the ISP1301 be configured for DAT_SE0.
++
++ config OTG_OMAP_H2_4_WIRE
++ bool 'Enable Pin Group 1, 4 Wire Configuration'
++ depends on OMAP_H2 !=n
++ ---help---
++ The H2 board ISP1301 can be configured in either
++ the 3 wire or 4 wire configuration.
++ This enables the ISP1301 on the H2 board on Pin Group 1
++ using the 3-Wire OTG Transceiver Configuration. This
++ requires that the ISP1301 be configured for VP_VM.
++
++endchoice
++
++config OTG_PLATFORM_OTG
++ bool
++ default OTG_OMAP_H2
++
++config OTG_OMAP_H2_TR
++ tristate
++ depends on OTG_CFG_TR
++ default OTG_OMAP_H2
++
++config OTG_OMAP_H2_HO
++ tristate
++ depends on OTG_CFG_HO
++ default OTG_OMAP_H2
++
++config OTG_OMAP_H2_PO
++ tristate
++ depends on OTG_CFG_PO
++ default OTG_OMAP_H2
++
++config OTG_OMAP_H2_DR
++ tristate
++ depends on OTG_CFG_DR
++ default OTG_OMAP_H2
++
++
++config OTG_ISP1301
++ tristate
++ depends on OTG_OMAP_H2
++ default OTG_OMAP_H2
++
++config OTG_ISP1301_OMAP_H2
++ tristate
++ depends on OTG_OMAP_H2
++ default OTG_OMAP_H2
++
+diff -uNr linux/drivers/no-otg/config/README-CONFIG.txt linux/drivers/otg/config/README-CONFIG.txt
+--- linux/drivers/no-otg/config/README-CONFIG.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/config/README-CONFIG.txt 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,106 @@
++OTG System Configuration Stuart Lynne
++Belcarra Thu Jan 13 15:45:19 PST 2005
++
++This directory contains the linux system configurations to create
++architecture or board level drivers for On-The-Go or USB Device support.
++
++There are four generic configurations available
++
++ - tr - Traditional USB Peripheral
++ - po - OTG Peripheral only
++ - dr - OTG Dual-Role device
++
++Traditional USB Peripheral
++**************************
++
++Older types of systems supported USB peripherals as a separate module. It is
++not generally possible to configure these with On-The-Go support. In most
++cases the only customization available is for detection of the Vbus (cable
++attached) and control over the D-plus pullup resistor (soft-connect.)
++
++For most of these types of systems the generated driver will be of the form:
++
++ xxx_tr
++
++Where xxx is the system architecture:
++
++ au1x00
++ lh7a400
++ mx1
++ pxa
++ sa1100
++ smdk2500
++ superh
++
++
++OTG Modes
++*********
++
++Generally new systems that support On-The-Go have (at least) the following
++components:
++
++ - USB Peripheral
++ - USB Host
++ - OTG Transceiver
++ - Charge Pump (optional)
++
++In general the combination of the above is customized at the board or
++platform level, not the architecture (chip) level. There may be up to four
++different drivers implementing various combinations of the required support.
++
++Required support:
++
++ - pcd - Peripheral Controller Driver
++ - tcd - Transceiver Controller Driver
++ - hcd - Host Controller Driver
++ - ocd - OTG Controller Driver
++
++Typically the pcd and ocd drivers will be in a single module. This module
++will be named:
++
++ xxxx_ss
++
++Where xxxx is the platform, e.g.:
++
++ mainstone
++ mx1ads
++ omap-h2
++
++And ss is the type of OTG support being compiled:
++
++ - tr - traditonal usb
++ - ho - host only
++ - po - peripheral only
++ - dr - dual-role
++
++
++
++OTG Peripheral Only (po)
++************************
++
++This mode allows for implementing a restricted mode of On-The-Go support.
++The host driver module is not configured or available.
++
++
++OTG Dual-Role Device (dr)
++*************************
++
++This mode implements the full On-The-Go stack with both USB Device and Usb
++Host support.
++
++
++Configuration File
++******************
++
++Under linux, older systems (those that support only traditional devices) will
++have a generic Config.in / Kconfig file to generate the required driver.
++
++For newer systems that support OTG type configurations there should be a
++Configuration file for each major platform supported. This will specifically
++enable defines for all of the required drivers and their options.
++
++Platform files may have several sub-types with appropriate configuration
++selectors.
++
++
++
+diff -uNr linux/drivers/no-otg/dirs linux/drivers/otg/dirs
+--- linux/drivers/no-otg/dirs 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/dirs 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,11 @@
++!if 0
++Copyright (c) 2004 Belcarra
++!endif
++
++DIRS= \
++ wince \
++ otgcore \
++ functions/mouse
++
++OPTIONAL_DIRS= \
++
+diff -uNr linux/drivers/no-otg/functions/Makefile linux/drivers/otg/functions/Makefile
+--- linux/drivers/no-otg/functions/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/Makefile 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,53 @@
++#
++# Belcarra OTG - On-The-Go
++#
++# Copyright (c) 2004 Belcarra Technologies Corp
++
++TOPDIR ?= ../../../..
++
++subdir-y :=
++subdir-m :=
++subdir-n :=
++subdir- :=
++
++# The target object and module list name.
++
++O_TARGET := function_target.o
++
++# Function Drivers
++subdir-$(CONFIG_OTG_ACM) += acm
++subdir-$(CONFIG_OTG_ISOTEST) += isotest
++subdir-$(CONFIG_OTG_MSC) += msc
++subdir-$(CONFIG_OTG_MOUSE) += mouse
++subdir-$(CONFIG_OTG_NETWORK) += network
++
++# Object file lists.
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj- :=
++
++# Function drivers
++ifeq ($(CONFIG_OTG_ACM),y)
++obj-y += acm/acm_fd_drv.o
++endif
++ifeq ($(CONFIG_OTG_MOUSE),y)
++obj-y += mouse/mouse_target.o
++endif
++ifeq ($(CONFIG_OTG_NETWORK),y)
++obj-y += network/network_target.o
++endif
++ifeq ($(CONFIG_OTG_MSC),y)
++obj-y += msc/msc_target.o
++endif
++ifeq ($(CONFIG_OTG_PST),y)
++obj-y += pst/pst_target.o
++endif
++ifeq ($(CONFIG_OTG_TEST),y)
++obj-y += test/test_target.o
++endif
++
++include $(TOPDIR)/Rules.make
++EXTRA_CFLAGS += -Wno-format -Wall
++
+diff -uNr linux/drivers/no-otg/functions/Makefile-l26 linux/drivers/otg/functions/Makefile-l26
+--- linux/drivers/no-otg/functions/Makefile-l26 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/Makefile-l26 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,12 @@
++#
++# Belcarra OTG - On-The-Go
++#
++# Copyright (c) 2004 Belcarra Technologies Corp
++
++EXTRA_CFLAGS += -Wno-format -Wall
++# Function Drivers
++obj-$(CONFIG_OTG_ACM) += acm/
++obj-$(CONFIG_OTG_MSC) += msc/
++obj-$(CONFIG_OTG_MOUSE) += mouse/
++obj-$(CONFIG_OTG_NETWORK) += network/
++
+diff -uNr linux/drivers/no-otg/functions/acm/Config.in linux/drivers/otg/functions/acm/Config.in
+--- linux/drivers/no-otg/functions/acm/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/Config.in 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,28 @@
++#
++# CDC ACM Function Driver
++#
++# Copyright (C) 2003,2004 Belcarra
++#
++
++mainmenu_option next_comment
++comment "USB Peripheral Function Driver - CDC ACM"
++
++dep_tristate ' CDC ACM Function' CONFIG_OTG_ACM $CONFIG_OTG
++if [ "$CONFIG_OTG_ACM" != "n" ]; then
++ hex 'VendorID (hex value)' CONFIG_OTG_ACM_VENDORID "15ec"
++ hex 'ProductID (hex value)' CONFIG_OTG_ACM_PRODUCTID "f002"
++ hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_ACM_BCDDEVICE "0100"
++
++ string 'iManufacturer (string)' CONFIG_OTG_ACM_MANUFACTURER "Belcarra"
++ string 'iProduct (string)' CONFIG_OTG_ACM_PRODUCT_NAME "Belcarra ACM Device"
++
++ string 'iConfiguration (string)' CONFIG_OTG_ACM_DESC "Acm Cfg"
++ string 'Comm Interface iInterface (string)' CONFIG_OTG_ACM_COMM_INTF "Comm Intf"
++ string 'Data Interface iInterface (string)' CONFIG_OTG_ACM_DATA_INTF "Data Intf"
++
++ comment ''
++ #bool 'Communications Device' CONFIG_OTG_ACM_COMM
++ #bool 'TTY Device' CONFIG_OTG_ACM_TTY
++fi
++
++endmenu
+diff -uNr linux/drivers/no-otg/functions/acm/Kconfig linux/drivers/otg/functions/acm/Kconfig
+--- linux/drivers/no-otg/functions/acm/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/Kconfig 2006-09-01 21:41:25.000000000 +0200
+@@ -0,0 +1,54 @@
++menu "OTG ACM Function"
++
++config OTG_ACM
++ tristate " CDC ACM Function"
++ depends on OTG
++
++menu "OTG ACM function options"
++ depends on OTG && OTG_ACM
++
++config OTG_ACM_VENDORID
++ hex "VendorID (hex value)"
++ depends on OTG_ACM && OTG
++ default "0x15ec"
++
++config OTG_ACM_PRODUCTID
++ depends on OTG_ACM && OTG
++ hex "ProductID (hex value)"
++ default "0xe003"
++config OTG_ACM_BCDDEVICE
++ depends on OTG_ACM && OTG
++ hex "bcdDevice (binary-coded decimal)"
++ default "0x0100"
++
++config OTG_ACM_MANUFACTURER
++ depends on OTG_ACM && OTG
++ string "iManufacturer (string)"
++ default "Belcarra"
++
++config OTG_ACM_PRODUCT_NAME
++ depends on OTG_ACM && OTG
++ string "iProduct (string)"
++ default "Belcarra ACM Device"
++
++config OTG_ACM_DESC
++ depends on OTG_ACM && OTG
++ string "iConfiguration (string)"
++ default "Acm Cfg"
++
++config OTG_ACM_COMM_INTF
++ depends on OTG_ACM && OTG
++ string "Comm Interface iInterface (string)"
++ default "Comm Intf"
++
++config OTG_ACM_DATA_INTF
++ depends on OTG_ACM && OTG
++ string "Data Interface iInterface (string)"
++ default "Data Intf"
++
++config OTG_ACM_TRACE
++ depends on OTG_ACM && OTG
++ bool " ACM Tracing"
++ default n
++endmenu
++endmenu
+diff -uNr linux/drivers/no-otg/functions/acm/Makefile linux/drivers/otg/functions/acm/Makefile
+--- linux/drivers/no-otg/functions/acm/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/Makefile 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,80 @@
++#
++# Function driver for a CDC ACM USB Device
++#
++# Copyright (c) 2003 Belcarra
++
++# Multipart objects.
++
++O_TARGET := acm_fd_drv.o
++list-multi := acm_fd.o tty_fd.o
++
++#modem_fd-objs := acm-fd.o modem-l24-os.o modem.o
++#obex_fd-objs := acm-fd.o obex-l24-os.o obex.o
++tty_fd-objs := acm-fd.o tty-l24-os.o tty-fd.o
++
++# Objects that export symbols.
++#export-objs := acm-fd.o
++
++# Object file lists.
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj- :=
++
++# Each configuration option enables a list of files.
++
++#obj-$(CONFIG_OTG_ACM) += modem_fd.o
++#obj-$(CONFIG_OTG_ACM) += obex_fd.o
++obj-$(CONFIG_OTG_ACM) += tty_fd.o
++
++# Extract lists of the multi-part drivers.
++# The 'int-*' lists are the intermediate files used to build the multi's.
++
++multi-y := $(filter $(list-multi), $(obj-y))
++multi-m := $(filter $(list-multi), $(obj-m))
++int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
++int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
++
++# Files that are both resident and modular: remove from modular.
++
++obj-m := $(filter-out $(obj-y), $(obj-m))
++int-m := $(filter-out $(int-y), $(int-m))
++
++# Translate to Rules.make lists.
++
++O_OBJS := $(filter-out $(export-objs), $(obj-y))
++OX_OBJS := $(filter $(export-objs), $(obj-y))
++M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
++MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
++MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
++MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
++
++# The global Rules.make.
++
++ACMD=$(OTG)/functions/acm
++
++OTG_DIR=$(TOPDIR)/drivers/otg
++OTGCORE_DIR=$(OTG_DIR)/otgcore
++#USBDCORE_DIR=$(OTG_DIR)/usbdcore
++include $(TOPDIR)/Rules.make
++EXTRA_CFLAGS += -I$(OTG_DIR) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
++EXTRA_CFLAGS_nostdinc += -I$(OTG_DIR) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
++
++# Link rules for multi-part drivers.
++
++modem_fd.o: $(modem_fd-objs)
++ $(LD) -r -o $@ $(modem_fd-objs)
++
++obex_fd.o: $(obex_fd-objs)
++ $(LD) -r -o $@ $(obex_fd-objs)
++
++tty_fd.o: $(tty_fd-objs)
++ $(LD) -r -o $@ $(tty_fd-objs)
++
++# dependencies:
++
++#modem.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h
++#obex.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h
++#tty.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h
++
+diff -uNr linux/drivers/no-otg/functions/acm/Makefile-l26 linux/drivers/otg/functions/acm/Makefile-l26
+--- linux/drivers/no-otg/functions/acm/Makefile-l26 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/Makefile-l26 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,13 @@
++# Function driver for a CDC ACM OTG Device
++#
++# Copyright (c) 2004 Belcarra
++
++acm_fd-objs := acm-fd.o acm-l26-os.o
++
++obj-$(CONFIG_OTG_ACM) += acm_fd.o
++
++OTG=$(TOPDIR)/drivers/otg
++ACMD=$(OTG)/functions/acm
++USBDCORE_DIR=$(OTG)/usbdcore
++EXTRA_CFLAGS += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR)
++EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR)
+diff -uNr linux/drivers/no-otg/functions/acm/OBEX-TODO.txt linux/drivers/otg/functions/acm/OBEX-TODO.txt
+--- linux/drivers/no-otg/functions/acm/OBEX-TODO.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/OBEX-TODO.txt 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,16 @@
++OBEX TODO List Stuart Lynne
++Belcarra Tue Aug 24 21:36:09 PDT 2004
++
++
++1. OBEX documentation
++
++
++2. define obex requirements
++
++ - similiar to acm
++ - uses comm interface for ?
++ - impelements data/nodata inteface
++
++ - socket family interface
++ - char device interface
++
+diff -uNr linux/drivers/no-otg/functions/acm/TODO.txt linux/drivers/otg/functions/acm/TODO.txt
+--- linux/drivers/no-otg/functions/acm/TODO.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/TODO.txt 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,143 @@
++ACM TODO List Stuart Lynne
++Belcarra Wed Sep 01 19:48:38 PDT 2004
++
++
++1. The ACM driver needs to be expanded to allow it to build three ways:
++
++
++ - tty equivalent of old acm, uses linux tty layer
++ - modem present two simple char devices for data and comm interfaces
++ - obex single simple char device
++
++TTY
++ - present a single char device with TTY line discipline
++ - TIOCM calls represent "state" of emulated serial port on this
++ side of null modem
++ - notifications and line state only affect far side of null modem
++
++MODEM
++ - present two simple char device interfaces
++ - data char device will implement TIOCM ioctls
++ - comm char device will use encapsulated data over endpoint zero
++ - TIOCM calls represent state passed to/from host via notifications
++ and line state requests
++
++OBEX
++ - present a single simple char device interface
++ - no requirement for either TIOCM or TTY line discipline
++ - TIOCM calls not implemented
++
++
++
++2. The modem device must implement a virtual NULL modem and support the
++following IOCTL's.
++
++
++
++Virtual NULL Modem
++
++Peripheral to Host via Serial State Notification (write ioctls)
++
++Application TIOCM Null Modem ACM (DCE) Notificaiton Host (DTE)
++-------------------------------------------------------------------------------------------------------------
++Request to send TIOCM_RTS RTS -> CTS CTS Not Available Clear to Send (N/A)
++Data Terminal Ready TIOCM_DTR DTR -> DSR DSR bTxCarrier Data Set Ready
++ DTR -> DCD DCD bRXCarrier Carrier Detect
++Ring Indicator TIOCM_OUT1 OUT1 -> RI RI bRingSignal Ring Indicator
++Send Break TIOCM_OUT2 OUT2 -> Break Break bBreak Break Received
++-------------------------------------------------------------------------------------------------------------
++
++
++Host to Peripheral via Set Control Line State
++
++Host (DTE) Line State ACM (DCE) Null Modem TIOCM Peripheral (DTE)
++-------------------------------------------------------------------------------------------------------------
++Data Terminal Ready D0 DTR DTR -> DSR TIOCM_DSR Data Set Ready
++ DTR -> DCD TIOCM_CAR Carrier Detect
++Request To Send D1 RTS RTS -> CTS TIOCM_CTS Clear to Send
++-------------------------------------------------------------------------------------------------------------
++
++
++3. The following DEVICE REQUESTS have to be implemented and where
++possible support added to hook the results to the upper layers
++and applications.
++
++ SetCommFeature
++ GetCommFeature
++ ClearCommFeature
++
++ SendEncapsulatedCommand
++ GetEncapsulatedResponse
++
++ SetLineCoding
++ GetLineCoding
++
++ SetControlLineState
++
++ SendBreak
++
++4. The following NOTIFICATIONS need to be implemented and where
++possible hooked to appropriate indications from upper layers
++and applications.
++
++
++ RESPONSE_AVAILABLE
++ NETWORK_CONNECTION
++ SERIAL_STATE
++
++
++
++5. ACM documentation needs to be updated to reflect implementation(s).
++
++
++6. POSIX IOCTLS
++ - TIOCM*
++ - hook into flow control or set from flow control as required
++ - baudrate and other device settings
++ - CTS should disable receive urb
++
++7. COMM Interface
++ - GET / SEND Encapsulated command
++ - response available notification
++ - char device interface
++
++8. Optional TTY
++ - register as TTY device only if requested
++ - register as simple char device otherwise
++
++9. select()
++ - trackdown tty layer problem with select()
++ - sometimes fails under stress testing
++
++
++Notes..
++
++1. implementation of the comm and simple char device can probably share
++the same code base. These should queue received urbs. This will mean
++a small change in the os between acm_recv_urb() and the os specific
++layer.
++
++2. Now it is acm_os_recv_chars(), this must change to acm_os_recv_urbs().
++The acm_os_recv_urbs() function will be responsible for deallocing the
++urb when delivered.
++
++3. The various os specific functions will need to have a method to
++distinguish between the comm and data interfaces.
++
++
++
++
++10. WMC - Wireless Mobice Class
++
++ Call Management - bmCapabilities allow call management over data interface
++ ACM - bmCapabilities - 0x06, only SEND_BREAK, SET/GET LINE_CODING
++
++ NETWORK_CONNECTION, XXX_COMM_FEATURE not allowed
++
++ Call management over COMM interface optional
++
++ COMM_FEATURE - optional?
++
++
++
++
+diff -uNr linux/drivers/no-otg/functions/acm/acm-fd.c linux/drivers/otg/functions/acm/acm-fd.c
+--- linux/drivers/no-otg/functions/acm/acm-fd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/acm-fd.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,1077 @@
++/*
++ * otg/functions/acm/acm-fd.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ */
++/*!
++ * @file otg/functions/acm/acm-fd.c
++ * @brief ACM Function Driver private defines
++ *
++ * An ACM (Abstract Control Model) driver is composed of several pieces:
++ *
++ * 1) An OS and function specific piece that handles creating and operating
++ * a device for the given OS suitable for a specific function.
++ *
++ * 2) A set descriptors suitable for the function.
++ *
++ * 3) This acm-fd library which implements the interface to
++ * the usb peripheral and otgcore stacks.
++ *
++ * If the USB piece interfaces with the host usbcore layer you get
++ * an ACM class driver. If the USB piece interfaces with the otgcore
++ * layer you get an ACM function driver.
++ *
++ *
++ * @ingroup ACMFunction
++ */
++
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <otg/otg-trace.h>
++#include <otg/otg-api.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++
++static u32 max_queued_urbs;
++static u32 max_queued_bytes;
++
++// Define the low order 16 bits of an urb's memory address as it's ID for tracing.
++#define urbID(urb) (0xffff & (u32) (void *) urb)
++
++/* ******************************************************************************************* */
++
++STATIC int acm_send_int_notification(struct acm_private *acm, int , int );
++STATIC int acmfd_urb_sent_bulk (struct usbd_urb *urb, int rc);
++STATIC int acmfd_urb_sent_int (struct usbd_urb *urb, int rc);
++STATIC int acmfd_recv_urb (struct usbd_urb *urb, int rc);
++STATIC void acm_schedule_recv(struct acm_private *acm, int interface);
++
++/* ******************************************************************************************* */
++
++/*! acm_ready
++ * @param acm
++ */
++STATIC int acm_ready(struct acm_private *acm)
++{
++ TRACE_MSG5(acm->trace_tag,"CONFIGURED: %x OPENED: %x THROTTLED: %x CARRIER: %x READY: %d",
++ acm->flags & ACM_CONFIGURED, acm->flags & ACM_OPENED,
++ acm->flags & ACM_THROTTLED, acm->flags & ACM_CARRIER,
++ (acm->flags & ACM_CONFIGURED) && (acm->flags & ACM_CARRIER) ? 1 : 0);
++
++ return (acm->flags & ACM_CONFIGURED) &&
++ (acm->flags & ACM_OPENED) &&
++ !(acm->flags & ACM_THROTTLED) &&
++ ((acm->flags & ACM_LOCAL) ? 1 : (acm->flags & ACM_CARRIER) )
++ ;
++
++}
++
++/*! acm_open
++ * @param acm
++ * @param interface
++ * @return int
++ */
++STATIC int acm_open(struct acm_private *acm, int interface)
++{
++ TRACE_MSG0(acm->trace_tag,"OPEN");
++ acm->flags |= ACM_OPENED;
++ acm->flags &= ~ACM_THROTTLED;
++ acm->bmUARTState = CDC_UARTSTATE_BRXCARRIER_DCD | CDC_UARTSTATE_BTXCARRIER_DSR;
++ acm_schedule_recv(acm, interface);
++ TRACE_MSG1(acm->trace_tag,"bmUARTState: %04x", acm->bmUARTState);
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++ return 0;
++}
++
++/*! acm_flush
++ * @param acm
++ * @param interface
++ * @return number of urbs in queue
++ */
++STATIC void acm_flush(struct acm_private *acm, int interface)
++{
++ struct usbd_function_instance *function = acm->function;
++ unsigned long flags;
++ TRACE_MSG0(acm->trace_tag,"FLUSH");
++ RETURN_UNLESS(function);
++ local_irq_save(flags);
++ acm->bytes_received = 0;
++ acm->bytes_forwarded = 0;
++ acm->bmUARTState = CDC_UARTSTATE_BRXCARRIER_DCD | CDC_UARTSTATE_BTXCARRIER_DSR;
++ TRACE_MSG1(acm->trace_tag,"bmUARTState: %04x", acm->bmUARTState);
++ // TBR: 20040705 use ...le32() not le16, spotted by Zhao Liang
++ acm->line_coding.dwDTERate = cpu_to_le32(0x1c200); // 115200
++ acm->line_coding.bDataBits = 0x08;
++
++ usbd_flush_endpoint_index(function, BULK_IN);
++ usbd_flush_endpoint_index(function, BULK_OUT);
++ local_irq_restore(flags);
++}
++
++/*! acm_close
++ * @param acm
++ * @param interface
++ * @return number of urbs in queue
++ */
++STATIC int acm_close(struct acm_private *acm, int interface)
++{
++ TRACE_MSG0(acm->trace_tag,"CLOSE");
++ acm->flags &= ~ACM_OPENED;
++ acm->flags &= ~ACM_THROTTLED;
++ acm_flush(acm, interface);
++ acm->bmUARTState = 0;
++ TRACE_MSG1(acm->trace_tag,"bmUARTState: %04x", acm->bmUARTState);
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++ return 0;
++}
++
++
++/*! acmfd_start_recv_urbs
++ * @param acm
++ * @param interface
++ * @return number of urbs in queue
++ */
++STATIC int acmfd_start_recv_urbs(struct acm_private *acm, int interface)
++{
++ /*
++ * Queue as many receive urbs as the OS layer has room for. Return
++ * the number in the queue (may be more than we queue here).
++ */
++ struct usbd_function_instance *function = acm->function;
++ unsigned long flags;
++ int num_in_queue = 0;
++
++ switch (interface) {
++ case COMM_INTF:
++ return 0;
++ case DATA_INTF:
++ TRACE_MSG1(acm->trace_tag,"START RECV: acm: %x", (int)acm);
++ TRACE_MSG2(acm->trace_tag,"START RECV: connected: %x recv_urbs: %d",
++ acm->flags & ACM_CONFIGURED, acm->recv_urbs);
++
++ local_irq_save(flags);
++ if (acm_ready(acm)) {
++
++ TRACE_MSG2(acm->trace_tag,"START RECV: throttled: %d space_avail: %d", acm->flags & ACM_THROTTLED,
++ acm->function_services->recv_space_available(acm, DATA_INTF));
++
++ while (((acm->recv_urbs + 1) * 64) < acm->function_services->recv_space_available(acm, DATA_INTF)) {
++ struct usbd_urb *urb;
++
++ BREAK_IF(!(urb = usbd_alloc_urb(function, BULK_OUT, usbd_endpoint_transferSize(function,
++ BULK_OUT, usbd_high_speed(function)), acmfd_recv_urb)));
++ acm->recv_urbs++;
++ urb->function_privdata = acm;
++ TRACE_MSG3(acm->trace_tag,"START RECV: %d urb#%p privdata#%p",acm->recv_urbs,urb,acm);
++ CONTINUE_UNLESS (usbd_start_out_urb(urb));
++ acm->recv_urbs--;
++ TRACE_MSG1(acm->trace_tag,"START RECV: %d", acm->recv_urbs);
++ usbd_free_urb(urb);
++ break;
++ }
++ if (!acm->recv_urbs)
++ acm_schedule_recv(acm, interface);
++ }
++ /* There needs to be at least one recv urb queued in order
++ * to keep driving the push to the OS layer, so return how
++ * many there are in case the OS layer must try again later.
++ */
++ num_in_queue = acm->recv_urbs;
++ local_irq_restore(flags);
++ break;
++ }
++ return(num_in_queue);
++}
++
++
++struct acm_work acm_work;
++
++/*! acmfd_start_recv
++ * @param data
++ */
++STATIC void acmfd_start_recv(void *data)
++{
++ struct acm_work *work = data;
++ struct acm_private *acm;
++ unsigned long flags;
++ int interface;
++
++ local_irq_save(flags);
++ acm = work->acm;
++ interface = work->interface;
++ work->acm = NULL;
++ work->interface = -1;
++ acm->recv_tqueue.data = NULL;
++ local_irq_restore(flags);
++ acmfd_start_recv_urbs(acm, interface);
++}
++
++
++/*! acm_schedule_recv
++ * @param acm
++ * @param interface
++ */
++void acm_schedule_recv(struct acm_private *acm, int interface)
++{
++ unsigned long flags;
++ struct acm_work *work = &acm_work;
++ TRACE_MSG1(acm->trace_tag, "sync: %d", acm->recv_tqueue.sync);
++ local_irq_save(flags);
++
++ UNLESS (acm->recv_tqueue.sync && acm->recv_tqueue.data) {
++ work->acm = acm;
++ work->interface = interface;
++ acm->recv_tqueue.routine = &acmfd_start_recv;
++ acm->recv_tqueue.data = work;
++ #if defined(LINUX24)
++ queue_task(&acm->recv_tqueue, &tq_timer);
++ #else
++ SCHEDULE_WORK(acm->recv_tqueue);
++ #endif
++ }
++
++ local_irq_restore(flags);
++}
++
++/* Transmit INTERRUPT ************************************************************************** */
++
++/*! acm_send_int_notfication
++ * Generates a response urb on the notification (INTERRUPT) endpoint.
++ * CALLED from interrupt context.
++ * @return non-zero if error
++ */
++STATIC int acm_send_int_notification(struct acm_private *acm, int bnotification, int data)
++{
++ struct usbd_function_instance *function = acm->function;
++ struct usbd_urb *urb = NULL;
++ struct cdc_notification_descriptor *notification;
++ unsigned long flags;
++ int rc = 0;
++
++ TRACE_MSG4(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d data: %04x",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED, data);
++
++ RETURN_ZERO_UNLESS((acm->flags & ACM_CONFIGURED));
++
++ local_irq_save(flags);
++
++ do {
++ BREAK_IF(!(urb = usbd_alloc_urb(function, INT_IN,
++ sizeof(struct cdc_notification_descriptor),
++ acmfd_urb_sent_int)));
++
++ urb->function_privdata = acm;
++
++ memset(urb->buffer, 0, urb->buffer_length);
++ urb->actual_length = sizeof(struct cdc_notification_descriptor);
++
++ /* fill in notification structure */
++ notification = (struct cdc_notification_descriptor *) urb->buffer;
++
++ notification->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
++ notification->bNotification = bnotification;
++
++ switch (bnotification) {
++ case CDC_NOTIFICATION_NETWORK_CONNECTION:
++ notification->wValue = data;
++ break;
++ case CDC_NOTIFICATION_SERIAL_STATE:
++ notification->wLength = cpu_to_le16(2);
++ *((unsigned short *)notification->data) = cpu_to_le16(data);
++ break;
++ }
++
++ BREAK_IF(!(rc = usbd_start_in_urb (urb)));
++
++ urb->function_privdata = NULL;
++ usbd_free_urb (urb);
++
++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> 0",urbID(urb));
++
++ } while(0);
++
++ local_irq_restore(flags);
++ return(rc);
++}
++
++/* Callback functions for TX urb completion (function chosen when urb is allocated) ***********/
++
++/*! acmfd_urb_sent_bulk - called to indicate bulk URB transmit finished
++ * @param urb pointer to struct usbd_urb
++ * @param rc result
++ * @return non-zero for error
++ */
++STATIC int acmfd_urb_sent_bulk (struct usbd_urb *urb, int rc)
++{
++ struct acm_private *acm = urb->function_privdata;
++ struct usbd_function_instance *function;
++
++ TRACE_MSG2(acm->trace_tag,"entered urbID#%04x rc=%d",urbID(urb),rc);
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
++
++ if (!urb || !(function = urb->function_instance)) {
++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> -EINVAL",urbID(urb));
++ return(-EINVAL);
++ }
++ TRACE_MSG1(acm->trace_tag,"IN length=%d",urb->actual_length);
++
++ atomic_sub(urb->actual_length, &acm->queued_bytes);
++ atomic_dec(&acm->queued_urbs);
++ urb->function_privdata = NULL;
++ usbd_free_urb (urb);
++ if (acm->flags & ACM_OPENED)
++ acm->function_services->schedule_wakeup_writers(acm);
++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> 0",urbID(urb));
++ return 0;
++}
++
++/*! acmfd_urb_sent_int - called to indicate int URB transmit finished
++ * @param urb pointer to struct usbd_urb
++ * @param rc result
++ * @return non-zero for error
++ */
++STATIC int acmfd_urb_sent_int (struct usbd_urb *urb, int rc)
++{
++ struct acm_private *acm = urb->function_privdata;
++ struct usbd_function_instance *function;
++
++ TRACE_MSG2(acm->trace_tag,"entered urbID#%04x rc=%d",urbID(urb),rc);
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
++ if (!urb || !(function = urb->function_instance)) {
++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> -EINVAL",urbID(urb));
++ return(-EINVAL);
++ }
++ TRACE_MSG1(acm->trace_tag,"INT length=%d",urb->actual_length);
++
++ urb->function_privdata = NULL;
++ usbd_free_urb(urb);
++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> 0",urbID(urb));
++ return 0;
++}
++
++/*! acmfd_urb_sent_ep0 - called to indicate ep0 URB transmit finished
++ * @param urb pointer to struct usbd_urb
++ * @param rc result
++ * @return non-zero for error
++ */
++STATIC int acmfd_urb_sent_ep0 (struct usbd_urb *urb, int rc)
++{
++ struct acm_private *acm = urb->function_privdata;
++ struct usbd_function_instance *function;
++
++ TRACE_MSG2(acm->trace_tag,"entered urbID#%04x rc=%d",urbID(urb),rc);
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
++
++ RETURN_EINVAL_IF (!urb || !(function = urb->function_instance));
++
++ TRACE_MSG1(acm->trace_tag,"INT length=%d",urb->actual_length);
++
++ urb->function_privdata = NULL;
++ usbd_free_urb(urb);
++ return 0;
++}
++
++/* USB Device Functions ************************************************************************ */
++
++typedef enum mesg {
++ mesg_unknown,
++ mesg_configured,
++ mesg_reset,
++} mesg_t;
++mesg_t acm_last_mesg;
++
++char * acm_messages[3] = {
++ "",
++ "ACM Configured",
++ "ACM Reset",
++};
++
++
++/*! acmfd_check_mesg
++ * @param curr_mesg
++ */
++void acmfd_check_mesg(mesg_t curr_mesg)
++{
++ RETURN_UNLESS(acm_last_mesg != curr_mesg);
++ acm_last_mesg = curr_mesg;
++ otg_message(acm_messages[curr_mesg]);
++}
++
++
++/*! acmfd_event_handler - process a device event
++ * @param function
++ * @param event
++ * @param data
++ */
++void acmfd_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data)
++{
++ struct acm_private *acm = function->privdata;
++ int i;
++
++ TRACE_MSG1(acm->trace_tag,"entered ev: %d",event);
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d ready: %d", atomic_read(&acm->used), MOD_IN_USE, acm_ready(acm));
++
++ switch (event) {
++
++ case DEVICE_CONFIGURED:
++ TRACE_MSG0(acm->trace_tag,"CONFIGURED");
++ acm->flags |= ACM_CONFIGURED;
++ acmfd_check_mesg(mesg_configured);
++ acmfd_start_recv_urbs(acm, DATA_INTF);
++ break;
++
++ case DEVICE_RESET:
++ case DEVICE_DE_CONFIGURED:
++ TRACE_MSG0(acm->trace_tag,"RESET");
++
++ /* if configured and open then schedule hangup
++ */
++ if ((acm->flags & ACM_OPENED) && (acm->flags & ACM_CONFIGURED))
++ acm->function_services->schedule_hangup(acm);
++
++ acm->flags &= ~(ACM_CONFIGURED | ACM_CARRIER);
++ acmfd_check_mesg(mesg_reset);
++ BREAK_IF(!acm->flags & ACM_CONFIGURED);
++ TRACE_MSG0(acm->trace_tag,"RESET continue");
++ // XXX flush
++ // Release any queued urbs
++ break;
++
++ default:
++ break;
++ }
++ TRACE_MSG0(acm->trace_tag,"exited");
++}
++
++/*! acm_write_room
++ * @param acm
++ * @param interface
++ * @return non-zero if error
++ */
++STATIC int acm_write_room(struct acm_private *acm, int interface)
++{
++ switch (interface) {
++ case COMM_INTF:
++ return 0;
++ case DATA_INTF:
++ return (!acm->function || max_queued_urbs <= atomic_read(&acm->queued_urbs) ||
++ max_queued_bytes <= atomic_read(&acm->queued_bytes) ) ? 0 : acm->writesize ;
++ }
++ return 0;
++}
++
++/*! acm_chars_in_buffer
++ * @param acm
++ * @param interface
++ * @return non-zero if error
++ */
++STATIC int acm_chars_in_buffer(struct acm_private *acm, int interface)
++{
++ int rc;
++ switch (interface) {
++ case COMM_INTF:
++ return 0;
++ case DATA_INTF:
++ return atomic_read(&acm->queued_bytes);
++ }
++ return 0;
++}
++
++static int throttle_count = 0;
++static int unthrottle_count = 0;
++
++/*! acm_throttle
++ * @param acm
++ * @param interface
++ */
++STATIC void acm_throttle(struct acm_private *acm, int interface)
++{
++ switch (interface) {
++ case COMM_INTF:
++ break;
++ case DATA_INTF:
++ throttle_count += 1;
++ TRACE_MSG1(acm->trace_tag,"entered %d",throttle_count);
++ acm->flags |= ACM_THROTTLED;
++ TRACE_MSG1(acm->trace_tag,"exited %d",throttle_count);
++ break;
++ }
++}
++
++/*! acm_unthrottle
++ * @param acm
++ * @param interface
++ */
++STATIC void acm_unthrottle(struct acm_private *acm, int interface)
++{
++ switch (interface) {
++ case COMM_INTF:
++ break;
++ case DATA_INTF:
++ unthrottle_count += 1;
++ TRACE_MSG1(acm->trace_tag,"entered %d",unthrottle_count);
++ acm->flags &= ~ACM_THROTTLED;
++ TRACE_MSG1(acm->trace_tag,"exited %d",unthrottle_count);
++ UNLESS(acm->recv_urbs)
++ acm_schedule_recv(acm, interface);
++ break;
++ }
++}
++
++/*! acm_xmit_chars
++ * @param acm
++ * @param interface
++ * @param count
++ * @param from_user
++ * @param buf
++ * @return number of bytes sent.
++ */
++STATIC int acm_xmit_chars(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf)
++{
++ struct usbd_function_instance *function = acm->function;
++ struct usbd_urb *urb;
++
++ RETURN_ZERO_UNLESS((acm->flags & ACM_CONFIGURED) /*&& (acm->flags & ACM_CARRIER)*/);
++
++ TRACE_MSG2(acm->trace_tag, "count: %d from_user: %d", count, from_user);
++
++ switch (interface) {
++ case COMM_INTF:
++ return 0;
++ case DATA_INTF:
++ // sanity check and are we connect
++ RETURN_ZERO_UNLESS(atomic_read(&acm->used));
++ TRACE_MSG0(acm->trace_tag,"used OK");
++ RETURN_ZERO_UNLESS (count);
++ RETURN_ZERO_UNLESS (acm->flags & ACM_CONFIGURED);
++ TRACE_MSG0(acm->trace_tag,"connected OK");
++ RETURN_ZERO_IF(max_queued_urbs <= atomic_read(&acm->queued_urbs));
++ TRACE_MSG0(acm->trace_tag,"max_queued_urbs OK");
++
++ // allocate a write urb
++ count = MIN(count, acm->writesize);
++
++ RETURN_ZERO_UNLESS ((urb = usbd_alloc_urb (function, BULK_IN, count, acmfd_urb_sent_bulk)));
++
++ if (from_user)
++ copy_from_user ((void *)urb->buffer, (void *)buf, count);
++ else
++ memcpy ((void *)urb->buffer, (void *)buf, count);
++
++ urb->function_privdata = acm;
++ urb->actual_length = count;
++ atomic_add(count, &acm->queued_bytes);
++ atomic_inc(&acm->queued_urbs);
++ usbd_start_in_urb(urb);
++ TRACE_MSG2(acm->trace_tag,"urbID#%04x --> count: %d",urbID(urb),count);
++ return count;
++ default:
++ break;
++ }
++ return 0;
++}
++
++/*! acmfd_recv_urb
++ * @param urb
++ * @param rc
++ * @return non-zero if error
++ */
++STATIC int acmfd_recv_urb (struct usbd_urb *urb, int rc)
++{
++ /* Return 0 if urb has been accepted,
++ * return 1 and expect caller to deal with urb release otherwise.
++ */
++ struct acm_private *acm = urb->function_privdata;
++ unsigned long flags;
++
++ local_irq_save(flags);
++ acm->recv_urbs--; // this could probably be atomic operation....
++ local_irq_restore(flags);
++
++ if (RECV_CANCELLED == rc) {
++ TRACE_MSG1(acm->trace_tag,"cancelled URB=%p",urb);
++ return -EINVAL;
++ }
++ if (RECV_OK != rc) {
++ TRACE_MSG2(acm->trace_tag,"rejected URB=%p rc=%d",urb,rc);
++ usbd_free_urb(urb);
++ acm_schedule_recv(acm, DATA_INTF);
++ //acmfd_start_recv_urbs(acm, DATA_INTF);
++ return 0;
++ }
++
++ /* loopback mode
++ */
++ if (acm->flags & ACM_LOOPBACK)
++ acm_xmit_chars(acm, DATA_INTF, urb->actual_length, 0, urb->buffer);
++
++ /* acmfd_start_recv_urbs() will never queue more urbs than there is currently
++ * room in the upper layer buffer for. So we are guaranteed that any data actually
++ * received can be given to the upper layers without worrying if we will
++ * actually have room.
++ */
++ else
++ if ((rc = acm->function_services->recv_chars(acm, DATA_INTF, urb->buffer, urb->actual_length)))
++ return(rc); // XXX
++
++
++ acm->bytes_received += urb->actual_length;
++ TRACE_MSG1(acm->trace_tag,"bytes_received: %d",acm->bytes_received);
++ usbd_free_urb(urb);
++ acm_schedule_recv(acm, DATA_INTF);
++ return 0;
++
++#if 0
++ // XXX it may be reasonable to schedule here....
++ UNLESS (acmfd_start_recv_urbs(acm, DATA_INTF)) {
++ /* If start recv returns zero it means that there are no-queued urbs,
++ * and we should queue a work item to restart.
++ */
++ acm_schedule_recv(acm, DATA_INTF);
++ }
++#endif
++ return 0;
++}
++
++/*! acmfd_line_coding_urb_received - callback for sent URB
++ *
++ * Handles notification that an urb has been sent (successfully or otherwise).
++ *
++ * @param urb
++ * @param urb_rc
++ * @return non-zero for failure.
++ */
++STATIC int acmfd_line_coding_urb_received (struct usbd_urb *urb, int urb_rc)
++{
++ struct acm_private *acm = urb->function_privdata;
++ TRACE_MSG2(acm->trace_tag,"urbID#%04x rc=%d",urbID(urb),urb_rc);
++
++ RETURN_EINVAL_IF (RECV_OK != urb_rc);
++ RETURN_EINVAL_IF (urb->actual_length < sizeof(struct cdc_acm_line_coding));
++
++ RETURN_EINVAL_UNLESS (memcpy(&acm->line_coding, urb->buffer, sizeof(struct cdc_acm_line_coding)));
++
++ // something changed, copy and notify
++
++ memcpy(&acm->line_coding, urb->buffer, sizeof(struct cdc_acm_line_coding));
++
++ // XXX notify application if baudrate has changed
++
++ return -EINVAL; // caller will de-allocate
++}
++
++/*! acmfd_device_request - called to indicate urb has been received
++ * @param function
++ * @param request
++ * @return non-zero if error
++ */
++int acmfd_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
++{
++ struct acm_private *acm = (struct acm_private *) (function->privdata);
++
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
++
++ TRACE_SETUP(acm->trace_tag,request);
++
++ // verify that this is a usb class request per cdc-acm specification or a vendor request.
++ if (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR))) {
++ TRACE_MSG0(acm->trace_tag,"--> 0");
++ return(0);
++ }
++
++ // determine the request direction and process accordingly
++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
++
++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
++ switch (request->bRequest) {
++ case CDC_CLASS_REQUEST_SEND_ENCAPSULATED: break;
++ case CDC_CLASS_REQUEST_SET_COMM_FEATURE: break;
++ case CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE: break;
++ case CDC_CLASS_REQUEST_SET_LINE_CODING:
++ {
++ struct usbd_urb *urb;
++ int len = le16_to_cpu(request->wLength);
++ TRACE_MSG1(acm->trace_tag,"SET_LINE_CODING wLength=%d",len);
++ if (len <= 0) {
++ TRACE_MSG0(acm->trace_tag,"(len<=0)--> 0");
++ return(0);
++ }
++
++ /* Set up an ep0 recv urb for the rest of it. */
++ UNLESS ((urb = usbd_alloc_urb_ep0(function, len, acmfd_line_coding_urb_received))) {
++ TRACE_MSG0(acm->trace_tag,"no mem for ep0 recv urb");
++ return(-ENOMEM);
++ }
++ urb->function_privdata = acm;
++ if (usbd_start_out_urb(urb)) {
++ TRACE_MSG0(acm->trace_tag,"usbd_start_out_urb() failed");
++ usbd_free_urb(urb); // de-alloc if error
++ TRACE_MSG0(acm->trace_tag,"--> -EINVAL");
++ return(-EINVAL);
++ }
++ }
++ break;
++
++ case CDC_CLASS_REQUEST_SET_CONTROL_LINE_STATE:
++ {
++ unsigned int prev_bmLineState = acm->bmLineState;
++ acm->bmLineState = le16_to_cpu(request->wValue);
++
++ // schedule writers or hangup IFF open
++ BREAK_IF(!acm->privdata);
++ TRACE_MSG3(acm->trace_tag,"set control state, bmLineState: %04x previous: %04x changed: %04x",
++ acm->bmLineState, prev_bmLineState, acm->bmLineState ^ prev_bmLineState);
++
++ // make sure there really is a state change
++ if ((acm->bmLineState ^ prev_bmLineState) & CDC_LINESTATE_D0_DTR) {
++
++ TRACE_MSG1(acm->trace_tag,"DTR state changed -> %x",
++ (acm->bmLineState & CDC_LINESTATE_D0_DTR));
++
++ if (acm->bmLineState & CDC_LINESTATE_D0_DTR) {
++ if (acm->flags & ACM_OPENED)
++ acm->function_services->schedule_wakeup_writers(acm);
++ acm->flags |= ACM_CARRIER;
++ acm_schedule_recv(acm, DATA_INTF);
++ }
++ else {
++ if (acm->flags & ACM_OPENED)
++ acm->function_services->schedule_hangup(acm);
++
++ acm->flags &= ~ACM_CARRIER;
++ acm_flush(acm, DATA_INTF);
++ }
++
++ /* wake up blocked opens */
++ acm->function_services->wakeup_opens(acm);
++
++ /* wake up blocked ioctls */
++ acm->function_services->wakeup_state(acm);
++
++ }
++
++
++
++ /* send notification if we have DCD */
++ TRACE_MSG2(acm->trace_tag,"bmUARTState: %04x privdata: %p sending (DCD|DSR) notification",
++ acm->bmUARTState, acm->privdata);
++
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++ }
++ break;
++
++ case CDC_CLASS_REQUEST_SEND_BREAK: break;
++ default: break;
++ }
++ TRACE_MSG0(acm->trace_tag,"--> 0");
++ return 0;
++
++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
++ switch (request->bRequest) {
++ case CDC_CLASS_REQUEST_GET_ENCAPSULATED: break;
++ case CDC_CLASS_REQUEST_GET_COMM_FEATURE: break;
++ case CDC_CLASS_REQUEST_GET_LINE_CODING:
++ {
++ struct usbd_urb *urb;
++ RETURN_ENOMEM_IF (!(urb = usbd_alloc_urb_ep0(function, sizeof(struct cdc_acm_line_coding),
++ acmfd_urb_sent_ep0)));
++ urb->function_privdata = acm;
++
++ memcpy(urb->buffer, &acm->line_coding, sizeof(struct cdc_acm_line_coding));
++
++ urb->actual_length = sizeof(struct cdc_acm_line_coding);
++
++ TRACE_MSG1(acm->trace_tag,"sending line coding urb: %p",(u32)(void*)urb);
++ RETURN_ZERO_UNLESS(usbd_start_in_urb(urb));
++ usbd_free_urb(urb);
++ TRACE_MSG0(acm->trace_tag,"(send failed)--> -EINVAL");
++ }
++ return -EINVAL;
++ default: break;
++ }
++ TRACE_MSG0(acm->trace_tag,"--> 0");
++ return 0;
++
++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR: break;
++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR: break;
++
++ default: break;
++ }
++ TRACE_MSG0(acm->trace_tag,"--> 0");
++ return 0;
++}
++
++
++/*! acmfd_function_enable
++ * @param function
++ * @return non-zero if error
++ */
++STATIC int acmfd_function_enable (struct usbd_function_instance *function)
++{
++ struct acm_private *acm = (struct acm_private *) (function->privdata);
++
++ TRACE_MSG0(acm->trace_tag,"entered");
++ TRACE_MSG1(acm->trace_tag,"INC: %d", MOD_IN_USE);
++ acm->function_services->enable(function);
++ acm->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, 0) * 24;
++
++ TRACE_MSG0(acm->trace_tag,"-> 0");
++ return 0;
++}
++
++/*! acmfd_function_disable
++ * @param function
++ */
++STATIC void acmfd_function_disable (struct usbd_function_instance *function)
++{
++ struct acm_private *acm = (struct acm_private *) (function->privdata);
++
++ TRACE_MSG0(acm->trace_tag,"entered");
++ acm->writesize = 0;
++ acm->function_services->disable(function);
++ TRACE_MSG1(acm->trace_tag,"DEC: %d", MOD_IN_USE);
++ TRACE_MSG0(acm->trace_tag,"exited");
++}
++
++
++/*! function_ops
++ */
++struct usbd_function_operations function_ops = {
++ event_handler: acmfd_event_handler,
++ device_request: acmfd_device_request,
++ function_enable: acmfd_function_enable,
++ function_disable: acmfd_function_disable,
++};
++
++/*! acm_fd_init
++ * @param acm
++ * @param usbd_module_info
++ * @param vendor_id
++ * @param product_id
++ * @param wmax_urbs
++ * @param wmax_bytes
++ * @return non-zero if error
++ */
++STATIC int acm_fd_init(struct acm_private *acm, char *usbd_module_info, u32 vendor_id, u32 product_id,
++ u32 wmax_urbs, u32 wmax_bytes)
++{
++
++ TRACE_MSG0(acm->trace_tag,"entered");
++
++ if (vendor_id)
++ acm->function_driver->idVendor = cpu_to_le16(vendor_id);
++ if (product_id)
++ acm->function_driver->idProduct = cpu_to_le16(product_id);
++ max_queued_urbs = wmax_urbs;
++ max_queued_bytes = wmax_bytes;
++
++ THROW_IF (NULL == (acm->function = usbd_register_function (acm->function_driver, "acm-fd", acm)), error);
++
++ CATCH(error) {
++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__);
++
++ if (acm->function) {
++ usbd_deregister_function (acm->function);
++ acm->function = NULL;
++ }
++ TRACE_MSG0(acm->trace_tag,"--> -EINVAL");
++ return -EINVAL;
++ }
++ TRACE_MSG0(acm->trace_tag,"--> 0");
++ return 0;
++}
++
++
++
++/*! acm_wait_task
++ * @param acm
++ * @param queue
++ */
++void acm_wait_task(struct acm_private *acm, WORK_ITEM *queue)
++{
++ TRACE_MSG1(acm->trace_tag,"entered data=%p",queue->data);
++ RETURN_IF(!queue->data);
++ queue->data = NULL;
++#if defined(LINUX24)
++ while (queue->sync) {
++ TRACE_MSG1(acm->trace_tag,"waiting for queue: %p",queue);
++ schedule_timeout(HZ);
++ }
++#else
++ while(PENDING_WORK_ITEM((*queue))){
++ TRACE_MSG1(acm->trace_tag,"waiting for queue: %p",queue);
++ SCHEDULE_TIMEOUT(1); // 1 second delay
++ }
++#endif
++ TRACE_MSG0(acm->trace_tag,"exited");
++}
++
++
++/*! acm_get_dtr
++ * Get DTR status.
++ */
++int acm_get_dtr(struct acm_private *acm)
++{
++ return (acm->bmLineState & CDC_LINESTATE_D0_DTR) ? 1 : 0;
++}
++
++/*! acm_get_dsr
++ * Get DSR status
++ */
++int acm_get_dsr(struct acm_private *acm)
++{
++ return (acm->bmUARTState & CDC_UARTSTATE_BTXCARRIER_DSR) ? 1 : 0;
++}
++
++/*! acm_get_dcd
++ * Get DCD status
++ */
++int acm_get_dcd(struct acm_private *acm)
++{
++ return (acm->bmUARTState & CDC_UARTSTATE_BRXCARRIER_DCD) ? 1 : 0;
++}
++
++/*! acm_set_dsr
++ * Set DSR status
++ * @param acm
++ * @param value
++ */
++void acm_set_dsr(struct acm_private *acm, int value)
++{
++ acm->bmUARTState &= ~CDC_UARTSTATE_BTXCARRIER_DSR;
++ acm->bmUARTState |= value ? CDC_UARTSTATE_BTXCARRIER_DSR : 0;
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++}
++
++/*! acm_set_dcd
++ * Set DCD status
++ * @param acm
++ * @param value
++ */
++void acm_set_dcd(struct acm_private *acm, int value)
++{
++ acm->bmUARTState &= ~CDC_UARTSTATE_BRXCARRIER_DCD;
++ acm->bmUARTState |= value ? CDC_UARTSTATE_BRXCARRIER_DCD : 0;
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++}
++
++/*! acm_ring
++ * Indicate Ring signal to host.
++ */
++void acm_ring(struct acm_private *acm)
++{
++ acm->bmUARTState |= CDC_UARTSTATE_BRINGSIGNAL;
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++ acm->bmUARTState &= ~CDC_UARTSTATE_BRINGSIGNAL;
++}
++
++/*! acm_send_break
++ * Indicate Break signal to host.
++ */
++void acm_send_break(struct acm_private *acm)
++{
++ acm->bmUARTState |= CDC_UARTSTATE_BBREAK;
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++ acm->bmUARTState &= ~CDC_UARTSTATE_BBREAK;
++}
++
++/*! acm_overrun
++ * Indicate Overrun signal to host.
++ */
++void acm_overrun(struct acm_private *acm)
++{
++ acm->bmUARTState |= CDC_UARTSTATE_BOVERRUN;
++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
++ acm->bmUARTState &= ~CDC_UARTSTATE_BOVERRUN;
++}
++
++/*! acm_set_local
++ * Set LOCAL status
++ * @param acm
++ * @param value
++ */
++void acm_set_local(struct acm_private *acm, int value)
++{
++ acm->flags &= ~ACM_LOCAL;
++ acm->flags |= value ? ACM_LOCAL : 0;
++}
++
++/*! acm_set_loopback
++ * Set LOOPBACK status
++ * @param acm
++ * @param value
++ */
++void acm_set_loopback(struct acm_private *acm, int value)
++{
++ acm->flags &= ~ACM_LOOPBACK;
++ acm->flags |= value ? ACM_LOOPBACK : 0;
++}
++
++
++
++/*! acm_fd_term
++ * @param acm
++ */
++STATIC void acm_fd_exit(struct acm_private *acm)
++{
++ //acm_wait_task(acm->recv_tqueue);
++ RETURN_UNLESS (acm->function);
++ usbd_deregister_function (acm->function);
++ acm->function = NULL;
++}
++
++/*!
++ * Function table exported to the OS specific upper layer.
++ */
++struct acm_usb_services acm_fd_usb_ops = {
++ .fd_init = acm_fd_init,
++ .fd_exit = acm_fd_exit,
++ .xmit_chars = acm_xmit_chars,
++ .send_int_notification = acm_send_int_notification,
++ .throttle = acm_throttle,
++ .unthrottle = acm_unthrottle,
++ .write_room = acm_write_room,
++ .chars_in_buffer = acm_chars_in_buffer,
++ .wait_task = acm_wait_task,
++ .schedule_recv = acm_schedule_recv,
++ .open = acm_open,
++ .close = acm_close,
++ .flush = acm_flush,
++ .ready = acm_ready,
++ .get_dtr = acm_get_dtr,
++ .get_dsr = acm_get_dsr,
++ .get_dcd = acm_get_dcd,
++ .set_dsr = acm_set_dsr,
++ .set_dcd = acm_set_dcd,
++ .ring = acm_ring,
++ .send_break = acm_send_break,
++ .overrun = acm_overrun,
++ .set_local = acm_set_local,
++ .set_loopback = acm_set_loopback,
++};
++
+diff -uNr linux/drivers/no-otg/functions/acm/acm-fd.h linux/drivers/otg/functions/acm/acm-fd.h
+--- linux/drivers/no-otg/functions/acm/acm-fd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/acm-fd.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,242 @@
++/*
++ * otg/functions/acm/acm-fd.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/acm/acm-fd.h
++ * @brief ACM Function Driver private defines
++ *
++ * The top and bottom halves of the driver comunication via these structures.
++ *
++ * @ingroup ACMFunction
++ */
++
++#ifndef ACM_FD_H
++#define ACM_FD_H 1
++
++typedef void (*cpy_fn)(u8 *dst, u8 *src, int len);
++
++/*! acm_usb_services
++ * Services that the acm_fd library provides.
++ */
++struct acm_usb_services {
++
++ /*! fd_init - called to initialize the acm_fd library
++ * This call initialized the acm_fd library and registers the function driver
++ * with the USB Peripheral Stack.
++ */
++ int (*fd_init)(struct acm_private *acm, char *inf, u32 vendor_id, u32 product_id, u32 wmax_urbs, u32 wmax_bytes);
++
++ /*! fd_exit - called before exiting
++ * This call will cause the function driver to be de-registered.
++ */
++ void (*fd_exit)(struct acm_private *acm);
++
++ /*! open - device open
++ * This is called the device is opened (first open only if non-exclusive opens allowed).
++ */
++ int (*open)(struct acm_private *acm, int interface);
++
++ /*! close - Device close
++ * This is called when the device closed (last close only if non-exclusive opens allowed.)
++ */
++ int (*close)(struct acm_private *acm, int interface);
++
++
++ /*! flush - flush data urbs.
++ * Cancel outstanding data urbs.
++ */
++ void (*flush)(struct acm_private *acm, int interface);
++
++ /*! schedule_recv - queue as many data receive urbs as possible
++ * This will schedule a bottom half hander that will will start as
++ * many receive data urbs as are allowed given the amount of room
++ * available in the upper layer. If no urbs are queued by the
++ * bottom half handler it will re-schedule itself.
++ */
++ void (*schedule_recv)(struct acm_private *acm, int interface);
++
++ /*! throttle - set throttle flag for specified interface
++ * Receive urbs will not be queued when throttled.
++ */
++ void (*throttle)(struct acm_private *acm, int interface);
++
++ /*! unthrottle - reset throttle flag for specified interface
++ * Receive urbs are allowed to be queued. If no urbs are queued a
++ * bottom half handler will be scheduled to queue them.
++ */
++ void (*unthrottle)(struct acm_private *acm, int interface);
++
++
++ /*! xmit_chars - send data via specified interface
++ * This will start a transmit urb to send the specified data. The
++ * number of characters sent will be returned.
++ */
++ int (*xmit_chars)(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf);
++
++ /*! write_room
++ * Return amount of data that could be queued for sending.
++ */
++ int (*write_room)(struct acm_private *acm, int interface);
++
++ /*! chars_in_buffer
++ * Return number of chars in xmit buffer.
++ */
++ int (*chars_in_buffer)(struct acm_private *acm, int interface);
++
++
++ /*! send_int_notification - send notification via interrupt endpoint
++ * This can be used to queue network, serial state change notifications.
++ */
++ int (*send_int_notification)(struct acm_private *acm, int bnotification, int data);
++
++ /*! wait_task - wait for task to complete.
++ */
++ void (*wait_task)(struct acm_private *acm, WORK_ITEM *queue);
++
++ /*! ready - return true if connected and carrier
++ */
++ int (*ready)(struct acm_private *acm);
++
++ /*! get_dtr
++ * Get DTR status.
++ */
++ int (*get_dtr)(struct acm_private *acm);
++
++ /*! get_dsr
++ * Get DSR status
++ */
++ int (*get_dsr)(struct acm_private *acm);
++
++ /*! get_dcd
++ * Get DCD status
++ */
++ int (*get_dcd)(struct acm_private *acm);
++
++ /*! set_dsr
++ * Set DSR status
++ */
++ void (*set_dsr)(struct acm_private *acm, int value);
++
++ /*! set_dcd
++ * Set DCD status
++ */
++ void (*set_dcd)(struct acm_private *acm, int value);
++
++ /*! ring
++ * Indicate Ring signal to host.
++ */
++ void (*ring)(struct acm_private *acm);
++
++ /*! send_break
++ * Indicate Break signal to host.
++ */
++ void (*send_break)(struct acm_private *acm);
++
++ /*! overrun
++ * Indicate Overrun signal to host.
++ */
++ void (*overrun)(struct acm_private *acm);
++
++
++ /*! set_local
++ * Set LOCAL status
++ */
++ void (*set_local)(struct acm_private *acm, int value);
++
++ /*! set_loopback - set loopback mode
++ * Sets LOOP flag, data received from the host will be immediately
++ * returned without passing to the upper layer.
++ */
++ void (*set_loopback)(struct acm_private *acm, int value);
++};
++
++/*! acm_function_services
++ * Services that the top level driver provides to the lower library.
++ */
++struct acm_function_services {
++ /*! enable
++ * Enable the function driver.
++ */
++ void (*enable)(struct usbd_function_instance *function);
++
++ /*! disable
++ * Disable the function driver.
++ */
++ void (*disable)(struct usbd_function_instance *function);
++
++ /*! wakeup_opens
++ * Wakeup processes waiting for DTR.
++ */
++ void (*wakeup_opens)(struct acm_private *acm);
++
++ /*! wakeup_opens
++ * Wakeup processes waiting for state change.
++ */
++ void (*wakeup_state)(struct acm_private *acm);
++
++ /*! wakeup writers
++ * Wakeup pending writes.
++ */
++ void (*schedule_wakeup_writers)(struct acm_private *acm);
++
++ /*! recv_space_available
++ * Check for amount of receive space that is available, controls
++ * amount of receive urbs that will be queued.
++ */
++ int (*recv_space_available)(struct acm_private *acm, int interface);
++
++ /*! recv_chars
++ * Process chars received on specified interface.
++ */
++ int (*recv_chars)(struct acm_private *acm, int interface, u8 *cp, int n);
++
++
++ /*! schedule_hangup
++ * Schedule a work item that will perform a hangup.
++ */
++ void (*schedule_hangup)(struct acm_private *acm);
++
++ /*! comm_feature
++ * Tell function that comm feature has changed.
++ */
++ void (*comm_feature)(struct acm_private *acm);
++
++ /*! line_coding
++ * Tell function that line coding has changed.
++ */
++ void (*line_coding)(struct acm_private *acm);
++
++ /*! control_state
++ * Tell function that control state has changed.
++ */
++ void (*control_state)(struct acm_private *acm);
++
++ /*! send_break
++ * Tell function to send a break signal.
++ */
++ void (*send_break)(struct acm_private *acm);
++
++};
++
++extern struct acm_usb_services acm_fd_usb_ops;
++extern struct usbd_function_operations function_ops;
++
++
++/*
++ * otg-trace tag.
++ */
++extern otg_tag_t acm_fd_trace_tag;
++
++#define MAX_QUEUED_BYTES 256
++#define MAX_QUEUED_URBS 10 // Max for write
++
++
++#endif
+diff -uNr linux/drivers/no-otg/functions/acm/acm-os.h linux/drivers/otg/functions/acm/acm-os.h
+--- linux/drivers/no-otg/functions/acm/acm-os.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/acm-os.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * otg/functions/acm/acm-os.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ *
++ */
++/*!
++ * @file otg/functions/acm/acm-os.h
++ * @brief ACM Function Driver private defines
++ *
++ * An ACM (Abstract Control Model) driver is composed of two pieces:
++ * 1) An OS specific piece that handles creating and operating
++ * a serial device for the given OS.
++ * 2) A USB specific piece that interfaces either with the host
++ * usbcore layer, or with the otgcore layer.
++ *
++ * If the USB piece interfaces with the host usbcore layer you get
++ * an ACM class driver. If the USB piece interfaces with the otgcore
++ * layer you get an ACM function driver.
++ *
++ * This file describes the functions exported by the various acm-*-os.c
++ * files (implementing (1)) for use in acm-fd.c (2).
++ *
++ * @ingroup ACMFunction
++ */
++
++#ifndef ACM_OS_H
++#define ACM_OS_H 1
++
++/*
++ * acm_os_recv_space_available - return the number of bytes of data
++ * the OS specific piece can accept without
++ * dropping some.
++ */
++extern int acm_os_recv_space_available(struct acm_private *acm);
++
++/*
++ * acm_os_recv_chars - receive n bytes starting at cp. This will never be
++ * more than the last call to acm_os_recv_space_available().
++ * This will be called from interrupt context.
++ */
++extern int acm_os_recv_chars(struct acm_private *acm, u8 *cp, int n);
++
++/*
++ * acm_os_wakeup_writers - wakeup any blocked writers. This is called
++ * from interrupt context, so may need to queue
++ * the actual wakeup in a "bottom half".
++ */
++extern void acm_os_wakeup_writers(struct acm_private *acm);
++
++/*
++ * acm_os_hangup - send a hangup to any readers or writers. This can be
++ * called from interrupt context, so may need to queue
++ * the actual hangup in a "bottom half".
++ */
++extern void acm_os_hangup(struct acm_private *acm);
++
++/*
++ * acm_os_wakeup_opens - wakeup any blocked opens. This is called
++ * from interrupt context, so may need to queue
++ * the actual wakeup in a "bottom half".
++ */
++extern void acm_os_wakeup_opens(struct acm_private *acm);
++
++extern void acm_os_enable(struct usbd_function_instance *function);
++extern void acm_os_disable(struct usbd_function_instance *function);
++
++#endif
+diff -uNr linux/drivers/no-otg/functions/acm/acm.h linux/drivers/otg/functions/acm/acm.h
+--- linux/drivers/no-otg/functions/acm/acm.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/acm.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,113 @@
++/*
++ * otg/functions/acm/acm.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++
++/*!
++ * @defgroup ACMFunction ACM
++ * @ingroup functiongroup
++ */
++
++/*!
++ * @file otg/functions/acm/acm.h
++ * @brief ACM Function Driver private defines
++ *
++ * This is an ACM Function Driver. The upper edge is exposed
++ * to the hosting OS as a Posix type character device. The lower
++ * edge implements the USB Device Stack API.
++ *
++ * This driver implements the CDC ACM driver model and uses the CDC ACM
++ * protocols.
++ *
++ * Note that it appears to be impossible to determine the end of a receive
++ * bulk transfer larger than wMaxPacketsize. The host is free to send
++ * wMaxPacketsize chars in a single transfer. This means that we cannot
++ * queue receive urbs larger than wMaxPacketsize (typically 64 bytes.)
++ *
++ * This does not however prevent queuing transmit urbs with larger amounts
++ * of data. It is interpreted at the receiving (host) end as a series of
++ * wMaxPacketsize transfers but because there is no interpretation to the
++ * amounts of data sent it does affect anything if we treat the data as a
++ * single larger transfer.
++ *
++ * @ingroup ACMFunction
++ */
++
++
++// Endpoint indexes in acm_endpoint_requests[] and the endpoint map.
++#define BULK_OUT 0x00
++#define BULK_IN 0x01
++#define INT_IN 0x02
++#define ENDPOINTS 0x03
++
++#define COMM_INTF 0x00
++#define DATA_INTF 0x01
++
++
++
++// Most hosts don't care about BMAXPOWER, but the UUT tests want it to be 1
++#define BMAXPOWER 1
++#define BMATTRIBUTE 0
++
++#define STATIC
++//#define STATIC static
++//
++
++/*! @name ACM Flags
++ * @{
++ */
++#define ACM_OPENED (1 << 0) /*!< upper layer has opened the device port */
++#define ACM_CONFIGURED (1 << 1) /*!< device is configured */
++#define ACM_THROTTLED (1 << 2) /*!< upper layer doesn't want to receive data */
++#define ACM_CARRIER (1 << 3) /*!< host has set DTR, i.e. host has opened com device */
++#define ACM_LOOPBACK (1 << 4) /*!< upper layer wants local loopback */
++#define ACM_LOCAL (1 << 5) /*!< upper layer specified LOCAL mode */
++
++/*! @} */
++
++/*! struct acm_private
++ */
++struct acm_private {
++ struct usbd_function_instance *function;
++ struct usbd_function_driver *function_driver;
++ struct acm_function_services *function_services;
++ otg_tag_t trace_tag;
++
++ u32 flags;
++
++ int recv_urbs;
++ atomic_t used;
++ atomic_t queued_bytes;
++ atomic_t queued_urbs;
++ unsigned int writesize; // packetsize * 4
++
++ /*TBR debug receive flow control */
++ unsigned long bytes_received;
++ unsigned long bytes_forwarded;
++ /*TBR end debug */
++
++ struct cdc_acm_line_coding line_coding; // C.f. Table 50 - [GS]ET_LINE_CODING Device Request (to/from host)
++ cdc_acm_bmUARTState bmUARTState; // C.f. Table 51 - SET_CONTROL_LINE_STATE Device Request (from host)
++ cdc_acm_bmLineState bmLineState; // C.f. Table 69 - SERIAL_STATE Notification (to host)
++
++
++
++ struct tq_struct recv_tqueue;
++
++ void *privdata;
++
++};
++
++
++struct acm_work {
++ struct acm_private *acm;
++ int interface;
++};
++
+diff -uNr linux/drivers/no-otg/functions/acm/modem-l24-os.c linux/drivers/otg/functions/acm/modem-l24-os.c
+--- linux/drivers/no-otg/functions/acm/modem-l24-os.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/modem-l24-os.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,194 @@
++/*
++ * otg/functions/acm/modem-l24-os.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/acm/modem-l24-os.c
++ * @brief ACM Function Driver private defines
++ *
++ * An ACM (Abstract Control Model) driver is composed of two pieces:
++ * 1) An OS specific piece that handles creating and operating
++ * a serial device for the given OS.
++ * 2) A USB specific piece that interfaces either with the host
++ * usbcore layer, or with the otgcore layer.
++ *
++ * If the USB piece interfaces with the host usbcore layer you get
++ * an ACM class driver. If the USB piece interfaces with the otgcore
++ * layer you get an ACM function driver.
++ *
++ * This file is the OS specific piece that interfaces with Linux
++ * (kernel versions 2.4.*). It creates two simple characters devices.
++ * One for the data transport and one for the communications interface.
++ *
++ * @ingroup ACMFunction
++ */
++
++
++//#include <otg/osversion.h>
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com");
++
++MOD_DESCRIPTION ("Belcarra CDC-ACM Function");
++EMBED_LICENSE();
++
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <linux/capability.h>
++#include <otg/otg-trace.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++// May need an ifdef here to pick up the acm_cl_usb_ops in the future.
++static struct acm_usb_services *usb_ops = &acm_fd_usb_ops;
++
++#define MDM mdm_fd_trace_tag
++otg_tag_t mdm_fd_trace_tag;
++
++/* Module Parameters ************************************************************************* */
++
++static u32 vendor_id;
++static u32 product_id;
++static u32 max_queued_urbs = MAX_QUEUED_URBS;
++static u32 max_queued_bytes = MAX_QUEUED_BYTES;
++
++MOD_PARM (vendor_id, "i");
++MOD_PARM (product_id, "i");
++MOD_PARM (max_queued_urbs, "i");
++MOD_PARM (max_queued_bytes, "i");
++
++MOD_PARM_DESC (vendor_id, "Device Vendor ID");
++MOD_PARM_DESC (product_id, "Device Product ID");
++MOD_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs");
++MOD_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes");
++
++
++/* ACM ***************************************************************************************** */
++
++#define ACM_TTY_MAJOR 166
++#define ACM_TTY_MINOR 0
++#define ACM_TTY_MINORS 1
++
++static struct acm_private acm_private;
++
++/* USB Simple Char Device ********************************************************************** */
++
++
++/* USB Comm Char Device ************************************************************************ */
++
++
++/* USB Module init/exit ************************************************************************ */
++/*
++ * acm_modinit - module init
++ *
++ */
++STATIC int acm_modinit (void)
++{
++ int i;
++ printk (KERN_INFO "Copyright (c) 2003-2004 sl@belcarra.com, tbr@belcarra.com\n");
++ MDM = otg_trace_obtain_tag();
++
++ // initialize private structure
++ //acm_private.tty_driver = &acm_tty_driver;
++ //acm_private.wqueue.routine = acm_wakeup_writers;
++ //acm_private.wqueue.data = &acm_private;
++ //acm_private.hqueue.routine = acm_hangup;
++ //acm_private.hqueue.data = &acm_private;
++ //acm_private.recv_tqueue.routine = &acm_start_recv;
++ //acm_private.recv_tqueue.data = &acm_private;
++
++ //init_waitqueue_head(&acm_private.open_wait);
++
++
++ // register as usb function driver
++
++ THROW_IF (usb_ops->initialize_usb_part(&acm_private,"acm_fd",vendor_id,product_id,
++ max_queued_urbs,max_queued_bytes ), error);
++
++ CATCH(error) {
++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__);
++
++ TRACE_MSG0(MDM,"--> -EINVAL");
++ return -EINVAL;
++ }
++ TRACE_MSG0(MDM,"--> 0");
++ return 0;
++}
++
++void acm_wait_task(WORK_ITEM *queue)
++{
++ TRACE_MSG1(MDM,"entered data=%p",queue->data);
++ RETURN_IF(!queue->data);
++ queue->data = NULL;
++#if defined(LINUX24)
++ while (queue->sync) {
++ TRACE_MSG1(MDM,"waiting for queue: %p",queue);
++ schedule_timeout(HZ);
++ }
++#else
++ while(PENDING_WORK_ITEM((*queue))){
++ TRACE_MSG1(MDM,"waiting for queue: %p",queue);
++ SCHEDULE_TIMEOUT(1); // 1 second delay
++ }
++#endif
++ TRACE_MSG0(MDM,"exited");
++}
++
++/* acm_modexit - module cleanup
++ */
++STATIC void acm_modexit (void)
++{
++ unsigned long flags;
++ struct usbd_urb *urb;
++ TRACE_MSG0(MDM,"entered");
++
++ // Wake up any pending opens after setting the exiting flag.
++ local_irq_save(flags);
++ acm_private.exiting = 1;
++ //if (acm_private.open_wait_count > 0) {
++ // wake_up_interruptible(&acm_private.open_wait);
++ //}
++ local_irq_restore(flags);
++
++ // verify no tasks are running
++ //acm_wait_task(&acm_private.wqueue);
++ //acm_wait_task(&acm_private.hqueue);
++
++//#if defined(LINUX24)
++// run_task_queue(&tq_timer);
++//#else
++// blk_run_queues();
++//#endif
++
++ //acm_wait_task(&acm_private.recv_tqueue);
++
++ usb_ops->terminate_usb_part(&acm_private);
++ otg_trace_invalidate_tag(MDM);
++}
++
++
++module_init (acm_modinit);
++module_exit (acm_modexit);
+diff -uNr linux/drivers/no-otg/functions/acm/modem.c linux/drivers/otg/functions/acm/modem.c
+--- linux/drivers/no-otg/functions/acm/modem.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/modem.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,263 @@
++/*
++ * otg/functions/acm/modem.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ */
++/*!
++ * @file otg/functions/acm/modem.c
++ * @brief ACM-MODEM Descriptor Set
++ *
++ * This file is the USB specific piece that interfaces with the otgcore layer.
++ * If you're looking for the USB specific piece that interfaces with the
++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c
++ *
++ * Note: this function driver requires the following endpoints:
++ *
++ * BULK-IN
++ * BULK-OUT
++ * INTERRUPT-IN
++ *
++ * This function driver cannot be used on devices (such as the StrongArm
++ * SA1100) that do not have an interrupt endpoint.
++ *
++ * @ingroup MODEMFunction
++ */
++
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <otg/otg-trace.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++
++
++/*
++ * CDC ACM Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++/*! BULK OUT Data Endpoint Descriptor
++ */
++static u8 mdn_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, };
++
++/*! BULK In Data Endpoint Descriptor
++ */
++static u8 mdn_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, };
++
++/*! Data Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *mdn_alt_endpoints[] = {
++ (struct usbd_endpoint_descriptor *) mdn_alt_1,
++ (struct usbd_endpoint_descriptor *) mdn_alt_2, };
++
++/*! Data Endpoint Index List
++ */
++u8 mdn_alt_indexes[] = { BULK_OUT, BULK_IN, };
++
++/*! INTERRUPT In Comm Endpoint Descriptor
++ */
++static u8 mdn_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, };
++
++/*! Comm Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *mdn_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) mdn_comm_1 };
++
++
++/*! Comm Endpoint Index List
++ */
++u8 mdn_comm_indexes[] = { INT_IN, };
++
++/*! @{
++ * Class Descriptors
++ */
++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++/*! @} */
++
++/*! ACMF Class Descriptor
++ * ACMF - c.f. Table 28
++ * currenty set to 0x2 - Support Set_Line_Coding etc,
++ *
++ * XXX Should we also set 0x4 - Supports Network_Notification?
++ */
++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, };
++
++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] =
++ { (struct usbd_generic_class_descriptor *) cdc_class_1,
++ (struct usbd_generic_class_descriptor *) cdc_class_2,
++ (struct usbd_generic_class_descriptor *) cdc_class_3,
++ (struct usbd_generic_class_descriptor *) cdc_class_4, };
++
++
++
++/* Alternate Descriptors */
++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04)
++
++/*! Comm Alternate Descriptor
++ */
++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, };
++
++/*! Data Alternate Descriptor
++ */
++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, };
++
++
++
++/*! Comm alternate descriptions
++ */
++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_COMM_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor,
++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
++ class_list: cdc_comm_class_descriptors,
++ endpoint_list: mdn_comm_endpoints,
++ endpoints:sizeof (mdn_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: mdn_comm_indexes,
++ }, };
++
++/*! Data alternate descriptions
++ */
++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_DATA_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor,
++ endpoint_list: mdn_alt_endpoints,
++ endpoints:sizeof (mdn_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: mdn_alt_indexes,
++ }, };
++
++
++/*! Interface Descriptions List */
++static struct usbd_interface_description cdc_interfaces[] = {
++ {
++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_comm_alternate_descriptions,
++ },
++
++ {
++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_data_alternate_descriptions,
++ },
++};
++
++
++/*! Configuration Descriptor
++ */
++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = {
++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, };
++
++/*! Configuration Description
++ */
++struct usbd_configuration_description mdn_description[] = {
++ { iConfiguration: CONFIG_OTG_ACM_DESC,
++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor,
++ }, };
++
++/*! Device Descriptor
++ */
++static struct usbd_device_descriptor mdn_device_descriptor = {
++ bLength: sizeof(struct usbd_device_descriptor),
++ bDescriptorType: USB_DT_DEVICE,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++/*! High Speed Device Qualifier Descriptor
++ */
++static struct usbd_device_qualifier_descriptor mdn_device_qualifier_descriptor = {
++ bLength: sizeof(struct usbd_device_qualifier_descriptor),
++ bDescriptorType: USB_DT_DEVICE_QUALIFIER,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++
++/*! Endpoint Request List
++ */
++static struct usbd_endpoint_request mdn_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, },
++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, },
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
++ { 0, },
++};
++
++/*! OTG Descriptor
++ */
++static struct usbd_otg_descriptor mdn_otg_descriptor = {
++ bLength : sizeof(struct usbd_otg_descriptor),
++ bDescriptorType: USB_DT_OTG,
++ bmAttributes: 0,
++};
++
++/*! Device Description
++ */
++struct usbd_device_description mdn_device_description = {
++ device_descriptor: &mdn_device_descriptor,
++#ifdef CONFIG_OTG_HIGH_SPEED
++ device_qualifier_descriptor: &mdn_device_qualifier_descriptor,
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ otg_descriptor: &mdn_otg_descriptor,
++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER,
++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME,
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR,
++#endif
++};
++
++
++
++
++/*! function_driver
++ */
++struct usbd_function_driver modem_function_driver = {
++ name: "ACM-MDM",
++ fops:&function_ops,
++ device_description:&mdn_device_description,
++ bNumConfigurations:sizeof (mdn_description) / sizeof (struct usbd_configuration_description),
++ configuration_description:mdn_description,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ interface_list:cdc_interfaces,
++ endpointsRequested: ENDPOINTS,
++ requestedEndpoints: mdn_endpoint_requests,
++};
++
+diff -uNr linux/drivers/no-otg/functions/acm/modem.h linux/drivers/otg/functions/acm/modem.h
+--- linux/drivers/no-otg/functions/acm/modem.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/modem.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,118 @@
++/*
++ * otg/functions/acm/modem.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @defgroup MODEMFunction ACM-Modem
++ * @ingroup functiongroup
++ */
++/*!
++ * @file otg/functions/acm/modem.h
++ * @brief ACM Function Driver private defines
++ *
++ *
++ * This is an ACM Function Driver. The upper edge is exposed
++ * to the hosting OS as a Posix type character device. The lower
++ * edge implements the USB Device Stack API.
++ *
++ * These are emulated and set by the modem driver as appropriate
++ * to model a virutal serial port. The
++ *
++ * TIOCM_RNG RNG (Ring) not used
++ * TIOCM_LE DSR (Data Set Ready / Line Enable)
++ * TIOCM_DSR DSR (Data Set Ready)
++ * TIOCM_CAR DCD (Data Carrier Detect)
++ * TIOCM_CTS CTS (Clear to Send)
++ * TIOCM_CD TIOCM_CAR
++ * TIOCM_RI TIOCM_RNG
++ *
++ * These are set by the application:
++ *
++ * TIOCM_DTR DTR (Data Terminal Ready)
++ * TIOCM_RTS RTS (Request to Send)
++ *
++ * TIOCM_LOOP Set into loopback mode
++ * TIOCM_OUT1 Not used.
++ * TIOCM_OUT2 Not used.
++ *
++ *
++ * The following File and termio c_cflags are used:
++ *
++ * O_NONBLOCK don't block in modem_open()
++ * O_EXCL don't allow more than one open
++ *
++ * CLOCAL ignore DTR status
++ * CBAUD send break if set to B0
++ *
++ * Virtual NULL Modem
++ *
++ * Peripheral to Host via Serial State Notification (write ioctls)
++ *
++ * Application TIOCM Null Modem ACM (DCE) Notificaiton Host (DTE)
++ * -------------------------------------------------------------------------------------------------------------
++ * Request to send TIOCM_RTS RTS -> CTS CTS Not Available Clear to Send (N/A)
++ * Data Terminal Ready TIOCM_DTR DTR -> DSR DSR bTxCarrier Data Set Ready
++ * DTR -> DCD DCD bRXCarrier Carrier Detect
++ * Ring Indicator TIOCM_OUT1 OUT1 -> RI RI bRingSignal Ring Indicator
++ * Overrun TIOCM_OUT2 OUT2 -> Overrun Overrun bOverrun Overrun
++ * Send Break B0 B0 -> Break Break bBreak Break Received
++ * -------------------------------------------------------------------------------------------------------------
++ *
++ *
++ * Host to Peripheral via Set Control Line State
++ *
++ * Host (DTE) Line State ACM (DCE) Null Modem TIOCM Peripheral (DTE)
++ * -------------------------------------------------------------------------------------------------------------
++ * Data Terminal Ready D0 DTR DTR -> DSR TIOCM_DSR Data Set Ready
++ * DTR -> DCD TIOCM_CAR Carrier Detect
++ * Request To Send D1 RTS RTS -> CTS TIOCM_CTS Clear to Send
++ * -------------------------------------------------------------------------------------------------------------
++ *
++ *
++ * @ingroup MODEMFunction
++ */
++
++
++extern struct usbd_function_driver modem_function_driver;
++
++//#define MODEM_OPENED (1 << 0) /*!< OPENED flag */
++#define MODEMFD_CLOCAL (1 << 1) /*!< CLOCAL flag */
++//#define MODEMFD_LOOPBACK (1 << 2) /*!< LOOPBACK flag */
++#define MODEMFD_EXCLUSIVE (1 << 3) /*!< EXCLUSIVE flag */
++#define MODEMFD_THROTTLED (1 << 4) /*!< THROTTLED flag */
++
++/*! struct modem_private
++ */
++struct modem_private {
++
++ struct modem_driver *modem_driver; /*!< modem structure */
++ int modem_driver_registered; /*!< non-zero if modem_driver registered */
++ int usb_driver_registered; /*!< non-zero if usb function registered */
++
++ struct modem_struct *modem; /*!< non-null if modem open */
++ struct tq_struct wqueue; /*!< task queue for writer wakeup */
++ struct tq_struct hqueue; /*!< task queue for hangup */
++
++ u32 flags; /*!< flags */
++
++ u32 tiocm; /*!< tiocm settings */
++
++ struct serial_struct serial_struct; /*!< serial structure used for TIOCSSERIAL and TIOCGSERIAL */
++
++ wait_queue_head_t tiocm_wait;
++ u32 tiocgicount;
++
++ u32 c_cflag;
++
++ wait_queue_head_t open_wait; // wait queue for blocking open
++ int exiting; // True if module exiting
++};
++
++
+diff -uNr linux/drivers/no-otg/functions/acm/obex-l24-os.c linux/drivers/otg/functions/acm/obex-l24-os.c
+--- linux/drivers/no-otg/functions/acm/obex-l24-os.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/obex-l24-os.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,194 @@
++/*
++ * otg/functions/acm/obex-l24-os.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/acm/obex-l24-os.c
++ * @brief ACM Function Driver private defines
++ *
++ * An ACM (Abstract Control Model) driver is composed of two pieces:
++ * 1) An OS specific piece that handles creating and operating
++ * a serial device for the given OS.
++ * 2) A USB specific piece that interfaces either with the host
++ * usbcore layer, or with the otgcore layer.
++ *
++ * If the USB piece interfaces with the host usbcore layer you get
++ * an ACM class driver. If the USB piece interfaces with the otgcore
++ * layer you get an ACM function driver.
++ *
++ * This file is the OS specific piece that interfaces with Linux
++ * (kernel versions 2.4.*). It creates two simple characters devices.
++ * One for the data transport and one for the communications interface.
++ *
++ * @ingroup ACMFunction
++ */
++
++
++//#include <otg/osversion.h>
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com");
++
++MOD_DESCRIPTION ("Belcarra CDC-ACM Function");
++EMBED_LICENSE();
++
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <linux/capability.h>
++#include <otg/otg-trace.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++// May need an ifdef here to pick up the acm_cl_usb_ops in the future.
++static struct acm_usb_services *usb_ops = &acm_fd_usb_ops;
++
++#define OBX obx_fd_trace_tag
++otg_tag_t obx_fd_trace_tag;
++
++/* Module Parameters ************************************************************************* */
++
++static u32 vendor_id;
++static u32 product_id;
++static u32 max_queued_urbs = MAX_QUEUED_URBS;
++static u32 max_queued_bytes = MAX_QUEUED_BYTES;
++
++MOD_PARM (vendor_id, "i");
++MOD_PARM (product_id, "i");
++MOD_PARM (max_queued_urbs, "i");
++MOD_PARM (max_queued_bytes, "i");
++
++MOD_PARM_DESC (vendor_id, "Device Vendor ID");
++MOD_PARM_DESC (product_id, "Device Product ID");
++MOD_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs");
++MOD_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes");
++
++
++/* ACM ***************************************************************************************** */
++
++#define ACM_TTY_MAJOR 166
++#define ACM_TTY_MINOR 0
++#define ACM_TTY_MINORS 1
++
++static struct acm_private acm_private;
++
++/* USB Simple Char Device ********************************************************************** */
++
++
++/* USB Comm Char Device ************************************************************************ */
++
++
++/* USB Module init/exit ************************************************************************ */
++/*
++ * acm_modinit - module init
++ *
++ */
++STATIC int acm_modinit (void)
++{
++ int i;
++ printk (KERN_INFO "Copyright (c) 2003-2004 sl@belcarra.com, tbr@belcarra.com\n");
++ OBX = otg_trace_obtain_tag();
++
++ // initialize private structure
++ //acm_private.tty_driver = &acm_tty_driver;
++ //acm_private.wqueue.routine = acm_wakeup_writers;
++ //acm_private.wqueue.data = &acm_private;
++ //acm_private.hqueue.routine = acm_hangup;
++ //acm_private.hqueue.data = &acm_private;
++ //acm_private.recv_tqueue.routine = &acm_start_recv;
++ //acm_private.recv_tqueue.data = &acm_private;
++
++ //init_waitqueue_head(&acm_private.open_wait);
++
++
++ // register as usb function driver
++
++ THROW_IF (usb_ops->initialize_usb_part(&acm_private,"acm_fd",vendor_id,product_id,
++ max_queued_urbs,max_queued_bytes ), error);
++
++ CATCH(error) {
++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__);
++
++ TRACE_MSG0(OBX,"--> -EINVAL");
++ return -EINVAL;
++ }
++ TRACE_MSG0(OBX,"--> 0");
++ return 0;
++}
++
++void acm_wait_task(WORK_ITEM *queue)
++{
++ TRACE_MSG1(OBX,"entered data=%p",queue->data);
++ RETURN_IF(!queue->data);
++ queue->data = NULL;
++#if defined(LINUX24)
++ while (queue->sync) {
++ TRACE_MSG1(OBX,"waiting for queue: %p",queue);
++ schedule_timeout(HZ);
++ }
++#else
++ while(PENDING_WORK_ITEM((*queue))){
++ TRACE_MSG1(OBX,"waiting for queue: %p",queue);
++ SCHEDULE_TIMEOUT(1); // 1 second delay
++ }
++#endif
++ TRACE_MSG0(OBX,"exited");
++}
++
++/* acm_modexit - module cleanup
++ */
++STATIC void acm_modexit (void)
++{
++ unsigned long flags;
++ struct usbd_urb *urb;
++ TRACE_MSG0(OBX,"entered");
++
++ // Wake up any pending opens after setting the exiting flag.
++ local_irq_save(flags);
++ acm_private.exiting = 1;
++ //if (acm_private.open_wait_count > 0) {
++ // wake_up_interruptible(&acm_private.open_wait);
++ //}
++ local_irq_restore(flags);
++
++ // verify no tasks are running
++ //acm_wait_task(&acm_private.wqueue);
++ //acm_wait_task(&acm_private.hqueue);
++
++//#if defined(LINUX24)
++// run_task_queue(&tq_timer);
++//#else
++// blk_run_queues();
++//#endif
++
++ //acm_wait_task(&acm_private.recv_tqueue);
++
++ usb_ops->terminate_usb_part(&acm_private);
++ otg_trace_invalidate_tag(OBX);
++}
++
++
++module_init (acm_modinit);
++module_exit (acm_modexit);
+diff -uNr linux/drivers/no-otg/functions/acm/obex.c linux/drivers/otg/functions/acm/obex.c
+--- linux/drivers/no-otg/functions/acm/obex.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/obex.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,263 @@
++/*
++ * otg/functions/acm/obex.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ */
++/*!
++ * @file otg/functions/acm/obex.c
++ * @brief ACM-OBEX Descriptor Set
++ *
++ * This file is the USB specific piece that interfaces with the otgcore layer.
++ * If you're looking for the USB specific piece that interfaces with the
++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c
++ *
++ * Note: this function driver requires the following endpoints:
++ *
++ * BULK-IN
++ * BULK-OUT
++ * INTERRUPT-IN
++ *
++ * This function driver cannot be used on devices (such as the StrongArm
++ * SA1100) that do not have an interrupt endpoint.
++ *
++ * @ingroup OBEXFunction
++ */
++
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <otg/otg-trace.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++
++
++/*
++ * CDC ACM Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++/*! BULK OUT Data Endpoint Descriptor
++ */
++static u8 obx_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, };
++
++/*! BULK In Data Endpoint Descriptor
++ */
++static u8 obx_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, };
++
++/*! Data Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *obx_alt_endpoints[] = {
++ (struct usbd_endpoint_descriptor *) obx_alt_1,
++ (struct usbd_endpoint_descriptor *) obx_alt_2, };
++
++/*! Data Endpoint Index List
++ */
++u8 obx_alt_indexes[] = { BULK_OUT, BULK_IN, };
++
++/*! INTERRUPT In Comm Endpoint Descriptor
++ */
++static u8 obx_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, };
++
++/*! Comm Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *obx_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) obx_comm_1 };
++
++
++/*! Comm Endpoint Index List
++ */
++u8 obx_comm_indexes[] = { INT_IN, };
++
++/*! @{
++ * Class Descriptors
++ */
++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++/*! @} */
++
++/*! ACMF Class Descriptor
++ * ACMF - c.f. Table 28
++ * currenty set to 0x2 - Support Set_Line_Coding etc,
++ *
++ * XXX Should we also set 0x4 - Supports Network_Notification?
++ */
++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, };
++
++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] =
++ { (struct usbd_generic_class_descriptor *) cdc_class_1,
++ (struct usbd_generic_class_descriptor *) cdc_class_2,
++ (struct usbd_generic_class_descriptor *) cdc_class_3,
++ (struct usbd_generic_class_descriptor *) cdc_class_4, };
++
++
++
++/* Alternate Descriptors */
++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04)
++
++/*! Comm Alternate Descriptor
++ */
++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, };
++
++/*! Data Alternate Descriptor
++ */
++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, };
++
++
++
++/*! Comm alternate descriptions
++ */
++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_COMM_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor,
++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
++ class_list: cdc_comm_class_descriptors,
++ endpoint_list: obx_comm_endpoints,
++ endpoints:sizeof (obx_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: obx_comm_indexes,
++ }, };
++
++/*! Data alternate descriptions
++ */
++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_DATA_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor,
++ endpoint_list: obx_alt_endpoints,
++ endpoints:sizeof (obx_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: obx_alt_indexes,
++ }, };
++
++
++/*! Interface Descriptions List */
++static struct usbd_interface_description cdc_interfaces[] = {
++ {
++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_comm_alternate_descriptions,
++ },
++
++ {
++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_data_alternate_descriptions,
++ },
++
++};
++
++
++/*! Configuration Descriptor
++ */
++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = {
++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, };
++
++/*! Configuration Description
++ */
++struct usbd_configuration_description obx_description[] = {
++ { iConfiguration: CONFIG_OTG_ACM_DESC,
++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor,
++ }, };
++
++/*! Device Descriptor
++ */
++static struct usbd_device_descriptor obx_device_descriptor = {
++ bLength: sizeof(struct usbd_device_descriptor),
++ bDescriptorType: USB_DT_DEVICE,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++/*! High Speed Device Qualifier Descriptor
++ */
++static struct usbd_device_qualifier_descriptor obx_device_qualifier_descriptor = {
++ bLength: sizeof(struct usbd_device_qualifier_descriptor),
++ bDescriptorType: USB_DT_DEVICE_QUALIFIER,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++
++/*! Endpoint Request List
++ */
++static struct usbd_endpoint_request obx_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, },
++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, },
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
++ { 0, },
++};
++
++/*! OTG Descriptor
++ */
++static struct usbd_otg_descriptor obx_otg_descriptor = {
++ bLength : sizeof(struct usbd_otg_descriptor),
++ bDescriptorType: USB_DT_OTG,
++ bmAttributes: 0,
++};
++
++/*! Device Description
++ */
++struct usbd_device_description obx_device_description = {
++ device_descriptor: &obx_device_descriptor,
++#ifdef CONFIG_OTG_HIGH_SPEED
++ device_qualifier_descriptor: &obx_device_qualifier_descriptor,
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ otg_descriptor: &obx_otg_descriptor,
++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER,
++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME,
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR,
++#endif
++};
++
++
++
++/*! function_driver
++ */
++struct usbd_function_driver obex_function_driver = {
++ name: "ACM-OBX",
++ fops:&function_ops,
++ device_description:&obx_device_description,
++ bNumConfigurations:sizeof (obx_description) / sizeof (struct usbd_configuration_description),
++ configuration_description:obx_description,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ interface_list:cdc_interfaces,
++ endpointsRequested: ENDPOINTS,
++ requestedEndpoints: obx_endpoint_requests,
++};
++
+diff -uNr linux/drivers/no-otg/functions/acm/obex.h linux/drivers/otg/functions/acm/obex.h
+--- linux/drivers/no-otg/functions/acm/obex.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/obex.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,66 @@
++/*
++ * otg/functions/acm/obex.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @defgroup OBEXFunction ACM-OBEX
++ * @ingroup functiongroup
++ */
++/*!
++ * @file otg/functions/acm/obex.h
++ * @brief ACM Function Driver private defines
++ *
++ *
++ * This is an ACM Function Driver. The upper edge is exposed
++ * to the hosting OS as a Posix type character device. The lower
++ * edge implements the USB Device Stack API.
++ *
++ *
++ *
++ * @ingroup OBEXFunction
++ */
++
++
++extern struct usbd_function_driver tty_function_driver;
++
++//#define TTY_OPENED (1 << 0) /*!< OPENED flag */
++#define TTYFD_CLOCAL (1 << 1) /*!< CLOCAL flag */
++//#define TTYFD_LOOPBACK (1 << 2) /*!< LOOPBACK flag */
++#define TTYFD_EXCLUSIVE (1 << 3) /*!< EXCLUSIVE flag */
++#define TTYFD_THROTTLED (1 << 4) /*!< THROTTLED flag */
++
++/*! struct obex_private
++ */
++struct obex_private {
++
++ struct tty_driver *tty_driver; /*!< tty structure */
++ int tty_driver_registered; /*!< non-zero if tty_driver registered */
++ int usb_driver_registered; /*!< non-zero if usb function registered */
++
++ struct tty_struct *tty; /*!< non-null if tty open */
++ struct tq_struct wqueue; /*!< task queue for writer wakeup */
++ struct tq_struct hqueue; /*!< task queue for hangup */
++
++ u32 flags; /*!< flags */
++
++ u32 tiocm; /*!< tiocm settings */
++
++ struct serial_struct serial_struct; /*!< serial structure used for TIOCSSERIAL and TIOCGSERIAL */
++
++ wait_queue_head_t tiocm_wait;
++ u32 tiocgicount;
++
++ u32 c_cflag;
++
++ wait_queue_head_t open_wait; // wait queue for blocking open
++ int exiting; // True if module exiting
++};
++
++
+diff -uNr linux/drivers/no-otg/functions/acm/tty-fd.c linux/drivers/otg/functions/acm/tty-fd.c
+--- linux/drivers/no-otg/functions/acm/tty-fd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/tty-fd.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,263 @@
++/*
++ * otg/functions/acm/tty-fd.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ */
++/*!
++ * @file otg/functions/acm/tty-fd.c
++ * @brief ACM-TTY Descriptor Set
++ *
++ * This file is the USB specific piece that interfaces with the otgcore layer.
++ * If you're looking for the USB specific piece that interfaces with the
++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c
++ *
++ * Note: this function driver requires the following endpoints:
++ *
++ * BULK-IN
++ * BULK-OUT
++ * INTERRUPT-IN
++ *
++ * This function driver cannot be used on devices (such as the StrongArm
++ * SA1100) that do not have an interrupt endpoint.
++ *
++ * @ingroup TTYFunction
++ */
++
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <otg/otg-trace.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++
++
++/*
++ * CDC ACM Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++/*! BULK OUT Data Endpoint Descriptor
++ */
++static u8 tty_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, };
++
++/*! BULK In Data Endpoint Descriptor
++ */
++static u8 tty_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, };
++
++/*! Data Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *tty_alt_endpoints[] = {
++ (struct usbd_endpoint_descriptor *) tty_alt_1,
++ (struct usbd_endpoint_descriptor *) tty_alt_2, };
++
++/*! Data Endpoint Index List
++ */
++u8 tty_alt_indexes[] = { BULK_OUT, BULK_IN, };
++
++/*! INTERRUPT In Comm Endpoint Descriptor
++ */
++static u8 tty_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, };
++
++/*! Comm Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *tty_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) tty_comm_1 };
++
++
++/*! Comm Endpoint Index List
++ */
++u8 tty_comm_indexes[] = { INT_IN, };
++
++/*! @{
++ * Class Descriptors
++ */
++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++/*! @} */
++
++/*! ACMF Class Descriptor
++ * ACMF - c.f. Table 28
++ * currenty set to 0x2 - Support Set_Line_Coding etc,
++ *
++ * XXX Should we also set 0x4 - Supports Network_Notification?
++ */
++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, };
++
++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] =
++ { (struct usbd_generic_class_descriptor *) cdc_class_1,
++ (struct usbd_generic_class_descriptor *) cdc_class_2,
++ (struct usbd_generic_class_descriptor *) cdc_class_3,
++ (struct usbd_generic_class_descriptor *) cdc_class_4, };
++
++
++
++/* Alternate Descriptors */
++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04)
++
++/*! Comm Alternate Descriptor
++ */
++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, };
++
++/*! Data Alternate Descriptor
++ */
++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, };
++
++
++
++/*! Comm alternate descriptions
++ */
++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_COMM_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor,
++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
++ class_list: cdc_comm_class_descriptors,
++ endpoint_list: tty_comm_endpoints,
++ endpoints:sizeof (tty_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: tty_comm_indexes,
++ }, };
++
++/*! Data alternate descriptions
++ */
++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_DATA_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor,
++ endpoint_list: tty_alt_endpoints,
++ endpoints:sizeof (tty_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: tty_alt_indexes,
++ }, };
++
++
++/*! Interface Descriptions List */
++static struct usbd_interface_description cdc_interfaces[] = {
++ {
++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_comm_alternate_descriptions,
++ },
++
++ {
++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_data_alternate_descriptions,
++ },
++
++};
++
++
++/*! Configuration Descriptor
++ */
++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = {
++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, };
++
++/*! Configuration Description
++ */
++struct usbd_configuration_description tty_description[] = {
++ { iConfiguration: CONFIG_OTG_ACM_DESC,
++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor,
++ }, };
++
++/*! Device Descriptor
++ */
++static struct usbd_device_descriptor tty_device_descriptor = {
++ bLength: sizeof(struct usbd_device_descriptor),
++ bDescriptorType: USB_DT_DEVICE,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++/*! High Speed Device Qualifier Descriptor
++ */
++static struct usbd_device_qualifier_descriptor tty_device_qualifier_descriptor = {
++ bLength: sizeof(struct usbd_device_qualifier_descriptor),
++ bDescriptorType: USB_DT_DEVICE_QUALIFIER,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++
++/*! Endpoint Request List
++ */
++static struct usbd_endpoint_request tty_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, },
++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, },
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
++ { 0, },
++};
++
++/*! OTG Descriptor
++ */
++static struct usbd_otg_descriptor tty_otg_descriptor = {
++ bLength : sizeof(struct usbd_otg_descriptor),
++ bDescriptorType: USB_DT_OTG,
++ bmAttributes: 0,
++};
++
++/*! Device Description
++ */
++struct usbd_device_description tty_device_description = {
++ device_descriptor: &tty_device_descriptor,
++#ifdef CONFIG_OTG_HIGH_SPEED
++ device_qualifier_descriptor: &tty_device_qualifier_descriptor,
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ otg_descriptor: &tty_otg_descriptor,
++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER,
++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME,
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR,
++#endif
++};
++
++/*! function_driver
++ */
++struct usbd_function_driver tty_function_driver = {
++ name: "ACM-TTY",
++ fops:&function_ops,
++ device_description:&tty_device_description,
++ bNumConfigurations:sizeof (tty_description) / sizeof (struct usbd_configuration_description),
++ configuration_description:tty_description,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ interface_list:cdc_interfaces,
++ endpointsRequested: ENDPOINTS,
++ requestedEndpoints: tty_endpoint_requests,
++};
++
++
++
+diff -uNr linux/drivers/no-otg/functions/acm/tty-if.c linux/drivers/otg/functions/acm/tty-if.c
+--- linux/drivers/no-otg/functions/acm/tty-if.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/tty-if.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,263 @@
++/*
++ * otg/functions/acm/tty-if.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ */
++/*!
++ * @file otg/functions/acm/tty-if.c
++ * @brief ACM-TTY Descriptor Set
++ *
++ * This file is the USB specific piece that interfaces with the otgcore layer.
++ * If you're looking for the USB specific piece that interfaces with the
++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c
++ *
++ * Note: this function driver requires the following endpoints:
++ *
++ * BULK-IN
++ * BULK-OUT
++ * INTERRUPT-IN
++ *
++ * This function driver cannot be used on devices (such as the StrongArm
++ * SA1100) that do not have an interrupt endpoint.
++ *
++ * @ingroup TTYFunction
++ */
++
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <otg/otg-trace.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++
++
++/*
++ * CDC ACM Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++/*! BULK OUT Data Endpoint Descriptor
++ */
++static u8 tty_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, };
++
++/*! BULK In Data Endpoint Descriptor
++ */
++static u8 tty_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, };
++
++/*! Data Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *tty_alt_endpoints[] = {
++ (struct usbd_endpoint_descriptor *) tty_alt_1,
++ (struct usbd_endpoint_descriptor *) tty_alt_2, };
++
++/*! Data Endpoint Index List
++ */
++u8 tty_alt_indexes[] = { BULK_OUT, BULK_IN, };
++
++/*! INTERRUPT In Comm Endpoint Descriptor
++ */
++static u8 tty_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, };
++
++/*! Comm Endpoint Descriptor List
++ */
++static struct usbd_endpoint_descriptor *tty_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) tty_comm_1 };
++
++
++/*! Comm Endpoint Index List
++ */
++u8 tty_comm_indexes[] = { INT_IN, };
++
++/*! @{
++ * Class Descriptors
++ */
++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
++/*! @} */
++
++/*! ACMF Class Descriptor
++ * ACMF - c.f. Table 28
++ * currenty set to 0x2 - Support Set_Line_Coding etc,
++ *
++ * XXX Should we also set 0x4 - Supports Network_Notification?
++ */
++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, };
++
++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] =
++ { (struct usbd_generic_class_descriptor *) cdc_class_1,
++ (struct usbd_generic_class_descriptor *) cdc_class_2,
++ (struct usbd_generic_class_descriptor *) cdc_class_3,
++ (struct usbd_generic_class_descriptor *) cdc_class_4, };
++
++
++
++/* Alternate Descriptors */
++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04)
++
++/*! Comm Alternate Descriptor
++ */
++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, };
++
++/*! Data Alternate Descriptor
++ */
++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, };
++
++
++
++/*! Comm alternate descriptions
++ */
++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_COMM_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor,
++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
++ class_list: cdc_comm_class_descriptors,
++ endpoint_list: tty_comm_endpoints,
++ endpoints:sizeof (tty_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: tty_comm_indexes,
++ }, };
++
++/*! Data alternate descriptions
++ */
++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_ACM_DATA_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor,
++ endpoint_list: tty_alt_endpoints,
++ endpoints:sizeof (tty_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_indexes: tty_alt_indexes,
++ }, };
++
++
++/*! Interface Descriptions List */
++static struct usbd_interface_description cdc_interfaces[] = {
++ {
++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_comm_alternate_descriptions,
++ },
++
++ {
++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:cdc_data_alternate_descriptions,
++ },
++
++};
++
++
++/*! Configuration Descriptor
++ */
++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = {
++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, };
++
++/*! Configuration Description
++ */
++struct usbd_configuration_description tty_description[] = {
++ { iConfiguration: CONFIG_OTG_ACM_DESC,
++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor,
++ }, };
++
++/*! Device Descriptor
++ */
++static struct usbd_device_descriptor tty_device_descriptor = {
++ bLength: sizeof(struct usbd_device_descriptor),
++ bDescriptorType: USB_DT_DEVICE,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++/*! High Speed Device Qualifier Descriptor
++ */
++static struct usbd_device_qualifier_descriptor tty_device_qualifier_descriptor = {
++ bLength: sizeof(struct usbd_device_qualifier_descriptor),
++ bDescriptorType: USB_DT_DEVICE_QUALIFIER,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++
++/*! Endpoint Request List
++ */
++static struct usbd_endpoint_request tty_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, },
++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, },
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
++ { 0, },
++};
++
++/*! OTG Descriptor
++ */
++static struct usbd_otg_descriptor tty_otg_descriptor = {
++ bLength : sizeof(struct usbd_otg_descriptor),
++ bDescriptorType: USB_DT_OTG,
++ bmAttributes: 0,
++};
++
++/*! Device Description
++ */
++struct usbd_device_description tty_device_description = {
++ device_descriptor: &tty_device_descriptor,
++#ifdef CONFIG_OTG_HIGH_SPEED
++ device_qualifier_descriptor: &tty_device_qualifier_descriptor,
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ otg_descriptor: &tty_otg_descriptor,
++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER,
++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME,
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR,
++#endif
++};
++
++/*! function_driver
++ */
++struct usbd_function_driver tty_function_driver = {
++ name: "ACM-TTY",
++ fops:&function_ops,
++ device_description:&tty_device_description,
++ bNumConfigurations:sizeof (tty_description) / sizeof (struct usbd_configuration_description),
++ configuration_description:tty_description,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE),
++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description),
++ interface_list:cdc_interfaces,
++ endpointsRequested: ENDPOINTS,
++ requestedEndpoints: tty_endpoint_requests,
++};
++
++
++
+diff -uNr linux/drivers/no-otg/functions/acm/tty-l24-os.c linux/drivers/otg/functions/acm/tty-l24-os.c
+--- linux/drivers/no-otg/functions/acm/tty-l24-os.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/tty-l24-os.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,1291 @@
++/*
++ * otg/functions/acm/tty-l24-os.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++
++/*!
++ * @file otg/functions/acm/tty-l24-os.c
++ * @brief ACM Function Driver private defines
++ *
++ * An ACM (Abstract Control Model) driver is composed of two pieces:
++ *
++ * 1) An OS specific piece that handles creating and operating
++ * a serial device for the given OS.
++ *
++ * 2) A USB specific piece that interfaces either with the host
++ * usbcore layer, or with the otgcore layer.
++ *
++ * If the USB piece interfaces with the host usbcore layer you get an ACM
++ * class driver. If the USB piece interfaces with the otgcore layer you get
++ * an ACM function driver.
++ *
++ * This file is the OS specific piece that interfaces with Linux (kernel
++ * versions 2.4.*). It talks to the bottom edge of the Linux tty layer.
++ *
++ * See the file acm/tty.c for USB descriptor definitions used for this
++ * function.
++ *
++ * @ingroup TTYFunction
++ */
++
++
++//#include <otg/osversion.h>
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com");
++
++MOD_DESCRIPTION ("Belcarra TTY Function");
++EMBED_LICENSE();
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++#include <linux/serial.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++
++#include <linux/capability.h>
++#include "tty.h"
++#include <otg/otg-trace.h>
++#include "acm.h"
++#include "acm-fd.h"
++#include "acm-os.h"
++
++static struct acm_usb_services *usb_ops = &acm_fd_usb_ops;
++
++#define TTY tty_fd_trace_tag
++otg_tag_t tty_fd_trace_tag;
++
++/* Module Parameters ************************************************************************* */
++
++static u32 vendor_id;
++static u32 product_id;
++static u32 max_queued_urbs = MAX_QUEUED_URBS;
++static u32 max_queued_bytes = MAX_QUEUED_BYTES;
++
++MOD_PARM (vendor_id, "i");
++MOD_PARM (product_id, "i");
++MOD_PARM (max_queued_urbs, "i");
++MOD_PARM (max_queued_bytes, "i");
++
++MOD_PARM_DESC (vendor_id, "Device Vendor ID");
++MOD_PARM_DESC (product_id, "Device Product ID");
++MOD_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs");
++MOD_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes");
++
++
++/* ACM ***************************************************************************************** */
++
++#define ACM_TTY_MAJOR 166
++#define ACM_TTY_MINOR 0
++#define ACM_TTY_MINORS 1
++
++extern struct acm_private acmfd_private;
++extern struct tty_private ttyfd_private;
++
++
++/* ******************************************************************************************* */
++
++#define TTY_OVERFLOW_SIZE 512
++
++u8 tty_overflow_buffer[TTY_OVERFLOW_SIZE];
++int tty_overflow_used;
++
++/* ******************************************************************************************* */
++
++/*! ttyfd_schedule
++ *
++ * Schedule a bottom half handler work item.
++ *
++ * @param queue
++ */
++STATIC void ttyfd_schedule(WORK_ITEM *queue)
++{
++ RETURN_IF(NO_WORK_DATA((*queue)) || PENDING_WORK_ITEM((*queue)));
++#if defined(LINUX24)
++ queue_task(queue, &tq_immediate);
++ mark_bh(IMMEDIATE_BH);
++ TRACE_MSG1(TTY,"task %p scheduled and marked",queue);
++#else
++ SCHEDULE_WORK((*queue));
++ TRACE_MSG1(TTY,"task %p scheduled",queue);
++#endif
++}
++
++
++/*! ttyfd_block_until_ready
++ *
++ * Called from tty_open to implement blocking open. Wait for DTR indication
++ * from acm-fd.
++ *
++ * Called by TTY layer to open device.
++ *
++ * @param tty
++ * @param filp
++ * @param acm
++ * @returns non-zero for error
++ */
++STATIC int ttyfd_block_until_ready(struct tty_struct *tty, struct file *filp, struct acm_private *acm)
++{
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ unsigned long flags;
++ int rc = 0;
++
++ local_irq_save(flags);
++ //TRACE_MSG1(TTY,"open_wait_count: %d --> at entry", tty_private->open_wait_count);
++ //tty_private->open_wait_count += 1;
++ for (;;) {
++ /* check if the file has been closed */
++ if (tty_hung_up_p(filp)) {
++ TRACE_MSG0(TTY,"tty_hung_up_p()");
++ rc = -ERESTARTSYS;
++ break;
++ }
++ /* check for pending signals */
++ if (signal_pending(current)) {
++ TRACE_MSG0(TTY,"signal_pending()");
++ rc = -ERESTARTSYS;
++ break;
++ }
++ /* check if the module is unloading */
++ if (tty_private->exiting) {
++ TRACE_MSG0(TTY,"module exiting()");
++ rc = -ENODEV;
++ break;
++ }
++ /* check for what we want, do we have DTR?
++ */
++ if (acm->bmLineState & CDC_LINESTATE_D0_DTR) {
++ // OK, there's somebody on the other end, let's go...
++ TRACE_MSG0(TTY,"found DTR");
++ rc = 0;
++ break;
++ }
++ //TRACE_MSG1(TTY,"open_wait_count: %d sleeping...", tty_private->open_wait_count);
++
++ /* restore irqs */
++ local_irq_restore(flags);
++
++ /* sleep */
++ interruptible_sleep_on(&tty_private->open_wait);
++
++ /* lock irqs again */
++ local_irq_save(flags);
++
++ //TRACE_MSG1(TTY,"open_wait_count: %d got WAKEUP", tty_private->open_wait_count);
++ }
++ //tty_private->open_wait_count -= 1;
++ //TRACE_MSG1(TTY,"open_wait_count: %d <-- at exit", tty_private->open_wait_count);
++ local_irq_restore(flags);
++ return(rc);
++}
++
++/*! ttyfd_call_hangup
++ *
++ * Bottom half handler to safely send hangup signal to process group.
++ *
++ * @param private - point to acm_private dat structure
++ */
++STATIC void ttyfd_call_hangup(void *private)
++{
++ struct acm_private *acm = (struct acm_private *) private;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++
++ struct tty_struct *tty = (NULL == acm) ? NULL : tty_private->tty;
++
++ TRACE_MSG5(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d c_cflag: %04x clocal: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED,
++ tty_private->c_cflag, tty_private->c_cflag & CLOCAL);
++
++
++ if (tty && !(tty_private->c_cflag & CLOCAL))
++ tty_hangup(tty);
++
++ wake_up_interruptible(&tty_private->open_wait);
++
++ TRACE_MSG0(TTY,"exited");
++}
++
++/*! ttyfd_schedule_hangup
++ *
++ * Schedule hangup bottom half handler.
++ *
++ * @param acm - point to acm_private dat structure
++ */
++void ttyfd_schedule_hangup(struct acm_private *acm)
++{
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++
++ TRACE_MSG0(TTY, "entered");
++ ttyfd_schedule(&tty_private->hqueue);
++}
++
++
++/*! tty_open
++ *
++ * Called by TTY layer to open device.
++ *
++ * @param tty
++ * @param filp
++ * @returns non-zero for error
++ */
++STATIC int tty_open(struct tty_struct *tty, struct file *filp)
++{
++ struct acm_private *acm = &acmfd_private;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ int used;
++ int nonblocking;
++ int rc = 0;
++ unsigned long flags;
++
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
++
++ TRACE_MSG2(TTY,"f_flags: %08x NONBLOCK: %x",
++ filp->f_flags, filp->f_flags & O_NONBLOCK);
++
++ nonblocking = filp->f_flags & O_NONBLOCK;
++
++ /* Lock and increment used counter, save current value.
++ * Check for exclusive open at the same time.
++ */
++ local_irq_save(flags);
++
++ /* The value of acm->used controls MOD_{INC/DEC}_USE_COUNT, so
++ * it has to be incremented unconditionally, and no early return
++ * made until after USE_COUNT has been adjusted to match.
++ */
++ used = atomic_post_inc(&acm->used);
++#ifdef MCEL
++ filp->f_flags |= O_EXCL; // QQQ Can we persuade MCEL to add this to their app?
++ if (nonblocking && !(acm->connected)) {
++ // QQQ Is MCEL actually using this "feature"? (See below for printk)
++ rc = -EINVAL;
++ }
++ else
++#endif
++#if 0
++ if (filp->f_flags & O_EXCL) {
++ /* This is intended to be an exclusive open, so
++ * make sure no one has the device open already, and
++ * set the exclusive flag so no one can open it later.
++ */
++ if (used > 0) {
++ // Someone already has it.
++ rc = -EBUSY;
++ }
++ else {
++ tty_private->flags |= TTYFD_EXCLUSIVE;
++ set_bit(TTY_EXCLUSIVE, &tty->flags);
++ }
++ }
++#if defined(LINUX24)
++ else if ((tty_private->flags & TTYFD_EXCLUSIVE) && !suser())
++#else
++ if ((tty_private->flags & TTYFD_EXCLUSIVE) && !capable(CAP_SYS_TTY_CONFIG))
++#endif
++ {
++ // Only the superuser can do a normal open of an O_EXCL tty
++ rc = -EBUSY;
++ }
++#endif
++ local_irq_restore(flags);
++
++ // OK, now it's safe to make an early return if we are failing.
++ if (rc) {
++#ifdef MCEL
++ // This can dissappear when the "feature" above does.
++ if (-EINVAL == rc)
++ //printk(KERN_INFO "\nusb cable not connected!\n");
++#endif
++ return(rc);
++ }
++ local_irq_save(flags);
++
++
++ /* To truly emulate the old dual-device approach of having a non-blocking
++ * device (e.g cu0) and a blocking device (e.g. tty0) we would need to
++ * track blocking and non-blocking opens separately. We don't. This
++ * may lead to funny behavior in the multiple open case.
++ */
++ UNLESS (used) {
++ MOD_INC_USE_COUNT;
++ TRACE_MSG1(TTY,"INC: %d", MOD_IN_USE);
++ // First open.
++ TRACE_MSG2(TTY, "FIRST OPEN nonblocking: %x exclusive: %x", nonblocking, tty_private->flags & TTYFD_EXCLUSIVE);
++ tty->driver_data = acm;
++ tty_private->tty = tty;
++ tty->low_latency = 1;
++ usb_ops->open(acm, DATA_INTF);
++ usb_ops->set_local(acm, 1);
++ }
++ local_irq_restore(flags);
++
++
++ /* All done if configured
++ */
++ RETURN_ZERO_UNLESS(usb_ops->ready(acm));
++
++ /* All done if non blocking open
++ */
++ RETURN_ZERO_IF(nonblocking);
++
++ /* Block open - wait until ready
++ */
++ TRACE_MSG0(TTY, "BLOCKING - wait until ready");
++ rc = ttyfd_block_until_ready(tty,filp,acm);
++
++ /* The tty layer calls tty_close() even if this open fails,
++ * so any cleanup (rc != 0) will be done there.
++ */
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
++ return(rc);
++}
++
++/*! ttyfd_wakeup_opens
++ *
++ * Called by acm_fd to wakeup processes blocked in open waiting for DTR.
++ *
++ * @param acm - pointer to acm private data structure
++ */
++void ttyfd_wakeup_opens(struct acm_private *acm)
++{
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ TRACE_MSG0(TTY, "entered");
++ //if (tty_private->open_wait_count > 0)
++ wake_up_interruptible(&tty_private->open_wait);
++}
++
++/*! tty_close
++ *
++ * Called by TTY layer to close device.
++ *
++ * @param tty
++ * @param filp
++ * @returns non-zero for error
++ */
++STATIC void tty_close(struct tty_struct *tty, struct file *filp)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ struct usbd_function_instance *function;
++ int used;
++
++ TRACE_MSG0(TTY, "entered");
++
++ //UNLESS (acm) {
++ // printk(KERN_INFO"%s: ACM null\n", __FUNCTION__);
++ // return;
++ //}
++
++ usb_ops->close(acm, DATA_INTF);
++ tty_private = (struct tty_private *) acm->privdata;
++ function = acm->function;
++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
++
++ // lock and decrement used counter, save result
++ used = atomic_pre_dec(&acm->used);
++
++ // finished unless this is the last close
++
++ RETURN_IF (used > 0);
++
++ TRACE_MSG0(TTY,"FINAL CLOSE");
++
++ tty_private->tty = NULL;
++
++ /* This should never happen if this is the last close,
++ * but it can't hurt to check.
++ */
++ //if (tty_private->open_wait_count)
++ wake_up_interruptible(&tty_private->open_wait);
++
++ if (tty_private->flags & TTYFD_EXCLUSIVE) {
++ tty_private->flags &= ~TTYFD_EXCLUSIVE;
++ if (tty)
++ clear_bit(TTYFD_EXCLUSIVE, &tty->flags);
++ }
++
++ _MOD_DEC_USE_COUNT;
++ TRACE_MSG1(TTY,"DEC: %d", MOD_IN_USE);
++ TRACE_MSG1(TTY,"LAST CLOSE r-f=%d",(acm->bytes_received-acm->bytes_forwarded));
++
++ MOD_DEC_USE_COUNT;
++
++ TRACE_MSG0(TTY,"FINISHED");
++}
++
++/* Transmit Function - called by tty layer ****************************************************** */
++
++int ttyfd_recv_space_available(struct acm_private *acm, int interface);
++int ttyfd_recv_chars(struct acm_private *acm, int interface, u8 *cp, int n);
++
++#define LOOP_BUF 16
++/*! ttyfd_loop_xmit_chars
++ *
++ * Implement data loop back, send xmit data back as received data.
++ *
++ * @param acm - pointer to acm private data structure
++ * @param interface - data interface to send on
++ * @param count - number of bytes to send
++ * @param from_user - true if from user memory space
++ * @param buf - pointer to data to send
++ * @return count - number of bytes actually sent.
++ */
++STATIC int ttyfd_loop_xmit_chars(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf)
++{
++ int received = 0;
++ while (count > 0) {
++ u8 copybuf[LOOP_BUF];
++ int i;
++ int space = ttyfd_recv_space_available(acm, DATA_INTF);
++ int recv = MIN (space, LOOP_BUF);
++ recv = MIN(recv, count);
++
++ //TRACE_MSG7(TTY, "buf: %8x buf+received: %8x received: %d from_user: %d count: %d space: %d recv: %d\n",
++ // buf, buf + received, received, from_user, count, space, recv);
++
++ BREAK_UNLESS(recv);
++
++ if (from_user)
++ copy_from_user ((void *)copybuf, (void*)(buf + received), recv);
++ else
++ memcpy ((void *)copybuf, (void*)(buf + received), recv);
++
++ count -= recv;
++ received += recv;
++ TRACE_MSG3(TTY, "count: %d recv: %d received: %d", count, recv, received);
++ ttyfd_recv_chars(acm, DATA_INTF, copybuf, recv);
++ }
++ return received;
++}
++
++/*! ttyfd_overflow_send
++ *
++ * Check if there is data in overflow buffer and send if neccessary.
++ *
++ * @param acm - pointer to acm private data structure
++ * @param interface - data interface to send on
++ * @param force - force the send
++ */
++void ttyfd_overflow_send(struct acm_private *acm, int interface, int force)
++{
++ int chars_in_buffer;
++ int count;
++
++ unsigned long flags;
++ local_irq_save(flags);
++
++ chars_in_buffer = usb_ops->chars_in_buffer(acm, DATA_INTF);
++
++ TRACE_MSG3(TTY, "overflow_used: %d chars_in_buffer: %d force: %d", tty_overflow_used, chars_in_buffer, force);
++
++ if (force || !chars_in_buffer || tty_overflow_used > 200) {
++
++ count = usb_ops->xmit_chars(acm, DATA_INTF, tty_overflow_used, 0, tty_overflow_buffer);
++
++ TRACE_MSG2(TTY, "overflow_used: %d count: %d", tty_overflow_used, count);
++
++ tty_overflow_used = 0;
++
++ }
++ local_irq_restore(flags);
++}
++
++
++/*! ttyfd_overflow_xmit_chars
++ *
++ * Implement overflow buffer. The TTY layer implements echo with single
++ * character writes which can quickly exhaust the urbs allowed for sending.
++ * This results in data being lost.
++ *
++ * This function is called to accumulate small amounts of data when there is
++ * already an bulk in urb in the queue.
++ *
++ * The urb sent function calls wakeup writers which will call
++ * ttyfd_overflow_send which will send the data.
++ *
++ * @param acm - pointer to acm private data structure
++ * @param interface - data interface to send on
++ * @param count - number of bytes to send
++ * @param from_user - true if from user memory space
++ * @param buf - pointer to data to send
++ * @return count - number of bytes actually sent.
++ */
++STATIC int ttyfd_overflow_xmit_chars(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf)
++{
++ unsigned long flags;
++ int overflow_room;
++ int write_room = usb_ops->write_room(acm, DATA_INTF);
++
++ TRACE_MSG2(TTY,"-> count: %d from_usr: %d", count, from_user);
++ local_irq_save(flags);
++
++ if ((overflow_room = TTY_OVERFLOW_SIZE - tty_overflow_used) > 0) {
++
++ count = MIN(overflow_room, count);
++ if (from_user)
++ copy_from_user ((void *)tty_overflow_buffer + tty_overflow_used, (void*)buf, count);
++ else
++ memcpy ((void *)tty_overflow_buffer + tty_overflow_used, (void*)buf, count);
++
++ tty_overflow_used += count;
++ }
++ else
++ count = 0;
++
++ local_irq_restore(flags);
++
++ /* start sending from overflow if neccessary
++ */
++ ttyfd_overflow_send(acm, interface, 0);
++
++ return count;
++}
++
++/*! tty_chars_in_buffer
++ *
++ * Called by TTY layer to determine amount of pending write data.
++ *
++ * @param tty - pointer to tty data structure
++ * @return amount of pending write data
++ */
++STATIC int tty_chars_in_buffer(struct tty_struct *tty)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ int rc;
++
++ //TRACE_MSG0(TTY, "entered");
++
++ rc = usb_ops->chars_in_buffer(acm, DATA_INTF);
++ TRACE_MSG1(TTY, "chars in buffer: %d", rc);
++ return rc;
++ //return(usb_ops->chars_in_buffer(acm, DATA_INTF));
++}
++
++/*! tty_write
++ *
++ * Called by TTY layer to write data.
++ * @param tty - pointer to tty data structure
++ * @param from_user - true if from user memory space
++ * @param buf - pointer to data to send
++ * @param count - number of bytes want to send.
++ * @return count - number of bytes actually sent.
++ */
++STATIC int tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++
++ TRACE_MSG3(TTY,"-> count: %d loopback: %d from_usr: %d", count, tty_private->tiocm & TIOCM_LOOP, from_user);
++
++ /* loopback mode
++ */
++ if (tty_private->tiocm & TIOCM_LOOP)
++ return ttyfd_loop_xmit_chars(acm, DATA_INTF, count, from_user, buf);
++
++ /* overflow mode
++ */
++ if (tty_overflow_used || (tty_chars_in_buffer(tty_private->tty) && (count < 64)))
++ return ttyfd_overflow_xmit_chars(acm, DATA_INTF, count, from_user, buf);
++
++ /* straight through mode
++ */
++ return usb_ops->xmit_chars(acm, DATA_INTF, count, from_user, buf);
++
++}
++
++/*! tty_write_room
++ *
++ * Called by TTY layer get amount of write room available.
++ *
++ * @param tty - pointer to tty data structure
++ * @return amount of write room available
++ */
++STATIC int tty_write_room(struct tty_struct *tty)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ int rc;
++
++ TRACE_MSG0(TTY, "entered");
++ /* loopback mode
++ */
++ if (tty_private->tiocm & TIOCM_LOOP)
++ return ttyfd_recv_space_available(acm, DATA_INTF);
++
++ rc = usb_ops->write_room(acm, DATA_INTF) + (TTY_OVERFLOW_SIZE - tty_overflow_used);
++ TRACE_MSG1(TTY, "write room: %d", rc);
++
++ return rc;
++
++
++ //return(usb_ops->write_room(acm, DATA_INTF));
++}
++
++static int throttle_count = 0;
++static int unthrottle_count = 0;
++
++/*! tty_throttle
++ *
++ * Called by TTY layer to throttle (do not allow received data.)
++ *
++ * @return amount of write room available
++ */
++STATIC void tty_throttle(struct tty_struct *tty)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++
++ TRACE_MSG0(TTY, "entered");
++ //tty_private->flags |= TTYFD_THROTTLED;
++ usb_ops->throttle(acm, DATA_INTF);
++}
++
++/*! tty_unthrottle
++ *
++ * Called by TTY layer to unthrottle (allow received data.)
++ *
++ * @return amount of write room available
++ */
++STATIC void tty_unthrottle(struct tty_struct *tty)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++
++ TRACE_MSG0(TTY, "entered");
++ //tty_private->flags &= ~TTYFD_THROTTLED;
++ usb_ops->unthrottle(acm, DATA_INTF);
++
++ /* This function is called while the TTY_DONT_FLIP flag is still
++ * set, so there is no point trying to push the flip buffer. Just
++ * try to queue some recv urbs, and keep trying until we do manage
++ * to get some queued.
++ */
++ tty_schedule_flip(tty);
++}
++
++/* ********************************************************************************************** */
++
++/*! ttyfd_tiocm
++ * @param acm
++ * @param tiocm
++ * @return - computed tiocm value
++ */
++unsigned int ttyfd_tiocm(struct acm_private *acm, unsigned int tiocm)
++{
++ tiocm &= ~ ( TIOCM_DTR | TIOCM_DSR | TIOCM_CAR);
++
++ return tiocm |
++ usb_ops->get_dtr(acm) ? TIOCM_DTR : 0 |
++ usb_ops->get_dsr(acm) ? TIOCM_DSR : 0 |
++ usb_ops->get_dcd(acm) ? TIOCM_CAR : 0 ;
++}
++
++/*! tty_ioctl
++ *
++ * Used by TTY layer to execute IOCTL command.
++ *
++ * Called by TTY layer to open device.
++ *
++ * Unhandled commands must return -ENOIOCTLCMD for correct operation.
++ *
++ * @param tty
++ * @param file
++ * @param cmd
++ * @param arg
++ * @returns non-zero for error
++ */
++STATIC int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ unsigned int mask;
++ unsigned int newctrl;
++ unsigned int tiocm;
++ unsigned int saved_tiocm;
++ unsigned int changed_tiocm;
++ int rc;
++
++ //TRACE_MSG3(TTY,"entered: %8x dir: %x size: %x", cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd));
++
++ switch(cmd) {
++
++ case TIOCMGET:
++ TRACE_MSG1(TTY, "TIOCMGET: tiocm: %08x", tiocm);
++ tty_private->tiocm = ttyfd_tiocm(acm, tiocm);
++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &tty_private->tiocm, sizeof(int)));
++ return 0;
++
++ case TIOCMBIS:
++ case TIOCMBIC:
++ case TIOCMSET:
++ TRACE_MSG0(TTY, "TIOCMXXX: copying tiocm arguement");
++
++ TRACE_MSG1(TTY, "access: %d", access_ok(VERIFY_READ, (void *)arg, sizeof(int)));
++ RETURN_EINVAL_UNLESS(access_ok(VERIFY_READ, (void *)arg, sizeof(int)));
++
++
++ //RETURN_EINVAL_IF (copy_from_user(&tiocm, (void *)arg, sizeof(int)));
++ rc = copy_from_user((void *)&tiocm, (void *)arg, sizeof(int));
++ TRACE_MSG2(TTY, "copy_from_user: %d tiocm: %08x", rc, tiocm);
++
++ TRACE_MSG2(TTY, "tiocm: %08x mask: %08x", tiocm,
++ TIOCM_RTS | TIOCM_DTR | TIOCM_OUT1 | TIOCM_OUT2 | TIOCM_LOOP);
++
++ tiocm &= TIOCM_RTS | TIOCM_DTR | TIOCM_OUT1 | TIOCM_OUT2 | TIOCM_LOOP;
++ saved_tiocm = tty_private->tiocm;
++
++ switch (cmd) {
++ case TIOCMBIS:
++ TRACE_MSG1(TTY, "TIOCMBIS: tiocm: %08x", tiocm);
++ tty_private->tiocm |= tiocm; /* turn on flags set in tiocm */
++ break;
++ case TIOCMBIC:
++ TRACE_MSG1(TTY, "TIOCMBIC: tiocm: %08x", tiocm);
++ tty_private->tiocm &= ~tiocm; /* turn off flags set in tiocm */
++ break;
++ case TIOCMSET:
++ TRACE_MSG1(TTY, "TIOCMSET: tiocm: %08x", tiocm);
++ tty_private->tiocm = tiocm; /* set all flags as in tiocm */
++ break;
++ }
++ /* make changes
++ */
++ changed_tiocm = saved_tiocm ^ tty_private->tiocm;
++ TRACE_MSG4(TTY, "TIOCMSET: tiocm: %08x saved: %08x set: %08x changed: %d",
++ tiocm, saved_tiocm, tty_private->tiocm, changed_tiocm);
++
++ if (changed_tiocm) {
++ /* DTR -> (DSR/DCD) */
++ tiocm = tty_private->tiocm;
++ if (changed_tiocm & TIOCM_DTR) {
++ usb_ops->set_dsr(acm, tiocm & TIOCM_DTR);
++ usb_ops->set_dcd(acm, tiocm & TIOCM_DTR);
++ }
++ /* OUT1 -> Ring */
++ if (changed_tiocm & TIOCM_OUT1)
++ usb_ops->ring(acm);
++ /* OUT2 -> Overrun */
++ if (changed_tiocm & TIOCM_OUT2)
++ usb_ops->overrun(acm);
++ /* LOOPBACK */
++ if (changed_tiocm & TIOCM_OUT2)
++ usb_ops->set_loopback(acm, tiocm & TIOCM_LOOP);
++ }
++
++ return 0;
++
++ case TIOCGSERIAL:
++ TRACE_MSG0(TTY, "TIOCGSERIAL");
++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &tty_private->serial_struct, sizeof(struct serial_struct)));
++ return 0;
++
++ case TIOCSSERIAL:
++ TRACE_MSG0(TTY, "TIOCSSERIAL");
++ RETURN_EFAULT_IF (copy_from_user(&tty_private->serial_struct, (void *)arg, sizeof(struct serial_struct)));
++ return 0;
++
++ case TIOCSERCONFIG:
++ case TIOCSERGETLSR: /* Get line status register */
++ case TIOCSERGSTRUCT:
++ TRACE_MSG0(TTY, "TIOCSER*");
++ return -EINVAL;
++
++ /*
++ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
++ * - mask passed in arg for lines of interest
++ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
++ * Caller should use TIOCGICOUNT to see which one it was
++ */
++ case TIOCMIWAIT:
++ TRACE_MSG0(TTY, "TIOCGMIWAIT");
++ while (1) {
++
++ saved_tiocm = tty_private->tiocm;
++
++ interruptible_sleep_on(&tty_private->tiocm_wait);
++
++ /* see if a signal did it */
++ if (signal_pending(current))
++ return -ERESTARTSYS;
++
++ tty_private->tiocm = ttyfd_tiocm(acm, tiocm);
++ changed_tiocm = saved_tiocm ^ tty_private->tiocm;
++ RETURN_ZERO_IF ( (changed_tiocm | TIOCM_CAR) | (changed_tiocm | TIOCM_DSR) );
++ /* loop */
++ }
++ /* NOTREACHED */
++
++ /*
++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
++ * Return: write counters to the user passed counter struct
++ * NB: both 1->0 and 0->1 transitions are counted except for
++ * RI where only 0->1 is counted.
++ */
++ case TIOCGICOUNT:
++ TRACE_MSG0(TTY, "TIOCGICOUNT");
++ if (copy_to_user((void *)arg, &tty_private->tiocgicount, sizeof(int)))
++ return -EFAULT;
++ return 0;
++
++ case TCSETS:
++ TRACE_MSG1(TTY, "TCSETS: tiocm: %08x", tiocm);
++ return -ENOIOCTLCMD;
++ case TCFLSH:
++ TRACE_MSG1(TTY, "TCFLSH: tiocm: %08x", tiocm);
++ return -ENOIOCTLCMD;
++
++ case TCGETS:
++ TRACE_MSG1(TTY, "TCGETS: tiocm: %08x", tiocm);
++ return -ENOIOCTLCMD;
++
++ default:
++ TRACE_MSG1(TTY, "unknown cmd: %08x", cmd);
++ return -ENOIOCTLCMD;
++ }
++ return -ENOIOCTLCMD;
++}
++
++/*! ttyfd_wakeup_state
++ *
++ * Called by acm_fd to wakeup processes blocked waiting for state change
++ *
++ * @param acm - pointer to acm private data structure
++ */
++void ttyfd_wakeup_state(struct acm_private *acm)
++{
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ TRACE_MSG0(TTY, "entered");
++ wake_up_interruptible(&tty_private->tiocm_wait);
++}
++
++
++/*! tty_set_termios
++ *
++ * Used by TTY layer to set termios structure according to current status.
++ *
++ * @param tty - pointer to acm private data structure
++ * @param termios_old - termios structure
++ */
++STATIC void tty_set_termios(struct tty_struct *tty, struct termios *termios_old)
++{
++ struct acm_private *acm = tty->driver_data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++
++ unsigned int c_cflag = tty->termios->c_cflag;
++
++ /* see if CLOCAL has changed */
++ if ((termios_old->c_cflag ^ tty->termios->c_cflag ) & CLOCAL)
++ usb_ops->set_local(acm, tty->termios->c_cflag & CLOCAL);
++
++ /* save cflags
++ */
++ tty_private->c_cflag = c_cflag;
++
++ /* send break?
++ */
++ if ((termios_old->c_cflag & CBAUD) && !(c_cflag & CBAUD))
++ usb_ops->send_break(acm);
++}
++
++/* ********************************************************************************************* */
++/*! ttyfd_wakeup_writers
++ *
++ * Bottom half handler to wakeup pending writers.
++ *
++ * @param data - pointer to acm private data structure
++ */
++STATIC void ttyfd_wakeup_writers(void *data)
++{
++ struct acm_private *acm = (struct acm_private *) data;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ struct tty_struct *tty = tty_private->tty;
++ unsigned long flags;
++
++ TRACE_MSG2(TTY,"used: %d MOD_IN_USE: %d", atomic_read(&acm->used), MOD_IN_USE);
++
++ RETURN_UNLESS(usb_ops->ready(acm));
++ RETURN_UNLESS(atomic_read(&acm->used));
++ RETURN_UNLESS(tty);
++
++ /* start sending from overflow buffer if necessary
++ */
++ ttyfd_overflow_send(acm, DATA_INTF, 0);
++
++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
++ (tty->ldisc.write_wakeup)(tty);
++
++ wake_up_interruptible(&tty->write_wait);
++}
++
++/*! ttyfd_schedule_wakeup_writers
++ *
++ * Called by acm-fd to schedule a wakeup writes bottom half handler.
++ *
++ * @param acm - pointer to acm private data structure
++ */
++void ttyfd_schedule_wakeup_writers(struct acm_private *acm)
++{
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++
++ TRACE_MSG2(TTY,"used: %d MOD_IN_USE: %d", atomic_read(&acm->used), MOD_IN_USE);
++ ttyfd_schedule(&tty_private->wqueue);
++}
++
++/* ********************************************************************************************* */
++
++/*! ttyfd_recv_space_available
++ *
++ * Used by acm-fd to determine receive space available. This will determine
++ * how many receive urbs can be queued as there are never more receive urbs
++ * pending than there is currently room for data to be received.
++ *
++ * @param acm - pointer to acm private data structure
++ * @param interface - data interface to send on
++ * @return count - number of bytes available
++ */
++int ttyfd_recv_space_available(struct acm_private *acm, int interface)
++{
++ struct tty_private *tty_private;
++ struct tty_struct *tty;
++ int rc;
++
++ TRACE_MSG0(TTY, "entered");
++ RETURN_ZERO_UNLESS(acm);
++
++ tty_private = (struct tty_private *) acm->privdata;
++
++ RETURN_ZERO_UNLESS(tty_private);
++
++ RETURN_ZERO_UNLESS(tty_private->tty);
++
++ switch (interface) {
++ case COMM_INTF:
++ return 0;
++ case DATA_INTF:
++ tty = tty_private->tty;
++ rc = TTY_FLIPBUF_SIZE - tty->flip.count;
++ TRACE_MSG1(TTY, "recv space available: %d", rc);
++ return rc;
++ return(TTY_FLIPBUF_SIZE - tty->flip.count);
++ }
++ return 0;
++}
++
++/*! ttyfd_recv_chars
++ *
++ * Called by acm-fd when data has been received. This will
++ * receive n bytes starting at cp. This will never be
++ * more than the last call to ttyfd_recv_space_available().
++ * This will be called from interrupt context.
++ *
++ * @param acm - pointer to acm private data structure
++ * @param interface - data interface to send on
++ * @param n - number of bytes to send
++ * @param cp - pointer to data to send
++ * @return non-zero if error
++ */
++int ttyfd_recv_chars(struct acm_private *acm, int interface, u8 *cp, int n)
++{
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ struct tty_struct *tty = tty_private->tty;
++ unsigned long flags;
++
++ /* acm_start_recv_urbs() will never queue more urbs than there is currently
++ * room in the upper layer buffer for. So we are guaranteed that any data actually
++ * received can be given to the upper layers without worrying if we will
++ * actually have room.
++ *
++ * XXX I think that if the tty layer does get behind and we reach a point where
++ * there are no outstanding receive urbs, then we will also have been throttled
++ * so we will have an oppourtunity to queue more receive urbs when we get unthrottled.
++ *
++ * XXX If the above assumption is not true that a timer will be needed to periodically
++ * check if we can restart.
++ *
++ * YYY What can happen is that the tty_flip_buffer_push() call can fail for
++ * a variety of reasons (locked out while a read call is in progress, ldisc
++ * buffer is full, and probably others). We need to ensure that we keep
++ * trying to push the flip buffer until it does go, and there is room for
++ * more receive urbs. acm_start_recv_urbs() will queue a task to push and
++ * try again if it can't queue any now.
++ *
++ * ZZZ Yet another failure mode is to call tty_flip_buffer_push() too many
++ * times while throttled. This will result in the ldisc layer silently
++ * dropping data inside the n_tty_receive_buf() routine. (Acutal numbers
++ * for the 2.4.20 kernel this was discovered on are 128 bytes left in the
++ * ldisc buffer when throttle is called, and upto 7 64 byte urbs outstanding.
++ * The urbs will fit into the flip buffer, but NOT the ldisc buffer.) This
++ * means we must not call tty_flip_buffer_push() when throttled. We will
++ * count on the tty_unthrottle() call to kick off the final push.
++ */
++
++ {
++ int i;
++ local_irq_save(flags);
++ TRACE_MSG2(TTY, "recv: %d buffer: %x", n, cp);
++
++ for (i = 0; i < n; i += 8) {
++ TRACE_MSG8(TTY, "%02x %02x %02x %02x %02x %02x %02x %02x",
++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
++ );
++ }
++
++ local_irq_restore(flags);
++ }
++ switch (interface) {
++ case COMM_INTF:
++ return 0;
++ case DATA_INTF:
++
++ RETURN_EINVAL_UNLESS(tty);
++
++#define WORD_COPY 1
++#if defined(WORD_COPY)
++ local_irq_save(flags);
++ memcpy(tty->flip.char_buf_ptr,cp,n);
++ memset(tty->flip.flag_buf_ptr,TTY_NORMAL,n);
++ tty->flip.count += n;
++ tty->flip.char_buf_ptr += n;
++ tty->flip.flag_buf_ptr += n;
++ acm->bytes_forwarded += n;
++ local_irq_restore(flags);
++#else
++ int i;
++ for (i = 0; i < n; i++) {
++ /* tty_flip_char() has no lock out from any calls
++ * to tty_flip_buffer_push() that may have been queued
++ * by acm_unthrottle() or acm_start_recv_urbs(), so...
++ */
++ local_irq_save(flags);
++ tty_insert_flip_char(tty, *cp++, TTY_NORMAL);
++ local_irq_restore(flags);
++ }
++ acm->bytes_forwarded += n;
++#endif
++
++ UNLESS (test_bit(TTY_THROTTLED, &tty->flags))
++ tty_flip_buffer_push(tty);
++
++ //UNLESS (tty_private->flags & TTYFD_THROTTLED)
++ // tty_flip_buffer_push(tty);
++
++ return 0;
++ }
++ return 0;
++}
++
++/* ********************************************************************************************* */
++
++/*! ttyfd_enable
++ *
++ * Called by acm-fd when the function driver is enabled.
++ */
++void ttyfd_enable(struct usbd_function_instance *function)
++{
++ //function->privdata = &acm_private;
++ //acm_private.function = function;
++}
++
++/*! ttyfd_enable
++ *
++ * Called by acm-fd when the function driver is disabled.
++ */
++void ttyfd_disable(struct usbd_function_instance *function)
++{
++ //function->privdata = NULL;
++ //acm_private.function = NULL;
++}
++
++/* ************************************************************************** */
++
++#if defined(LINUX24)
++static int tty_refcount;
++#else
++//Maintained inside tty_driver in LINUX 2.6
++#endif
++static struct tty_struct *tty_table[ACM_TTY_MINORS];
++static struct termios *tty_termios[ACM_TTY_MINORS];
++static struct termios *tty_termios_locked[ACM_TTY_MINORS];
++
++/*! tty_driver
++ */
++static struct tty_driver tty_driver = {
++ .magic = TTY_DRIVER_MAGIC,
++ .type = TTY_DRIVER_TYPE_SERIAL,
++ .subtype = SERIAL_TYPE_NORMAL,
++ .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
++ .driver_name = "acm-CDC",
++ .name = "usb/acm/%d",
++ .major = ACM_TTY_MAJOR,
++ .num = ACM_TTY_MINORS,
++ .minor_start = 0,
++
++ .open = tty_open,
++ .close = tty_close,
++ .write = tty_write,
++ .write_room = tty_write_room,
++ .ioctl = acm_tty_ioctl,
++ .throttle = tty_throttle,
++ .unthrottle = tty_unthrottle,
++ .chars_in_buffer = tty_chars_in_buffer,
++ .set_termios = tty_set_termios,
++
++#if defined(LINUX24)
++ .refcount = &tty_refcount,
++#else
++ .refcount = 0,
++#endif
++ .table = tty_table,
++ .termios = tty_termios,
++ .termios_locked = tty_termios_locked,
++};
++
++/* USB Module init/exit ************************************************************************ */
++
++struct tty_private ttyfd_private = {
++ .tty_driver = &tty_driver,
++ //.wqueue.routine = ttyfd_wakeup_writers,
++ //.wqueue.data = &acmfd_private,
++ .wqueue = {
++ .routine = ttyfd_wakeup_writers,
++ .data = &acmfd_private,
++ },
++ //.hqueue.routine = ttyfd_call_hangup,
++ //.hqueue.data = &acmfd_private,
++ .hqueue = {
++ .routine = ttyfd_call_hangup,
++ .data = &acmfd_private,
++ },
++};
++
++
++/*! tty_function_services
++ *
++ * This structure contains the list of services
++ */
++struct acm_function_services tty_function_services = {
++ .schedule_wakeup_writers = ttyfd_schedule_wakeup_writers,
++ .recv_space_available = ttyfd_recv_space_available,
++ .recv_chars = ttyfd_recv_chars,
++ .enable = ttyfd_enable,
++ .disable = ttyfd_disable,
++ .schedule_hangup = ttyfd_schedule_hangup,
++ .wakeup_opens = ttyfd_wakeup_opens,
++ .wakeup_state = ttyfd_wakeup_state,
++};
++
++struct acm_private acmfd_private = {
++ .function_driver = &tty_function_driver,
++ .function_services = &tty_function_services,
++ .privdata = &ttyfd_private,
++ // TBR: 20040705 use ...le32() not le16, spotted by Zhao Liang
++ //.line_coding.dwDTERate = __constant_cpu_to_le32(0x1c200), // 115200
++ //.line_coding.bDataBits = 0x08,
++ .line_coding = {
++ .dwDTERate = __constant_cpu_to_le32(0x1c200), // 115200
++ .bDataBits = 0x08,
++ },
++};
++
++
++/*! ttyfd_modinit - module init
++ *
++ * This is called immediately after the module is loaded or during
++ * the kernel driver initialization if linked into the kernel.
++ *
++ */
++STATIC int ttyfd_modinit (void)
++{
++ int i;
++
++ /* initialize private structures */
++ acmfd_private.trace_tag = TTY = otg_trace_obtain_tag();
++ init_waitqueue_head(&ttyfd_private.open_wait);
++ init_waitqueue_head(&ttyfd_private.tiocm_wait);
++
++ /* update init_termios and register as tty driver */
++ tty_driver.init_termios = tty_std_termios;
++ tty_driver.init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
++ tty_driver.init_termios.c_lflag &= ~(ECHO | ICANON);
++ THROW_IF(tty_register_driver(&tty_driver), error);
++ tty_register_devfs(&tty_driver, 0, ACM_TTY_MINOR);
++ ttyfd_private.tty_driver_registered++;
++
++ /* register as usb function driver via acm-fd */
++ THROW_IF (usb_ops->fd_init(&acmfd_private, "acm_fd", vendor_id, product_id, max_queued_urbs,max_queued_bytes ), error);
++
++ CATCH(error) {
++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__);
++ if (ttyfd_private.tty_driver_registered) {
++ tty_unregister_driver(&tty_driver);
++ ttyfd_private.tty_driver_registered = 0;
++ }
++ return -EINVAL;
++ }
++ return 0;
++}
++
++
++/*! ttyfd_modexit - module cleanup
++ *
++ * This is called prior to the module being unloaded.
++ */
++STATIC void ttyfd_modexit (void)
++{
++ struct acm_private *acm = &acmfd_private;
++ struct tty_private *tty_private = (struct tty_private *) acm->privdata;
++ unsigned long flags;
++ struct usbd_urb *urb;
++
++ /* Wake up any pending opens after setting the exiting flag. */
++ local_irq_save(flags);
++ ttyfd_private.exiting = 1;
++ //if (ttyfd_private.open_wait_count > 0)
++ wake_up_interruptible(&ttyfd_private.open_wait);
++ local_irq_restore(flags);
++
++ /* verify no tasks are running */
++ usb_ops->wait_task(acm, &acm->recv_tqueue);
++ usb_ops->wait_task(acm, &ttyfd_private.wqueue);
++ usb_ops->wait_task(acm, &ttyfd_private.hqueue);
++
++#if defined(LINUX24)
++ run_task_queue(&tq_timer);
++#else
++ blk_run_queues();
++#endif
++ /* de-register as tty and usb drivers */
++ if (ttyfd_private.tty_driver_registered)
++ tty_unregister_driver(&tty_driver);
++
++ /* de-register as function driver via acm-fd */
++ usb_ops->fd_exit(&acmfd_private);
++ otg_trace_invalidate_tag(TTY);
++}
++
++module_init (ttyfd_modinit);
++module_exit (ttyfd_modexit);
++
+diff -uNr linux/drivers/no-otg/functions/acm/tty-os.h linux/drivers/otg/functions/acm/tty-os.h
+--- linux/drivers/no-otg/functions/acm/tty-os.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/tty-os.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * otg/functions/acm/tty-os.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ *
++ */
++/*!
++ * @file otg/functions/acm/tty-os.h
++ * @brief ACM Function Driver private defines
++ *
++ * An ACM (Abstract Control Model) driver is composed of two pieces:
++ * 1) An OS specific piece that handles creating and operating
++ * a serial device for the given OS.
++ * 2) A USB specific piece that interfaces either with the host
++ * usbcore layer, or with the otgcore layer.
++ *
++ * If the USB piece interfaces with the host usbcore layer you get
++ * an ACM class driver. If the USB piece interfaces with the otgcore
++ * layer you get an ACM function driver.
++ *
++ * This file describes the functions exported by the various acm-*-os.c
++ * files (implementing (1)) for use in acm-fd.c (2).
++ *
++ * @ingroup TTYFunction
++ */
++
++#ifndef ACM_OS_H
++#define ACM_OS_H 1
++
++/*
++ * acm_os_recv_space_available - return the number of bytes of data
++ * the OS specific piece can accept without
++ * dropping some.
++ */
++extern int acm_os_recv_space_available(struct acm_private *acm);
++
++/*
++ * acm_os_recv_chars - receive n bytes starting at cp. This will never be
++ * more than the last call to acm_os_recv_space_available().
++ * This will be called from interrupt context.
++ */
++extern int acm_os_recv_chars(struct acm_private *acm, u8 *cp, int n);
++
++/*
++ * acm_os_wakeup_writers - wakeup any blocked writers. This is called
++ * from interrupt context, so may need to queue
++ * the actual wakeup in a "bottom half".
++ */
++extern void acm_os_wakeup_writers(struct acm_private *acm);
++
++/*
++ * acm_os_hangup - send a hangup to any readers or writers. This can be
++ * called from interrupt context, so may need to queue
++ * the actual hangup in a "bottom half".
++ */
++extern void acm_os_hangup(struct acm_private *acm);
++
++/*
++ * acm_os_wakeup_opens - wakeup any blocked opens. This is called
++ * from interrupt context, so may need to queue
++ * the actual wakeup in a "bottom half".
++ */
++extern void acm_os_wakeup_opens(struct acm_private *acm);
++
++extern void acm_os_enable(struct usbd_function_instance *function);
++extern void acm_os_disable(struct usbd_function_instance *function);
++
++#endif
+diff -uNr linux/drivers/no-otg/functions/acm/tty.h linux/drivers/otg/functions/acm/tty.h
+--- linux/drivers/no-otg/functions/acm/tty.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/acm/tty.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,117 @@
++/*
++ * otg/functions/acm/tty.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @defgroup TTYFunction ACM-TTY
++ * @ingroup functiongroup
++ */
++/*!
++ * @file otg/functions/acm/tty.h
++ * @brief ACM Function Driver private defines
++ *
++ *
++ * This is an ACM Function Driver. The upper edge is exposed
++ * to the hosting OS as a Posix type character device. The lower
++ * edge implements the USB Device Stack API.
++ *
++ * These are emulated and set by the tty driver as appropriate
++ * to model a virutal serial port. The
++ *
++ * TIOCM_RNG RNG (Ring) not used
++ * TIOCM_LE DSR (Data Set Ready / Line Enable)
++ * TIOCM_DSR DSR (Data Set Ready)
++ * TIOCM_CAR DCD (Data Carrier Detect)
++ * TIOCM_CTS CTS (Clear to Send)
++ * TIOCM_CD TIOCM_CAR
++ * TIOCM_RI TIOCM_RNG
++ *
++ * These are set by the application:
++ *
++ * TIOCM_DTR DTR (Data Terminal Ready)
++ * TIOCM_RTS RTS (Request to Send)
++ *
++ * TIOCM_LOOP Set into loopback mode
++ * TIOCM_OUT1 Not used.
++ * TIOCM_OUT2 Not used.
++ *
++ *
++ * The following File and termio c_cflags are used:
++ *
++ * O_NONBLOCK don't block in tty_open()
++ * O_EXCL don't allow more than one open
++ *
++ * CLOCAL ignore DTR status
++ * CBAUD send break if set to B0
++ *
++ * Virtual NULL Modem
++ *
++ * Peripheral to Host via Serial State Notification (write ioctls)
++ *
++ * Application TIOCM Null Modem ACM (DCE) Notificaiton Host (DTE)
++ * -------------------------------------------------------------------------------------------------------------
++ * Request to send TIOCM_RTS RTS -> CTS CTS Not Available Clear to Send (N/A)
++ * Data Terminal Ready TIOCM_DTR DTR -> DSR DSR bTxCarrier Data Set Ready
++ * DTR -> DCD DCD bRXCarrier Carrier Detect
++ * Ring Indicator TIOCM_OUT1 OUT1 -> RI RI bRingSignal Ring Indicator
++ * Overrun TIOCM_OUT2 OUT2 -> Overrun Overrun bOverrun Overrun
++ * Send Break B0 B0 -> Break Break bBreak Break Received
++ * -------------------------------------------------------------------------------------------------------------
++ *
++ *
++ * Host to Peripheral via Set Control Line State
++ *
++ * Host (DTE) Line State ACM (DCE) Null Modem TIOCM Peripheral (DTE)
++ * -------------------------------------------------------------------------------------------------------------
++ * Data Terminal Ready D0 DTR DTR -> DSR TIOCM_DSR Data Set Ready
++ * DTR -> DCD TIOCM_CAR Carrier Detect
++ * Request To Send D1 RTS RTS -> CTS TIOCM_CTS Clear to Send
++ * -------------------------------------------------------------------------------------------------------------
++ *
++ *
++ * @ingroup TTYFunction
++ */
++
++extern struct usbd_function_driver tty_function_driver;
++
++//#define TTY_OPENED (1 << 0) /*! OPENED flag */
++#define TTYFD_CLOCAL (1 << 1) /*! CLOCAL flag */
++//#define TTYFD_LOOPBACK (1 << 2) /*! LOOPBACK flag */
++#define TTYFD_EXCLUSIVE (1 << 3) /*! EXCLUSIVE flag */
++#define TTYFD_THROTTLED (1 << 4) /*! THROTTLED flag */
++
++/*! @struct tty_private
++ */
++struct tty_private {
++
++ struct tty_driver *tty_driver; /*!< tty structure */
++ int tty_driver_registered; /*!< non-zero if tty_driver registered */
++ int usb_driver_registered; /*!< non-zero if usb function registered */
++
++ struct tty_struct *tty; /*!< non-null if tty open */
++ struct tq_struct wqueue; /*!< task queue for writer wakeup */
++ struct tq_struct hqueue; /*!< task queue for hangup */
++
++ u32 flags; /*!< flags */
++
++ u32 tiocm; /*!< tiocm settings */
++
++ struct serial_struct serial_struct; /*!< serial structure used for TIOCSSERIAL and TIOCGSERIAL */
++
++ wait_queue_head_t tiocm_wait;
++ u32 tiocgicount;
++
++ u32 c_cflag;
++
++ wait_queue_head_t open_wait; /*! wait queue for blocking open*/
++ int exiting; /*! True if module exiting */
++};
++
++
+diff -uNr linux/drivers/no-otg/functions/isotest/Config.in linux/drivers/otg/functions/isotest/Config.in
+--- linux/drivers/no-otg/functions/isotest/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/Config.in 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,28 @@
++#
++# Loop Function
++#
++# Copyright (C) 2003-2004 Belcarra
++# Enhanced Jan 2004 to provide greater runtime selection
++# of xmit buffer patterns from the device to the host
++#
++
++mainmenu_option next_comment
++comment "ISO Test Function"
++
++dep_tristate ' Loop Function Driver' CONFIG_OTG_ISOTEST $CONFIG_OTG
++
++if [ "$CONFIG_OTG_ISOTEST" = "y" -o "$CONFIG_OTG_ISOTEST" = "m" ]; then
++
++ hex ' idVendor (hex value)' CONFIG_OTG_ISOTEST_VENDORID "15ec"
++ hex ' idProduct (hex value)' CONFIG_OTG_ISOTEST_PRODUCTID "f004"
++ hex ' bcdDevice (binary-coded decimal)' CONFIG_OTG_ISOTEST_BCDDEVICE "0100"
++ string ' iManufacturer (string)' CONFIG_OTG_ISOTEST_MANUFACTURER ""
++ string ' iProduct (string)' CONFIG_OTG_ISOTEST_PRODUCT_NAME ""
++
++ string ' iConfiguration (string)' CONFIG_OTG_ISOTEST_DESC "ISO Test Cfg"
++ string ' Data Interface iInterface (string)' CONFIG_OTG_ISOTEST_DATA_INTF "ISO Data Intf"
++ #bool ' Runtime pattern buffer selection ' CONFIG_OTG_ISOTEST_XMIT_PATTERN "y"
++
++fi
++
++endmenu
+diff -uNr linux/drivers/no-otg/functions/isotest/Makefile linux/drivers/otg/functions/isotest/Makefile
+--- linux/drivers/no-otg/functions/isotest/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/Makefile 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,73 @@
++#
++# Function driver ISO Test Device
++#
++# Copyright (c) 2003 Belcarra
++
++# Multipart objects.
++
++O_TARGET := isotest_fd_drv.o
++list-multi := isotest_fd.o isotest.o
++
++isotest_fd-objs := iso.o test.o fermat.o
++isotest-objs := host.o test.o
++
++# Objects that export symbols.
++export-objs := iso.o
++
++# Object file lists.
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj- :=
++
++# Each configuration option enables a list of files.
++
++obj-$(CONFIG_OTG_ISOTEST) += isotest_fd.o isotest.o
++
++# Extract lists of the multi-part drivers.
++# The 'int-*' lists are the intermediate files used to build the multi's.
++
++multi-y := $(filter $(list-multi), $(obj-y))
++multi-m := $(filter $(list-multi), $(obj-m))
++int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
++int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
++
++# Files that are both resident and modular: remove from modular.
++
++obj-m := $(filter-out $(obj-y), $(obj-m))
++int-m := $(filter-out $(int-y), $(int-m))
++
++# Translate to Rules.make lists.
++
++O_OBJS := $(filter-out $(export-objs), $(obj-y))
++OX_OBJS := $(filter $(export-objs), $(obj-y))
++M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
++MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
++MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
++MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
++
++# The global Rules.make.
++
++OTG=$(TOPDIR)/drivers/otg
++ISOD=$(OTG)/functions/isotest
++USBDCORE_DIR=$(OTG)/usbdcore
++include $(TOPDIR)/Rules.make
++EXTRA_CFLAGS += -I$(ISOD) -I$(OTG) -Wno-unused -Wno-format
++EXTRA_CFLAGS_nostdinc += -I$(ISOD) -I$(OTG) -Wno-unused -Wno-format
++
++# Link rules for multi-part drivers.
++
++isotest_fd.o: $(isotest_fd-objs)
++ $(LD) -r -o $@ $(isotest_fd-objs)
++
++isotest.o: $(isotest-objs)
++ $(LD) -r -o $@ $(isotest-objs)
++
++# dependencies:
++
++isotest.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h test.h
++host.o: host.c test.h
++
++test.o:test.h
++
+diff -uNr linux/drivers/no-otg/functions/isotest/fermat.c linux/drivers/otg/functions/isotest/fermat.c
+--- linux/drivers/no-otg/functions/isotest/fermat.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/fermat.c 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,132 @@
++/*
++ * otg/network_fd/fermat.c - Network Function Driver
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Bruce Balden <balden@belcarra.com>
++ *
++ *
++ * 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.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++#ifdef CONFIG_OTG_ISOTEST_MODULE
++
++#include "fermat.h"
++
++#ifndef ISO_FERMAT_DEFINED
++typedef unsigned char BYTE;
++typedef struct fermat {
++ int length;
++ BYTE power[256];
++} FERMAT;
++#endif
++
++
++static int fermat_setup(FERMAT *p, int seed){
++ int i = 0;
++ unsigned long x,y;
++ y = 1;
++ do{
++ x = y;
++ p->power[i] = ( x == 256 ? 0 : x);
++ y = ( seed * x ) % 257;
++ i += 1;
++ }while( y != 1);
++ p->length = i;
++ return i;
++}
++
++static void fermat_xform(FERMAT *p, BYTE *data, int length){
++ BYTE *pw = p->power;
++ int i, j;
++ BYTE * q ;
++ for(i = 0, j=0, q = data; i < length; i++, j++, q++){
++ if(j>=p->length){
++ j = 0;
++ }
++ *q ^= pw[j];
++ }
++}
++
++static FERMAT default_fermat;
++static const int primitive_root = 5;
++void fermat_init(){
++ (void) fermat_setup(&default_fermat, primitive_root);
++}
++
++// Here are the public official versions.
++// Change the primitive_root above to another primitive root
++// if you need better scatter. Possible values are 3 and 7
++
++
++void fermat_encode(BYTE *data, int length){
++ fermat_xform(&default_fermat, data, length);
++}
++
++void fermat_decode(BYTE *data, int length){
++ fermat_xform(&default_fermat, data, length);
++}
++
++
++// Note: the seed must be a "primitive root" of 257. This means that
++// the return value of the setup routine must be 256 (otherwise the
++// seed is not a primitive root. The routine will still work fine
++// but will be less pseudo-random.
++
++#undef TEST
++#if TEST
++#include <stdio.h>
++#include <memory.h>
++
++// Use FERMAT in two ways: to encode, and to generate test data.
++
++main(){
++ //Note 3, 5, and 7 are primitive roots of 257
++ // 11 is not a primitive root
++ FERMAT three, five, seven;
++
++ FERMAT three2;
++ printf("Cycle lengths: 3,5,7 %d %d %d \n",
++ fermat_setup(&three, 3),
++ fermat_setup(&five, 5),
++ fermat_setup(&seven, 7));
++ three2=three; // Copy data from three
++ fermat_xform(&three,three2.power,three2.length);
++ fermat_xform(&five,three2.power,three2.length);
++ fermat_xform(&seven,three2.power,three2.length);
++ fermat_xform(&seven,three2.power,three2.length);
++ fermat_xform(&five,three2.power,three2.length);
++ fermat_xform(&three,three2.power,three2.length);
++
++ //At this stage, three2 and three should be identical
++ if(memcpy(&three,&three2,sizeof(FERMAT))){
++ printf("Decoded intact\n");
++ }
++
++ fermat_init();
++ fermat_encode(three2.power,256);
++
++}
++#endif
++
++#endif /* CONFIG_OTG_ISOTEST */
++
+diff -uNr linux/drivers/no-otg/functions/isotest/fermat.h linux/drivers/otg/functions/isotest/fermat.h
+--- linux/drivers/no-otg/functions/isotest/fermat.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/fermat.h 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,38 @@
++/*
++ * otg/network_fd/fermat.h - Network Function Driver
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Bruce Balden <balden@belcarra.com>
++ *
++ * 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 ISO_FERMAT_DEFINED
++#define ISO_FERMAT_DEFINED 1
++typedef unsigned char BYTE;
++typedef struct fermat {
++ int length;
++ BYTE power[256];
++} FERMAT;
++
++void fermat_init(void);
++void fermat_encode(BYTE *data, int length);
++void fermat_decode(BYTE *data, int length);
++#endif
++
+diff -uNr linux/drivers/no-otg/functions/isotest/host.c linux/drivers/otg/functions/isotest/host.c
+--- linux/drivers/no-otg/functions/isotest/host.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/host.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,840 @@
++/*
++ * otg/isotest_fd/host.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * USB ISO Test
++ *
++ *
++ * Copyright (c) 2003, 2004 sl@belcarra.com
++ *
++ */
++
++//#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++//#include <linux/sched.h>
++#include <linux/signal.h>
++#include <linux/errno.h>
++//#include <linux/poll.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/fcntl.h>
++#include <linux/module.h>
++#include <linux/spinlock.h>
++#include <linux/list.h>
++#include <linux/smp_lock.h>
++#include <linux/usb.h>
++#include <linux/interrupt.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/proc_fs.h>
++
++#include <linux/vmalloc.h>
++
++#include <asm/atomic.h>
++#include <asm/io.h>
++
++
++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
++#include <asm/dma.h>
++#include <asm/mach/dma.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/types.h>
++#endif
++
++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++#include <asm/au1000.h>
++#include <asm/au1000_dma.h>
++#include <asm/mipsregs.h>
++#endif
++
++#if defined(CONFIG_ARCH_SAMSUNG)
++#include <asm/arch/timers.h>
++#include <asm/arch/hardware.h>
++#endif
++
++#include "test.h"
++
++
++/* Use our own dbg macro */
++#undef dbg
++#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0)
++
++#define MIN(a,b) (((a) < (b))?(a):(b))
++#define MAX(a,b) (((a) > (b))?(a):(b))
++
++#define THROW(x) goto x
++#define CATCH(x) while(0) x:
++#define THROW_IF(e, x) if (e) { goto x; }
++#define BREAK_IF(x) if (x) { break; }
++#define CONTINUE_IF(x) if (x) { continue; }
++#define RETURN_IF(y) if (y) { return; }
++#define RETURN_ZERO_IF(y) if (y) { return 0; }
++#define RETURN_NULL_IF(y) if (y) { return NULL; }
++
++
++/* Version Information */
++#define DRIVER_VERSION "v0.9"
++#define DRIVER_AUTHOR "sl@belcarra.com"
++#define DRIVER_DESC "USB ISO Test"
++
++/* Define these values to match your device */
++
++#ifdef CONFIG_OTG_ISOTEST_VENDORID
++ #undef USB_ISOTEST_VENDOR_ID
++ #define USB_ISOTEST_VENDOR_ID CONFIG_OTG_ISOTEST_VENDORID
++#else
++ #define USB_ISOTEST_VENDOR_ID 0xfff0
++#endif
++
++#ifdef CONFIG_OTG_ISOTEST_PRODUCTID
++ #undef USB_ISOTEST_PRODUCT_ID
++ #define USB_ISOTEST_PRODUCT_ID CONFIG_OTG_ISOTEST_PRODUCTID
++#else
++ #define USB_ISOTEST_PRODUCT_ID 0xfff1
++#endif
++
++/* Module paramaters */
++//MODULE_PARM(debug, "i");
++//MODULE_PARM_DESC(debug, "Debug enabled or not");
++
++static int send;
++MODULE_PARM(send, "i");
++MODULE_PARM_DESC(send, "send test");
++
++static int recv;
++MODULE_PARM(recv, "i");
++MODULE_PARM_DESC(recv, "recv test");
++
++static u32 vendor_id; // no default
++static u32 product_id; // no default
++
++
++MODULE_PARM_DESC(vendor_id, "User specified USB idVendor");
++MODULE_PARM_DESC(product_id, "User specified USB idProduct");
++MODULE_PARM(vendor_id, "i");
++MODULE_PARM(product_id, "i");
++
++
++/* table of devices that work with this driver */
++static struct usb_device_id isotest_table [] = {
++ { USB_DEVICE(USB_ISOTEST_VENDOR_ID, USB_ISOTEST_PRODUCT_ID) },
++ { }, /* extra entry */
++ { }, /* Terminating entry */
++};
++
++MODULE_DEVICE_TABLE (usb, isotest_table);
++
++/* ********************************************************************************************* */
++
++
++/* ********************************************************************************************* */
++/*
++struct iso_test_data {
++
++ // sender info
++ u32 sender_id;
++ time_t send_time;
++ u32 send_crc;
++
++ // loop info
++ u32 recv_id;
++ time_t recv_time;
++ u32 recv_crc;
++
++ // payload
++ u32 size;
++ u8 data[0];
++};
++*/
++
++#define IN_URBS 10
++#define OUT_URBS 10
++
++
++
++
++/* Structure to hold all of our device specific stuff */
++struct usb_isotest {
++ struct usb_device * udev; /* save off the usb device pointer */
++ struct usb_interface * interface; /* the interface for this device */
++
++ u8 closing;
++ u8 num_interrupt_in; /* number of interrupt in endpoints we have */
++ u8 num_iso_in; /* number of iso in endpoints we have */
++ u8 num_iso_out; /* number of iso out endpoints we have */
++
++ int iso_in_size; /* the size of the receive buffer */
++ struct urb * iso_in_urbs[IN_URBS]; /* the urb used to send data */
++ u8 iso_in_endpointAddr; /* the address of the iso in endpoint */
++ int in_urbs;
++
++ int iso_out_size; /* the size of the send buffer */
++ struct urb * iso_out_urbs[OUT_URBS]; /* the urb used to send data */
++ u8 iso_out_endpointAddr; /* the address of the iso out endpoint */
++ int out_urbs;
++
++ struct semaphore sem; /* locks this structure */
++ struct tq_struct iso_bh;
++ wait_queue_head_t iso_wq;
++
++ struct isotest_stats stats;
++
++ int first;
++};
++
++
++/* local function prototypes */
++
++static void * isotest_probe (struct usb_device *, unsigned int , const struct usb_device_id *);
++static void isotest_disconnect (struct usb_device *, void *);
++
++
++void isotest_schedule_bh(struct usb_isotest *isotest);
++
++
++/* ISO OUT - Transmit ************************************************************************** */
++
++
++#if 1
++static void isotest_iso_out_free_urb(struct urb *urb)
++{
++ struct usb_isotest *isotest;
++ int i;
++ unsigned long flags;
++
++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
++
++ RETURN_IF(!urb);
++
++ if (urb->transfer_buffer) {
++ kfree(urb->transfer_buffer);
++ }
++
++ isotest = (struct usb_isotest *)urb->context;
++ usb_free_urb(urb);
++
++ RETURN_IF(!isotest);
++
++ local_irq_save (flags);
++ for (i = 0; i < OUT_URBS; i++) {
++ CONTINUE_IF(isotest->iso_out_urbs[i] != urb);
++ //printk(KERN_INFO"%s: zeroing %d urb: %p\n", __FUNCTION__, i, urb);
++ isotest->iso_out_urbs[i] = NULL;
++ break;
++ }
++ local_irq_restore (flags);
++}
++#endif
++
++static int iso_transfer_count;
++
++int iso_out_submit(struct usb_isotest *isotest, struct urb *urb)
++{
++ int i;
++ int j;
++ int rc = 0;
++
++ //printk(KERN_INFO"%s: transfer: %d size: %d frames: %x\n", __FUNCTION__,
++ // iso_transfer_count, urb->transfer_buffer_length, urb->number_of_packets);
++
++ RETURN_ZERO_IF(isotest->closing);
++
++ urb->dev = isotest->udev;
++
++ iso_transfer_count++;
++
++ for (j = urb->transfer_buffer_length, i = 0; i < urb->number_of_packets; i++) {
++
++ int send = MIN(isotest->iso_out_size, j);
++
++ u8 *cp = urb->transfer_buffer + (isotest->iso_out_size * i);
++
++ j -= send;
++
++ urb->iso_frame_desc[i].offset = i * isotest->iso_out_size;
++ urb->iso_frame_desc[i].length = send;
++
++ // iso_transfer_count
++ *cp++ = cpu_to_le16(iso_transfer_count) & 0xff;
++ *cp++ = (cpu_to_le16(iso_transfer_count) >> 8) & 0xff;
++ *cp++ = (cpu_to_le16(iso_transfer_count) >> 16) & 0xff;
++ *cp++ = (cpu_to_le16(iso_transfer_count) >> 24) & 0xff;
++
++ // iso transfer length
++ *cp++ = cpu_to_le16(urb->transfer_buffer_length) & 0xff;
++ *cp++ = (cpu_to_le16(urb->transfer_buffer_length) >> 8) & 0xff;
++
++ // iso frame size
++ *cp++ = cpu_to_le16(isotest->iso_out_size) & 0xff;
++ *cp++ = (cpu_to_le16(isotest->iso_out_size) >> 8) & 0xff;
++
++ // total frames
++ *cp++ = cpu_to_le16(urb->number_of_packets) & 0xff;
++ *cp++ = (cpu_to_le16(urb->number_of_packets) >> 8) & 0xff;
++
++ // this packet number
++ *cp++ = cpu_to_le16(i+1) & 0xff;
++ *cp++ = (cpu_to_le16(i+1) >> 8) & 0xff;
++
++ }
++
++ //printk(KERN_INFO"%s: submitting\n", __FUNCTION__);
++
++ RETURN_ZERO_IF(!(rc = usb_submit_urb(urb)));
++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc);
++
++ return rc;
++}
++
++void isotest_iso_out_complete (struct urb *urb)
++{
++ struct usb_isotest *isotest = (struct usb_isotest *)urb->context;
++ int rc;
++
++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
++
++ RETURN_IF(!urb);
++
++ if (urb->status /* && (urb->status != -ENOENT) && (urb->status != -ECONNRESET)*/) {
++ //printk(KERN_INFO"%s: - nonzero write iso status received: %d\n", __FUNCTION__, urb->status);
++ }
++
++ iso_out_submit(isotest, urb);
++}
++
++
++#define ISO_SEND_TOTAL 1000
++#define ISO_SEND_TRANSFERS 1200
++
++static int out_count;
++
++struct urb *iso_out_start(struct usb_isotest *isotest)
++{
++ struct urb * urb = NULL;
++ int rc = 0;
++ int i;
++ int j;
++
++
++ int size = ((ISO_SEND_TOTAL % isotest->iso_out_size) < 20) ? ISO_SEND_TOTAL + 20 : ISO_SEND_TOTAL;
++ int frames = (size / isotest->iso_out_size) + 1;
++
++ //RETURN_NULL_IF(out_count-- <= 0);
++
++ //printk(KERN_INFO"%s: %d %02x\n", __FUNCTION__, out_count, isotest->iso_out_endpointAddr);
++
++
++ //printk(KERN_INFO"%s: frames: %x packet: %d size: %d\n", __FUNCTION__, frames, isotest->iso_out_size, size);
++
++ // allocate urb and buffer, fill buffer with some data
++ THROW_IF(!(urb = usb_alloc_urb(frames + 1)), error);
++
++ THROW_IF (!(urb->transfer_buffer = kmalloc(size, GFP_ATOMIC)), error);
++
++ for (i = 0; i < size; i++) {
++ unsigned char *cp = urb->transfer_buffer + i;
++ *cp = i % 256;
++ }
++
++ //printk(KERN_INFO"%s: CCC\n", __FUNCTION__);
++
++ urb->hcpriv = NULL;
++ urb->context = isotest;
++ urb->transfer_flags = USB_ISO_ASAP;
++ urb->complete = isotest_iso_out_complete;
++ urb->pipe = usb_sndisocpipe(isotest->udev, isotest->iso_out_endpointAddr);
++
++ urb->transfer_buffer_length = size;
++
++ urb->number_of_packets = frames;
++
++ THROW_IF((rc = iso_out_submit(isotest, urb)), error);
++
++ //printk(KERN_INFO"%s: OK frames: %d\n", __FUNCTION__, frames);
++
++ CATCH(error) {
++ printk(KERN_INFO"%s: FAILED rc: %d\n", __FUNCTION__, rc);
++ //isotest_iso_out_free_urb(urb);
++ return NULL;
++ }
++ return urb;
++}
++
++/* ISO IN - Receive **************************************************************************** */
++
++static int in_count = 5;
++static int in_submitted;
++static int in_resubmitted;
++static long in_completed;
++static long in_total_received;
++
++#if 0
++static void isotest_iso_in_free_urb(struct urb *urb)
++{
++ struct usb_isotest *isotest;
++ int i;
++ unsigned long flags;
++
++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
++
++ RETURN_IF(!urb);
++
++ if (urb->transfer_buffer) {
++ kfree(urb->transfer_buffer);
++ }
++
++ isotest = (struct usb_isotest *)urb->context;
++ usb_free_urb(urb);
++
++ RETURN_IF(!isotest);
++
++ local_irq_save (flags);
++ for (i = 0; i < IN_URBS; i++) {
++ CONTINUE_IF(isotest->iso_in_urbs[i] != urb);
++ //printk(KERN_INFO"%s: clearing %d urb: %p\n", __FUNCTION__, i, urb);
++ isotest->iso_in_urbs[i] = NULL;
++ break;
++ }
++ local_irq_restore (flags);
++}
++#endif
++
++struct urb *iso_in_start(struct usb_isotest *isotest);
++
++int iso_in_submit(struct usb_isotest *isotest, struct urb *urb)
++{
++ int i;
++ int j;
++ int rc = 0;
++
++ //printk(KERN_INFO"%s: %p\n", __FUNCTION__, urb->complete);
++
++ RETURN_ZERO_IF(isotest->closing);
++
++ urb->dev = isotest->udev;
++ urb->actual_length = 0;
++
++ for (j = urb->transfer_buffer_length, i = 0; i < urb->number_of_packets; i++) {
++
++ int send = MIN(isotest->iso_in_size, j);
++ j -= send;
++ urb->iso_frame_desc[i].offset = i * isotest->iso_in_size;
++ urb->iso_frame_desc[i].length = send;
++ }
++
++ RETURN_ZERO_IF(!(rc = usb_submit_urb(urb)));
++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc);
++
++ //isotest_iso_in_free_urb(urb);
++ return rc;
++}
++
++
++/**
++ * isotest_iso_in_complete
++ */
++void isotest_iso_in_complete (struct urb *urb)
++{
++ struct usb_isotest *isotest = (struct usb_isotest *)urb->context;
++ int i;
++ int rc;
++ int status;
++
++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
++
++ RETURN_IF(!urb);
++
++ if (isotest->closing) {
++ //printk(KERN_INFO"%s: urb: %p pre urbs: %d\n", __FUNCTION__, urb, isotest->in_urbs);
++ if (urb->transfer_buffer) {
++ kfree(urb->transfer_buffer);
++ }
++ usb_free_urb(urb);
++ isotest->in_urbs--;
++ //printk(KERN_INFO"%s: urb: %p pre urbs: %d\n", __FUNCTION__, urb, isotest->in_urbs);
++ return;
++ }
++ status = urb->status;
++
++ if (status /* && (status != -ENOENT) && (status != -ECONNRESET)*/) {
++ //printk(KERN_INFO"%s: - urb: %p nonzero write iso status received: %x\n", __FUNCTION__, urb, status);
++ }
++
++ else if (urb->actual_length) {
++ //printk(KERN_INFO"%s: urb: %p lenght: %d\n", __FUNCTION__, urb, urb->actual_length);
++ in_completed++;
++ in_total_received += urb->actual_length;
++
++ //printk(KERN_INFO"%s: ", __FUNCTION__);
++ for (i = 0; i < urb->number_of_packets; i++) {
++
++ iso_trace_recv_data(&isotest->stats,
++ urb->transfer_buffer + urb->iso_frame_desc[i].offset,
++ urb->iso_frame_desc[i].actual_length, 0);
++
++ // printk("%d:%d:%x ", i,
++ // urb->iso_frame_desc[i].actual_length,
++ // urb->iso_frame_desc[i].status);
++
++
++ }
++ //printk("\n");
++ }
++
++ THROW_IF((rc = iso_in_submit(isotest, urb)), error);
++
++ in_resubmitted++;
++
++ CATCH(error) {
++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc);
++ }
++}
++
++
++struct urb *iso_in_start(struct usb_isotest *isotest)
++{
++ struct urb * urb = NULL;
++ int frames = (ISO_SEND_TOTAL + isotest->iso_in_size) / isotest->iso_in_size;
++ int iso_transfer_size;
++ int rc = 0;
++
++
++ //RETURN_NULL_IF(in_count-- <= 0);
++
++ //printk(KERN_INFO"%s: %d %02x\n", __FUNCTION__, in_count, isotest->iso_in_endpointAddr);
++
++ //iso_transfer_size = ((ISO_SEND_TOTAL % isotest->iso_in_size) > 20) ?
++ // ISO_SEND_TOTAL :
++ // ISO_SEND_TOTAL + (20 - (ISO_SEND_TOTAL % isotest->iso_in_size));
++
++ iso_transfer_size = frames * isotest->iso_in_size;
++
++ THROW_IF(!(urb = usb_alloc_urb(frames + 1)), error);
++ THROW_IF (!(urb->transfer_buffer = kmalloc(iso_transfer_size, GFP_ATOMIC)), error);
++ memset(urb->transfer_buffer, 0, isotest->iso_in_size);
++
++ urb->hcpriv = NULL;
++ urb->context = isotest;
++ urb->transfer_flags = USB_ISO_ASAP;
++ urb->complete = isotest_iso_in_complete;
++ urb->pipe = usb_rcvisocpipe(isotest->udev, isotest->iso_in_endpointAddr);
++
++ urb->transfer_buffer_length = iso_transfer_size;
++ urb->number_of_packets = frames;
++
++ THROW_IF((rc = iso_in_submit(isotest, urb)), error);
++ in_submitted++;
++ isotest->in_urbs++;
++ //printk(KERN_INFO"%s: new urbs: %d\n", __FUNCTION__, isotest->in_urbs);
++ return urb;
++
++ CATCH(error) {
++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc);
++ //isotest_iso_in_free_urb(urb);
++ return NULL;
++ }
++}
++
++
++
++
++/* ********************************************************************************************* */
++
++void isotest_schedule_bh(struct usb_isotest *isotest)
++{
++ unsigned long flags;
++
++ //RETURN_IF(!isotest->iso_bh.data);
++
++ // schedule more data
++ local_irq_save (flags);
++ if (isotest->iso_bh.data && !isotest->iso_bh.sync) {
++ MOD_INC_USE_COUNT;
++ queue_task(&isotest->iso_bh, &tq_immediate);
++ mark_bh(IMMEDIATE_BH);
++ }
++ local_irq_restore (flags);
++}
++
++
++static void bottomhalf(void *data)
++{
++ int i;
++ unsigned long flags;
++ struct usb_isotest *isotest = (struct usb_isotest *) data;
++
++ if (isotest->first) {
++ //sleep_on_timeout(&isotest->iso_wq, 200);
++ udelay(100);
++ isotest->first = 0;
++ }
++
++ THROW_IF(!isotest, error);
++
++ if (isotest->closing) {
++ struct urb *urb;
++ //printk(KERN_INFO"%s: closing\n", __FUNCTION__);
++
++ // unlink outstanding urbs, this has side-effect of calling completion routing
++ local_irq_save (flags);
++ for (i = 0; i < IN_URBS; i++) {
++ CONTINUE_IF(!(urb = isotest->iso_in_urbs[i]));
++ //printk(KERN_INFO"%s: unlinking: %d IN urb: %p\n", __FUNCTION__, i, urb);
++ isotest->iso_in_urbs[i] = NULL;
++ urb->transfer_flags |= USB_ASYNC_UNLINK;
++ usb_unlink_urb(urb);
++ }
++ local_irq_restore (flags);
++
++ local_irq_save (flags);
++ for (i = 0; i < OUT_URBS; i++) {
++ CONTINUE_IF(!(urb = isotest->iso_out_urbs[i]));
++ //printk(KERN_INFO"%s: unlinking: %d OUT urb: %p\n", __FUNCTION__, i, urb);
++ isotest->iso_out_urbs[i] = NULL;
++ urb->transfer_flags |= USB_ASYNC_UNLINK;
++ usb_unlink_urb(urb);
++ }
++ local_irq_restore (flags);
++
++ // tell disconnect that we are finished
++ isotest->iso_bh.data = NULL;
++
++ }
++ else {
++
++ //printk(KERN_INFO"%s: normal\n", __FUNCTION__);
++
++ if (send && out_count) {
++ local_irq_save (flags);
++ for (i = 0; i < OUT_URBS; i++) {
++ CONTINUE_IF(isotest->iso_out_urbs[i]);
++ isotest->iso_out_urbs[i] = iso_out_start(isotest);
++ //printk(KERN_INFO"%s: starting: %d OUT urb: %p\n", __FUNCTION__, i, isotest->iso_out_urbs[i]);
++ }
++ local_irq_restore (flags);
++ }
++
++ if (recv && in_count) {
++ local_irq_save (flags);
++ for (i = 0; i < IN_URBS; i++) {
++ CONTINUE_IF(isotest->iso_in_urbs[i]);
++ isotest->iso_in_urbs[i] = iso_in_start(isotest);
++ //printk(KERN_INFO"%s: starting: %d IN urb: %p\n", __FUNCTION__, i, isotest->iso_in_urbs[i]);
++ }
++ local_irq_restore (flags);
++ }
++ }
++
++ CATCH(error) {
++ printk(KERN_ERR"%s: isotest NULL\n", __FUNCTION__);
++ }
++ MOD_DEC_USE_COUNT;
++}
++
++/* ********************************************************************************************* */
++
++/* usb specific object needed to register this driver with the usb subsystem */
++static struct usb_driver isotest_driver = {
++ name: "isotest",
++ probe: isotest_probe,
++ disconnect: isotest_disconnect,
++ id_table: isotest_table,
++};
++
++/**
++ * isotest_probe
++ *
++ * Called by the usb core when a new device is connected that it thinks
++ * this driver might be interested in.
++ */
++static void * isotest_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
++{
++ struct usb_isotest *isotest = NULL;
++ struct usb_interface *interface;
++ struct usb_device_descriptor *device = &udev->descriptor;
++ struct usb_interface_descriptor *interface_descriptor;
++ int i;
++
++ printk(KERN_INFO"%s: %04x %04x\n", __FUNCTION__, device->idVendor, device->idProduct);
++
++ // See if the device offered us matches what we can accept
++ //if ((device->idVendor != vendor_id) || (device->idProduct != product_id)) {
++ // printk(KERN_INFO"%s: FAILED\n", __FUNCTION__);
++ // return NULL;
++ //}
++
++ // allocate memory for our device state and intialize it
++ if (!(isotest = kmalloc (sizeof(struct usb_isotest), GFP_KERNEL))) {
++ printk(KERN_INFO"%s: Out of memory\n", __FUNCTION__);
++ return NULL;
++ }
++
++ memset (isotest, 0x00, sizeof (*isotest));
++ init_MUTEX (&isotest->sem);
++ init_waitqueue_head(&isotest->iso_wq);
++
++ isotest->udev = udev;
++ isotest->first = 1;
++ isotest->closing = 0;
++ isotest->in_urbs = 0;
++ isotest->interface = interface = &udev->actconfig->interface[ifnum];
++ isotest->iso_bh.routine = bottomhalf;
++ isotest->iso_bh.data = (void *)isotest;
++ interface_descriptor = &interface->altsetting[0];
++
++ // set up the endpoint information and check out the endpoints
++
++ for (i = 0; i < interface_descriptor->bNumEndpoints; ++i) {
++
++ struct usb_endpoint_descriptor *endpoint = &interface_descriptor->endpoint[i];
++
++ //printk(KERN_INFO"%s: looking at %02x\n", __FUNCTION__, endpoint->bEndpointAddress);
++
++ if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x01)) {
++
++ //printk(KERN_INFO"%s: found ISO IN %02x\n", __FUNCTION__, endpoint->bEndpointAddress);
++ isotest->iso_in_size = endpoint->wMaxPacketSize;
++ isotest->iso_in_endpointAddr = endpoint->bEndpointAddress;
++ }
++
++ if (((endpoint->bEndpointAddress & 0x80) == 0x00) && ((endpoint->bmAttributes & 3) == 0x01)) {
++
++ //printk(KERN_INFO"%s: found ISO OUT %02x\n", __FUNCTION__, endpoint->bEndpointAddress);
++
++ isotest->iso_out_size = endpoint->wMaxPacketSize;
++ isotest->iso_out_endpointAddr = endpoint->bEndpointAddress;
++ }
++ }
++
++ out_count = ISO_SEND_TRANSFERS;
++
++ // let the user know what node this device is now attached to
++ //printk(KERN_INFO"%s: USB TEST device now attached to ISOTEST\n", __FUNCTION__);
++
++ isotest_schedule_bh(isotest);
++
++ return isotest;
++}
++
++/**
++ * isotest_disconnect
++ *
++ * Called by the usb core when the device is removed from the system.
++ */
++static void isotest_disconnect(struct usb_device *udev, void *ptr)
++{
++ struct usb_isotest *isotest;
++
++ printk(KERN_INFO"%s: in_submitted: %d in_resubmitted: %d in_completed: %ld in_total: %ld\n",
++ __FUNCTION__, in_submitted, in_resubmitted, in_completed, in_total_received);
++
++ RETURN_IF(!(isotest = (struct usb_isotest *)ptr));
++
++ // set flag to say we are closing
++ isotest->closing = 1;
++ isotest_schedule_bh(isotest);
++
++ while (isotest->iso_bh.data) {
++ isotest_schedule_bh(isotest);
++ printk(KERN_INFO"%s: waiting for bh\n", __FUNCTION__);
++ sleep_on_timeout(&isotest->iso_wq, 20);
++ }
++
++ while (isotest->in_urbs) {
++ printk(KERN_INFO"%s: waiting for urbs\n", __FUNCTION__);
++ sleep_on_timeout(&isotest->iso_wq, 20);
++ }
++
++ kfree(isotest);
++
++ printk(KERN_INFO"%s: USB ISOTEST now disconnected\n", __FUNCTION__);
++}
++
++
++/* ********************************************************************************************* */
++
++void iso_start_in(int count)
++{
++
++}
++
++void iso_start_out(int count)
++{
++
++}
++
++
++/**
++ * isotest_init
++ */
++static int isotest_init(void)
++{
++ int result;
++
++ printk(KERN_INFO"%s:\n", __FUNCTION__);
++
++ if (vendor_id && product_id) {
++ int i;
++ for (i = 0; i < (sizeof(isotest_table) / sizeof(struct usb_device_id) - 1); i++) {
++
++ if ((isotest_table[i].idVendor == vendor_id) && isotest_table[i].idProduct == product_id ) {
++ printk(KERN_INFO"%s: vendor_id: %04x product_id: %04x already in table\n",
++ __FUNCTION__, vendor_id, product_id);
++ break;
++ }
++ printk(KERN_INFO"%s: vendor_id: %04x product_id: %04x\n",
++ __FUNCTION__, isotest_table[i].idVendor, isotest_table[i].idProduct);
++ }
++ if (!isotest_table[i].idVendor && !isotest_table[i].idProduct) {
++ printk(KERN_INFO"%s: inserting vendor_id: %04x product_id: %04x into table\n",
++ __FUNCTION__, vendor_id, product_id);
++
++ isotest_table[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
++ isotest_table[i].idVendor = vendor_id;
++ isotest_table[i].idProduct = product_id;
++ isotest_table[i].bDeviceClass = 0;
++ isotest_table[i].bDeviceSubClass = 0;
++ }
++ }
++
++ iso_trace_init("isotest_host");
++
++ /* register this driver with the USB subsystem */
++ result = usb_register(&isotest_driver);
++ if (result < 0) {
++ printk(KERN_INFO"%s: usb_register failed for the "__FILE__" driver. Error number %d\n", KERN_INFO, result);
++ return -1;
++ }
++
++ printk(KERN_INFO "%s: " DRIVER_DESC " " DRIVER_VERSION "\n", __FUNCTION__);
++ return 0;
++}
++
++
++/**
++ * isotest_exit
++ */
++static void isotest_exit(void)
++{
++ /* deregister this driver with the USB subsystem */
++ usb_deregister(&isotest_driver);
++
++ iso_trace_exit("isotest_host");
++}
++
++
++module_init (isotest_init);
++module_exit (isotest_exit);
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
++MODULE_LICENSE("PRIVATE");
++#endif
++
+diff -uNr linux/drivers/no-otg/functions/isotest/iso.c linux/drivers/otg/functions/isotest/iso.c
+--- linux/drivers/no-otg/functions/isotest/iso.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/iso.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,620 @@
++/*
++ * otg/isotest_fd/iso.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com");
++MODULE_DESCRIPTION ("USB Device Serial Function");
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
++MODULE_LICENSE("GPL");
++#endif
++
++#include <linux/init.h>
++#include <linux/list.h>
++#include <asm/uaccess.h>
++#include <linux/slab.h>
++#include <linux/interrupt.h>
++
++#include <linux/smp_lock.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/string.h>
++
++#include "usbp-chap9.h"
++#include <usbp-mem.h>
++#include <usbp-func.h>
++#include <usbp-admin.h>
++
++USBD_MODULE_INFO ("isotest_fd 2.0-beta");
++
++#include "test.h"
++#include "fermat.h"
++
++struct usb_isotest {
++ int interface;
++ struct usbd_function_instance *function;
++ rwlock_t rwlock;
++
++ int open;
++ int closing;
++ struct tq_struct iso_bh;
++ wait_queue_head_t iso_wq;
++};
++
++#define ISO_OUT 0x00
++#define ISO_IN 0x01
++
++#define ENDPOINTS 0x02
++
++
++u8 isotest_requested_endpoints[ENDPOINTS+1] = {
++ USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS,
++ USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS,
++ 0,
++};
++
++#define ISO_OUT_PKTSIZE 90
++#define ISO_IN_PKTSIZE 90
++#define isotest_requested_transferSizes xfer_sizes
++
++u16 xfer_sizes[ENDPOINTS+1] = {
++ ISO_OUT_PKTSIZE,
++ ISO_IN_PKTSIZE,
++ 0,
++};
++
++
++
++/* Module Parameters ************************************************************************* */
++// override vendor ID
++static u32 vendor_id;
++MODULE_PARM (vendor_id, "i");
++MODULE_PARM_DESC (vendor_id, "vendor id");
++
++// override product ID
++static u32 product_id;
++MODULE_PARM (product_id, "i");
++MODULE_PARM_DESC (product_id, "product id");
++
++MODULE_PARM (xfer_sizes, "3-3h");
++MODULE_PARM_DESC (xfer_sizes, "Requested transfer sizes for each endpoint; default 90 for iso in and out");
++
++// packet sizes
++static u32 in = ISO_IN_PKTSIZE;
++MODULE_PARM (in, "i");
++MODULE_PARM_DESC (in, "in size");
++
++static u32 out = ISO_OUT_PKTSIZE;
++MODULE_PARM (out, "i");
++MODULE_PARM_DESC (out, "out size");
++
++static int fermat=0;
++MODULE_PARM (fermat, "i");
++MODULE_PARM_DESC (fermat, "Apply randomization to buffer");
++
++static int custom=0;
++MODULE_PARM (custom, "i");
++MODULE_PARM_DESC (custom, "Supply custom pattern via xmit_pattern parameter");
++
++static int print_all=0;
++MODULE_PARM (print_all, "i");
++MODULE_PARM_DESC (print_all, "Print all buffers, not just the first");
++
++#define ZERO4 0,0,0,0
++#define ZERO16 ZERO4,ZERO4,ZERO4,ZERO4
++#define ZERO64 ZERO16,ZERO16,ZERO16,ZERO16
++static u8 xmit_pattern[256]={1,0xE,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe, ZERO16,ZERO16,ZERO16,ZERO64,ZERO64,ZERO64};
++MODULE_PARM(xmit_pattern,"1-256b");
++MODULE_PARM_DESC(xmit_pattern, "pattern to be transmitted, count, size, payload; size is "
++ "the size of the payload following. count is the number of times to repeat the pattern (0=infinite)");
++static struct {
++ int count;
++ int size;
++ u8 *payload_start, *payload_end;
++ u8 *current;
++ int total_size;
++ int sent;
++} xps; // Transmit pattern state
++
++static void xmit_pattern_state_init(void);
++static u8 xmit_pattern_next(void);
++static void fill_xmit_buffer(u32 size, u8*buffer);
++
++static void xmit_pattern_state_init(){
++ xps.count = xmit_pattern[0];
++ xps.size = xmit_pattern[1];
++ xps.payload_start = xmit_pattern+2;
++ xps.payload_end = xps.payload_start + xps.size;
++ xps.current = xps.payload_start;
++ xps.sent = 0;
++ xps.total_size = xps.count * xps.size;
++}
++
++static u8 xmit_pattern_next(){
++
++ // current always points at the next character to send
++ u8 next_value = *xps.current++;
++
++ xps.sent += 1;
++
++ if (xps.current >= xps.payload_end)
++ xps.current = xps.payload_start; //Rewind buffer
++
++ // if Total pattern has been sent; rewind
++ if (xps.sent >= xps.total_size){
++ xmit_pattern_state_init();
++ }
++
++ return next_value;
++}
++
++static void fill_xmit_buffer(u32 size, u8 *buffer)
++{
++ u8 * limit = buffer + size;
++ xmit_pattern_state_init();
++ while(buffer < limit){
++ *buffer++ = xmit_pattern_next();
++ }
++}
++
++static void fill_xmit_buffer_default(u32 size, u8 * buffer){
++ int j;
++ for(j = 0; j < size; j++){
++ buffer[j] = j & 0xff;
++ }
++}
++
++static void print_buffer(u32 size, u8 * buffer){
++ // Print up to 16 bytes of the buffer, first time called only
++ static int first = 1;
++ int n = ( size >=16 ? 16 : size);
++ int j;
++ if((first) || (print_all)){
++ char *prefix = first? "\n" : "";
++ printk(KERN_INFO "%sxmit buffer:", prefix);
++ for( j=0 ; j < n; j++){
++ printk("%02x",buffer[j]);
++ }
++ printk("\n");
++ first = 0;
++ }
++}
++
++
++
++/* ************************************************************************** */
++
++static struct isotest_stats isotest_stats;
++static struct usb_isotest isotest;
++
++
++/* Classes descriptors
++ */
++
++static u8 isotest_ep_1[7] = { 0x07, USB_DT_ENDPOINT, 0, ISOCHRONOUS, ISO_OUT_PKTSIZE&0xff, (ISO_OUT_PKTSIZE>>8)&0xff, 0x00};
++static u8 isotest_ep_2[7] = { 0x07, USB_DT_ENDPOINT, 0, ISOCHRONOUS, ISO_IN_PKTSIZE&0xff, (ISO_IN_PKTSIZE>>8)&0xff, 0x00};
++
++static struct usbd_endpoint_descriptor *isotest_data_endpoints[] = {
++ (struct usbd_endpoint_descriptor *) &isotest_ep_1,
++ (struct usbd_endpoint_descriptor *) &isotest_ep_2 };
++
++u8 isotest_data_indexes [] = { ISO_OUT, ISO_IN, };
++
++
++/* Alternate descriptors
++ */
++static u8 isotest_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, 0x01, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
++ 0x00, 0x00, 0x00, 0x00,
++};
++
++
++/* Alternate descriptions
++ */
++static struct usbd_alternate_description isotest_data_alternate_descriptions[] = {
++ { iInterface:CONFIG_OTG_ISOTEST_DATA_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&isotest_data_alternate_descriptor,
++ endpoints:sizeof (isotest_data_endpoints) / sizeof(u8 *),
++ endpoint_list: isotest_data_endpoints,
++ endpoint_indexes: isotest_data_indexes,
++ },
++};
++
++
++/* Interface descriptions
++ */
++static struct usbd_interface_description isotest_interfaces[] = {
++ { alternates:sizeof (isotest_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++alternate_list:isotest_data_alternate_descriptions,
++ },
++};
++
++
++/* Configuration descriptions
++ */
++static u8 isotest_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = {
++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength
++ sizeof (isotest_interfaces) / sizeof (struct usbd_interface_description),
++ 0x01, 0x00, // bConfigurationValue, iConfiguration
++ 0, 0,
++};
++
++
++struct usbd_configuration_description isotest_description[] = {
++ { iConfiguration:CONFIG_OTG_ISOTEST_DESC,
++ configuration_descriptor: (struct usbd_configuration_descriptor *)isotest_configuration_descriptor,
++ },
++};
++
++/* Device Description
++ */
++static struct usbd_device_descriptor isotest_device_descriptor = {
++ bLength: sizeof(struct usbd_device_descriptor),
++ bDescriptorType: USB_DT_DEVICE,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x00,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++static struct usbd_device_qualifier_descriptor isotest_device_qualifier_descriptor = {
++ bLength: sizeof(struct usbd_device_qualifier_descriptor),
++ bDescriptorType: USB_DT_DEVICE_QUALIFIER,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
++ bDeviceSubClass: 0x00,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++static struct usbd_endpoint_request iso_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS, ISO_OUT_PKTSIZE, ISO_OUT_PKTSIZE * 4, },
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS, ISO_IN_PKTSIZE, ISO_OUT_PKTSIZE * 4, },
++ { 1, },
++};
++
++static struct usbd_otg_descriptor iso_otg_descriptor = {
++bLength : sizeof(struct usbd_otg_descriptor),
++bDescriptorType: USB_DT_OTG,
++bmAttributes: 0,
++};
++
++struct usbd_device_description isotest_device_description = {
++ device_descriptor: &isotest_device_descriptor,
++ #ifdef CONFIG_OTG_HIGH_SPEED
++ device_qualifier_descriptor: &isotest_device_qualifier_descriptor,
++ #endif /* CONFIG_OTG_HIGH_SPEED */
++ otg_descriptor: &iso_otg_descriptor,
++ iManufacturer: CONFIG_OTG_ISOTEST_MANUFACTURER,
++ iProduct: CONFIG_OTG_ISOTEST_PRODUCT_NAME,
++ #if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR,
++ #endif
++};
++
++void iso_start_in(int count);
++void iso_start_out(int count);
++
++void schedule_bh(void)
++{
++ unsigned long flags;
++
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++
++ local_irq_save (flags);
++ if (!isotest.iso_bh.sync) {
++ MOD_INC_USE_COUNT;
++ queue_task(&isotest.iso_bh, &tq_immediate);
++ mark_bh(IMMEDIATE_BH);
++ }
++ local_irq_restore (flags);
++
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++}
++
++
++int isotest_urb_sent (struct usbd_urb *urb, int rc);
++int isotest_recv_urb (struct usbd_urb *urb, int rc);
++
++/* Transmit Function *************************************************************************** */
++
++static int out_count;
++static int in_count;
++static int send_size = 1000;
++static int iso_transfer_in_count;
++
++static int isotest_xmit_data (void)
++{
++ int i;
++ int j;
++ int frames;
++ int size = ((send_size % in) < 20) ? send_size + 20 : send_size;
++ struct usbd_urb *urb;
++ struct usbd_function_instance *function = isotest.function;
++
++ //printk(KERN_INFO"%s: open: %d in_count: %d\n", __FUNCTION__, isotest.open, in_count);
++
++ RETURN_ZERO_IF(!isotest.open);
++
++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb (isotest.function, ISO_IN, size, isotest_urb_sent)));
++
++ frames = (size / in) + 1;
++
++ iso_transfer_in_count++;
++
++ for (i = 0; i < frames; i++) {
++
++ u8 *cp = urb->buffer + (i * in);
++ if(custom){
++ (void) fill_xmit_buffer(in, cp);
++ } else {
++ (void) fill_xmit_buffer_default(in,cp);
++ }
++ if(fermat){
++ fermat_encode(cp, in);
++ }
++ print_buffer(in, cp);
++
++ // iso_transfer_count
++ *cp++ = cpu_to_le16(iso_transfer_in_count) & 0xff;
++ *cp++ = (cpu_to_le16(iso_transfer_in_count) >> 8) & 0xff;
++ *cp++ = (cpu_to_le16(iso_transfer_in_count) >> 16) & 0xff;
++ *cp++ = (cpu_to_le16(iso_transfer_in_count) >> 24) & 0xff;
++
++ // iso transfer length
++ *cp++ = cpu_to_le16(size) & 0xff;
++ *cp++ = (cpu_to_le16(size) >> 8) & 0xff;
++
++ // iso frame size
++ *cp++ = cpu_to_le16(in) & 0xff;
++ *cp++ = (cpu_to_le16(in) >> 8) & 0xff;
++
++ // total frames
++ *cp++ = cpu_to_le16(frames) & 0xff;
++ *cp++ = (cpu_to_le16(frames) >> 8) & 0xff;
++
++ // this packet number
++ *cp++ = cpu_to_le16(i+1) & 0xff;
++ *cp++ = (cpu_to_le16(i+1) >> 8) & 0xff;
++
++ }
++ urb->actual_length = size;
++
++ // push it down into the usb-device layer
++ //printk(KERN_INFO"%s: sending: %p length: %d\n", __FUNCTION__, urb, urb->actual_length);
++ return usbd_start_in_urb (urb);
++}
++
++
++/* isotest_urb_sent - called to indicate URB transmit finished
++ * @urb: pointer to struct usbd_urb
++ * @rc: result
++ */
++int isotest_urb_sent (struct usbd_urb *urb, int rc)
++{
++ //printk(KERN_INFO"%s: sent: %p status: %d\n", __FUNCTION__, urb, urb->status);
++
++ usbd_free_urb (urb);
++
++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__);
++ schedule_bh();
++ return 0;
++}
++
++
++/* USB Device Functions ************************************************************************ */
++
++/* isotest_event_handler - process a device event
++ *
++ */
++void isotest_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data)
++{
++ int i;
++ switch (event) {
++
++ case DEVICE_RESET:
++ case DEVICE_DESTROY:
++ if (isotest.open)
++ usbd_disable_irq(NULL);
++
++ isotest.open = 0;
++ break;
++
++ case DEVICE_CONFIGURED:
++ isotest.open = 1;
++ for (i = 0; i < 2; i++) {
++ struct usbd_urb *urb;
++ BREAK_IF(!(urb = usbd_alloc_urb (function, ISO_OUT,
++ usbd_endpoint_transferSize(
++ function, ISO_OUT,usbd_high_speed(function)),
++ isotest_recv_urb
++ )));
++ if (usbd_start_out_urb(urb))
++ usbd_free_urb(urb);
++ }
++ iso_start_in(100);
++ break;
++
++ case DEVICE_BUS_INACTIVE:
++ break;
++
++ case DEVICE_BUS_ACTIVITY:
++ break;
++
++ default:
++ break;
++ }
++}
++
++
++/* isotest_recv_urb - called to indicate URB has been received
++ * @urb - pointer to struct usbd_urb
++ *
++ * Return non-zero if we failed and urb is still valid (not disposed)
++ */
++int isotest_recv_urb (struct usbd_urb *urb, int rc)
++{
++ //struct usbd_device_instance *device = urb->device;
++#if 0
++ printk(KERN_INFO"%s: urb: %p length: %d framenum: %04x %d\n", __FUNCTION__,
++ urb, urb->actual_length, urb->framenum, urb->framenum);
++#endif
++ iso_trace_recv_data(&isotest_stats, urb->buffer, urb->actual_length, 0);
++
++ // start_recv urb
++ return (usbd_start_out_urb (urb));
++}
++
++
++/* ********************************************************************************************* */
++
++void iso_start_in(int count)
++{
++ in_count = count;
++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__);
++ schedule_bh();
++}
++
++void iso_start_out(int count)
++{
++ out_count = count;
++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__);
++ schedule_bh();
++}
++
++static void
++bottomhalf(void *data)
++{
++ //printk(KERN_INFO"%s: closing: %d\n", __FUNCTION__, isotest.closing);
++ if (isotest.closing)
++ isotest.iso_bh.data = NULL;
++
++ else if (isotest.open)
++ isotest_xmit_data ();
++
++ MOD_DEC_USE_COUNT;
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++}
++
++
++/* ********************************************************************************************* */
++
++static int isotest_function_enable (struct usbd_function_instance *function)
++{
++ //printk(KERN_INFO"%s:\n", __FUNCTION__);
++ MOD_INC_USE_COUNT;
++
++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__);
++ isotest.closing = 0;
++ schedule_bh();
++ return 0;
++}
++
++static void isotest_function_disable (struct usbd_function_instance *function)
++{
++ isotest.closing = 1;
++ while (isotest.iso_bh.data) {
++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__);
++ schedule_bh();
++ //printk(KERN_INFO"%s: waiting\n", __FUNCTION__);
++ sleep_on_timeout(&isotest.iso_wq, 20);
++ }
++ MOD_DEC_USE_COUNT;
++}
++
++
++struct usbd_function_operations function_ops = {
++ event_handler:isotest_event_handler,
++ function_enable: isotest_function_enable,
++ function_disable: isotest_function_disable,
++};
++
++struct usbd_function_driver function_driver = {
++ name:"ISO Test Function",
++ fops:&function_ops,
++ device_description:&isotest_device_description,
++ bNumConfigurations:sizeof (isotest_description) / sizeof (struct usbd_configuration_description),
++ configuration_description:isotest_description,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_BCDDEVICE),
++ bNumInterfaces:sizeof (isotest_interfaces) / sizeof (struct usbd_interface_description),
++ interface_list:isotest_interfaces,
++ endpointsRequested: ENDPOINTS,
++ requestedEndpoints: iso_endpoint_requests,
++};
++
++
++/*
++ * isotest_modinit - module init
++ *
++ */
++static int isotest_modinit (void)
++{
++ printk (KERN_INFO "vendor_id: %04x product_id: %04x in: %02x out: %02x\n",
++ vendor_id, product_id, in, out);
++
++ if (vendor_id)
++ function_driver.idVendor = cpu_to_le16(vendor_id);
++
++ if (product_id)
++ function_driver.idProduct = cpu_to_le16(product_id);
++
++
++ isotest_ep_1[4] = out&0xff;
++ isotest_ep_1[5] = (out>>8)&0xff;
++ isotest_ep_2[4] = in&0xff;
++ isotest_ep_2[5] = (in>>8)&0xff;
++
++ // XXX should this be the endpoint request structure?
++ iso_endpoint_requests[0].fs_requestedTransferSize = out;
++ iso_endpoint_requests[1].fs_requestedTransferSize = in;
++
++ iso_trace_init("isotest_fd");
++ if(fermat){
++ fermat_init();
++ }
++
++ isotest.iso_bh.routine = bottomhalf;
++ isotest.iso_bh.data = (void *)&isotest;
++ init_waitqueue_head(&isotest.iso_wq);
++
++ // register us with the usb device support layer
++ RETURN_EINVAL_IF (usbd_register_function (&function_driver, "isotest", NULL));
++
++ // return
++ return 0;
++}
++
++
++/* isotest_modexit - module cleanup
++ */
++static void isotest_modexit (void)
++{
++ usbd_deregister_function (&function_driver);
++ iso_trace_exit("isotest_fd");
++}
++
++module_init (isotest_modinit);
++module_exit (isotest_modexit);
++
+diff -uNr linux/drivers/no-otg/functions/isotest/iso_fermat linux/drivers/otg/functions/isotest/iso_fermat
+--- linux/drivers/no-otg/functions/isotest/iso_fermat 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/iso_fermat 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,34 @@
++#!/bin/sh
++XFER=$1
++PATTERN=$2
++if [ -z "$PATTERN" ]
++then
++ PATTERN="0x1,0x8,1,2,3,4,5,6,7,8"
++fi
++echo "Using PATTERN='$PATTERN'"
++set -x
++
++insmod /tmp/usbdcore.o
++insmod /tmp/usbdprocfs.o
++
++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=0 fermat=1 custom=0
++
++insmod /tmp/au1x00_bi.o
++echo "enable" > /proc/usbd-switch
++
++
++set +x
++echo -n "INSERT CABLE"
++sleep 15
++echo -n "; REMOVE CABLE AND PRESS RETURN"
++read junk
++set -x
++cp /proc/isotest_fd client
++cp /proc/isotest_host host
++echo "disable" > /proc/usbd-switch
++
++rmmod au1x00_bi
++rmmod isotest_fd
++rmmod usbdprocfs
++rmmod usbdcore
++
+diff -uNr linux/drivers/no-otg/functions/isotest/iso_one linux/drivers/otg/functions/isotest/iso_one
+--- linux/drivers/no-otg/functions/isotest/iso_one 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/iso_one 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,34 @@
++#!/bin/sh
++XFER=$1
++PATTERN=$2
++if [ -z "$PATTERN" ]
++then
++ PATTERN="0x1,0x1,0xFF"
++fi
++echo "Using PATTERN='$PATTERN'"
++set -x
++
++insmod /tmp/usbdcore.o
++insmod /tmp/usbdprocfs.o
++
++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=1 fermat=0 custom=1
++
++insmod /tmp/au1x00_bi.o
++echo "enable" > /proc/usbd-switch
++
++
++set +x
++echo -n "INSERT CABLE"
++sleep 15
++echo -n "; REMOVE CABLE AND PRESS RETURN"
++read junk
++set -x
++cp /proc/isotest_fd client
++cp /proc/isotest_host host
++echo "disable" > /proc/usbd-switch
++
++rmmod au1x00_bi
++rmmod isotest_fd
++rmmod usbdprocfs
++rmmod usbdcore
++
+diff -uNr linux/drivers/no-otg/functions/isotest/iso_zero linux/drivers/otg/functions/isotest/iso_zero
+--- linux/drivers/no-otg/functions/isotest/iso_zero 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/iso_zero 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,34 @@
++#!/bin/sh
++XFER=$1
++PATTERN=$2
++if [ -z "$PATTERN" ]
++then
++ PATTERN="0x1,0x1,0x00"
++fi
++echo "Using PATTERN='$PATTERN'"
++set -x
++
++insmod /tmp/usbdcore.o
++insmod /tmp/usbdprocfs.o
++
++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=1 fermat=0 custom=1
++
++insmod /tmp/au1x00_bi.o
++echo "enable" > /proc/usbd-switch
++
++
++set +x
++echo -n "INSERT CABLE"
++sleep 15
++echo -n "; REMOVE CABLE AND PRESS RETURN"
++read junk
++set -x
++cp /proc/isotest_fd client
++cp /proc/isotest_host host
++echo "disable" > /proc/usbd-switch
++
++rmmod au1x00_bi
++rmmod isotest_fd
++rmmod usbdprocfs
++rmmod usbdcore
++
+diff -uNr linux/drivers/no-otg/functions/isotest/iso_zero_silent linux/drivers/otg/functions/isotest/iso_zero_silent
+--- linux/drivers/no-otg/functions/isotest/iso_zero_silent 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/iso_zero_silent 2006-09-01 21:41:26.000000000 +0200
+@@ -0,0 +1,34 @@
++#!/bin/sh
++XFER=$1
++PATTERN=$2
++if [ -z "$PATTERN" ]
++then
++ PATTERN="0x1,0x1,0x00"
++fi
++echo "Using PATTERN='$PATTERN'"
++set -x
++
++insmod /tmp/usbdcore.o
++insmod /tmp/usbdprocfs.o
++
++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=0 fermat=0 custom=1
++
++insmod /tmp/au1x00_bi.o
++echo "enable" > /proc/usbd-switch
++
++
++set +x
++echo -n "INSERT CABLE"
++sleep 15
++echo -n "; REMOVE CABLE AND PRESS RETURN"
++read junk
++set -x
++cp /proc/isotest_fd client
++cp /proc/isotest_host host
++echo "disable" > /proc/usbd-switch
++
++rmmod au1x00_bi
++rmmod isotest_fd
++rmmod usbdprocfs
++rmmod usbdcore
++
+diff -uNr linux/drivers/no-otg/functions/isotest/test.c linux/drivers/otg/functions/isotest/test.c
+--- linux/drivers/no-otg/functions/isotest/test.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/test.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,456 @@
++/*
++ * otg/isotest_fd/test.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++//#include <linux/sched.h>
++#include <linux/signal.h>
++#include <linux/errno.h>
++//#include <linux/poll.h>
++#include <linux/init.h>
++
++#include <asm/system.h>
++#include <asm/atomic.h>
++//#include <linux/interrupt.h>
++//#include <linux/pci.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <linux/proc_fs.h>
++#include <linux/vmalloc.h>
++
++#include <asm/io.h>
++#include <asm/string.h>
++
++#include <linux/proc_fs.h>
++
++#include <linux/netdevice.h>
++#include <linux/cache.h>
++
++
++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
++#include <asm/dma.h>
++#include <asm/mach/dma.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/types.h>
++#endif
++
++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++#include <asm/au1000.h>
++#include <asm/au1000_dma.h>
++#include <asm/mipsregs.h>
++#endif
++
++#if defined(CONFIG_ARCH_SAMSUNG)
++#include <asm/arch/timers.h>
++#include <asm/arch/hardware.h>
++#endif
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/pgtable.h>
++#include <asm/pgalloc.h>
++#include "test.h"
++
++
++int iso_trace_first;
++int iso_trace_next;
++iso_trace_t *iso_traces;
++
++
++/* ******************************************************************************************* */
++
++void dump_stats(struct isotest_stats *stats)
++{
++ if (stats->errors || ((stats->ok % 100) == 99)) {
++ TRACE_ISO(stats->iso_transfer_number, stats->total_frames,
++ stats->received, stats->errors, stats->missed, stats->skipped, stats->ok);
++ }
++ if (stats->errors) {
++ memset(stats, 0, sizeof(struct isotest_stats));
++ }
++ else {
++ stats->ok++;
++ stats->iso_transfer_number++;
++ stats->last_packet = 0;
++ }
++}
++
++u16 getu16(u8 **cp)
++{
++ u16 val = 0;
++
++ val = *(*cp)++;
++ val |= *(*cp)++ << 8;
++
++ return val;
++}
++
++u16 getu32(u8 **cp)
++{
++ u32 val = 0;
++
++ val = *(*cp)++;
++ val |= *(*cp)++ << 8;
++ val |= *(*cp)++ << 16;
++ val |= *(*cp)++ << 24;
++
++ return val;
++}
++
++static int iso_in_received;
++
++void iso_trace_recv_data (struct isotest_stats *stats, u8 *cp, int length, int framenum)
++{
++ //struct usb_device_instance *device = urb->device;
++
++ u16 iso_transfer_number;
++ u16 total_size;
++ u16 packet_size;
++ u16 total_frames;
++ u16 this_packet;
++ u8 frames;
++
++
++ iso_transfer_number = getu32(&cp);
++ total_size = getu16(&cp);
++ packet_size = getu16(&cp);
++ total_frames = getu16(&cp);
++ this_packet = getu16(&cp);
++
++
++ // did we miss the end of the last ISO transfer?
++ if (stats->iso_transfer_number && (stats->iso_transfer_number != iso_transfer_number)) {
++ stats->errors++;
++ stats->missed++;
++ //printk(KERN_INFO"%s: ERROR bad iso number: %x %x %x %x %x expecting: %x\n", __FUNCTION__, iso_transfer_number,
++ // total_size, packet_size, total_frames, this_packet, stats->iso_transfer_number);
++ dump_stats(stats);
++ return;
++ }
++
++ if (!stats->iso_transfer_number) {
++ stats->framenum = framenum;
++ stats->iso_transfer_number = iso_transfer_number;
++ stats->total_frames = total_frames;
++ }
++
++ // is this the packet we are expecting?
++ if (stats->last_packet && ((stats->last_packet + 1) != this_packet)) {
++ stats->errors++;
++ stats->missed++;
++ //printk(KERN_INFO"%s: ERROR bad packet: %x %x %x %x %x expecting: %x\n", __FUNCTION__, iso_transfer_number,
++ // total_size, packet_size, total_frames, this_packet, stats->last_packet + 1);
++ }
++
++ // did we miss a frame?
++ frames = (stats->framenum < framenum) ? (framenum - stats->framenum) : (stats->framenum - framenum);
++
++ if (frames > 1) {
++ stats->errors++;
++ stats->skipped++;
++ //printk(KERN_INFO"%s: SKIPPED %x %x %x %x %x\n", __FUNCTION__, iso_transfer_number,
++ // total_size, packet_size, total_frames, this_packet);
++ }
++
++ // update stats
++ stats->received++;
++ stats->last_packet = this_packet;
++
++ // last packet?
++ if (stats->total_frames == this_packet)
++ dump_stats(stats);
++}
++
++
++
++/* Proc Filesystem *************************************************************************** */
++
++/* *
++ * iso_trace_proc_read - implement proc file system read.
++ * @file
++ * @buf
++ * @count
++ * @pos
++ *
++ * Standard proc file system read function.
++ */
++static ssize_t iso_trace_proc_read (struct file *file, char *buf, size_t count, loff_t * pos)
++{
++ unsigned long page;
++ int len = 0;
++ int index;
++ int oindex;
++ int previous;
++
++ MOD_INC_USE_COUNT;
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++ // get a page, max 4095 bytes of data...
++ if (!(page = get_free_page (GFP_KERNEL))) {
++ MOD_DEC_USE_COUNT;
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++ return -ENOMEM;
++ }
++
++ len = 0;
++ oindex = index = (*pos)++;
++
++ if (index == 0) {
++ //len += sprintf ((char *) page + len, " uS Exp Rcv Err Miss Skip OK\n");
++ //len += sprintf ((char *) page + len, " uS Exp Rcv Err Miss Skip Xfers\n");
++ len += sprintf ((char *) page + len, " uS Tr Exp Rcv Err Miss Skip Xfers\n");
++
++// uS Exp Rcv Err Miss Skip Xfers
++// uS Tr Exp Rcv Err Miss Skip Xfers
++// 0 | 100 9 900 0 0 0 99
++
++ }
++
++ index += iso_trace_first;
++ if (index >= TRACE_MAX) {
++ index -= TRACE_MAX;
++ }
++ previous = (index) ? (index - 1) : (TRACE_MAX - 1);
++
++ //printk(KERN_INFO"first: %d next: %d index: %d %d prev: %d\n", iso_trace_first, iso_trace_next, oindex, index, previous);
++
++ if (
++ ((iso_trace_first < iso_trace_next) && (index >= iso_trace_first) && (index < iso_trace_next)) ||
++ ((iso_trace_first > iso_trace_next) && ((index < iso_trace_next) || (index >= iso_trace_first)))
++ )
++ {
++
++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++ u32 ticks = 0;
++#elif defined(CONFIG_ARCH_SAMSUNG)
++ u32 ticks = 0;
++#else
++ u64 jifs = 0;
++#endif
++ iso_trace_t *p = iso_traces + index;
++ //unsigned char *cp;
++ //int skip = 0;
++
++ if (oindex > 0) {
++ iso_trace_t *o = iso_traces + previous;
++
++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
++ /*
++ * oscr is 3.6864 Mhz free running counter,
++ *
++ * 1/3.6864 = .2712
++ * 60/221 = .2714
++ *
++ */
++ if (o->ocsr) {
++ ticks = (p->ocsr > o->ocsr) ? (p->ocsr - o->ocsr) : (o->ocsr - p->ocsr) ;
++ ticks = (ticks * 60) / 221;
++ }
++
++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++ /*
++ * cp0_count is incrementing timer at system clock
++ */
++ if (o->cp0_count) {
++ //ticks = (p->cp0_count > o->cp0_count) ?
++ // (p->cp0_count - o->cp0_count) : (o->cp0_count - p->cp0_count) ;
++ ticks = (p->cp0_count - o->cp0_count) ;
++ ticks = ticks / CONFIG_OTG_AU1X00_SCLOCK;
++ }
++
++#elif defined(CONFIG_ARCH_SAMSUNG)
++ /*
++ * tcnt1 is a count-down timer running at the system bus clock
++ * The divisor must be set as a configuration value, typically 66 or 133.
++ */
++ if (o->tcnt1) {
++ ticks = (p->tcnt1 < o->tcnt1) ? (o->tcnt1 - p->tcnt1) : (p->tcnt1 - o->tcnt1) ;
++ ticks /= CONFIG_OTG_SMDK2500_BCLOCK;
++ }
++#else
++ if (o->jiffies) {
++ jifs = p->jiffies - iso_traces[previous].jiffies;
++ }
++#endif
++
++ //if (o->interrupts != p->interrupts) {
++ // skip++;
++ //}
++ }
++
++
++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++ len += sprintf ((char *) page + len, "%9d ", ticks);
++#elif defined(CONFIG_ARCH_SAMSUNG)
++ //len += sprintf ((char *) page + len, "%8u ", p->jiffies);
++ //len += sprintf ((char *) page + len, "%8u ", p->tcnt0);
++ len += sprintf ((char *) page + len, "%8u ", p->tcnt1);
++ if (ticks > 1024*1024) {
++ len += sprintf ((char *) page + len, "%8dM ", ticks>>20);
++ }
++ else {
++ len += sprintf ((char *) page + len, "%8d ", ticks);
++ }
++#else
++ if (jifs > 1024) {
++ len += sprintf ((char *) page + len, "%4dK", (int)jifs>>20);
++ }
++ else {
++ len += sprintf ((char *) page + len, "%4d ", (int)jifs);
++ }
++#endif
++
++ len += sprintf ((char *) page + len, "| %8d", p->iso_transfer_number);
++ len += sprintf ((char *) page + len, "%6d", p->expected);
++ len += sprintf ((char *) page + len, "%6d", p->received);
++ len += sprintf ((char *) page + len, "%6d", p->errors);
++ len += sprintf ((char *) page + len, "%6d", p->missed);
++ len += sprintf ((char *) page + len, "%6d", p->skipped);
++ len += sprintf ((char *) page + len, "%6d", p->ok);
++ len += sprintf ((char *) page + len, "\n");
++ }
++
++#if 1
++ if ((len > count) ) {
++ printk(KERN_INFO"%s: ((len > count) count=%d len=%d\n", __FUNCTION__, count, len);
++ len = -EINVAL;
++ }
++
++#else
++ if ((len > count) || (len == 0)) {
++ printk(KERN_INFO"%s: ((len > count) || (len == 0)) count=%d len=%d\n", __FUNCTION__, count, len);
++ len = -EINVAL;
++ }
++#endif
++ else if (len > 0 && copy_to_user (buf, (char *) page, len)) {
++ printk(KERN_INFO"%s: (len > 0 && copy_to_user (buf, (char *) page, len) \n", __FUNCTION__);
++ len = -EFAULT;
++ }
++ free_page (page);
++ MOD_DEC_USE_COUNT;
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++ return len;
++}
++
++void iso_start_in(int count);
++void iso_start_out(int count);
++
++/* *
++ * iso_trace_proc_write - implement proc file system write.
++ * @file
++ * @buf
++ * @count
++ * @pos
++ *
++ * Proc file system write function, used to signal monitor actions complete.
++ * (Hotplug script (or whatever) writes to the file to signal the completion
++ * of the script.) An ugly hack.
++ */
++static ssize_t iso_trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos)
++{
++ //struct usb_device_instance *device;
++ size_t n = count;
++ char command[64];
++ char *cp = command;
++ int i = 0;
++
++ MOD_INC_USE_COUNT;
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++ //printk(KERN_DEBUG "%s: count=%u\n",__FUNCTION__,count);
++ while ((n > 0) && (i < 64)) {
++ // Not too efficient, but it shouldn't matter
++ if (copy_from_user (cp++, buf + (count - n), 1)) {
++ count = -EFAULT;
++ break;
++ }
++ *cp = '\0';
++ i++;
++ n -= 1;
++ //printk(KERN_DEBUG "%s: %u/%u %02x\n",__FUNCTION__,count-n,count,c);
++ }
++ if (!strncmp (command, "in", 2)) {
++ iso_start_in (10);
++ }
++ else if (!strncmp (command, "out", 3)) {
++ iso_start_out (10);
++ }
++ // XXX need to be able to set serial number here
++ MOD_DEC_USE_COUNT;
++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE));
++ return (count);
++}
++
++static struct file_operations iso_trace_proc_operations_functions = {
++read:iso_trace_proc_read,
++write:iso_trace_proc_write,
++};
++
++#if defined(CONFIG_ARCH_SAMSUNG)
++#endif
++
++/**
++ * iso_trace_init
++ *
++ * Return non-zero if not successful.
++ */
++int iso_trace_init (char *name)
++{
++ printk(KERN_INFO"%s:\n", __FUNCTION__);
++ if (!(iso_traces = vmalloc(sizeof(iso_trace_t) * TRACE_MAX))) {
++ printk(KERN_ERR"%s: malloc failed %p %d\n", __FUNCTION__, iso_traces, sizeof(iso_trace_t) * TRACE_MAX);
++ return -EINVAL;
++ }
++ memset(iso_traces, 0, sizeof(iso_trace_t) * TRACE_MAX);
++
++ {
++ struct proc_dir_entry *p;
++
++ // create proc filesystem entries
++ if ((p = create_proc_entry (name, 0, 0)) == NULL) {
++ printk(KERN_INFO"BITRACE PROC FS failed\n");
++ }
++ else {
++ p->proc_fops = &iso_trace_proc_operations_functions;
++ }
++ }
++#if defined(CONFIG_ARCH_SAMSUNG)
++ *(volatile u32 *)TMOD |= 0x3 << 3;
++#endif
++ printk(KERN_INFO"%s: OK\n", __FUNCTION__);
++ return 0;
++}
++
++/**
++ * udc_release_io - release UDC io region
++ */
++void iso_trace_exit (char *name)
++{
++ {
++ unsigned long flags;
++ local_irq_save (flags);
++ remove_proc_entry (name, NULL);
++ if (iso_traces) {
++ iso_trace_t *p = iso_traces;
++ iso_traces = 0;
++ vfree(p);
++ }
++ local_irq_restore (flags);
++ }
++}
++
++
++/* End of FILE */
++
+diff -uNr linux/drivers/no-otg/functions/isotest/test.h linux/drivers/otg/functions/isotest/test.h
+--- linux/drivers/no-otg/functions/isotest/test.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/isotest/test.h 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,129 @@
++/*
++ * otg/isotest_fd/test.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ *
++ */
++
++#if defined(CONFIG_ARCH_SAMSUNG)
++#ifndef CONFIG_OTG_SMDK2500_BCLOCK
++#define CONFIG_OTG_SMDK2500_BCLOCK 66
++#endif
++#endif
++
++#define NUSBD_FRAMENUM 0xB0200038
++
++
++struct isotest_stats {
++
++ u16 count;
++
++ u16 iso_transfer_number;
++ u16 total_size;
++ u16 packet_size;
++ u16 total_frames;
++ u16 last_packet;
++
++
++ u16 framenum;
++ u16 received;
++ u16 missed;
++ u16 skipped;
++ u16 errors;
++
++ u32 ok;
++
++};
++
++
++
++
++
++typedef struct iso_trace_types {
++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
++ u32 ocsr;
++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++ u32 cp0_count;
++#elif defined(CONFIG_ARCH_SAMSUNG)
++ u32 tcnt1;
++#else
++ u64 jiffies;
++#endif
++ u16 iso_transfer_number;
++
++ u16 expected;
++ u16 received;
++ u16 errors;
++ u16 missed;
++ u16 skipped;
++
++ u32 ok;
++
++} iso_trace_t;
++
++
++#define TRACE_MAX 3000
++
++extern int iso_trace_first;
++extern int iso_trace_next;
++
++extern iso_trace_t *iso_traces;
++
++static __inline__ iso_trace_t *ISO_TRACE_NEXT(void)
++{
++ iso_trace_t *p;
++
++ p = iso_traces + iso_trace_next;
++
++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
++ p->ocsr = OSCR;
++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++ p->cp0_count = read_c0_count();
++#elif defined(CONFIG_ARCH_SAMSUNG)
++ p->tcnt1 = *(volatile u32 *)TCNT1;
++#else
++ p->jiffies = jiffies;
++#endif
++//#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
++// p->iso_transfer_number = au_readl(NUSBD_FRAMENUM);
++//#endif
++
++ iso_trace_next++;
++ iso_trace_next = (iso_trace_next == TRACE_MAX) ? 0 : iso_trace_next;
++
++ if (iso_trace_next == iso_trace_first) {
++ iso_trace_first++;
++ iso_trace_first = (iso_trace_first == TRACE_MAX) ? 0 : iso_trace_first;
++ }
++
++ return p;
++}
++
++static __inline__ void TRACE_ISO(u16 iso_transfer_number, u16 expected, u16 received, u16 errors, u16 missed, u16 skipped, u32 ok)
++{
++ iso_trace_t *p = NULL;
++
++ //printk(KERN_INFO"%s: %d %d %d %d %d first: %d next: %d traces: %p\n", __FUNCTION__,
++ // expected, received, errors, missed, skipped, iso_trace_first, iso_trace_next, traces);
++
++ if (!iso_traces) return;
++
++ p = ISO_TRACE_NEXT();
++ p->iso_transfer_number = iso_transfer_number;
++ p->expected = expected;
++ p->received = received;
++ p->errors = errors;
++ p->missed = missed;
++ p->skipped = skipped;
++ p->ok = ok;
++}
++
++void iso_trace_recv_data (struct isotest_stats *stats, u8 *cp, int length, int framenum);
++int iso_trace_init (char *str);
++void iso_trace_exit (char *str);
++
+diff -uNr linux/drivers/no-otg/functions/mouse/Config.in linux/drivers/otg/functions/mouse/Config.in
+--- linux/drivers/no-otg/functions/mouse/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/Config.in 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,28 @@
++#
++# Mouse Function Driver
++#
++# Copyright (C) 2003 Belcarra
++#
++
++
++mainmenu_option next_comment
++
++comment "USB Peripheral Function Driver - Random Mouse"
++dep_tristate ' Mouse Function' CONFIG_OTG_MOUSE $CONFIG_OTG
++
++if [ "$CONFIG_OTG_MOUSE" != "n" ]; then
++ hex 'VendorID (hex value)' CONFIG_OTG_MOUSE_VENDORID "15ec"
++ hex 'ProductID (hex value)' CONFIG_OTG_MOUSE_PRODUCTID "f003"
++ hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_MOUSE_BCDDEVICE "0100"
++
++ string 'iManufacturer (string)' CONFIG_OTG_MOUSE_MANUFACTURER "Belcarra"
++ #string 'iProduct (string)' CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse"
++
++ string 'iConfiguration (string)' CONFIG_OTG_MOUSE_DESC "Mouse Cfg"
++ string 'Comm Interface iInterface (string)' CONFIG_OTG_MOUSE_COMM_INTF "Comm Intf"
++ bool 'Mouse BH Test' CONFIG_OTG_MOUSE_BH
++ int 'Number of packets (zero is continous ' CONFIG_OTG_MOUSE_PACKETS "10"
++ int 'Polling Interval ' CONFIG_OTG_MOUSE_INTERVAL "1"
++
++fi
++endmenu
+diff -uNr linux/drivers/no-otg/functions/mouse/Kconfig linux/drivers/otg/functions/mouse/Kconfig
+--- linux/drivers/no-otg/functions/mouse/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/Kconfig 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,74 @@
++menu "OTG Random Mouse function"
++ depends on OTG
++
++config OTG_MOUSE
++ tristate " Random Mouse Function"
++ depends on OTG
++ ---help---
++ This implements a simple Mouse driver that sends HID packets with
++ small random movements. This is a good function for initial testing
++ of Peripheral Controller Drivers as it provides for a complicated
++ enumeration but a simple uni-directional (send Interrupt only) data
++ transport.
++
++menu "OTG Random Mouse function options"
++ depends on OTG && OTG_MOUSE
++
++config OTG_MOUSE_VENDORID
++ hex "VendorID (hex value)"
++ depends on OTG && OTG_MOUSE
++ default "0x15ec"
++
++config OTG_MOUSE_PRODUCTID
++ hex "ProductID (hex value)"
++ depends on OTG && OTG_MOUSE
++ default "0xf003"
++
++config OTG_MOUSE_BCDDEVICE
++ hex "bcdDevice (binary-coded decimal)"
++ depends on OTG && OTG_MOUSE
++ default "0x0100"
++
++config OTG_MOUSE_MANUFACTURER
++ string "iManufacturer (string)"
++ depends on OTG && OTG_MOUSE
++ default "Belcarra"
++
++config OTG_MOUSE_PRODUCT_NAME
++ string "iProduct (string)"
++ depends on OTG && OTG_MOUSE
++ default "Random Mouse Class - Interrupt"
++
++config OTG_MOUSE_COMM_INTF
++ string "MOUSE Bulk Only iInterface (string)"
++ depends on OTG && OTG_MOUSE
++ default "MOUSE Data Intf"
++
++config OTG_MOUSE_DESC
++ string "Data Interface iConfiguration (string)"
++ depends on OTG && OTG_MOUSE
++ default "MOUSE Configuration"
++
++config OTG_MOUSE_BH
++ bool " MOUSE BH Test"
++ depends on OTG && OTG_MOUSE
++ default n
++ ---help---
++ Implement the Mouse send packet in a bottom half handler,
++ this will delay responses to test the PCD works correctly
++ when the host polls before a send data urb is queued.
++
++
++config OTG_MOUSE_PACKETS
++ int "Number of packets (zero is continous)"
++ depends on OTG && OTG_MOUSE
++ default 10
++ ---help---
++ Number of Mouse packets to send, will run
++ forever if set to zero.
++
++
++endmenu
++
++
++endmenu
+diff -uNr linux/drivers/no-otg/functions/mouse/Makefile linux/drivers/otg/functions/mouse/Makefile
+--- linux/drivers/no-otg/functions/mouse/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/Makefile 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,81 @@
++#
++# Function driver for a Mouse USB Device
++#
++# Copyright (c) 2003 Belcarra
++
++# Multipart objects.
++
++O_TARGET := mouse_target.o
++#list-multi := mouse_fd.o cmouse_fd.o
++list-multi := mouse_fd.o
++
++mouse_fd-objs := mouse-fd.o mouse-l24.o
++#cmouse_fd-objs := mouse-cf.o mouse-if.o cmouse-l24.o
++#cmouse_fd-objs := mouse-if.o cmouse-l24.o
++
++# Objects that export symbols.
++#export-objs := mouse.o
++
++# Object file lists.
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj- :=
++
++# Each configuration option enables a list of files.
++
++obj-$(CONFIG_OTG_MOUSE) += mouse_fd.o
++#obj-$(CONFIG_OTG_MOUSE) += cmouse_fd.o
++
++# Extract lists of the multi-part drivers.
++# The 'int-*' lists are the intermediate files used to build the multi's.
++
++multi-y := $(filter $(list-multi), $(obj-y))
++multi-m := $(filter $(list-multi), $(obj-m))
++int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
++int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
++
++# Files that are both resident and modular: remove from modular.
++
++obj-m := $(filter-out $(obj-y), $(obj-m))
++int-m := $(filter-out $(int-y), $(int-m))
++
++# Translate to Rules.make lists.
++
++O_OBJS := $(filter-out $(export-objs), $(obj-y))
++OX_OBJS := $(filter $(export-objs), $(obj-y))
++M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
++MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
++MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
++MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
++
++# The global Rules.make.
++
++#MOUSED=$(OTG)/functions/mouse
++
++OTG_WARNINGS=-Wno-unused -Wno-format
++OTG=$(TOPDIR)/drivers/otg
++OTG_INCLUDES=-I$(OTG)
++OTG_EXTRA=$(OTG_INCLUDES) $(OTG_WARNINGS)
++OTGCORE_DIR=$(OTG)/otgcore
++##USBDCORE_DIR=$(OTG)/usbdcore
++include $(TOPDIR)/Rules.make
++#EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR)
++#EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR)
++EXTRA_CFLAGS += $(OTG_EXTRA)
++EXTRA_CFLAGS_nostdinc += $(OTG_EXTRA)
++
++# Link rules for multi-part drivers.
++
++mouse_fd.o: $(mouse_fd-objs)
++ $(LD) -r -o $@ $(mouse_fd-objs)
++
++#cmouse_fd.o: $(cmouse_fd-objs)
++# $(LD) -r -o $@ $(cmouse_fd-objs)
++
++# dependencies:
++
++#mouse.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h
++
++
+diff -uNr linux/drivers/no-otg/functions/mouse/Makefile-l26 linux/drivers/otg/functions/mouse/Makefile-l26
+--- linux/drivers/no-otg/functions/mouse/Makefile-l26 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/Makefile-l26 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,14 @@
++# Function driver for a Random Mouse
++#
++# Copyright (c) 2004 Belcarra
++
++mouse_fd-objs := mouse-fd.o mouse-l24.o
++
++obj-$(CONFIG_OTG_MOUSE) += mouse_fd.o
++
++OTG=$(TOPDIR)/drivers/otg
++ACMD=$(OTG)/functions/mouse
++OTGCORE_DIR=$(OTG)/otgcore
++USBDCORE_DIR=$(OTG)/usbdcore
++EXTRA_CFLAGS += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR)
++EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR)
+diff -uNr linux/drivers/no-otg/functions/mouse/cmouse-l24.c linux/drivers/otg/functions/mouse/cmouse-l24.c
+--- linux/drivers/no-otg/functions/mouse/cmouse-l24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/cmouse-l24.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,199 @@
++/*
++ * otg/functions/mouse/cmouse-l24.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/mouse/cmouse-l24.c
++ * @brief Mouse Function Driver Linux 2.4 initialization
++ *
++ *
++ * This file contains the Linux 2.4 module init and exit functions
++ * to load and unload the Random Mouse Function.
++ *
++ * Notes
++ *
++ * This driver does not export any upper edge functionality to
++ * for the user or applications to use. It simply sends a configurable
++ * number of data packets to the USB Host when configured. Each data
++ * packet contains the equivalent of a small mouse report which will
++ * cause the mouse cursor to move on the host.
++ *
++ * To use simply load with something like:
++ *
++ * insmod cmouse_fd.o vendor_id=0xffff product_id=0xffff
++ *
++ * And attach to a Windows box. Windows should recognize as a mouse and
++ * immediately start receiving a stream of data.
++ *
++ * To terminate simply unplug.
++ *
++ *
++ * 1. The mouse driver is product name is hard coded to a string that will
++ * generate a string descriptor that is 32 bytes long. This will test
++ * most UDC's ep0 ZLP handling as it is a multiple of the most common
++ * UDC endpoint zero size. (An option can be added later to allow for
++ * 64 byte ep0 packetsize.)
++ *
++ * 2. The CONFIG_OTG_MOUSE_BH option can be enabled to delay the HID report
++ * to being generated by a bottom half handler. This will verify that
++ * the bus interface driver properly handles the case of a delayed
++ * CONTROL READ. I.e. when the usbd_device_request() function returns
++ * zero for successful completion but there is no tx_urb containing the
++ * requested data. The bus interface driver must setup the conditions for
++ * ACK'ing the SETUP packet but then NAK the IN data for endpoint zero
++ * until the tx_urb is started later.
++ *
++ * 3. The CONFIG_OTG_MOUSE_PACKETS option is used to set the number of
++ * mouse data reports to send. Set to zero for a continous stream
++ * of data.
++ *
++ * @ingroup MouseFunction
++ */
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++
++#include "cmouse.h"
++
++
++/* Module Parameters ******************************************************** */
++/* !
++ * @name XXXXX MODULE Parameters
++ */
++/* ! @{ */
++
++MOD_AUTHOR ("sl@belcarra.com");
++EMBED_LICENSE();
++MOD_DESCRIPTION ("Belcarra Random Walk MOUSE Function");
++
++static u32 vendor_id; /*!< override built-in vendor ID */
++static u32 product_id; /*!< override built-in product ID */
++
++MOD_PARM (vendor_id, "i");
++MOD_PARM (product_id, "i");
++
++MOD_PARM_DESC (vendor_id, "Device Vendor ID");
++MOD_PARM_DESC (product_id, "Device Product ID");
++
++otg_tag_t MOUSE;
++/* ! *} */
++
++/* USB Module init/exit ***************************************************** */
++
++struct mouse_private mouse_private;
++
++char *mouse_interface_list[2] = {
++ "mouse-random-interface",
++ NULL,
++};
++
++/*!
++ * mouse_modinit() - module init
++ *
++ * This is called by the Linux kernel; either when the module is loaded
++ * if compiled as a module, or during the system intialization if the
++ * driver is linked into the kernel.
++ *
++ * This function will parse module parameters if required and then register
++ * the mouse driver with the USB Device software.
++ *
++ * If the CONFIG_OTG_MOUSE_BH option is enabled it will also setup the mouse
++ * bottom half handler.
++ *
++ */
++static int mouse_modinit (void)
++{
++ struct usbd_function_instance *mfi;
++ printk (KERN_INFO "%s: vendor_id: %04x product_id: %04x\n", __FUNCTION__, vendor_id, product_id);
++
++ #if !defined(OTG_C99)
++ mouse_global_init();
++ mouse_ops_init();
++ #endif /* defined(OTG_C99) */
++
++ MOUSE = otg_trace_obtain_tag();
++ TRACE_MSG2(MOUSE, "vendor_id: %04x product_id: %04x",vendor_id, product_id);
++
++ if (vendor_id)
++ mouse_composite_driver.idVendor = cpu_to_le16(vendor_id);
++ if (product_id)
++ mouse_composite_driver.idProduct = cpu_to_le16(product_id);
++
++ mouse_hid.wItemLength = cpu_to_le16(0x34); // XXX mips compiler bug.....
++
++ // register as usb function driver
++ TRACE_MSG2(MOUSE, "%s %d", mouse_interface_driver.name, mouse_interface_driver.endpointsRequested);
++ THROW_UNLESS ((mouse_private.interface =
++ usbd_register_interface (&mouse_interface_driver, "mouse-random-interface", NULL)), error);
++
++ TRACE_MSG2(MOUSE, "%s %d", mouse_interface_driver.name, mouse_interface_driver.endpointsRequested);
++
++ THROW_UNLESS ((mouse_private.composite =
++ usbd_register_composite (&mouse_composite_driver, "mouse-random-cf",
++ mouse_interface_list, NULL)), error);
++
++ #ifdef CONFIG_OTG_MOUSE_BH
++ mouse_private.notification_bh.routine = mouse_hid_bh;
++ mouse_private.notification_bh.data = NULL;
++ #endif
++ CATCH(error) {
++ if (mouse_private.interface) {
++ usbd_deregister_function (mouse_private.interface);
++ mouse_private.interface = NULL;
++ }
++ if (mouse_private.composite) {
++ usbd_deregister_function (mouse_private.composite);
++ mouse_private.composite = NULL;
++ }
++ otg_trace_invalidate_tag(MOUSE);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++module_init (mouse_modinit);
++
++#if OTG_EPILOGUE
++/*!
++ * mouse_modexit() - module init
++ *
++ * This is called by the Linux kernel; when the module is being unloaded
++ * if compiled as a module. This function is never called if the
++ * driver is linked into the kernel.
++ *
++ * @param void
++ * @return void
++ */
++static void mouse_modexit (void)
++{
++ #ifdef CONFIG_OTG_MOUSE_BH
++ while (PENDING_WORK_ITEM(mouse_private.notification_bh)) {
++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__);
++ schedule_timeout(10 * HZ);
++ }
++ #endif
++ if (mouse_private.interface) {
++ usbd_deregister_function (mouse_private.interface);
++ mouse_private.interface = NULL;
++ }
++ if (mouse_private.composite) {
++ usbd_deregister_function (mouse_private.composite);
++ mouse_private.composite = NULL;
++ }
++
++ otg_trace_invalidate_tag(MOUSE);
++}
++
++module_exit (mouse_modexit);
++#endif
++
+diff -uNr linux/drivers/no-otg/functions/mouse/cmouse.h linux/drivers/otg/functions/mouse/cmouse.h
+--- linux/drivers/no-otg/functions/mouse/cmouse.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/cmouse.h 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ * otg/functions/mouse/mouse.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++
++/*!
++ * @defgroup MouseFunction Random Mouse
++ * @ingroup functiongroup
++ */
++/*!
++ * @file otg/functions/mouse/mouse.h
++ * @brief Mouse Function Driver private defines
++ *
++ *
++ * This is a test USB HID Function Driver designed to test and
++ * verify that INTERRUPT IN endpoints work properly.
++ *
++ *
++ * @ingroup MouseFunction
++ */
++
++
++/*!
++ * The mouse_private structure is used to collect all of the mouse driver
++ * global variables into one place.
++ */
++struct mouse_private {
++ struct usbd_function_instance *interface; /*!< function instance for this module */
++ struct usbd_function_instance *composite; /*!< function instance for this module */
++
++#ifdef CONFIG_OTG_MOUSE_BH
++ struct WORK_STRUCT notification_bh;
++#endif /* CONFIG_OTG_MOUSE_BH */
++ int usb_driver_registered; /*!< non-zero if usb function registered */
++ unsigned char connected; /*!< non-zero if connected to host (configured) */
++ unsigned int writesize; /*!< packetsize * 4 */
++ struct usbd_urb *tx_urb; /*!< saved copy of current tx urb */
++ int wLength;
++ int x;
++ int y;
++ int last_x;
++ int last_y;
++ int n;
++};
++
++extern struct mouse_private mouse_private;
++extern struct usbd_function_driver mouse_interface_driver;
++extern struct usbd_function_driver mouse_composite_driver;
++extern struct hid_descriptor mouse_hid;
++
++#ifndef OTG_C99
++extern void mouse_global_init(void);
++#endif /* OTG_C99 */
++
++#define MOUSE mouse_trace_tag
++extern otg_tag_t MOUSE;
++
+diff -uNr linux/drivers/no-otg/functions/mouse/getmouse.c linux/drivers/otg/functions/mouse/getmouse.c
+--- linux/drivers/no-otg/functions/mouse/getmouse.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/getmouse.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,107 @@
++/*
++ *
++ *
++ * 3.2. Non-Canonical Input Processing
++ *
++ * In non-canonical input processing mode, input is not assembled into lines and
++ * input processing (erase, kill, delete, etc.) does not occur. Two parameters
++ * control the behavior of this mode: c_cc[VTIME] sets the character timer, and
++ * c_cc[VMIN] sets the minimum number of characters to receive before satisfying
++ * the read.
++ *
++ * If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before
++ * the read is satisfied. As TIME is zero, the timer is not used.
++ *
++ * If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be
++ * satisfied if a single character is read, or TIME is exceeded (t = TIME *0.1
++ * s). If TIME is exceeded, no character will be returned.
++ *
++ * If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read
++ * will be satisfied if MIN characters are received, or the time between two
++ * characters exceeds TIME. The timer is restarted every time a character is
++ * received and only becomes active after the first character has been received.
++ *
++ * If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of
++ * characters currently available, or the number of characters requested will be
++ * returned. According to Antonino (see contributions), you could issue a fcntl
++ * (fd, F_SETFL, FNDELAY); before reading to get the same result.
++ *
++ * By modifying newtio.c_cc[VTIME] and newtio.c_cc[VMIN] all modes described
++ * above can be tested.
++ *
++ */
++
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <termios.h>
++#include <stdio.h>
++
++#define BAUDRATE B1200
++#define MODEMDEVICE "/dev/ttyS0"
++#define _POSIX_SOURCE 1 /* POSIX compliant source */
++#define FALSE 0
++#define TRUE 1
++
++volatile int STOP=FALSE;
++
++main()
++{
++ int fd,c, res;
++ struct termios oldtio,newtio;
++ unsigned char buf[255];
++
++ int bytes;
++ unsigned char mouse[3];
++
++ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
++ if (fd <0) {perror(MODEMDEVICE); exit(-1); }
++
++ tcgetattr(fd,&oldtio); /* save current port settings */
++
++ bzero(&newtio, sizeof(newtio));
++ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
++ newtio.c_iflag = IGNPAR;
++ newtio.c_oflag = 0;
++
++ /* set input mode (non-canonical, no echo,...) */
++ newtio.c_lflag = 0;
++
++ newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
++ newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */
++
++ tcflush(fd, TCIFLUSH);
++ tcsetattr(fd,TCSANOW,&newtio);
++
++
++ bytes = 0;
++ while (STOP==FALSE) { /* loop for input */
++ unsigned char c;
++
++ res = read(fd,buf,1); /* returns after 5 chars have been input */
++ buf[res]=0; /* so we can printf... */
++ //fprintf(stderr, ":%02x:%d\n", buf[0], res);
++ //if (buf[0]=='z') STOP=TRUE;
++
++ c = buf[0];
++
++ if ( c & 0x40 ) {
++ bytes = 1;
++ mouse[0] = c;
++ }
++ else if (bytes == 1) {
++ bytes = 2;
++ mouse[1] = c;
++ }
++ else if (bytes == 2) {
++ bytes = 0;
++ mouse[2] = c;
++ fprintf(stderr, "%02x %02x %02x\n", mouse[0], mouse[1], mouse[2]);
++ //printf("%c%c%c", mouse[0], mouse[1], mouse[2]);
++ }
++ }
++ tcsetattr(fd,TCSANOW,&oldtio);
++}
++
++
++/* End of FILE */
+diff -uNr linux/drivers/no-otg/functions/mouse/mouse-ce42.c linux/drivers/otg/functions/mouse/mouse-ce42.c
+--- linux/drivers/no-otg/functions/mouse/mouse-ce42.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/mouse-ce42.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,89 @@
++/*
++ * otg/functions/mouse/mouse-ce42.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Chris Lynne <sl@belcarra.com>,
++ * Stuart Lynne <sl@belcarra.com>,
++ *
++ * This is a test USB HID Function Driver designed to test and
++ * verify that INTERRUPT IN endpoints work properly.
++ *
++ * This emulates a simple USB mouse and generates a constant stream
++ * of small random mouse movements.
++ *
++ * To use simply load with something like:
++ *
++ * insmod mouse_fd.o vendor_id=0xffff product_id=0xffff
++ *
++ * And attach to a Windows box. Windows should recognize as a mouse and
++ * immediately start receiving a stream of data.
++ *
++ * To terminate simply unplug.
++ *
++ * The mouse driver has several other characteristics to allow testing of
++ * other features of the bus interface driver:
++ *
++ * - ep0 ZLP handling
++ *
++ * - ep0 delayed CONTROL READ
++ *
++ *
++ * Notes
++ *
++ * 1. The mouse driver is product name is hard coded to a string that will
++ * generate a string descriptor that is 32 bytes long. This will test
++ * most UDC's ep0 ZLP handling as it is a multiple of the most common
++ * UDC endpoint zero size. (An option can be added later to allow for
++ * 64 byte ep0 packetsize.)
++ *
++ * 2. The CONFIG_OTG_MOUSE_BH option can be enabled to delay the HID report
++ * to being generated by a bottom half handler. This will verify that
++ * the bus interface driver properly handles the case of a delayed
++ * CONTROL READ. I.e. when the usbd_device_requesdevice_requesdevice_request
++ * zero for successful completion but there is no tx_urb containing the
++ * requested data. The bus interface driver must setup the conditions for
++ * ACK'ing the SETUP packet but then NAK the IN data for endpoint zero
++ * until the tx_urb is started later.
++ */
++
++
++
++#include "otg/usbp-chap9.h"
++#include <otg/usbp-mem.h>
++#include <otg/usbp-func.h>
++
++/*
++ * mouse_modinit - module init
++ *
++ */
++static int mouse_modinit (void)
++{
++
++ // register as usb function driver
++ THROW_IF (NULL == (mouse_private.function = usbd_register_function (&mouse_function_driver, "mouse", NULL)), error);
++#ifdef CONFIG_OTG_MOUSE_BH
++ mouse_private.notification_bh.routine = mouse_hid_bh;
++ mouse_private.notification_bh.data = NULL;
++#endif
++ CATCH(error) {
++ if (mouse_private.function) {
++ usbd_deregister_function (mouse_private.function);
++ mouse_private.function = NULL;
++ }
++ return -EINVAL;
++ }
++ return 0;
++}
++
++/* mouse_modexit - module cleanup
++ */
++static void mouse_modexit (void)
++{
++ if (mouse_private.function) {
++ usbd_deregister_function (mouse_private.function);
++ mouse_private.function = NULL;
++ }
++}
++
+diff -uNr linux/drivers/no-otg/functions/mouse/mouse-fd.c linux/drivers/otg/functions/mouse/mouse-fd.c
+--- linux/drivers/no-otg/functions/mouse/mouse-fd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/mouse-fd.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,826 @@
++/*
++ * otg/functions/mouse/mouse-fd.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/mouse/mouse-fd.c
++ * @brief Mouse Function Driver protocol implementation.
++ *
++ * This file implements the Mouse HID protocols and will generate
++ * random mouse data on the INTERRUPT endpoint.
++ *
++ * The primary purpose of this driver is to demonstrate how to
++ * implement a simple function driver and to provide a simple
++ * uni-directional driver for testing USB Device peripheral drivers.
++ *
++ * The mouse driver has several other characteristics to allow testing of
++ * other features of USB Device peripheral drivers:
++ *
++ * - ep0 ZLP handling
++ *
++ * - ep0 delayed CONTROL READ
++ *
++ * To verify that ep0 ZLP processing is being handling correctly this
++ * driver has a hard coded Product name that is returned as a 32 byte
++ * string. This forces most any implementations that have an endpoint
++ * zero packetsize of 8, 16 or 32 to send a Zero Length Packet to
++ * terminate the string transfer when the Product name is requested
++ * by the host.
++ *
++ * The delayed CONTROL READ test verifies that that USB Device peripheral
++ * drivers will correctly NAK until data is provide for device requests.
++ * This is done by (optionally) delaying the HID report via a bottom half
++ * handler. The device request returns normally and the peripheral driver
++ * must properly recognize that while the device request had a non-zero
++ * wLength field, there is currently no queued urb.
++ *
++ * When the bottom half handler is scheduled the HID report urb will be
++ * queued on endpoint zero and then returned to the host.
++ *
++ *
++ * @ingroup MouseFunction
++ */
++
++#include <linux/config.h>
++#include <otg/otg-compat.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++#include <otg/otg-api.h>
++
++#include "mouse.h"
++
++struct mouse_private mouse_private;
++
++/*
++ * ep0 testing.... ensure that this is exactly 16 bytes
++ */
++#undef CONFIG_OTG_MOUSE_PRODUCT_NAME
++#define CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse"
++
++/*!
++ * @name Mouse Descriptors
++ *
++ * MouseHIDReport
++ * MOUSE Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++/*!
++ * Mouse Descriptors
++ * @{
++ */
++
++#define BULK_INT 0x00 /*!< Interrupt endpoint number */
++#define ENDPOINTS 0x01 /*!< Number of endpoints required */
++
++/*! List of required endpoint numbers
++ */
++static u8 mouse_indexes[] = { BULK_INT, };
++
++/*! List of requested endpoints
++ */
++static struct usbd_endpoint_request mouse_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
++ { 0, },
++};
++
++/*! This is the HID report returned for the HID Device Request.
++ * There is no pre-defined descriptor type for this.
++ */
++static char MouseHIDReport[52] = {
++ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */
++ 0x09, 0x02, /*!< USAGE (Mouse) */
++ 0xa1, 0x01, /*!< COLLECTION (Application) */
++ 0x09, 0x01, /*!< USAGE (Pointer) */
++ 0xa1, 0x00, /*!< COLLECTION (Physical) */
++ 0x05, 0x09, /*!< USAGE_PAGE (Button) */
++ 0x19, 0x01, /*!< USAGE_MINIMUM (Button 1) */
++ 0x29, 0x03, /*!< USAGE_MAXIMUM (Button 3) */
++ 0x15, 0x00, /*!< LOGICAL_MINIMUM (0) */
++ 0x25, 0x01, /*!< LOGICAL_MAXIMUM (1) */
++ 0x95, 0x03, /*!< REPORT_COUNT (3) */
++ 0x75, 0x01, /*!< REPORT_SIZE (1) */
++ 0x81, 0x02, /*!< INPUT (Data,Var,Abs) */
++ 0x95, 0x01, /*!< REPORT_COUNT (1) */
++ 0x75, 0x05, /*!< REPORT_SIZE (5) */
++ 0x81, 0x03, /*!< INPUT (Cnst,Var,Abs) */
++ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */
++ 0x09, 0x30, /*!< USAGE (X) */
++ 0x09, 0x31, /*!< USAGE (Y) */
++ 0x09, 0x38, /*!< USAGE (WHEEL) */
++ 0x15, 0x81, /*!< LOGICAL_MINIMUM (-127) */
++ 0x25, 0x7f, /*!< LOGICAL_MAXIMUM (127) */
++ 0x75, 0x08, /*!< REPORT_SIZE (8) */
++ 0x95, 0x03, /*!< REPORT_COUNT (3) */
++ 0x81, 0x06, /*!< INPUT (Data,Var,Rel) */
++ 0xc0, /*!< END_COLLECTION */
++ 0xc0 /*!< END_COLLECTION */
++};
++
++#if !defined(OTG_C99)
++/*! This is the interrupt endpoint descriptor.
++ */
++static struct usbd_endpoint_descriptor mouse_data;
++
++/*! The list of endpoint descriptors
++ */
++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, };
++
++
++/*! This is the HID desccriptor.
++ */
++struct hid_descriptor mouse_hid;
++
++/*! List of class descriptors
++ */
++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = {
++ (struct usbd_generic_class_descriptor *)&mouse_hid, };
++
++
++/*! Data Interface Alternate description
++ */
++static struct usbd_interface_descriptor mouse_data_alternate_descriptor;
++
++/*! Interface Descriptions
++ */
++static struct usbd_alternate_description mouse_data_alternate_descriptions[1];
++
++/*! List of Interface description(s)
++ */
++static struct usbd_interface_description mouse_interfaces[1];
++
++/*! Configuration description(s)
++ */
++static struct usbd_configuration_descriptor mouse_configuration_descriptor;
++
++/*! Mouse Configuration Description
++ */
++struct usbd_configuration_description mouse_configuration_description[1];
++
++/*! Device Description
++ */
++static struct usbd_device_descriptor mouse_device_descriptor;
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++/*! High Speed Device Description
++ */
++static struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor;
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++/*! OTG Descriptor
++ */
++static struct usbd_otg_descriptor mouse_otg_descriptor;
++
++/*! Device Description
++ */
++struct usbd_device_description mouse_device_description;
++
++void mouse_global_init(void)
++{
++
++ /*! This is the interrupt endpoint descriptor.
++ */
++ ZERO(mouse_data);
++ mouse_data.bLength = 0x07;
++ mouse_data.bDescriptorType = USB_DT_ENDPOINT;
++ mouse_data.bEndpointAddress = USB_DIR_IN;
++ mouse_data.bmAttributes = INTERRUPT;
++ mouse_data.wMaxPacketSize = __constant_cpu_to_le16(0x10);
++#if defined (CONFIG_OTG_MOUSE_INTERVAL)
++ mouse_data.bInterval = CONFIG_OTG_MOUSE_INTERVAL;
++#else
++ mouse_data.bInterval = 0x01;
++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */
++
++
++ /*! This is the HID desccriptor.
++ */
++ ZERO(mouse_hid);
++ mouse_hid.bLength = 0x09;
++ mouse_hid.bDescriptorType = 0x21;
++ mouse_hid.bcdHID = __constant_cpu_to_le16(0x110);
++ mouse_hid.bCountryCode = 0x00;
++ mouse_hid.bNumDescriptors = 0x01;
++ mouse_hid.bReportType = 0x22;
++ mouse_hid.wItemLength = __constant_cpu_to_le16(0x34);
++
++
++ /*! Data Interface Alternate description
++ */
++ ZERO(mouse_data_alternate_descriptor);
++ mouse_data_alternate_descriptor.bLength = 0x09;
++ mouse_data_alternate_descriptor.bDescriptorType = USB_DT_INTERFACE;
++ mouse_data_alternate_descriptor.bInterfaceNumber = 0x00;
++ mouse_data_alternate_descriptor.bAlternateSetting = 0x00;
++ mouse_data_alternate_descriptor.bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor);
++ mouse_data_alternate_descriptor.bInterfaceClass = 0x03;
++ mouse_data_alternate_descriptor.bInterfaceSubClass = 0x01;
++ mouse_data_alternate_descriptor.bInterfaceProtocol = 0x02;
++ mouse_data_alternate_descriptor.iInterface = 0x00;
++
++ /*! Interface Descriptions
++ */
++ ZERO(mouse_data_alternate_descriptions);
++ mouse_data_alternate_descriptions[0].iInterface = "Random Mouse Interface - Interrupt";
++ mouse_data_alternate_descriptions[0].interface_descriptor = &mouse_data_alternate_descriptor;
++ mouse_data_alternate_descriptions[0].classes =
++ sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *);
++ mouse_data_alternate_descriptions[0].class_list = mouse_hid_descriptors;
++ mouse_data_alternate_descriptions[0].endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *);
++ mouse_data_alternate_descriptions[0].endpoint_list = mouse_default;
++ mouse_data_alternate_descriptions[0].endpoint_indexes = mouse_indexes;
++
++ /*! List of Interface description(s)
++ */
++ ZERO(mouse_interfaces);
++ mouse_interfaces[0].alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description);
++ mouse_interfaces[0].alternate_list = mouse_data_alternate_descriptions;
++
++
++ /*! Configuration description(s)
++ */
++ ZERO(mouse_configuration_descriptor);
++ mouse_configuration_descriptor.bLength = 0x09;
++ mouse_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION;
++ mouse_configuration_descriptor.wTotalLength = 0x00;
++ mouse_configuration_descriptor.bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
++ mouse_configuration_descriptor.bConfigurationValue = 0x01;
++ mouse_configuration_descriptor.iConfiguration = 0x00;
++ mouse_configuration_descriptor.bmAttributes = 0;
++ mouse_configuration_descriptor.bMaxPower = 0;
++
++ /*! Mouse Configuration Description
++ */
++ ZERO(mouse_configuration_description);
++ mouse_configuration_description[0].configuration_descriptor = &mouse_configuration_descriptor;
++ mouse_configuration_description[0].iConfiguration = "USB Random Mouse Configuration";
++ mouse_configuration_description[0].bNumInterfaces =
++ sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
++ mouse_configuration_description[0].interface_list = mouse_interfaces;
++
++ /*! Device Description
++ */
++ ZERO(mouse_device_descriptor);
++ mouse_device_descriptor.bLength = sizeof(struct usbd_device_descriptor);
++ mouse_device_descriptor.bDescriptorType = USB_DT_DEVICE;
++ mouse_device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION);
++ mouse_device_descriptor.bDeviceClass = 0x00;
++ mouse_device_descriptor.bDeviceSubClass = 0x00;
++ mouse_device_descriptor.bDeviceProtocol = 0x00;
++ mouse_device_descriptor.bMaxPacketSize0 = 0x00;
++ mouse_device_descriptor.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID);
++ mouse_device_descriptor.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID);
++ mouse_device_descriptor.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE);
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++ /*! High Speed Device Description
++ */
++ ZERO(mouse_device_qualifier_descriptor);
++ mouse_device_qualifier_descriptor.bLength = sizeof(struct usbd_device_qualifier_descriptor);
++ mouse_device_qualifier_descriptor.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
++ mouse_device_qualifier_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION);
++ mouse_device_qualifier_descriptor.bDeviceClass = 0x00;
++ mouse_device_qualifier_descriptor.bDeviceSubClass = 0x00;
++ mouse_device_qualifier_descriptor.bDeviceProtocol = 0x00;
++ mouse_device_qualifier_descriptor.bMaxPacketSize0 = 0x00;
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++ /*! OTG Descriptor
++ */
++ ZERO(mouse_otg_descriptor);
++ mouse_otg_descriptor.bLength = sizeof(struct usbd_otg_descriptor);
++ mouse_otg_descriptor.bDescriptorType = USB_DT_OTG;
++ mouse_otg_descriptor.bmAttributes = 0;
++
++ /*! Device Description
++ */
++ ZERO(mouse_device_description);
++ mouse_device_description.device_descriptor = &mouse_device_descriptor;
++#ifdef CONFIG_OTG_HIGH_SPEED
++ mouse_device_description.device_qualifier_descriptor = &mouse_device_qualifier_descriptor;
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ mouse_device_description.otg_descriptor = &mouse_otg_descriptor;
++ mouse_device_description.iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER;
++ mouse_device_description.iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME;
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ mouse_device_description.iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR;
++#endif
++}
++
++#else /* defined(OTG_C99) */
++
++/*! This is the interrupt endpoint descriptor.
++ */
++static struct usbd_endpoint_descriptor mouse_data = {
++ .bLength = 0x07,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = INTERRUPT,
++ .wMaxPacketSize = __constant_cpu_to_le16(0x10),
++#if defined (CONFIG_OTG_MOUSE_INTERVAL)
++ .bInterval = CONFIG_OTG_MOUSE_INTERVAL,
++#else
++ .bInterval = 0x01,
++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */
++};
++
++/*! The list of endpoint descriptors
++ */
++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, };
++
++/*! This is the HID desccriptor.
++ */
++struct hid_descriptor mouse_hid = {
++ .bLength = 0x09,
++ .bDescriptorType = 0x21,
++ .bcdHID = __constant_cpu_to_le16(0x110),
++ .bCountryCode = 0x00,
++ .bNumDescriptors = 0x01,
++ .bReportType = 0x22,
++ .wItemLength = __constant_cpu_to_le16(0x34),
++};
++
++/*! List of class descriptors
++ */
++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = {
++ (struct usbd_generic_class_descriptor *)&mouse_hid, };
++
++
++/*! Data Interface Alternate description
++ */
++static struct usbd_interface_descriptor mouse_data_alternate_descriptor = {
++ .bLength = 0x09,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x00,
++ .bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor),
++ .bInterfaceClass = 0x03,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = 0x00,
++};
++
++/*! Interface Descriptions
++ */
++static struct usbd_alternate_description mouse_data_alternate_descriptions[] = {
++ { .iInterface = "Random Mouse Interface - Interrupt",
++ .interface_descriptor = &mouse_data_alternate_descriptor,
++ .classes = sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
++ .class_list = mouse_hid_descriptors,
++ .endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *),
++ .endpoint_list = mouse_default,
++ .endpoint_indexes = mouse_indexes,
++ },
++};
++
++/*! List of Interface description(s)
++ */
++static struct usbd_interface_description mouse_interfaces[] = {
++ { .alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ .alternate_list = mouse_data_alternate_descriptions,},
++};
++
++
++/*! Configuration description(s)
++ */
++static struct usbd_configuration_descriptor mouse_configuration_descriptor = {
++ .bLength = 0x09,
++ .bDescriptorType = USB_DT_CONFIGURATION,
++ .wTotalLength = 0x00,
++ .bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description),
++ .bConfigurationValue = 0x01,
++ .iConfiguration = 0x00,
++ .bmAttributes = 0,
++ .bMaxPower = 0,
++};
++
++/*! Mouse Configuration Description
++ */
++struct usbd_configuration_description mouse_configuration_description[] = {
++ { .configuration_descriptor = &mouse_configuration_descriptor,
++ .iConfiguration = "USB Random Mouse Configuration",
++ },
++};
++
++/*! Device Description
++ */
++static struct usbd_device_descriptor mouse_device_descriptor = {
++ .bLength = sizeof(struct usbd_device_descriptor),
++ .bDescriptorType = USB_DT_DEVICE,
++ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION),
++ .bDeviceClass = 0x00,
++ .bDeviceSubClass = 0x00,
++ .bDeviceProtocol = 0x00,
++ .bMaxPacketSize0 = 0x00,
++ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID),
++ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID),
++ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++/*! High Speed Device Description
++ */
++static struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor = {
++ .bLength = sizeof(struct usbd_device_qualifier_descriptor),
++ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
++ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION),
++ .bDeviceClass = 0x00,
++ .bDeviceSubClass = 0x00,
++ .bDeviceProtocol = 0x00,
++ .bMaxPacketSize0 = 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++/*! OTG Descriptor
++ */
++static struct usbd_otg_descriptor mouse_otg_descriptor = {
++ .bLength = sizeof(struct usbd_otg_descriptor),
++ .bDescriptorType = USB_DT_OTG,
++ .bmAttributes = 0,
++};
++
++/*! Device Description
++ */
++struct usbd_device_description mouse_device_description = {
++ .device_descriptor = &mouse_device_descriptor,
++#ifdef CONFIG_OTG_HIGH_SPEED
++ .device_qualifier_descriptor = &mouse_device_qualifier_descriptor,
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ .otg_descriptor = &mouse_otg_descriptor,
++ .iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER,
++ .iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME,
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ .iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR,
++#endif
++};
++#endif /* defined(OTG_C99) */
++
++
++/*! @} */
++
++static int mouse_count;
++
++/* MOUSE ***************************************************************************************** */
++/* Transmit Function *************************************************************************** */
++
++int get_xy[8] = {
++ 0, 0, 1, 1,
++ 0, 0, -1, -1,
++};
++
++/*!
++ * mouse_send() - send a mouse data urb with random data
++ * @param function
++ * @return void
++ */
++void mouse_send(struct usbd_function_instance *function)
++{
++ struct mouse_private *mouse = &mouse_private;
++ int new_x = 0;
++ int new_y = 0;
++ u8 random;
++
++ memset(mouse->tx_urb->buffer, 0, 4);
++ if (!mouse->n) {
++
++ get_random_bytes(&random, 1);
++
++ mouse->last_x = MAX(-4, MIN(4, mouse->last_x + get_xy[random & 0x7]));
++ mouse->last_y = MAX(-4, MIN(4, mouse->last_y + get_xy[(random >> 3) & 0x7]));
++ mouse->n = (random>>6) & 0x3;
++
++ new_x = mouse->x + mouse->last_x;
++ new_y = mouse->y + mouse->last_y;
++
++ mouse->tx_urb->buffer[1] = mouse->last_x;
++ mouse->x = new_x;
++ mouse->tx_urb->buffer[2] = mouse->last_y;
++ mouse->y = new_y;
++ mouse->tx_urb->actual_length = 4;
++
++ }
++ else if ((mouse->n)&1) {
++ mouse->n--;
++ }
++ else {
++ mouse->n--;
++ mouse->tx_urb->buffer[1] = mouse->last_x;
++ mouse->x = new_x;
++ mouse->tx_urb->buffer[2] = mouse->last_y;
++ mouse->y = new_y;
++ }
++ usbd_start_in_urb(mouse->tx_urb);
++}
++
++/*!
++ * mouse_urb_sent() - called to indicate URB transmit finished
++ * This function is the callback function for sent urbs.
++ * It simply queues up another urb until the packets to be sent
++ * configuration parameter is reached (or forever if zero.)
++ * @param urb The urb to be sent.
++ * @param rc result
++ * @return int Return non-zero for failure.
++ */
++int mouse_urb_sent (struct usbd_urb *urb, int rc)
++{
++ struct mouse_private *mouse = &mouse_private;
++ struct usbd_function_instance *function = mouse->function;
++
++ RETURN_ZERO_IF(usbd_get_device_status(function) == USBD_CLOSING);
++ RETURN_ZERO_IF(usbd_get_device_status(function) != USBD_OK);
++ RETURN_ZERO_IF(usbd_get_device_state(function) != STATE_CONFIGURED);
++ TRACE_MSG1(MOUSE, "mouse_count: %d", mouse_count);
++ #ifdef CONFIG_OTG_MOUSE_PACKETS
++ RETURN_ZERO_IF (CONFIG_OTG_MOUSE_PACKETS && (mouse_count++ > CONFIG_OTG_MOUSE_PACKETS));
++ #endif /* CONFIG_OTG_MOUSE_PACKETS */
++ mouse_send(function); // re-send
++ return 0;
++}
++
++/* USB Device Functions ************************************************************************ */
++
++typedef enum mesg {
++ mesg_unknown,
++ mesg_configured,
++ mesg_reset,
++} mesg_t;
++mesg_t mouse_last_mesg;
++
++char * mouse_messages[3] = {
++ "",
++ "Mouse Configured",
++ "Mouse Reset",
++};
++
++void mouse_check_mesg(mesg_t curr_mesg)
++{
++ RETURN_UNLESS(mouse_last_mesg != curr_mesg);
++ mouse_last_mesg = curr_mesg;
++ otg_message(mouse_messages[curr_mesg]);
++}
++
++/*!
++ * mouse_event_handler() - process a device event
++ * This function is called to process USB Device Events.
++ *
++ * The DEVICE_CONFIGURED event causes the mouse_send() to be called
++ * to start the data flow.
++ *
++ * The DEVICE_RESET or DEVICE_DE_CONFIGURED events cause the outstanding
++ * transmit urb to be cancelled.
++ *
++ * @param function the function instance
++ * @param event the event
++ * @param data
++ * @return void
++ *
++ */
++void mouse_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data)
++{
++ struct mouse_private *mouse = &mouse_private;
++
++ switch (event) {
++ case DEVICE_CONFIGURED:
++ TRACE_MSG0(MOUSE, "Mouse Configured");
++ mouse_check_mesg(mesg_configured);
++ mouse_count = 0;
++ mouse->connected = 1;
++ if (!(mouse->tx_urb = usbd_alloc_urb (function, BULK_INT, 4, mouse_urb_sent)))
++ printk(KERN_INFO"%s: alloc failed\n", __FUNCTION__);
++ mouse_send(function); // start sending
++ break;
++
++ case DEVICE_RESET:
++ case DEVICE_DE_CONFIGURED:
++ TRACE_MSG0(MOUSE, "Mouse De-Configured");
++ mouse_check_mesg(mesg_reset);
++ BREAK_IF(!mouse->connected);
++ mouse->connected = 0;
++ if (mouse->tx_urb) {
++ usbd_free_urb (mouse->tx_urb);
++ mouse->tx_urb = NULL;
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++/*! copy_report()
++ * This function copies the Mouse HID report into the provided URB.
++ * @param urb Destination
++ * @param data Source
++ * @param size Amount of data to copy.
++ * @param max_buf Size of buffer
++ * @return Non-zero if error.
++ *
++ */
++static int copy_report (struct usbd_urb *urb, void *data, int size, int max_buf)
++{
++ int available;
++ int length;
++
++ RETURN_EINVAL_IF (!urb);
++ RETURN_EINVAL_IF (!data);
++ RETURN_EINVAL_IF (!(length = size));
++ RETURN_EINVAL_IF ((available = max_buf - urb->actual_length) <= 0);
++
++ length = (length < available) ? length : available;
++ memcpy (urb->buffer + urb->actual_length, data, length);
++ urb->actual_length += length;
++ return 0;
++}
++
++/*!
++ * mouse_send_hid() - send an EP0 urb containing HID report
++ * This is called to send the Mouse HID report.
++ */
++static int mouse_send_hid (void *data)
++{
++ struct mouse_private *mouse = &mouse_private;
++ struct usbd_function_instance *function = mouse->function;
++ struct usbd_urb *urb = usbd_alloc_urb_ep0(function, mouse->wLength, NULL);
++ int wMaxPacketSize = usbd_endpoint_zero_wMaxPacketSize(urb->function_instance, 0);
++
++ TRACE_MSG1(MOUSE, "Send Hid wLength: %d", mouse->wLength);
++ RETURN_EINVAL_IF (copy_report(urb, MouseHIDReport, sizeof(MouseHIDReport), mouse->wLength));
++ RETURN_EINVAL_UNLESS (wMaxPacketSize);
++
++ if (!(urb->actual_length % wMaxPacketSize) && (urb->actual_length < mouse->wLength))
++ urb->flags |= USBD_URB_SENDZLP;
++
++ RETURN_ZERO_IF(!usbd_start_in_urb(urb));
++ usbd_free_urb(urb);
++ return -EINVAL;
++}
++
++#ifdef CONFIG_OTG_MOUSE_BH
++/*!
++ * mouse_hid_bh() - Bottom half handler to send a HID report
++ * @param data
++ */
++static void mouse_hid_bh (void *data)
++{
++ struct usbd_function_instance *function = mouse_private.function;
++ mouse_send_hid(function);
++}
++
++/*! mouse_schedule_bh - schedule a call for mouse_hid_bh
++ * @param void
++ */
++static int mouse_schedule_bh (void)
++{
++ TRACE_MSG0(MOUSE, "Scheduling");
++ return (!schedule_task (&mouse_private.notification_bh)) ? EINVAL : 0;
++}
++#endif /* CONFIG_OTG_MOUSE_BH */
++
++/*!
++ * mouse_device_request - called to indicate urb has been received
++ * @param function
++ * @param request
++ */
++int mouse_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
++{
++ /* verify that this is a usb class request per cdc-mouse specification or a vendor request.
++ * determine the request direction and process accordingly
++ */
++
++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
++
++ case USB_REQ_HOST2DEVICE:
++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR:
++ return 0;
++
++ case USB_REQ_DEVICE2HOST :
++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR:
++
++ switch (request->bRequest) {
++ case USB_REQ_GET_DESCRIPTOR:
++ switch (le16_to_cpu(request->wValue)>>8) {
++ case HID_REPORT:
++ mouse_private.wLength = request->wLength;
++ #ifdef CONFIG_OTG_MOUSE_BH
++ return mouse_schedule_bh();
++ #else
++ return mouse_send_hid(function);
++ #endif
++ }
++ default: break;
++ }
++ break;
++
++ default:
++ break;
++ }
++ return -EINVAL;
++}
++
++
++/*! mouse_function_enable - called by USB Device Core to enable the driver
++ * @param function The function instance for this driver to use.
++ * @return non-zero if error.
++ */
++static int mouse_function_enable (struct usbd_function_instance *function)
++{
++ struct mouse_private *mouse = &mouse_private;
++ mouse->function = function;
++ mouse->n = 0;
++ mouse->x = 0;
++ mouse->y = 0;
++ mouse->last_x = 0;
++ mouse->last_y = 0;
++ mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, 0);
++ return 0;
++}
++
++/*! mouse_function_disable - called by the USB Device Core to disable the driver
++ * @param function The function instance for this driver
++ */
++static void mouse_function_disable (struct usbd_function_instance *function)
++{
++ struct mouse_private *mouse = &mouse_private;
++ mouse->function = NULL;
++ mouse->writesize = 0;
++ mouse->function = NULL;
++}
++
++/* ********************************************************************************************* */
++#if !defined(OTG_C99)
++/*! function_ops - operations table for the USB Device Core
++ */
++static struct usbd_function_operations mouse_function_ops;
++
++/*! mouse_function_driver - USB Device Core function driver definition
++ */
++struct usbd_function_driver mouse_function_driver;
++
++void mouse_ops_init(void)
++{
++ /*! function_ops - operations table for the USB Device Core
++ */
++ ZERO(mouse_function_ops);
++ mouse_function_ops.event_handler = mouse_event_handler; /*! called for each USB Device Event */
++ mouse_function_ops.device_request = mouse_device_request; /*! called for each received device request */
++ mouse_function_ops.function_enable = mouse_function_enable; /*! called to enable the function driver */
++ mouse_function_ops.function_disable = mouse_function_disable; /*! called to disable the function driver */
++
++ /*! function_driver - USB Device Core function driver definition
++ */
++ ZERO(mouse_function_driver);
++ mouse_function_driver.name = "mouse-random"; /*! driver name */
++ mouse_function_driver.fops = &mouse_function_ops; /*! operations table */
++ mouse_function_driver.device_description = &mouse_device_description; /*! mouse device description */
++ mouse_function_driver.bNumConfigurations =
++ sizeof (mouse_configuration_description) / sizeof (struct usbd_configuration_description);
++ mouse_function_driver.configuration_description =
++ mouse_configuration_description; /*! mouse configuration description */
++ mouse_function_driver.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID);
++ mouse_function_driver.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID);
++ mouse_function_driver.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE);
++
++
++ mouse_function_driver.bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
++ mouse_function_driver.interface_list = mouse_interfaces;
++ mouse_function_driver.endpointsRequested = ENDPOINTS;
++ mouse_function_driver.requestedEndpoints = mouse_endpoint_requests;
++}
++#else /* defined(OTG_C99) */
++/*! function_ops - operations table for the USB Device Core
++ */
++static struct usbd_function_operations mouse_function_ops = {
++ .event_handler = mouse_event_handler, /*!< called for each USB Device Event */
++ .device_request = mouse_device_request, /*!< called for each received device request */
++ .function_enable = mouse_function_enable, /*!< called to enable the function driver */
++ .function_disable = mouse_function_disable, /*!< called to disable the function driver */
++};
++
++/*! mouse_function_driver - USB Device Core function driver definition
++ */
++struct usbd_function_driver mouse_function_driver = {
++ .name = "mouse-random", /*!< driver name */
++ .fops = &mouse_function_ops, /*!< operations table */
++ .device_description = &mouse_device_description, /*!< mouse device description */
++ .bNumConfigurations = sizeof (mouse_configuration_description) / sizeof (struct usbd_configuration_description),
++ .configuration_description = mouse_configuration_description, /*!< mouse configuration description */
++ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID),
++ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID),
++ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE),
++ .bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description),
++ .interface_list = mouse_interfaces,
++ .endpointsRequested = ENDPOINTS,
++ .requestedEndpoints = mouse_endpoint_requests,
++};
++#endif /* defined(OTG_C99) */
++
+diff -uNr linux/drivers/no-otg/functions/mouse/mouse-if.c linux/drivers/otg/functions/mouse/mouse-if.c
+--- linux/drivers/no-otg/functions/mouse/mouse-if.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/mouse-if.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,499 @@
++/*
++ * otg/functions/mouse/mouse-if.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/mouse/mouse-if.c
++ * @brief Mouse Interface Function Driver protocol implementation.
++ *
++ * This file implements the Mouse HID protocols and will generate
++ * random mouse data on the INTERRUPT endpoint.
++ *
++ * The primary purpose of this driver is to demonstrate how to
++ * implement a simple function driver and to provide a simple
++ * uni-directional driver for testing USB Device peripheral drivers.
++ *
++ * The mouse driver has several other characteristics to allow testing of
++ * other features of USB Device peripheral drivers:
++ *
++ * - ep0 ZLP handling
++ *
++ * - ep0 delayed CONTROL READ
++ *
++ * To verify that ep0 ZLP processing is being handling correctly this
++ * driver has a hard coded Product name that is returned as a 32 byte
++ * string. This forces most any implementations that have an endpoint
++ * zero packetsize of 8, 16 or 32 to send a Zero Length Packet to
++ * terminate the string transfer when the Product name is requested
++ * by the host.
++ *
++ * The delayed CONTROL READ test verifies that that USB Device peripheral
++ * drivers will correctly NAK until data is provide for device requests.
++ * This is done by (optionally) delaying the HID report via a bottom half
++ * handler. The device request returns normally and the peripheral driver
++ * must properly recognize that while the device request had a non-zero
++ * wLength field, there is currently no queued urb.
++ *
++ * When the bottom half handler is scheduled the HID report urb will be
++ * queued on endpoint zero and then returned to the host.
++ *
++ *
++ * @ingroup MouseFunction
++ */
++
++#include <linux/config.h>
++#include <otg/otg-compat.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++#include <otg/otg-api.h>
++
++#include "mouse.h"
++
++
++/*
++ * ep0 testing.... ensure that this is exactly 16 bytes
++ */
++#undef CONFIG_OTG_MOUSE_PRODUCT_NAME
++#define CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse"
++
++/*!
++ * @name Mouse Descriptors
++ *
++ * MouseHIDReport
++ * MOUSE Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++/*!
++ * Mouse Descriptors
++ * @{
++ */
++
++#define BULK_INT 0x00 /*!< Interrupt endpoint number */
++#define ENDPOINTS 0x01 /*!< Number of endpoints required */
++
++/*! List of required endpoint numbers
++ */
++static u8 mouse_indexes[] = { BULK_INT, };
++
++/*! List of requested endpoints
++ */
++static struct usbd_endpoint_request mouse_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
++ { 0, },
++};
++
++#if !defined(OTG_C99)
++/*! This is the interrupt endpoint descriptor.
++ */
++static struct usbd_endpoint_descriptor mouse_data;
++
++/*! The list of endpoint descriptors
++ */
++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, };
++
++
++/*! List of class descriptors
++ */
++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = {
++ (struct usbd_generic_class_descriptor *)&mouse_hid, };
++
++
++/*! Data Interface Alternate description
++ */
++static struct usbd_interface_descriptor mouse_data_alternate_descriptor;
++
++/*! Interface Descriptions
++ */
++static struct usbd_alternate_description mouse_data_alternate_descriptions[1];
++
++/*! List of Interface description(s)
++ */
++static struct usbd_interface_description mouse_interfaces[1];
++
++
++void mouse_if_global_init(void)
++{
++
++ /*! This is the interrupt endpoint descriptor.
++ */
++ ZERO(mouse_data);
++ mouse_data.bLength = 0x07;
++ mouse_data.bDescriptorType = USB_DT_ENDPOINT;
++ mouse_data.bEndpointAddress = USB_DIR_IN;
++ mouse_data.bmAttributes = INTERRUPT;
++ mouse_data.wMaxPacketSize = __constant_cpu_to_le16(0x10);
++#if defined (CONFIG_OTG_MOUSE_INTERVAL)
++ mouse_data.bInterval = CONFIG_OTG_MOUSE_INTERVAL;
++#else
++ mouse_data.bInterval = 0x01;
++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */
++
++
++ /*! Data Interface Alternate description
++ */
++ ZERO(mouse_data_alternate_descriptor);
++ mouse_data_alternate_descriptor.bLength = 0x09;
++ mouse_data_alternate_descriptor.bDescriptorType = USB_DT_INTERFACE;
++ mouse_data_alternate_descriptor.bInterfaceNumber = 0x00;
++ mouse_data_alternate_descriptor.bAlternateSetting = 0x00;
++ mouse_data_alternate_descriptor.bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor);
++ mouse_data_alternate_descriptor.bInterfaceClass = 0x03;
++ mouse_data_alternate_descriptor.bInterfaceSubClass = 0x01;
++ mouse_data_alternate_descriptor.bInterfaceProtocol = 0x02;
++ mouse_data_alternate_descriptor.iInterface = 0x00;
++
++ /*! Interface Descriptions
++ */
++ ZERO(mouse_data_alternate_descriptions);
++ mouse_data_alternate_descriptions[0].iInterface = "Random Mouse Interface - Interrupt";
++ mouse_data_alternate_descriptions[0].interface_descriptor = &mouse_data_alternate_descriptor;
++ mouse_data_alternate_descriptions[0].classes =
++ sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *);
++ mouse_data_alternate_descriptions[0].class_list = mouse_hid_descriptors;
++ mouse_data_alternate_descriptions[0].endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *);
++ mouse_data_alternate_descriptions[0].endpoint_list = mouse_default;
++ mouse_data_alternate_descriptions[0].endpoint_indexes = mouse_indexes;
++
++ /*! List of Interface description(s)
++ */
++ ZERO(mouse_interfaces);
++ mouse_interfaces[0].alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description);
++ mouse_interfaces[0].alternate_list = mouse_data_alternate_descriptions;
++
++
++#else /* defined(OTG_C99) */
++
++/*! This is the interrupt endpoint descriptor.
++ */
++static struct usbd_endpoint_descriptor mouse_data = {
++ .bLength = 0x07,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = INTERRUPT,
++ .wMaxPacketSize = __constant_cpu_to_le16(0x10),
++#if defined (CONFIG_OTG_MOUSE_INTERVAL)
++ .bInterval = CONFIG_OTG_MOUSE_INTERVAL,
++#else
++ .bInterval = 0x01,
++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */
++};
++
++/*! The list of endpoint descriptors
++ */
++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, };
++
++
++/*! List of class descriptors
++ */
++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = {
++ (struct usbd_generic_class_descriptor *)&mouse_hid, };
++
++
++/*! Data Interface Alternate description
++ */
++static struct usbd_interface_descriptor mouse_data_alternate_descriptor = {
++ .bLength = 0x09,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x00,
++ .bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor),
++ .bInterfaceClass = 0x03,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = 0x00,
++};
++
++/*! Interface Descriptions
++ */
++static struct usbd_alternate_description mouse_data_alternate_descriptions[] = {
++ { .iInterface = "Random Mouse Interface - Interrupt",
++ .interface_descriptor = &mouse_data_alternate_descriptor,
++ .classes = sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
++ .class_list = mouse_hid_descriptors,
++ .endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *),
++ .endpoint_list = mouse_default,
++ .endpoint_indexes = mouse_indexes,
++ },
++};
++
++/*! List of Interface description(s)
++ */
++static struct usbd_interface_description mouse_interfaces[] = {
++ { .alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ .alternate_list = mouse_data_alternate_descriptions,},
++};
++
++#endif /* defined(OTG_C99) */
++
++/*! @} */
++
++static int mouse_count;
++
++/* MOUSE ***************************************************************************************** */
++/* Transmit Function *************************************************************************** */
++
++int get_xy[8] = {
++ 0, 0, 1, 1,
++ 0, 0, -1, -1,
++};
++
++/*!
++ * mouse_send() - send a mouse data urb with random data
++ * @param function
++ * @return void
++ */
++void mouse_send(struct usbd_function_instance *function)
++{
++ struct mouse_private *mouse = &mouse_private;
++ int new_x = 0;
++ int new_y = 0;
++ u8 random;
++
++ memset(mouse->tx_urb->buffer, 0, 4);
++ if (!mouse->n) {
++
++ get_random_bytes(&random, 1);
++
++ mouse->last_x = MAX(-4, MIN(4, mouse->last_x + get_xy[random & 0x7]));
++ mouse->last_y = MAX(-4, MIN(4, mouse->last_y + get_xy[(random >> 3) & 0x7]));
++ mouse->n = (random>>6) & 0x3;
++
++ new_x = mouse->x + mouse->last_x;
++ new_y = mouse->y + mouse->last_y;
++
++ mouse->tx_urb->buffer[1] = mouse->last_x;
++ mouse->x = new_x;
++ mouse->tx_urb->buffer[2] = mouse->last_y;
++ mouse->y = new_y;
++ mouse->tx_urb->actual_length = 4;
++
++ }
++ else if ((mouse->n)&1) {
++ mouse->n--;
++ }
++ else {
++ mouse->n--;
++ mouse->tx_urb->buffer[1] = mouse->last_x;
++ mouse->x = new_x;
++ mouse->tx_urb->buffer[2] = mouse->last_y;
++ mouse->y = new_y;
++ }
++ usbd_start_in_urb(mouse->tx_urb);
++}
++
++/*!
++ * mouse_urb_sent() - called to indicate URB transmit finished
++ * This function is the callback function for sent urbs.
++ * It simply queues up another urb until the packets to be sent
++ * configuration parameter is reached (or forever if zero.)
++ * @param urb The urb to be sent.
++ * @param rc result
++ * @return int Return non-zero for failure.
++ */
++int mouse_urb_sent (struct usbd_urb *urb, int rc)
++{
++ struct mouse_private *mouse = &mouse_private;
++ struct usbd_function_instance *function = mouse->function;
++
++ RETURN_ZERO_IF(usbd_get_device_status(function) == USBD_CLOSING);
++ RETURN_ZERO_IF(usbd_get_device_status(function) != USBD_OK);
++ RETURN_ZERO_IF(usbd_get_device_state(function) != STATE_CONFIGURED);
++ TRACE_MSG1(MOUSE, "mouse_count: %d", mouse_count);
++ #ifdef CONFIG_OTG_MOUSE_PACKETS
++ RETURN_ZERO_IF (CONFIG_OTG_MOUSE_PACKETS && (mouse_count++ > CONFIG_OTG_MOUSE_PACKETS));
++ #endif /* CONFIG_OTG_MOUSE_PACKETS */
++ mouse_send(function); // re-send
++ return 0;
++}
++
++/* USB Device Functions ************************************************************************ */
++
++typedef enum mesg {
++ mesg_unknown,
++ mesg_configured,
++ mesg_reset,
++} mesg_t;
++static mesg_t mouse_last_mesg;
++
++static char * mouse_messages[3] = {
++ "",
++ "Mouse-if Configured",
++ "Mouse-if Reset",
++};
++
++static void mouse_check_mesg(mesg_t curr_mesg)
++{
++ RETURN_UNLESS(mouse_last_mesg != curr_mesg);
++ mouse_last_mesg = curr_mesg;
++ otg_message(mouse_messages[curr_mesg]);
++}
++
++/*!
++ * mouse_event_handler() - process a device event
++ * This function is called to process USB Device Events.
++ *
++ * The DEVICE_CONFIGURED event causes the mouse_send() to be called
++ * to start the data flow.
++ *
++ * The DEVICE_RESET or DEVICE_DE_CONFIGURED events cause the outstanding
++ * transmit urb to be cancelled.
++ *
++ * @param function the function instance
++ * @param event the event
++ * @param data
++ * @return void
++ *
++ */
++static void mouse_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data)
++{
++ struct mouse_private *mouse = &mouse_private;
++
++ switch (event) {
++ case DEVICE_CONFIGURED:
++ TRACE_MSG0(MOUSE, "Mouse-if Configured");
++ mouse_check_mesg(mesg_configured);
++ mouse_count = 0;
++ mouse->connected = 1;
++ if (!(mouse->tx_urb = usbd_alloc_urb (function, BULK_INT, 4, mouse_urb_sent)))
++ printk(KERN_INFO"%s: alloc failed\n", __FUNCTION__);
++ mouse_send(function); // start sending
++ break;
++
++ case DEVICE_RESET:
++ case DEVICE_DE_CONFIGURED:
++ TRACE_MSG0(MOUSE, "Mouse-if De-Configured");
++ mouse_check_mesg(mesg_reset);
++ BREAK_IF(!mouse->connected);
++ mouse->connected = 0;
++ if (mouse->tx_urb) {
++ usbd_free_urb (mouse->tx_urb);
++ mouse->tx_urb = NULL;
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++#ifdef CONFIG_OTG_MOUSE_BH
++/*!
++ * mouse_hid_bh() - Bottom half handler to send a HID report
++ * @param data
++ */
++static void mouse_hid_bh (void *data)
++{
++ struct usbd_function_instance *function = mouse_private.function;
++ mouse_send_hid(function);
++}
++
++/*! mouse_schedule_bh - schedule a call for mouse_hid_bh
++ * @param void
++ */
++static int mouse_schedule_bh (void)
++{
++ TRACE_MSG0(MOUSE, "Scheduling");
++ return (!schedule_task (&mouse_private.notification_bh)) ? EINVAL : 0;
++}
++#endif /* CONFIG_OTG_MOUSE_BH */
++
++/*!
++ * mouse_device_request - called to indicate urb has been received
++ * @param function
++ * @param request
++ */
++static int mouse_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
++{
++ return -EINVAL;
++}
++
++
++/*! mouse_function_enable - called by USB Device Core to enable the driver
++ * @param function The function instance for this driver to use.
++ * @return non-zero if error.
++ */
++static int mouse_function_enable (struct usbd_function_instance *function)
++{
++ struct mouse_private *mouse = &mouse_private;
++ mouse->function = function;
++ mouse->n = 0;
++ mouse->x = 0;
++ mouse->y = 0;
++ mouse->last_x = 0;
++ mouse->last_y = 0;
++ mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, 0);
++ return 0;
++}
++
++/*! mouse_function_disable - called by the USB Device Core to disable the driver
++ * @param function The function instance for this driver
++ */
++static void mouse_function_disable (struct usbd_function_instance *function)
++{
++ struct mouse_private *mouse = &mouse_private;
++ mouse->function = NULL;
++ mouse->writesize = 0;
++ mouse->function = NULL;
++}
++
++/* ********************************************************************************************* */
++#if !defined(OTG_C99)
++/*! function_ops - operations table for the USB Device Core
++ */
++static struct usbd_function_operations mouse_function_ops;
++
++/*! mouse_interfacedriver - USB Device Core function driver definition
++ */
++struct usbd_function_driver mouse_interfacedriver;
++
++void mouse_if_ops_init(void)
++{
++ /*! function_ops - operations table for the USB Device Core
++ */
++ ZERO(mouse_function_ops);
++ mouse_function_ops.event_handler = mouse_event_handler; /*! called for each USB Device Event */
++ mouse_function_ops.device_request = mouse_device_request; /*! called for each received device request */
++ mouse_function_ops.function_enable = mouse_function_enable; /*! called to enable the function driver */
++ mouse_function_ops.function_disable = mouse_function_disable; /*! called to disable the function driver */
++
++ /*! function_driver - USB Device Core function driver definition
++ */
++ ZERO(mouse_interfacedriver);
++ mouse_interface_driver.name = "mouse-random-if-driver-noc99"; /*! driver name */
++ mouse_interface_driver.fops = &mouse_function_ops; /*! operations table */
++
++ mouse_function_driver.bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
++ mouse_function_driver.interface_list = mouse_interfaces;
++ mouse_interface_driver.endpointsRequested = ENDPOINTS;
++ mouse_interface_driver.requestedEndpoints = mouse_endpoint_requests;
++
++}
++#else /* defined(OTG_C99) */
++/*! function_ops - operations table for the USB Device Core
++ */
++static struct usbd_function_operations mouse_function_ops = {
++ .event_handler = mouse_event_handler, /*!< called for each USB Device Event */
++ .device_request = mouse_device_request, /*!< called for each received device request */
++ .function_enable = mouse_function_enable, /*!< called to enable the function driver */
++ .function_disable = mouse_function_disable, /*!< called to disable the function driver */
++};
++
++/*! mouse_interface_driver - USB Device Core function driver definition
++ */
++struct usbd_function_driver mouse_interface_driver = {
++ .name = "mouse-random-if-driver", /*!< driver name */
++ .fops = &mouse_function_ops, /*!< operations table */
++ .endpointsRequested = ENDPOINTS,
++ .requestedEndpoints = mouse_endpoint_requests,
++};
++#endif /* defined(OTG_C99) */
++
+diff -uNr linux/drivers/no-otg/functions/mouse/mouse-l24.c linux/drivers/otg/functions/mouse/mouse-l24.c
+--- linux/drivers/no-otg/functions/mouse/mouse-l24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/mouse-l24.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,175 @@
++/*
++ * otg/functions/mouse/mouse-l24.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/mouse/mouse-l24.c
++ * @brief Mouse Function Driver Linux 2.4 initialization
++ *
++ *
++ * This file contains the Linux 2.4 module init and exit functions
++ * to load and unload the Random Mouse Function.
++ *
++ * Notes
++ *
++ * This driver does not export any upper edge functionality to
++ * for the user or applications to use. It simply sends a configurable
++ * number of data packets to the USB Host when configured. Each data
++ * packet contains the equivalent of a small mouse report which will
++ * cause the mouse cursor to move on the host.
++ *
++ * To use simply load with something like:
++ *
++ * insmod mouse_fd.o vendor_id=0xffff product_id=0xffff
++ *
++ * And attach to a Windows box. Windows should recognize as a mouse and
++ * immediately start receiving a stream of data.
++ *
++ * To terminate simply unplug.
++ *
++ *
++ * 1. The mouse driver is product name is hard coded to a string that will
++ * generate a string descriptor that is 32 bytes long. This will test
++ * most UDC's ep0 ZLP handling as it is a multiple of the most common
++ * UDC endpoint zero size. (An option can be added later to allow for
++ * 64 byte ep0 packetsize.)
++ *
++ * 2. The CONFIG_OTG_MOUSE_BH option can be enabled to delay the HID report
++ * to being generated by a bottom half handler. This will verify that
++ * the bus interface driver properly handles the case of a delayed
++ * CONTROL READ. I.e. when the usbd_device_request() function returns
++ * zero for successful completion but there is no tx_urb containing the
++ * requested data. The bus interface driver must setup the conditions for
++ * ACK'ing the SETUP packet but then NAK the IN data for endpoint zero
++ * until the tx_urb is started later.
++ *
++ * 3. The CONFIG_OTG_MOUSE_PACKETS option is used to set the number of
++ * mouse data reports to send. Set to zero for a continous stream
++ * of data.
++ *
++ * @ingroup MouseFunction
++ */
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++
++#include "mouse.h"
++
++
++/* Module Parameters ******************************************************** */
++/* !
++ * @name XXXXX MODULE Parameters
++ */
++/* ! @{ */
++
++MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com");
++EMBED_LICENSE();
++MOD_DESCRIPTION ("Belcarra Random Walk MOUSE Function");
++
++static u32 vendor_id; /*!< override built-in vendor ID */
++static u32 product_id; /*!< override built-in product ID */
++
++MOD_PARM (vendor_id, "i");
++MOD_PARM (product_id, "i");
++
++MOD_PARM_DESC (vendor_id, "Device Vendor ID");
++MOD_PARM_DESC (product_id, "Device Product ID");
++
++otg_tag_t MOUSE;
++/* ! *} */
++
++/* USB Module init/exit ***************************************************** */
++
++/*!
++ * mouse_modinit() - module init
++ *
++ * This is called by the Linux kernel; either when the module is loaded
++ * if compiled as a module, or during the system intialization if the
++ * driver is linked into the kernel.
++ *
++ * This function will parse module parameters if required and then register
++ * the mouse driver with the USB Device software.
++ *
++ * If the CONFIG_OTG_MOUSE_BH option is enabled it will also setup the mouse
++ * bottom half handler.
++ *
++ */
++static int mouse_modinit (void)
++{
++ struct usbd_function_instance *mfi;
++ printk (KERN_INFO "%s: vendor_id: %04x product_id: %04x\n", __FUNCTION__, vendor_id, product_id);
++
++ #if !defined(OTG_C99)
++ mouse_global_init();
++ mouse_ops_init();
++ #endif /* defined(OTG_C99) */
++
++ MOUSE = otg_trace_obtain_tag();
++ TRACE_MSG2(MOUSE, "vendor_id: %04x product_id: %04x",vendor_id, product_id);
++
++ if (vendor_id)
++ mouse_function_driver.idVendor = cpu_to_le16(vendor_id);
++ if (product_id)
++ mouse_function_driver.idProduct = cpu_to_le16(product_id);
++
++ mouse_hid.wItemLength = cpu_to_le16(0x34); // XXX mips compiler bug.....
++
++ // register as usb function driver
++ THROW_UNLESS ((mouse_private.function = usbd_register_function (&mouse_function_driver, "mouse", NULL)), error);
++ #ifdef CONFIG_OTG_MOUSE_BH
++ mouse_private.notification_bh.routine = mouse_hid_bh;
++ mouse_private.notification_bh.data = NULL;
++ #endif
++ CATCH(error) {
++ if (mouse_private.function) {
++ usbd_deregister_function (mouse_private.function);
++ mouse_private.function = NULL;
++ }
++ otg_trace_invalidate_tag(MOUSE);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++module_init (mouse_modinit);
++
++#if OTG_EPILOGUE
++/*!
++ * mouse_modexit() - module init
++ *
++ * This is called by the Linux kernel; when the module is being unloaded
++ * if compiled as a module. This function is never called if the
++ * driver is linked into the kernel.
++ *
++ * @param void
++ * @return void
++ */
++static void mouse_modexit (void)
++{
++ #ifdef CONFIG_OTG_MOUSE_BH
++ while (PENDING_WORK_ITEM(mouse_private.notification_bh)) {
++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__);
++ schedule_timeout(10 * HZ);
++ }
++ #endif
++ if (mouse_private.function) {
++ usbd_deregister_function (mouse_private.function);
++ mouse_private.function = NULL;
++ }
++
++ otg_trace_invalidate_tag(MOUSE);
++}
++
++module_exit (mouse_modexit);
++#endif
++
+diff -uNr linux/drivers/no-otg/functions/mouse/mouse.h linux/drivers/otg/functions/mouse/mouse.h
+--- linux/drivers/no-otg/functions/mouse/mouse.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/mouse/mouse.h 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,63 @@
++/*
++ * otg/functions/mouse/mouse.h
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++
++/*!
++ * @defgroup MouseFunction Random Mouse
++ * @ingroup functiongroup
++ */
++/*!
++ * @file otg/functions/mouse/mouse.h
++ * @brief Mouse Function Driver private defines
++ *
++ *
++ * This is a test USB HID Function Driver designed to test and
++ * verify that INTERRUPT IN endpoints work properly.
++ *
++ *
++ * @ingroup MouseFunction
++ */
++
++
++/*!
++ * The mouse_private structure is used to collect all of the mouse driver
++ * global variables into one place.
++ */
++struct mouse_private {
++ struct usbd_function_instance *function; /*!< function instance for this module */
++#ifdef CONFIG_OTG_MOUSE_BH
++ struct WORK_STRUCT notification_bh;
++#endif /* CONFIG_OTG_MOUSE_BH */
++ int usb_driver_registered; /*!< non-zero if usb function registered */
++ unsigned char connected; /*!< non-zero if connected to host (configured) */
++ unsigned int writesize; /*!< packetsize * 4 */
++ struct usbd_urb *tx_urb; /*!< saved copy of current tx urb */
++ int wLength;
++ int x;
++ int y;
++ int last_x;
++ int last_y;
++ int n;
++};
++
++extern struct mouse_private mouse_private;
++extern struct usbd_function_driver mouse_function_driver;
++extern struct usbd_function_driver mouse_interface_driver;
++extern struct usbd_function_driver mouse_composite_driver;
++extern struct hid_descriptor mouse_hid;
++
++#ifndef OTG_C99
++extern void mouse_global_init(void);
++#endif /* OTG_C99 */
++
++#define MOUSE mouse_trace_tag
++extern otg_tag_t MOUSE;
++
+diff -uNr linux/drivers/no-otg/functions/msc/Config.in linux/drivers/otg/functions/msc/Config.in
+--- linux/drivers/no-otg/functions/msc/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/Config.in 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,25 @@
++#
++# Mass Storage Class Function Drivers
++#
++# Copyright (C) 2003 Belcarra
++#
++
++
++mainmenu_option next_comment
++
++comment "Mass Storage Function "
++dep_tristate ' Mass Storage Function' CONFIG_OTG_MSC $CONFIG_OTG
++
++if [ "$CONFIG_OTG_MSC" = "y" -o "$CONFIG_OTG_MSC" = "m" ]; then
++ hex 'VendorID (hex value)' CONFIG_OTG_MSC_VENDORID "15ec"
++ hex 'ProductID (hex value)' CONFIG_OTG_MSC_PRODUCTID "f006"
++ hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_MSC_BCDDEVICE "0100"
++
++ string 'iManufacturer (string)' CONFIG_OTG_MSC_MANUFACTURER "Belcarra"
++ string 'iProduct (string)' CONFIG_OTG_MSC_PRODUCT_NAME "Mass Storage Class - Bulk Only"
++ string 'MSC Bulk Only iInterface (string)' CONFIG_OTG_MSC_INTF "MSC BO Data Intf"
++ string 'Data Interface iConfiguration (string)' CONFIG_OTG_MSC_DESC "MSC BO Configuration"
++
++fi
++
++endmenu
+diff -uNr linux/drivers/no-otg/functions/msc/Kconfig linux/drivers/otg/functions/msc/Kconfig
+--- linux/drivers/no-otg/functions/msc/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/Kconfig 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,52 @@
++menu "OTG Mass Storage function"
++ depends on OTG
++
++config OTG_MSC
++ tristate " Mass Storage Function"
++ depends on OTG
++
++menu "OTG Mass Storage function options"
++ depends on OTG && OTG_MSC
++
++config OTG_MSC_VENDORID
++ hex "VendorID (hex value)"
++ depends on OTG && OTG_MSC
++ default "Ox15ec"
++
++config OTG_MSC_PRODUCTID
++ hex "ProductID (hex value)"
++ depends on OTG && OTG_MSC
++ default "Oxf006"
++
++config OTG_MSC_BCDDEVICE
++ hex "bcdDevice (binary-coded decimal)"
++ depends on OTG && OTG_MSC
++ default "Ox0100"
++
++config OTG_MSC_MANUFACTURER
++ string "iManufacturer (string)"
++ depends on OTG && OTG_MSC
++ default "Belcarra"
++
++config OTG_MSC_PRODUCT_NAME
++ string "iProduct (string)"
++ depends on OTG && OTG_MSC
++ default "Mass Storage Class - Bulk Only"
++
++config OTG_MSC_INTF
++ string "MSC Bulk Only iInterface (string)"
++ depends on OTG && OTG_MSC
++ default "MSC BO Data Intf"
++
++config OTG_MSC_DESC
++ string "Data Interface iConfiguration (string)"
++ depends on OTG && OTG_MSC
++ default "MSC BO Configuration"
++
++config OTG_MSC_REGISTER_TRACE
++ bool " MSC Tracing"
++ depends on OTG && OTG_MSC
++ default n
++endmenu
++
++endmenu
+diff -uNr linux/drivers/no-otg/functions/msc/Makefile linux/drivers/otg/functions/msc/Makefile
+--- linux/drivers/no-otg/functions/msc/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/Makefile 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,68 @@
++#
++# Function driver for a Mass Storage USB Device
++#
++# Copyright (c) 2003 Belcarra
++
++# Multipart objects.
++
++O_TARGET := msc_target.o
++list-multi := msc_fd.o
++
++#msc_fd-objs := msc.o crc.o
++msc_fd-objs := msc-fd.o crc.o msc-linux.o msc-bo.o msc-io-l24.o
++
++# Objects that export symbols.
++#export-objs := msc.o
++export-objs := msc-fd.o msc-linux.o msc-bo.o
++
++# Object file lists.
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj- :=
++
++# Each configuration option enables a list of files.
++
++obj-$(CONFIG_OTG_MSC) += msc_fd.o
++
++# Extract lists of the multi-part drivers.
++# The 'int-*' lists are the intermediate files used to build the multi's.
++
++multi-y := $(filter $(list-multi), $(obj-y))
++multi-m := $(filter $(list-multi), $(obj-m))
++int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
++int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
++
++# Files that are both resident and modular: remove from modular.
++
++obj-m := $(filter-out $(obj-y), $(obj-m))
++int-m := $(filter-out $(int-y), $(int-m))
++
++# Translate to Rules.make lists.
++
++O_OBJS := $(filter-out $(export-objs), $(obj-y))
++OX_OBJS := $(filter $(export-objs), $(obj-y))
++M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
++MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
++MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
++MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
++
++# The global Rules.make.
++
++MSCD=$(OTG)/functions/msc
++
++OTG=$(TOPDIR)/drivers/otg
++include $(TOPDIR)/Rules.make
++EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format
++EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format
++
++# Link rules for multi-part drivers.
++
++msc_fd.o: $(msc_fd-objs)
++ $(LD) -r -o $@ $(msc_fd-objs)
++
++# dependencies:
++
++mass.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h
++
+diff -uNr linux/drivers/no-otg/functions/msc/TODO.txt linux/drivers/otg/functions/msc/TODO.txt
+--- linux/drivers/no-otg/functions/msc/TODO.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/TODO.txt 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,118 @@
++MSC TODO List Stuart Lynne
++Belcarra Mon Oct 25 00:51:12 PDT 2004
++
++
++1. MSC documentation
++
++ - requirements and specifications
++ - comparison to mass storage specification, bulk-only and rbc
++ - implementation specifics
++ - rbc/spc commands
++ - sense keys
++ - control and usage
++
++2. PSC requirements
++
++ - notification to PSM layer for addroot and deleteroot
++
++
++3. expunge non Belcarra code and header files
++
++
++4. Insertion emulation
++
++ i. If not connected,
++ - do bdget(),
++ - do blkdev_get()
++ - set flags appropriately.
++
++ ii. If connected,
++ - ensure that there are no active requests (sleep if neccessary)
++ - do bdget()
++ - do blkdev_get()
++ - ensure that overlapped requests from the host while
++ doing the open will not cause problems
++
++5. Removal emulation
++
++ i. If not connected
++ - reset flags appropriately
++
++ ii. If connected,
++ - wait for any active requests to complete (sleep if necessary),
++ - reset flags
++ - ensure that overlapped requests from host while
++ resetting the flags will not cause problems
++ - wait for acknowledgement from host
++
++
++Note that all of the above must take place in the thread of execution of
++the ioctl call (i.e. a normal user thread or process.)
++
++The ioctl should not complete until all of the above is complete OR
++cannot complete and you return an error.
++
++
++6. ioctls
++
++Finish ioctl implementation.
++
++ start start using block device (major, minor specified)
++ stop stop using block device
++ status return current connection status
++ connect wait for connection
++ disconnect wait for dis-connection
++ connected return zero if connected, error otherwise
++ disconnected return zero if dis-connected, error otherwise
++
++
++7. tests
++
++Get the tests in the scripts directory working.
++
++These should verify the following:
++
++ 1. connect/disconnect while started/stopped
++ 2. start/stop while connected/disconnected
++
++
++Or as two matrixes:
++
++ Connected Disconnected
++ Start
++ Stop
++
++
++ Started Stopped
++ Connect
++ DisConnect
++
++
++All valid combinations of starting, stopping, connecting and disconnecting
++must be verified.
++
++
++
++8. Windows
++
++Get Windows test applicatoin working.
++
++9. Compatibility
++
++Test against:
++
++ WinXP
++ Win2k
++ Linux 2.4
++ (Linux 2.6)
++ (WinME)
++
++
++
++10. code review
++
++ - review other drivers to verify we have implemented all required
++ rbc/spc-2 commands and sense codes
++
++11. prevent removal
++
+diff -uNr linux/drivers/no-otg/functions/msc/crc.c linux/drivers/otg/functions/msc/crc.c
+--- linux/drivers/no-otg/functions/msc/crc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/crc.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * otg/msc_fd/crc.c - crc table
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Chris Lynne <cl@belcarra.com>
++ * Stuart Lynne <sl@belcarra.com>
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++
++#include <otg/otg-compat.h>
++
++//#include <usbd-chap9.h>
++//#include <usbd-mem.h>
++//#include <usbd.h>
++//#include <usbd-func.h>
++
++#include "crc.h"
++
++unsigned int *msc_crc32_table;
++
++/**
++ * Generate the crc32 table
++ *
++ * return non-zero if malloc fails
++ */
++int make_crc_table(void)
++{
++ unsigned int n;
++ if (msc_crc32_table) return 0;
++ if (!(msc_crc32_table = (unsigned int *)ckmalloc(256*4, GFP_KERNEL))) return -EINVAL;
++ for (n = 0; n < 256; n++) {
++ int k;
++ unsigned int c = n;
++ for (k = 0; k < 8; k++) {
++ c = (c & 1) ? (CRC32_POLY ^ (c >> 1)) : (c >> 1);
++ }
++ msc_crc32_table[n] = c;
++ }
++ return 0;
++}
++
++void free_crc_table(void)
++{
++ if (msc_crc32_table) {
++ lkfree(msc_crc32_table);
++ msc_crc32_table = NULL;
++ }
++}
++
++unsigned int crc32_compute(unsigned char *src, int len, unsigned int val)
++{
++ for (; len-- > 0; val = COMPUTE_FCS (val, *src++));
++ return val;
++}
++
+diff -uNr linux/drivers/no-otg/functions/msc/crc.h linux/drivers/otg/functions/msc/crc.h
+--- linux/drivers/no-otg/functions/msc/crc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/crc.h 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,29 @@
++/*
++ * otg/msc_fd/crc.c - crc table
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Chris Lynne <cl@belcarra.com>
++ * Stuart Lynne <sl@belcarra.com>
++ * Bruce Balden <balden@belcarra.com>
++ * Ted Powell <ted@belcarra.com>
++ *
++ *
++ */
++
++
++extern unsigned int *msc_crc32_table;
++
++#define CRC32_INIT 0xffffffff // Initial FCS value
++#define CRC32_GOOD 0xdebb20e3 // Good final FCS value
++
++#define CRC32_POLY 0xedb88320 // Polynomial for table generation
++
++#define COMPUTE_FCS(val, c) (((val) >> 8) ^ msc_crc32_table[((val) ^ (c)) & 0xff])
++
++int make_crc_table(void);
++void free_crc_table(void);
++unsigned int crc32_compute(unsigned char *src, int len, unsigned int val);
++
++
+diff -uNr linux/drivers/no-otg/functions/msc/msc-bo.c linux/drivers/otg/functions/msc/msc-bo.c
+--- linux/drivers/no-otg/functions/msc/msc-bo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-bo.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,176 @@
++/*
++ * otg/function/msc/msc-bo.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/msc/msc-bo.c
++ * @brief Mass Storage Driver private defines
++ *
++ * This is a Mass Storage Class Function that uses the Bulk Only protocol.
++ *
++ *
++ * @ingroup MSCFunction
++ */
++
++#include <otg/otg-compat.h>
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/random.h>
++#include <linux/slab.h>
++
++#include <linux/blkdev.h>
++
++
++#include "msc-scsi.h"
++#include "msc.h"
++#include "msc-fd.h"
++#include "crc.h"
++
++//#include "rbc.h"
++
++/*!
++ * Mass Storage Class - Bulk Only
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++static u8 msc_data_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, };
++static u8 msc_data_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, };
++static struct usbd_endpoint_descriptor *msc_default[] = {
++ (struct usbd_endpoint_descriptor *) msc_data_1,
++ (struct usbd_endpoint_descriptor *) msc_data_2, };
++u8 msc_indexes[] = { BULK_OUT, BULK_IN, };
++
++
++/*! Endpoint, Interface, Configuration and Device descriptions and descriptors
++ */
++static u8 msc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE,
++ 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
++ sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor), // bNumEndpoints
++ MASS_STORAGE_CLASS,
++ MASS_STORAGE_SUBCLASS_SCSI,
++ MASS_STORAGE_PROTO_BULK_ONLY,
++ 0x00,
++};
++
++static struct usbd_alternate_description msc_data_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_MSC_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&msc_data_alternate_descriptor,
++ endpoints:sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_list: msc_default,
++ endpoint_indexes: msc_indexes,
++ },
++};
++
++
++struct usbd_interface_description msc_interfaces[] = {
++ { alternates:sizeof (msc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:msc_data_alternate_descriptions,},
++};
++
++u8 msc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = {
++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength
++ sizeof (msc_interfaces) / sizeof (struct usbd_interface_description),
++ 0x01, 0x00, // bConfigurationValue, iConfiguration
++ 0, 0,
++};
++
++struct usbd_configuration_description msc_description[] = {
++ { iConfiguration: CONFIG_OTG_MSC_DESC,
++ configuration_descriptor: (struct usbd_configuration_descriptor *)msc_configuration_descriptor,
++ },
++};
++
++
++static struct usbd_device_descriptor msc_device_descriptor = {
++ bLength: sizeof(struct usbd_device_descriptor),
++ bDescriptorType: USB_DT_DEVICE,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: 0x00,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++static struct usbd_device_qualifier_descriptor msc_device_qualifier_descriptor = {
++ bLength: sizeof(struct usbd_device_qualifier_descriptor),
++ bDescriptorType: USB_DT_DEVICE_QUALIFIER,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: 0x00,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++
++
++
++static struct usbd_endpoint_request msc_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, },
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, },
++ { 0, },
++};
++
++struct usbd_otg_descriptor msc_otg_descriptor = {
++ bLength : sizeof(struct usbd_otg_descriptor),
++ bDescriptorType: USB_DT_OTG,
++ bmAttributes: 0,
++};
++
++struct usbd_device_description msc_device_description = {
++ device_descriptor: &msc_device_descriptor,
++#ifdef CONFIG_OTG_HIGH_SPEED
++ device_qualifier_descriptor: &msc_device_qualifier_descriptor,
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ otg_descriptor: &msc_otg_descriptor,
++ iManufacturer: CONFIG_OTG_MSC_MANUFACTURER,
++ iProduct: CONFIG_OTG_MSC_PRODUCT_NAME,
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR,
++#endif
++};
++
++
++extern struct usbd_function_operations msc_function_ops;
++
++struct usbd_function_driver msc_function_driver = {
++ name: "msc-bulkonly",
++ fops:&msc_function_ops,
++ device_description:&msc_device_description,
++ bNumConfigurations:sizeof (msc_description) / sizeof (struct usbd_configuration_description),
++ configuration_description:msc_description,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE),
++ bNumInterfaces:sizeof (msc_interfaces) / sizeof (struct usbd_interface_description),
++ interface_list:msc_interfaces,
++ endpointsRequested: ENDPOINTS,
++ requestedEndpoints: msc_endpoint_requests,
++};
++
+diff -uNr linux/drivers/no-otg/functions/msc/msc-fd.c linux/drivers/otg/functions/msc/msc-fd.c
+--- linux/drivers/no-otg/functions/msc/msc-fd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-fd.c 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,1233 @@
++/*
++ * otg/function/msc/msc.-fd.cc
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++
++/*!
++ * @file otg/functions/msc/msc-fd.c
++ * @brief Mass Storage Driver private defines
++ *
++ * This is a Mass Storage Class Function that uses the Bulk Only protocol.
++ *
++ *
++ * Notes:
++ *
++ * 1. Currently we only support the Bulk Only model. Microsoft states that
++ * further support for the mass storage driver will only be done for devices
++ * that conform to the Bulk Only model.
++ *
++ * 2. Multiple LUN's are not supported, but in theory they could be.
++ *
++ * 3. Error handling should be done with STALL but using ZLP seems to also
++ * work. ZLP is usually easier to implement (except possibly on the SA1100.)
++ * We may need to make STALL an option if we find devices (perhaps SA1100)
++ * that cannot reliaby send a ZLP on BULK-IN endpoint.
++ *
++ *
++ * 4. WinXP will match the following:
++ *
++ * USB: Class_08&SubClass_02&Prot_50
++ * USB: Class_08&SubClass_05&Prot_50
++ * USB: Class_08&SubClass_06&Prot_50
++ *
++ * SubClass 02 is MMC or SFF8020I
++ * SubClass 05 is SFF or SFF8070I
++ * SubClass 06 is SCSI
++ *
++ * From the Windows USB Storage FAQ:
++ *
++ * RBC not supported
++ *
++ * SubClass = 6 (SCSI)
++ * CDBs SHOULD NOT be padded to 12 bytes
++ * ModeSense/ModeSelect SHOULD NOT be translated from 1ah/15h to 5ah/55h
++ * should be used for FLASH
++ *
++ * SubClass !=6
++ * CDBs SHOULD be padded to 12 bytes
++ * ModeSense/ModeSelect SHOULD be translated from 1ah/15h to 5ah/55h
++ *
++ * We are using the former, SubClass = 6, and implement the required SCSI operations.
++ *
++ *
++ * TODO
++ *
++ *
++ *
++ * TODO FIXME Bus Interface Notes
++ *
++ * 1. The bus interface driver must correctly implement NAK'ing if not rcv_urb is
++ * present (see au1x00.c or wmmx.c for examples.)
++ *
++ * 2. The bus interface driver must implement USBD_URB_SENDZLP flag (see au1x00.c
++ * for example.)
++ *
++ * @ingroup MSCFunction
++ */
++
++
++#include <otg/otg-compat.h>
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/random.h>
++#include <linux/slab.h>
++
++#include <linux/blkdev.h>
++
++
++#include "msc-scsi.h"
++#include "msc.h"
++#include "msc-fd.h"
++#include "crc.h"
++
++
++/* Module Parameters ************************************************************************* */
++
++
++#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED
++#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT
++
++#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF
++#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON
++
++#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON
++
++#define DEVICE_PREVENT_REMOVAL 0x0020
++
++
++#define DEF_NUMBER_OF_HEADS 0x10
++#define DEF_SECTORS_PER_TRACK 0x20
++
++
++
++/* MSC ******************************************************************************************** */
++
++extern struct msc_private msc_private;
++
++int msc_urb_sent (struct usbd_urb *tx_urb, int rc);
++static int msc_recv_urb (struct usbd_urb *urb, int rc);
++
++otg_tag_t msc_fd_trace_tag;
++
++/* Sense Key *********************************************************************************** */
++
++/*! set_sense_data - set sensedata in msc private struture
++ * @param msc
++ * @param sensedata
++ * @param info
++ *
++ * This saves sense data. Sense data indicates what type of error
++ * has occurred and will be returned to the host when a request sense
++ * command is sent.
++ */
++void set_sense_data(struct msc_private *msc, u32 sensedata, u32 info)
++{
++ TRACE_SENSE(sensedata, info);
++ msc->sensedata = sensedata;
++ msc->info = info;
++}
++
++/* Check blockdev ****************************************************************************** */
++
++/*! msc_check_blockdev_name - check current status of the block device
++ *
++ * Check if the block device is operational, either generate a failed CSW
++ * or a ZLP if not ready.
++ *
++ * Returns non-zero if the block device is not available for I/O operations
++ * and a failed CSW has already been sent.
++ */
++int msc_check_blockdev_name(struct msc_private *msc, int zlp, char *name)
++{
++ if (msc->block_dev_state & DEVICE_EJECTED) {
++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_EJECTED");
++ ((SENDZLP == zlp) ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed)
++ (msc, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
++ return -EINVAL;
++ }
++ if (msc->block_dev_state & DEVICE_CHANGE_ON) {
++ msc->block_dev_state &= ~DEVICE_CHANGE_ON;
++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_CHANGE_ON");
++ ((SENDZLP == zlp) ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed)
++ (msc, SCSI_SENSEKEY_NOT_READY_TO_READY_CHANGE, msc->lba, USB_MSC_FAILED);
++ return -EINVAL;
++ }
++ //TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_INSERTED");
++ return 0;
++}
++
++/* Generic start recv urb and send csw ********************************************************* */
++
++/*! msc_start_recv - queue a receive urb
++ *
++ * Ensure that size is a multiple of the endpoint packetsize.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_start_recv_urb(struct usbd_function_instance *function, struct msc_private *msc, int size)
++{
++ struct usbd_urb *rcv_urb = NULL;
++ int wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function));
++ if ((size % wMaxPacketSize))
++ size = ((size + wMaxPacketSize) / wMaxPacketSize) * wMaxPacketSize;
++
++ RETURN_EINVAL_UNLESS((rcv_urb = usbd_alloc_urb (function, BULK_OUT, size, msc_recv_urb)));
++ rcv_urb->function_privdata = msc;
++ msc->rcv_urb_finished = NULL;
++ RETURN_ZERO_UNLESS(usbd_start_out_urb(rcv_urb));
++ TRACE_MSG0(MSC,"START RECV URB ERROR");
++ usbd_free_urb(rcv_urb);
++ return -EINVAL;
++}
++
++/*! msc_start_sending - start sending a new data or csw urb
++ *
++ * Generate a CSW (Command Status Wrapper) to send to the the host.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_start_sending_csw(struct usbd_function_instance *function, struct msc_private *msc, u8 status)
++{
++ COMMAND_STATUS_WRAPPER *csw;
++ int length = sizeof(COMMAND_STATUS_WRAPPER);
++ struct usbd_urb *tx_urb;
++
++ //TRACE_MSG1(MSC,"START SENDING CSW %08x", status);
++
++ msc->command_state = MSC_STATUS;
++
++ RETURN_EINVAL_UNLESS((tx_urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent)));
++
++ tx_urb->actual_length = length;
++ tx_urb->function_privdata = msc;
++
++ // fill in CSW and queue the urb
++ csw = (COMMAND_STATUS_WRAPPER *) tx_urb->buffer;
++ csw->dCSWSignature = CSW_SIGNATURE;
++ csw->dCSWTag = msc->command.dCBWTag;
++ csw->dCSWDataResidue = msc->command.dCBWDataTransferLength - msc->data_transferred_in_bytes;
++ csw->bCSWStatus = status;
++
++ TRACE_MSG2(MSC,"START SENDING CSW status: %02x data residue: %d", status, csw->dCSWDataResidue);
++
++ RETURN_ZERO_UNLESS(usbd_start_in_urb (tx_urb));
++ TRACE_MSG0(MSC,"START SENDING CSW FAILED");
++ usbd_free_urb (tx_urb);
++ return -EINVAL;
++}
++
++/*! msc_start_sending_csw_failed - starting sending a CSW showing failure
++ *
++ * Sets sensedata and generates a CSW with status set to USB_MSC_FAILED.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_start_sending_csw_failed(struct msc_private *msc, u32 sensedata, u32 info, int status)
++{
++ TRACE_MSG2(MSC, "sensedata: %x status: %x", sensedata, status);
++ set_sense_data(msc, sensedata, info);
++ return msc_start_sending_csw(msc->function, msc, status);
++}
++
++
++/* ********************************************************************************************* */
++
++/*! msc_alloc_urb - allocate an urb for returning a query
++ *
++ * Returns NULL if there is an error in the USB layer.
++ */
++struct usbd_urb * msc_alloc_urb(struct msc_private *msc, int length)
++{
++ struct usbd_function_instance *function;
++ struct usbd_urb *urb;
++
++ THROW_IF(!(function = msc->function), error);
++ THROW_IF(!(urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent)), error);
++ return urb;
++ CATCH(error) {
++ msc->command_state = MSC_READY;
++ return NULL;
++ }
++}
++
++/*! msc_dispatch_query_urb - dispatch an urb containing query data
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_dispatch_query_urb(struct usbd_urb *urb, struct msc_private *msc, int length, int status)
++{
++ int rc;
++ unsigned long flags;
++
++ TRACE_MSG1(MSC,"DISPATCH URB len: %d", length);
++
++ // save information in msc and urb
++ //
++ urb->function_privdata = msc;
++ urb->actual_length = msc->TransferLength_in_bytes = length;
++
++ // dispatch urb
++ local_irq_save(flags);
++ if ((rc = usbd_start_in_urb (urb))) {
++
++ TRACE_MSG0(MSC,"DISPATCH URB FAILED");
++ usbd_free_urb (urb);
++
++ // stall?
++ msc->command_state = MSC_READY;
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++ msc->command_state = MSC_QUERY;
++ msc->status = status;
++ local_irq_restore(flags);
++ return 0;
++}
++
++/*! msc_dispatch_query_urb_zlp - send a ZLP
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_dispatch_query_urb_zlp(struct msc_private *msc, u32 sensedata, u32 info, int status)
++{
++ struct usbd_urb *urb;
++ TRACE_MSG2(MSC, "sensedata: %x status: %x", sensedata, status);
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, 1)));
++ set_sense_data(msc, sensedata, info);
++ urb->flags |= USBD_URB_SENDZLP;
++ return msc_dispatch_query_urb(urb, msc, 0, status);
++}
++
++/* READ 10 COMMAND - read and send data to the host ******************************************** */
++extern int msc_scsi_read_10(struct msc_private *msc, char *name, int op);
++extern int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb, struct msc_private *msc);
++
++/* WRITE 10 - receive data from host and write to block device ********************************* */
++extern int msc_scsi_write_10(struct msc_private *msc, char *name, int op);
++extern void msc_recv_out_blocks(struct usbd_urb *rcv_urb, struct msc_private *msc);
++
++/* SCSI Commands ******************************************************************************* */
++
++/*! msc_scsi_inquiry - process an inquiry
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_inquiry(struct msc_private *msc, char *name, int op)
++{
++ SCSI_INQUIRY_COMMAND *command = (SCSI_INQUIRY_COMMAND *)&msc->command.CBWCB;
++ SCSI_INQUIRY_DATA *data;
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_INQUIRY_DATA);
++
++ /*
++ * C.f. SPC2 7.3 INQUIRY command
++ * C.f. Table 46 - Standard INQUIRY data format
++ *
++ * C.f. Table 47 - Peripheral Qualifier
++ *
++ * 000b The specified peripheral device type is currently connected to this
++ * logical unit.....
++ * 001b The device server is capable of of supporting the peripheral device
++ * type on this logical unit. However, the physical device is not currently
++ * connected to this logical unit.....
++ * 010b Reserved
++ * 011b The device server is not capable of supporting a physical device on
++ * this logical unit....
++ *
++ */
++
++ TRACE_MSG4(MSC,"INQUIRY EnableVPD: %02x LogicalUnitNumber: %02x PageCode: %02x AllocLen: %02x",
++ command->EnableVPD, command->LogicalUnitNumber, command->PageCode, command->AllocationLength);
++
++ // XXX THROW_IF(msc->command_state != MSC_READY, error);
++
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_INQUIRY_DATA *)urb->buffer;
++ data->PeripheralQaulifier = msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON) ? 0x1 : 0;
++ data->PeripheralDeviceType = 0x00;
++ data->RMB = 0x1;
++ data->ResponseDataFormat = 0x1;
++ data->AdditionalLength = 0x1f;
++
++ strncpy(data->VendorInformation, CONFIG_OTG_MSC_MANUFACTURER, strlen(CONFIG_OTG_MSC_MANUFACTURER));
++ strncpy(data->ProductIdentification, CONFIG_OTG_MSC_PRODUCT_NAME, strlen(CONFIG_OTG_MSC_PRODUCT_NAME));
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++/*! msc_scsi_read_format_capacity - process a query
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_read_format_capacity(struct msc_private *msc, char *name, int op)
++{
++ SCSI_READ_FORMAT_CAPACITY_DATA *data;
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_READ_FORMAT_CAPACITY_DATA);
++ u32 block_num = msc->capacity;
++ u32 block_len;
++
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_READ_FORMAT_CAPACITY_DATA *) urb->buffer;
++
++ data->CapacityListHeader.CapacityListLength = sizeof(data->CurrentMaximumCapacityDescriptor);
++
++ data->CurrentMaximumCapacityDescriptor.NumberofBlocks = block_num;
++ data->CurrentMaximumCapacityDescriptor.DescriptorCode = 0x03;
++ memcpy(data->CurrentMaximumCapacityDescriptor.BlockLength + 1, &block_len, sizeof(block_len));
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++/*! msc_read_capacity - process a read_capacity command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_read_capacity(struct msc_private *msc, char *name, int op)
++{
++ SCSI_READ_CAPACITY_COMMAND *command = (SCSI_READ_CAPACITY_COMMAND *)&msc->command.CBWCB;
++ SCSI_READ_CAPACITY_DATA *data;
++ struct usbd_urb *urb;
++ int length = 8;
++ u32 lba;
++
++ /*
++ * C.f. RBC 5.3
++ */
++ lba = be32_to_cpu(command->LogicalBlockAddress);
++
++ TRACE_MSG1(MSC,"READ CAPACITY LBA: %d", lba);
++
++ if ((command->PMI > 1) || (!command->PMI && lba)) {
++ TRACE_MSG1(MSC,"READ CAPACITY PMI: %d", command->PMI);
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, lba, USB_MSC_FAILED);
++ }
++
++ // alloc urb
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_READ_CAPACITY_DATA *) urb->buffer;
++
++ data->LastLogicalBlockAddress = cpu_to_be32(msc->capacity);
++ data->BlockLengthInBytes = cpu_to_be32(msc->block_size);
++
++ TRACE_MSG2(MSC,"RECV READ CAPACITY lba: %x block_size: %x",
++ be32_to_cpu(data->LastLogicalBlockAddress), be32_to_cpu(data->BlockLengthInBytes));
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++
++/*! msc_scsi_request_sense - process a request_sense command
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_request_sense(struct msc_private *msc, char *name, int op)
++{
++ SCSI_REQUEST_SENSE_COMMAND* command = (SCSI_REQUEST_SENSE_COMMAND *)&msc->command.CBWCB;
++ SCSI_REQUEST_SENSE_DATA *data;
++
++ /*
++ * C.f. SPC2 7.20 REQUEST SENSE command
++ */
++
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_REQUEST_SENSE_DATA);
++
++ // alloc urb
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_REQUEST_SENSE_DATA *) urb->buffer;
++ memset(command, 0x0, length);
++ data->ErrorCode = SCSI_ERROR_CURRENT;
++ data->SenseKey = msc->sensedata >> 16;
++ data->AdditionalSenseLength = 0xa; /* XXX is this needed */
++ data->AdditionalSenseCode = msc->sensedata >> 8;
++ data->AdditionalSenseCodeQualifier = msc->sensedata;
++ data->Valid = 1;
++
++ set_sense_data(msc, SCSI_SENSEKEY_NO_SENSE, 0);
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++/*! msc_scsi_mode_sense - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_mode_sense(struct msc_private *msc, char *name, int op)
++{
++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB;
++ SCSI_MODE_SENSE_DATA *data;
++ int length = sizeof(SCSI_MODE_SENSE_DATA);
++
++ struct usbd_urb *urb;
++ u8 *cp;
++
++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x",
++ command->DBD, command->PageControl, command->PageCode, 0);
++ length = 8;
++
++ // alloc urb
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ cp = (u8 *) urb->buffer;
++ memset(cp, 0x0, length);
++
++ cp[0] = 0;
++ cp[1] = 0;
++ cp[2] = 0; // 0x80 is writeprotect
++ cp[3] = 0x08;
++ cp[4] = 0;
++ cp[5] = 0;
++ cp[6] = 0;
++ cp[7] = 0;
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++/*! msc_scsi_mode_sense - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * XXX this doesn't work, need to re-implement and add these pages.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int new_msc_scsi_mode_sense(struct msc_private *msc, char *name, int op)
++{
++
++ /*
++ * C.f. SPC2 7.8.1 MODE SENSE(6) command
++ */
++
++ static READ_WRITE_ERROR_RECOVERY_PAGE page_01 = {
++ PageCode:0x01,
++ PageLength:0x0A,
++ ReadRetryCount:0x03,
++ WriteRetryCount:0x80,
++ };
++ static FLEXIBLE_DISK_PAGE page_05 = {
++ PageCode:0x05,
++ PageLength:0x1E,
++ TransferRate:__constant_cpu_to_be16(0xFA00),
++ NumberofHeads:0xA0,
++ SectorsperTrack:0x00,
++ DataBytesperSector:__constant_cpu_to_be16(0x0002),
++ NumberofCylinders:__constant_cpu_to_be16(0x0000),
++ MotorOnDelay:0x05,
++ MotorOffDelay:0x1E,
++ MediumRotationRate:__constant_cpu_to_be16(0x6801),
++ };
++ static REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE page_1b = {
++ PageCode:0x1B,
++ PageLength:0x0A,
++ TLUN:0x01,
++ };
++ static TIMER_AND_PROTECT_PAGE page_1c = {
++ PageCode:0x1c,
++ PageLength:0x06,
++ InactivityTimeMultiplier:0x0A,
++ };
++
++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB;
++ SCSI_MODE_SENSE_DATA *data;
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_MODE_SENSE_DATA);
++
++
++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x",
++ command->DBD, command->PageControl, command->PageCode, 0);
++
++
++ if (msc->block_dev_state & DEVICE_EJECTED) {
++ u16 sector = htons((unsigned short)msc->block_size);
++ u16 cylinder = 0;
++ page_05.NumberofHeads = 0;
++ page_05.SectorsperTrack = 0;
++ memcpy(&page_05.DataBytesperSector, &sector, sizeof(sector));
++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder));
++ }
++ else {
++ u16 sector = htons((unsigned short)msc->block_size);
++ u32 size = DEF_NUMBER_OF_HEADS * DEF_SECTORS_PER_TRACK * sector;
++ u16 cylinder = htons(msc->capacity / size);
++ page_05.NumberofHeads = DEF_NUMBER_OF_HEADS;
++ page_05.SectorsperTrack = DEF_SECTORS_PER_TRACK;
++ memcpy(&page_05.DataBytesperSector, &sector, sizeof(sector));
++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder));
++ }
++
++ if (SCSI_MODEPAGE_CONTROL_CURRENT != command->PageControl) {
++ TRACE_MSG2(MSC,"MODE SENSE - requeested other than CONTROL_CURRENT: %d %d",
++ command->PageControl, command->PageCode);
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED);
++ }
++
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++ data = (SCSI_MODE_SENSE_DATA *) urb->buffer;
++
++ data->ModeParameterHeader.WriteProtect = msc->block_dev_state & DEVICE_WRITE_PROTECTED ? 1 : 0;
++
++ switch (command->PageCode) {
++ case SCSI_MODEPAGE_ERROR_RECOVERY:
++ TRACE_MSG0(MSC, "MODEPAGE ERROR_RECOVERY");
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_01);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_01, sizeof(page_01));
++ break;
++ case SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE:
++ TRACE_MSG0(MSC, "MODEPAGE FLEXIBLE_DISK_PAGE");
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_05);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_05, sizeof(page_05));
++ break;
++ case SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS:
++ TRACE_MSG0(MSC, "MODEPAGE REMOVABLE_BLOCK_ACCESS");
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1b);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_1b, sizeof(page_1b));
++ break;
++ case SCSI_MODEPAGE_INFORMATION_EXCEPTIONS:
++ TRACE_MSG0(MSC, "MODEPAGE INFORMATION_EXCEPTIONS");
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1c);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_1c, sizeof(page_1c));
++ break;
++ case SCSI_MODEPAGE_ALL_SUPPORTED:
++ TRACE_MSG0(MSC, "MODEPAGE ALL_SUPPORTED");
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_ALL_PAGES);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages.ModeAllPages.ReadWriteErrorRecoveryPage, &page_01, sizeof(page_01));
++ memcpy(&data->ModePages.ModeAllPages.FlexibleDiskPage, &page_05, sizeof(page_05));
++ memcpy(&data->ModePages.ModeAllPages.RemovableBlockAccessCapabilitiesPage, &page_1b, sizeof(page_1b));
++ memcpy(&data->ModePages.ModeAllPages.TimerAndProtectPage, &page_1c, sizeof(page_1c));
++ break;
++ case SCSI_MODEPAGE_CACHING:
++ // see file_storage.c for an example if we want to support this
++ TRACE_MSG0(MSC, "MODEPAGE CACHING (not supported)");
++ break;
++ }
++
++ TRACE_MSG2(MSC,"LENGTH: %d %d", msc->command.dCBWDataTransferLength, length);
++
++ /*
++ * XXX verify that length is <= 256 bytes, return CHECK_CONDITION if it is
++ */
++ length = MIN(msc->command.dCBWDataTransferLength, length);
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++
++/*! msc_scsi_test_unit_ready - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_test_unit_ready(struct msc_private *msc, char *name, int op)
++{
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/*! msc_scsi_prevent_allow - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_prevent_allow(struct msc_private *msc, char *name, int op)
++{
++ SCSI_PREVENT_ALLOW_MEDIA_REMOVAL_COMMAND* command = (SCSI_PREVENT_ALLOW_MEDIA_REMOVAL_COMMAND*)&msc->command.CBWCB;
++
++ /*
++ * C.f. SPC2 7.12 Table 78 PREVENT ALLOW MEDIA REMOVAL Prevent Field
++ *
++ * 00b Medium removal shall be allowed from both the data transport
++ * element and the attached medium changer (if any).
++ * 01b Medium removal shall be prohibited from the data transport
++ * element but allowed from the attached medium changer (if any).
++ * 10b Medium removal shall be allowed for the data transport element
++ * but prohibited for the attached medium changer.
++ * 11b Medium remval shall be prohibited from both the data transport
++ * element and the attached medium changer
++ *
++ * Prevention shall terminate after 00b or 10b, after a SYNC CACHE or hard reset.
++ */
++
++ // XXX TODO
++ // this is from storageproto.c, shouldn't we implement something?
++ if (command->Prevent)
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED);
++
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/*! msc_scsi_start_stop - process a request_sense command
++ *
++ * C.f. RBC 5.4 and 5.4.2
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_start_stop(struct msc_private *msc, char *name, int op)
++{
++ SCSI_START_STOP_COMMAND* command = (SCSI_START_STOP_COMMAND*)&msc->command.CBWCB;
++
++ TRACE_MSG4(MSC,"START STOP: Immed: %d Power: %x LoEj: %d Start: %d",
++ command->IMMED, command->PowerConditions, command->LoEj, command->Start);
++ /*
++ * C.f. 5.4
++ *
++ * IMMED - if set return status immediately after command validation, otherwise
++ * return status as soon operation is completed.
++ *
++ * C.f. 5.4.1 Table 8 POWER CONDITIONS
++ *
++ * 0 - M - no change in power condition
++ * 1 - M - place device in active condition
++ * 2 - M - place device in idle condition
++ * 3 - M - place device in Standby condition
++ * 4 - - reserved
++ * 5 - M - place device in Sleep condition
++ * 6 - - reserved
++ * 7 - 0 - Device Control
++ *
++ * C.f. 5.4.2 Table 9 START STOP control bit definitions
++ *
++ * Power Load/Eject Start
++ * 1-7 x x LoEj and Start Ignored
++ * 0 0 0 Stop the medium
++ * 0 0 1 Make the medium ready
++ * 0 1 0 Unload the medium
++ * 0 1 1 Load the medium
++ *
++ */
++ // XXX TODO
++ // this is from storageproto.c, shouldn't we implement something?
++
++ if (command->Start && command->LoEj)
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED);
++
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++/*! msc_scsi_verify - process a verify command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_verify(struct msc_private *msc, char *name, int op)
++{
++ SCSI_VERIFY_COMMAND *command = (SCSI_VERIFY_COMMAND *)&msc->command.CBWCB;
++
++ /*
++ * C.f. RBC 5.7 VERIFY command
++ */
++ // XXX This actually should use the read_10 function and when
++ // finished reading simply send the following
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/*! msc_scsi_mode_select - process a select command
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_mode_select(struct msc_private *msc, char *name, int op)
++{
++ //SCSI_MODE_SELECT_COMMAND *command = (SCSI_MODE_SELECT_COMMAND *)&msc->command.CBWCB;
++
++ /*
++ * C.f. SPC2 7.6 MODE SELECT(6) command
++ */
++
++ // if less than correct amount of data return USB_MSC_PHASE_ERROR - see MV
++ //
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++/*! msc_private_pcs - process a private command
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_cmd_private_pcs(struct msc_private *msc, char *name, int op)
++{
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
++}
++
++/*! msc_cmd_unknown - process an unknown command
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_cmd_unknown(struct msc_private *msc, char *name, int op)
++{
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
++}
++
++struct rbc_dispatch {
++ u8 op;
++ char *name;
++ int (*rbc_command) (struct msc_private *, char *, int op);
++ int device_check;
++};
++
++/*! Command cross reference
++ *
++ * This is the list of commands observed from each host OS. It is necessarily
++ * incomplete in that we not have reached some condition necessary to have
++ * other commands used.
++ * Win2k WinXP
++ * SCSI_TEST_UNIT_READY yes yes
++ * SCSI_READ_10 yes yes
++ * SCSI_WRITE_10 yes yes
++ * SCSI_READ_CAPACITY yes yes
++ * SCSI_VERIFY yes
++ * SCSI_INQUIRY yes yes
++ * SCSI_MODE_SENSE yes
++ * SCSI_READ_FORMAT_CAPACITY yes yes
++ * SCSI_REQUEST_SENSE
++ * SCSI_PREVENT_ALLOW_MEDIA_REMOVAL
++ * SCSI_START_STOP
++ * SCSI_MODE_SELECT
++ * SCSI_FORMAT_UNIT
++ *
++ */
++
++struct rbc_dispatch rbc_dispatch_table[] = {
++ { SCSI_TEST_UNIT_READY, "SCSI_TEST_UNIT_READY", msc_scsi_test_unit_ready, NOZLP },
++ { SCSI_READ_10, "SCSI_READ_10", msc_scsi_read_10, SENDZLP },
++ { SCSI_WRITE_10, "SCSI_WRITE_10", msc_scsi_write_10, NOZLP },
++ { SCSI_READ_CAPACITY, "SCSI_READ_CAPACITY", msc_scsi_read_capacity, SENDZLP },
++ { SCSI_VERIFY, "SCSI_VERIFY", msc_scsi_verify, NOCHK },
++ { SCSI_INQUIRY, "SCSI_INQUIRY", msc_scsi_inquiry, NOCHK },
++ { SCSI_MODE_SENSE, "SCSI_MODE_SENSE", msc_scsi_mode_sense, NOCHK },
++ { SCSI_READ_FORMAT_CAPACITY, "SCSI_READ_FORMAT_CAPACITY", msc_scsi_read_format_capacity, SENDZLP },
++ { SCSI_REQUEST_SENSE, "SCSI_REQUEST_SENSE", msc_scsi_request_sense, NOCHK },
++ { SCSI_PREVENT_ALLOW_MEDIA_REMOVAL, "SCSI_PREVENT_ALLOW_MEDIA_REMOVAL", msc_scsi_prevent_allow, NOZLP },
++ { SCSI_START_STOP, "SCSI_START_STOP", msc_scsi_start_stop, NOZLP },
++ { SCSI_MODE_SELECT, "SCSI_MODE_SELECT", msc_scsi_mode_select, NOCHK },
++ { SCSI_FORMAT_UNIT, "SCSI_FORMAT_UNIT", msc_cmd_unknown, NOCHK },
++
++ { SCSI_READ_6, "SCSI_READ_6", msc_cmd_unknown, NOCHK },
++ { SCSI_WRITE_6, "SCSI_WRITE_6", msc_cmd_unknown, NOCHK },
++ { SCSI_RESERVE, "SCSI_RESERVE", msc_cmd_unknown, NOCHK },
++ { SCSI_RELEASE, "SCSI_RELEASE", msc_cmd_unknown, NOCHK },
++ { SCSI_SEND_DIAGNOSTIC, "SCSI_SEND_DIAGNOSTIC", msc_cmd_unknown, NOCHK },
++ { SCSI_SYNCHRONIZE_CACHE, "SCSI_SYNCHRONIZE_CACHE", msc_cmd_unknown, NOCHK },
++ { SCSI_MODE_SENSE_10, "SCSI_MODE_SENSE_10", msc_cmd_unknown, NOCHK },
++ { SCSI_REZERO_UNIT, "SCSI_REZERO_UNIT", msc_cmd_unknown, NOCHK },
++ { SCSI_SEEK_10, "SCSI_SEEK_10", msc_cmd_unknown, NOCHK },
++ { SCSI_WRITE_AND_VERIFY, "SCSI_WRITE_AND_VERIFY", msc_cmd_unknown, NOCHK },
++ { SCSI_WRITE_12, "SCSI_WRITE_12", msc_cmd_unknown, NOCHK },
++ { SCSI_READ_12, "SCSI_READ_12", msc_cmd_unknown, NOCHK },
++
++ { SCSI_PRIVATE_PCS, "SCSI_PRIVATE_PCS", msc_cmd_private_pcs, NOCHK },
++
++ { 0xff, "SCSI_UNKNOWN", msc_cmd_unknown, NOCHK },
++};
++
++
++/*! msc_recv_command - process a new CBW
++ *
++ * Return non-zero if urb was not disposed of.
++ */
++void msc_recv_command(struct usbd_urb *urb, struct msc_private *msc)
++{
++ COMMAND_BLOCK_WRAPPER *command = (COMMAND_BLOCK_WRAPPER *)urb->buffer;
++ u8 op = command->CBWCB[0];
++ struct rbc_dispatch *dispatch;
++
++ /*
++ * c.f. section 6.2 - Valid and Meaningful CBW
++ * c.f. section 6.2.1 - Valid CBW
++ *
++ * The CBW was received after the device had sent a CSW or after a
++ * reset XXX check that we only set MSC_READY after reset or sending
++ * CSW.
++ *
++ * The CBW is 31 (1Fh) bytes in length and the bCBWSignature is
++ * equal to 43425355h.
++ */
++ THROW_IF(31 != urb->actual_length, error);
++ THROW_IF(CBW_SIGNATURE != le32_to_cpu(command->dCBWSignature), error);
++
++ /*
++ * c.f. section 6.2.2 - Meaningful CBW
++ *
++ * no reserved bits are set
++ * the bCBWLUN contains a valid LUN supported by the device
++ * both bCBWCBlength and the content of the CBWCB are in accordance with bInterfaceSubClass
++ */
++
++ // XXX checklun etc
++
++ /*
++ * Success
++ */
++ memcpy(&msc->command, command, sizeof(COMMAND_BLOCK_WRAPPER));
++ msc->data_transferred_in_bytes = msc->TransferLength_in_blocks = msc->TransferLength_in_bytes = 0;
++
++ TRACE_TAG(command->dCBWTag, urb->framenum);
++ usbd_free_urb(urb);
++ urb = NULL;
++
++ /*
++ * Search using the opcode to find the dispatch function to use and
++ * call it.
++ */
++ for (dispatch = rbc_dispatch_table; dispatch->op != 0xff; dispatch++) {
++ CONTINUE_UNLESS ((dispatch->op == op));
++
++ TRACE_CBW(dispatch->name, dispatch->op, dispatch->device_check);
++ TRACE_RECV(&(command->CBWCB[1]));
++
++ /* Depending on the command we may need to check if the device is available
++ * and either fail or send a ZLP if it is not
++ */
++ if (dispatch->device_check)
++ RETURN_IF (msc_check_blockdev_name(msc, dispatch->device_check, dispatch->name));
++
++ /* Call the specific function that implements the specified command
++ */
++ if (dispatch->rbc_command(msc, dispatch->name, op))
++ TRACE_MSG0(MSC,"COMMAND ERROR");
++ return;
++ }
++
++ /* FALL THROUGH if no match is found */
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"RECV CBW ERROR");
++ if (urb)
++ usbd_free_urb(urb);
++
++ /* XXX which of these do we stall?
++ */
++ usbd_halt_endpoint(urb->function_instance, BULK_IN);
++ usbd_halt_endpoint(urb->function_instance, BULK_OUT);
++ }
++ msc_cmd_unknown(msc, "CMD_UNKNOWN", op);
++}
++
++
++/* Sent Function - process a sent urb ********************************************************** */
++
++/*! msc_urb_sent - called to indicate URB transmit finished
++ * @param tx_urb: pointer to struct usbd_urb
++ * @param rc: result
++ *
++ * This is called when an urb is sent. Depending on current state
++ * it may:
++ *
++ * - continue sending data
++ * - send a CSW
++ * - start a recv for a CBW
++ *
++ * This is called from BOTTOM HALF context.
++ *
++ * @return non-zero if urb was not disposed of.
++ */
++int msc_urb_sent (struct usbd_urb *tx_urb, int rc)
++{
++ struct usbd_function_instance *function;
++ struct msc_private *msc = &msc_private;
++
++ RETURN_EINVAL_IF(!(function = tx_urb->function_instance));
++ RETURN_EINVAL_IF(usbd_get_device_status(function) == USBD_CLOSING);
++ RETURN_EINVAL_IF(usbd_get_device_state(function) != STATE_CONFIGURED);
++
++ switch (msc->command_state) {
++ case MSC_DATA_IN_READ:
++ case MSC_DATA_IN_READ_FINISHED:
++ TRACE_MSG0(MSC,"URB SENT READ");
++ return msc_in_read_10_urb_sent(tx_urb, msc);
++
++ case MSC_QUERY:
++ // finished, send CSW
++ TRACE_MSG0(MSC,"URB SENT QUERY");
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++ break;
++
++ case MSC_STATUS:
++ default:
++ // sent a CSW need to receive the next CBW
++ TRACE_MSG0(MSC,"URB SENT STATUS");
++ msc->command_state = MSC_READY;
++ msc_start_recv_urb(msc->function, msc, sizeof(COMMAND_BLOCK_WRAPPER));
++ break;
++ }
++ usbd_free_urb (tx_urb);
++ return 0;
++}
++
++
++/* Receive Function - receiving an urb ********************************************************* */
++
++/*! msc_recv_urb - process a received urb
++ *
++ * Return non-zero if urb was not disposed of.
++ */
++static int msc_recv_urb (struct usbd_urb *rcv_urb, int rc)
++{
++ struct msc_private *msc = &msc_private;
++
++ RETURN_EINVAL_IF(!msc->connected);
++
++ //TRACE_MSG2(MSC, "RECV URB length: %d state: %d", rcv_urb->actual_length, msc->command_state);
++
++ switch(msc->command_state) {
++
++ // ready to start a new transaction
++ case MSC_READY:
++ msc_recv_command(rcv_urb, msc);
++ return 0;
++
++ // we think we are receiving data
++ case MSC_DATA_OUT_WRITE:
++ case MSC_DATA_OUT_WRITE_FINISHED:
++ msc_recv_out_blocks(rcv_urb, msc);
++ return 0;
++
++ // we think we are sending data
++ case MSC_DATA_IN_READ:
++ case MSC_DATA_IN_READ_FINISHED:
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
++ break;
++
++ // we think we are sending status
++ case MSC_STATUS:
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
++ break;
++
++ // we don't think
++ case MSC_UNKNOWN:
++ default:
++ TRACE_MSG0(MSC,"RECV URB ERROR");
++ usbd_halt_endpoint(rcv_urb->function_instance, BULK_OUT);
++ }
++ // let caller dispose of urb
++ return -EINVAL;
++}
++
++/* USB Device Functions ************************************************************************ */
++
++
++static void msc_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
++{
++ TRACE_MSG0(MSC,"--");
++}
++
++static void msc_set_configuration (struct usbd_function_instance *function, int wValue)
++{
++ TRACE_MSG1(MSC,"wValue: %02x", wValue);
++
++}
++
++static void msc_set_interface (struct usbd_function_instance *function, int wIndex, int wValue)
++{
++ TRACE_MSG2(MSC,"wIndex: %02x wValue: %02x", wIndex, wValue);
++
++}
++
++static void msc_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress)
++{
++ TRACE_MSG1(MSC,"bEndpointAddress: %02x", bEndpointAddress);
++
++}
++
++/* USB Device Functions ************************************************************************ */
++/*! msc_event_handler - process a device event
++ *
++ * This function is called when an USBD event occurs.
++ *
++ * This is called from INTERRUPT context.
++ */
++void msc_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data)
++{
++ unsigned long flags;
++ struct msc_private *msc = &msc_private;
++ int connected;
++ switch (event) {
++ case DEVICE_CONFIGURED:
++ TRACE_MSG0(MSC,"EVENT CONFIGURED");
++ msc->connected = 1;
++ msc->command_state = MSC_READY;
++ msc_start_recv_urb(function, msc, sizeof(COMMAND_BLOCK_WRAPPER));
++ #if 0
++ local_irq_save(flags);
++ if (msc->io_state & MSC_IOCTL_WAITING) {
++ msc->io_state &= ~MSC_IOCTL_WAITING;
++ TRACE_MSG0(MSC, "WAKEUP");
++ }
++ local_irq_restore(flags);
++ #endif
++ wake_up_interruptible(&msc->ioctl_wq);
++ break;
++
++ case DEVICE_BUS_INACTIVE:
++ case DEVICE_RESET:
++ case DEVICE_DE_CONFIGURED:
++ TRACE_MSG2(MSC,"EVENT RESET: connected %d msc->io_state", msc->connected, msc->io_state);
++ connected = msc->connected;
++ msc->connected = 0;
++ #if 0
++ local_irq_save(flags);
++ if (msc->io_state & MSC_IOCTL_WAITING) {
++ msc->io_state &= ~MSC_IOCTL_WAITING;
++ TRACE_MSG0(MSC, "WAKEUP");
++ wake_up_interruptible(&msc->ioctl_wq);
++ }
++ local_irq_restore(flags);
++ #endif
++ wake_up_interruptible(&msc->ioctl_wq);
++ BREAK_UNLESS(connected);
++
++ // XXX we should have a semaphore to protect this
++ BREAK_UNLESS (msc->rcv_urb_finished);
++ usbd_free_urb (msc->rcv_urb_finished);
++ msc->rcv_urb_finished = NULL;
++ break;
++
++ default:
++ TRACE_MSG0(MSC,"EVENT IGNORED");
++ break;
++ }
++}
++
++/*! msc_device_request - called to indicate urb has been received
++ *
++ * This function is called when a SETUP packet has been received that
++ * should be handled by the function driver. It will not be called to
++ * process the standard chapter nine defined requests.
++ *
++ * Return non-zero for failure.
++ */
++int msc_device_handler (struct usbd_function_instance *function, struct usbd_device_request *request)
++{
++ struct msc_private *msc = &msc_private;
++ struct usbd_urb *urb;
++
++ TRACE_MSG0(MSC,"RECV SETUP");
++
++ // verify that this is a usb class request per cdc-acm specification or a vendor request.
++ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR)));
++
++ // determine the request direction and process accordingly
++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
++
++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
++ switch (request->bRequest) {
++ case MSC_BULKONLY_RESET:
++ // XXX TODO FIXME
++ return 0;
++ }
++
++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
++ switch (request->bRequest) {
++ case MSC_BULKONLY_GETMAXLUN:
++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, 1, NULL)));
++ urb->buffer[0] = 0;
++ urb->actual_length = 1;
++ RETURN_ZERO_IF(!usbd_start_in_urb(urb));
++ usbd_free_urb(urb);
++ return -EINVAL;
++ }
++ default:
++ break;
++ }
++ return -EINVAL;
++}
++
++/*! msc_function_enable - this is called by the USBD core layer
++ *
++ * This is called to initialize the function when a bus interface driver
++ * is loaded.
++ */
++static int msc_function_enable (struct usbd_function_instance *function)
++{
++ struct msc_private *msc = &msc_private;
++
++ MOD_INC_USE_COUNT;
++
++ // XXX TODO need to verify that serial number is minimum of 12
++
++ msc->function = function;
++ msc->command_state = MSC_READY;
++
++ return 0;
++}
++
++/*! msc_function_disable - this is called by the USBD core layer
++ *
++ * This is called to close the function when a bus interface driver
++ * is unloaded.
++ */
++static void msc_function_disable (struct usbd_function_instance *function)
++{
++ struct msc_private *msc = &msc_private;
++
++ TRACE_MSG0(MSC,"FUNCTION EXIT");
++
++ msc->function = NULL;
++
++ MOD_DEC_USE_COUNT;
++}
++
++/* ********************************************************************************************* */
++struct usbd_function_operations msc_function_ops = {
++ event_handler: msc_event_handler,
++ device_request: msc_device_request,
++ function_enable: msc_function_enable,
++ function_disable: msc_function_disable,
++ endpoint_cleared: msc_device_request,
++ endpoint_cleared: msc_set_configuration,
++ endpoint_cleared: msc_set_interface,
++ endpoint_cleared: msc_endpoint_cleared,
++};
++
++/* ********************************************************************************************* */
+diff -uNr linux/drivers/no-otg/functions/msc/msc-fd.h linux/drivers/otg/functions/msc/msc-fd.h
+--- linux/drivers/no-otg/functions/msc/msc-fd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-fd.h 2006-09-01 21:41:27.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ * otg/msc_fd/msc.h - Mass Storage Class
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++
++#ifndef MSC_FD_H
++#define MSC_FD_H 1
++
++extern int msc_dispatch_query_urb(struct usbd_urb *urb, struct msc_private *msc, int length, int status);
++extern int msc_start_sending_csw(struct usbd_function_instance *function, struct msc_private *msc, u8 status);
++extern int msc_dispatch_query_urb_zlp(struct msc_private *msc, u32 sensedata, u32 info, int status);
++extern int msc_start_sending_csw_failed(struct msc_private *msc, u32 sensedata, u32 info, int status);
++extern int msc_start_recv_urb(struct usbd_function_instance *function, struct msc_private *msc, int size);
++
++#define NOCHK 0
++#define NOZLP 1
++#define SENDZLP 2
++
++#if 1
++static __inline__ void TRACE_SENSE(unsigned int sense, unsigned int info)
++{
++ TRACE_MSG2(MSC, "--> SENSE: %06x INFO: %08x", sense, info);
++}
++static __inline__ void TRACE_RLBA(unsigned int lba, unsigned int crc)
++{
++ TRACE_MSG2(MSC, "<-- rlba [%8x %08x]", lba, crc);
++}
++static __inline__ void TRACE_SLBA(unsigned int lba, unsigned int crc)
++{
++ TRACE_MSG2(MSC, "--> slba [%8x %08x]", lba, crc);
++}
++static __inline__ void TRACE_TLBA(unsigned int lba, unsigned int crc)
++{
++ TRACE_MSG2(MSC, "--> tlba [%8x %08x]", lba, crc);
++}
++static __inline__ void TRACE_TAG(unsigned int tag, unsigned int frame)
++{
++ TRACE_MSG2(MSC, "--> TAG: %8x FRAME: %03x", tag, frame);
++}
++static __inline__ void TRACE_CBW(char *msg, int val, int check)
++{
++ TRACE_MSG3(MSC, " --> %s %02x check: %d", msg, val, check);
++}
++static __inline__ void TRACE_RECV(unsigned char *cp)
++{
++ TRACE_MSG8(MSC, "<-- recv [%02x %02x %02x %02x %02x %02x %02x %02x]",
++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
++}
++static __inline__ void TRACE_SENT(unsigned char *cp)
++{
++ TRACE_MSG8(MSC, "--> sent [%02x %02x %02x %02x %02x %02x %02x %02x]",
++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
++}
++
++#endif
++
++
++#endif /* MSC_H */
+diff -uNr linux/drivers/no-otg/functions/msc/msc-io-l24.c linux/drivers/otg/functions/msc/msc-io-l24.c
+--- linux/drivers/no-otg/functions/msc/msc-io-l24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-io-l24.c 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,272 @@
++/*
++ * otg/function/msc/msc-io-l24.c - MSC IO
++ *
++ * Copyright (c) 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@lbelcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/msc/msc-io-l24.c
++ * @brief
++ *
++ * NOTES
++ *
++ * TODO
++ *
++ * 1. implement prevent removal command.
++ *
++ * @ingroup MSCFunction
++ */
++
++#include <otg/otg-compat.h>
++#include <otg/otg-module.h>
++
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++#include <otg/otg-api.h>
++
++#include <linux/poll.h>
++#include <linux/sched.h>
++#include <linux/devfs_fs_kernel.h>
++
++#include "msc-scsi.h"
++#include "msc.h"
++#include "msc-fd.h"
++#include "crc.h"
++#include "msc-io.h"
++
++extern void msc_open_blockdev (struct msc_private *msc);
++extern void msc_close_blockdev (struct msc_private *msc);
++extern int msc_connection_blockdev (struct msc_private *msc);
++extern struct msc_private msc_private;
++
++#define DEVICE_EJECTED 0x0001 //MEDIA_EJECTED
++#define DEVICE_INSERTED 0x0002 //MEDIA_INSERT
++#define DEVICE_MOUNTED 0x0001 //DEVICE_MOUNTED
++#define DEVICE_UNMOUNTED 0x0002 //DEVICE_UNMOUNTED
++
++extern u32 major;
++extern u32 minor;
++#if 0
++void msc_io_wait(struct msc_private *msc, u32 flag)
++{
++ unsigned long flags;
++ msc->io_state |= MSC_IOCTL_WAITING | flag;
++ TRACE_MSG1(MSC, "SLEEPING io_state: %x", msc->io_state);
++ interruptible_sleep_on(&msc->ioctl_wq);
++}
++
++void msc_io_wakup(struct msc_private *msc)
++{
++ unsigned long flags;
++ local_irq_save(flags);
++ if (msc->io_state & MSC_IOCTL_WAITING) {
++ msc->io_state &= ~MSC_IOCTL_WAITING;
++ TRACE_MSG0(MSC, "WAKEUP");
++ wake_up_interruptible(&msc->ioctl_wq);
++ }
++ local_irq_restore(flags);
++}
++#endif
++/*! msc_io_ioctl_internal
++ */
++int msc_io_ioctl_internal(unsigned int cmd, unsigned long arg)
++{
++ int i;
++ int len;
++ int flag;
++
++ static char func_buf[32];
++ struct otgmsc_mount mount;
++ struct msc_private *msc = &msc_private;
++ unsigned long flags;
++
++ TRACE_MSG2(MSC, "cmd: %d connect: %d", cmd, msc->connected);
++ memset(&mount, 0, sizeof(mount));
++ switch (cmd) {
++
++ /* Mount - make the specified major/minof device available for use
++ * by the USB Host, this does not require an active connection.
++ */
++ case OTGMSC_START:
++ TRACE_MSG0(MSC, "Mounting the device");
++ RETURN_EINVAL_IF(copy_from_user(&mount, (void *)arg, _IOC_SIZE(cmd)));
++
++ TRACE_MSG3(MSC, "major=%d minor=%d state=%d", mount.major, mount.minor, msc->block_dev_state);
++ major = mount.major;
++ minor = mount.minor;
++
++ //msc->io_state = MSC_INACTIVE;
++ msc_open_blockdev (msc);
++ RETURN_EAGAIN_UNLESS(msc->command_state == MSC_READY);
++
++ mount.status = (msc->block_dev_state == DEVICE_INSERTED) ? DEVICE_MOUNTED : DEVICE_UNMOUNTED;
++
++ TRACE_MSG1(MSC, "Device is mounted status: %d", mount.status);
++
++ // XXX Need to copy result back to user space
++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
++ TRACE_MSG0(MSC, "Device mounted");
++ return 0;
++
++ /* Umount - make the currently mounted device unavailable to the USB Host,
++ * if there is pending block i/o block until it has finished.
++ * Note that if the driver is unloaded the waiting ioctl process
++ * must be woken up and informed with error code.
++ */
++ case OTGMSC_STOP:
++
++ TRACE_MSG0(MSC, "Unmounting the device");
++
++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
++
++ if (msc->command_state != MSC_READY) {
++ TRACE_MSG0(MSC, "SLEEPING");
++ interruptible_sleep_on(&msc->ioctl_wq);
++ TRACE_MSG0(MSC, "AWAKE");
++ }
++
++ RETURN_EAGAIN_UNLESS(msc->command_state == MSC_INACTIVE);
++
++ // XXX Need to copy result back to user space
++ msc->major = mount.major;
++ msc->minor = mount.minor;
++ msc_close_blockdev(msc);
++ mount.status = DEVICE_UNMOUNTED;
++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
++ TRACE_MSG0(MSC, "Device unmounted");
++ return 0;
++
++
++ /* Status - return the current mount status.
++ */
++ case OTGMSC_STATUS:
++ TRACE_MSG0(MSC, "Mount status");
++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
++ if (msc->block_dev_state == DEVICE_EJECTED)
++ mount.status = DEVICE_UNMOUNTED;
++ else
++ mount.status = DEVICE_MOUNTED;
++
++ // XXX Need to copy result back to user space
++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
++ return 0;
++
++ /* Wait_Connect - if not already connected wait until connected,
++ * Note that if the driver is unloaded the waiting ioctl process
++ * must be woken up and informed with error code.
++ */
++ case OTGMSC_WAIT_CONNECT:
++ TRACE_MSG1(MSC, "Wait for connect: connected: %d", msc->connected);
++ local_irq_save(flags);
++
++ if (msc->connected == 0) {
++ TRACE_MSG0(MSC, "SLEEPING");
++ interruptible_sleep_on (&msc->ioctl_wq);
++ TRACE_MSG0(MSC, "AWAKE");
++ }
++ RETURN_EAGAIN_UNLESS(msc->connected);
++
++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
++ TRACE_MSG0(MSC, "Device connected");
++ return 0;
++
++ /* Wait_DisConnect - if not already disconnected wait until disconnected,
++ * Note that if the driver is unloaded the waiting ioctl process
++ * must be woken up and informed with error code.
++ */
++ case OTGMSC_WAIT_DISCONNECT:
++ TRACE_MSG1(MSC, "Wait for disconnect: connected: %d", msc->connected);
++
++ if (msc->connected == 1) {
++ TRACE_MSG0(MSC, "SLEEPING");
++ interruptible_sleep_on (&msc->ioctl_wq);
++ TRACE_MSG0(MSC, "AWAKE");
++ }
++ RETURN_EAGAIN_IF(msc->connected);
++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
++ TRACE_MSG0(MSC, "Device disconnected");
++ return 0;
++
++ default:
++ TRACE_MSG1(MSC, "Unknown command: %x", cmd);
++ TRACE_MSG1(MSC, "OTGMSC_START: %x", OTGMSC_START);
++ TRACE_MSG1(MSC, "OTGMSC_WRITEPROTECT: %x", OTGMSC_WRITEPROTECT);
++ TRACE_MSG1(MSC, "OTGMSC_STOP: %x", OTGMSC_STOP);
++ TRACE_MSG1(MSC, "OTGMSC_STATUS: %x", OTGMSC_STATUS);
++ TRACE_MSG1(MSC, "OTGMSC_WAIT_CONNECT: %x", OTGMSC_WAIT_CONNECT);
++ TRACE_MSG1(MSC, "OTGMSC_WAIT_DISCONNECT: %x", OTGMSC_WAIT_DISCONNECT);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++
++
++/*! msc_io_ioctl
++ */
++int msc_io_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ int i;
++ int len;
++ int flag;
++
++ //printk(KERN_INFO"%s: cmd: %08x arg: %08x\n", __FUNCTION__, cmd, arg);
++ TRACE_MSG6(MSC, "cmd: %08x arg: %08x type: %02d nr: %02d dir: %02d size: %02d",
++ cmd, arg, _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));
++
++ RETURN_EINVAL_UNLESS (_IOC_TYPE(cmd) == OTGMSC_MAGIC);
++ RETURN_EINVAL_UNLESS (_IOC_NR(cmd) <= OTGMSC_MAXNR);
++
++ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_READ) && !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)));
++ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_WRITE) && !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)));
++
++ return msc_io_ioctl_internal(cmd, arg);
++}
++
++
++
++int msc_io_proc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ return msc_io_ioctl(inode, filp, cmd, arg);
++}
++
++
++static struct file_operations msc_io_proc_switch_functions = {
++ ioctl:msc_io_proc_ioctl,
++};
++
++
++/* msc_io_init_l24 - initialize
++ */
++int msc_io_init_l24(void)
++{
++ struct proc_dir_entry *message = NULL;
++
++ THROW_IF (!(message = create_proc_entry ("msc_io", 0666, 0)), error);
++ message->proc_fops = &msc_io_proc_switch_functions;
++ CATCH(error) {
++ printk(KERN_ERR"%s: creating /proc/msc_io failed\n", __FUNCTION__);
++ if (message)
++ remove_proc_entry("msc_io", NULL);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++/* msc_io_exit_l24 - exit
++ */
++void msc_io_exit_l24(void)
++{
++ remove_proc_entry("msc_io", NULL);
++}
++
++
++
+diff -uNr linux/drivers/no-otg/functions/msc/msc-io.h linux/drivers/otg/functions/msc/msc-io.h
+--- linux/drivers/no-otg/functions/msc/msc-io.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-io.h 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,57 @@
++/*
++ * otg/functions/msc/msc-io.h - Mass Storage Class
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @file otg/functions/msc/msc-io.h
++ * @brief Mass Storage Driver private defines
++ *
++ * OTGMSC_START - make specified major/minor device available to USB Host
++ * OTGMSC_WRITEPROTECT - set or reset write protect flag
++ *
++ * OTGMSC_STOP - remove access to current device, block until pending I/O finished
++ * OTGMSC_STATUS - remove current USB connection status (connected or disconnected)
++ * OTGMSC_WAIT_CONNECT - wait until device is connected (may return immediately if already connected)
++ * OTGMSC_WAIT_DISCONNECT - wait until device is disconnected (may return immediately if already disconnected)
++ *
++ * @ingroup MSCFunction
++ */
++
++//#ifndef MSC_H
++//#define MSC_H 1
++
++#define MSC_IO "/proc/msc_io"
++
++struct otgmsc_mount {
++ int major;
++ int minor;
++ int lun;
++ int writeprotect;
++ int result;
++ int status;
++};
++
++#define OTGMSC_MAGIC 'M'
++#define OTGMSC_MAXNR 10
++
++#define OTGMSC_START _IOWR(OTGMSC_MAGIC, 1, struct otgmsc_mount)
++#define OTGMSC_WRITEPROTECT _IOWR(OTGMSC_MAGIC, 2, struct otgmsc_mount)
++
++#define OTGMSC_STOP _IOR(OTGMSC_MAGIC, 1, struct otgmsc_mount)
++#define OTGMSC_STATUS _IOR(OTGMSC_MAGIC, 2, struct otgmsc_mount)
++#define OTGMSC_WAIT_CONNECT _IOR(OTGMSC_MAGIC, 3, struct otgmsc_mount)
++#define OTGMSC_WAIT_DISCONNECT _IOR(OTGMSC_MAGIC, 4, struct otgmsc_mount)
++
++
++#define MSC_CONNECTED 0x01
++#define MSC_DISCONNECTED 0x02
++#define MSC_WRITEPROTECTED 0x04
++
++//#endif /* MSC_H */
++
+diff -uNr linux/drivers/no-otg/functions/msc/msc-linux.c linux/drivers/otg/functions/msc/msc-linux.c
+--- linux/drivers/no-otg/functions/msc/msc-linux.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-linux.c 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,935 @@
++/*
++ * otg/function/msc/msc-linux.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ *
++ */
++
++/*!
++ * @file otg/functions/msc/msc-linux.c
++ * @brief Mass Storage Driver private defines
++ *
++ *
++ * This is a Mass Storage Class Function that uses the Bulk Only protocol.
++ *
++ * To use simply load with something like:
++ *
++ * insmod msc_fd.o vendor_id=0xffff product_id=0xffff
++ *
++ * Notes:
++ *
++ * 1. Currently block I/O is done a page at a time. I have not determined if
++ * it is possible to dispatch a multiple page generic request. It should at
++ * least be possible to queue page requests.
++ *
++ * 2. Currently for READ operations we have a maximum of one outstanding
++ * read block I/O and send urb. These are allowed to overlap so that we can
++ * continue to read data while sending data to the host.
++ *
++ * 3. Currently for WRITE operations we have a maximum of one outstanding
++ * recv urb and one outstanding write block I/O. These are allowed to
++ * overlap so that we can continue to receive data from the host while
++ * waiting for writing to complete.
++ *
++ * 4. It would be possible to allow multiple writes to be pending, to the
++ * limit of the page cache, if desired.
++ *
++ * 5. It should be possible to allow multiple outstanding reads to be
++ * pending, to the limit of the page cache, but this potentially could
++ * require dealing with out of order completions of the reads. Essentially a
++ * list of completed buffer heads would be required to hold any completed
++ * buffer heads that cannot be sent prior to another, earlier request being
++ * completed.
++ *
++ * 6. Currently ioctl calls are available to start and stop device i/o.
++ *
++ * 7. The driver can optionally generate trace messages showing each sectors
++ * CRC as read or written with LBA. These can be compared by user programs to
++ * ensure that the correct data was read and/or written.
++ *
++ *
++ * TODO
++ *
++ * 1. error handling for block io, e.g. what if using with removable block
++ * device (like CF card) and it is removed.
++ *
++ * 2. Currently we memcpy() data from between the urb buffer and buffer
++ * head page. It should be possible to simply use the page buffer for the
++ * urb.
++ *
++ * 3. Should we offer using fileio as an option? This would allow direct access
++ * to a block device image stored in a normal file or direct access to (for example)
++ * ram disks. It would require implementing a separate file I/O kernel thread to
++ * do the actual I/O.
++ *
++ * 4. It may be interesting to support use of SCSI block device and pass the
++ * scsi commands directly to that. This would allow vendor commands for real
++ * devices to be passed through and executed with results being properly
++ * returned to the host. [This is the intended design for the mass storage
++ * specification.]
++ *
++ *
++ * TODO FIXME Bus Interface Notes
++ *
++ *
++ * @ingroup MSCFunction
++ */
++
++
++#include <otg/otg-compat.h>
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/random.h>
++#include <linux/slab.h>
++
++#include <linux/blkdev.h>
++
++
++#include "msc-scsi.h"
++#include "msc.h"
++#include "msc-fd.h"
++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
++#include "crc.h"
++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
++#include "msc-io.h"
++
++
++//#include "rbc.h"
++
++
++/* Module Parameters ************************************************************************* */
++
++u32 vendor_id;
++u32 product_id;
++
++u32 major;
++u32 minor;
++
++MODULE_PARM (vendor_id, "i");
++MODULE_PARM (product_id, "i");
++MODULE_PARM (major, "i");
++MODULE_PARM (minor, "i");
++
++MODULE_PARM_DESC (vendor_id, "Device Vendor ID");
++MODULE_PARM_DESC (product_id, "Device Product ID");
++MODULE_PARM_DESC (major, "Device Major");
++MODULE_PARM_DESC (minor, "Device Minor");
++
++
++#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED
++#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT
++
++#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF
++#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON
++
++#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON
++
++#define DEVICE_PREVENT_REMOVAL 0x0020
++
++
++#define DEF_NUMBER_OF_HEADS 0x10
++#define DEF_SECTORS_PER_TRACK 0x20
++
++
++DECLARE_MUTEX(msc_sem);
++
++
++/* MSC ******************************************************************************************** */
++
++struct msc_private msc_private;
++
++int msc_urb_sent (struct usbd_urb *tx_urb, int rc);
++
++
++/* Block Device ******************************************************************************** */
++
++/*! msc_open_blockdev - open the block device specified in msc->major, msc->minor
++ *
++ * Sets appropriate fields to show current status of block device.
++ *
++ * XXX TODO - this needs to be tested against RO and absent devices.
++ *
++ */
++void msc_open_blockdev(struct msc_private *msc)
++{
++ int rc;
++
++ down(&msc_sem);
++ msc->block_dev_state = DEVICE_EJECTED;
++
++ TRACE_MSG2(MSC, "OPEN BLOCKDEV: Major: %x Minor: %x", major, minor);
++
++ /*
++ * Check device information and verify access to the block device.
++ */
++ THROW_IF (!major, ejected);
++
++ msc->dev = MKDEV(major, minor);
++
++ THROW_IF (!(msc->bdev = bdget(kdev_t_to_nr(msc->dev))), ejected);
++
++ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0, BDEV_RAW))) {
++
++ TRACE_MSG0(MSC,"OPEN BLOCKDEV: cannot open RW");
++ THROW_IF ((rc = blkdev_get(msc->bdev, FMODE_READ, 0, BDEV_RAW)), ejected);
++ msc->block_dev_state |= DEVICE_WRITE_PROTECTED;
++ }
++
++ msc->io_state = MSC_INACTIVE;
++ msc->block_dev_state &= ~DEVICE_EJECTED;
++ msc->block_dev_state |= DEVICE_INSERTED | DEVICE_CHANGE_ON;
++
++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: opened block_dev_state: %x", msc->block_dev_state);
++
++ /*
++ * Note that capacity must return the LBA of the last addressable block
++ * c.f. RBC 4.4, RBC 5.3 and notes below RBC Table 6
++ *
++ * The blk_size array contains the number of addressable blocks or N,
++ * capacity is therefore N-1.
++ */
++
++ msc->capacity = (blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1) - 1;
++ msc->block_size = get_hardsect_size(msc->dev);
++ msc->max_blocks = PAGE_SIZE / msc->block_size;
++
++ TRACE_MSG2(MSC,"blk_size: %x %d",
++ blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1,
++ blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1);
++
++ TRACE_MSG2(MSC,"capacity: %x %d", msc->capacity, msc->capacity);
++ TRACE_MSG2(MSC,"block_size: %x %d", msc->block_size, msc->block_size);
++ TRACE_MSG2(MSC,"max_blocks: %x %d", msc->max_blocks, msc->max_blocks);
++
++ /* setup generic buffer_head
++ * XXX do we need two pages? it should be possible to have a single page
++ * for both read and write, in fact do we need a read_bh and write_bh?
++ * XXX ensure the page (or pages) get deallocated
++ */
++ msc->write_pending = msc->read_pending = 0;
++ msc->write_bh.b_rdev = msc->read_bh.b_rdev = msc->dev;
++ msc->write_bh.b_private = msc->read_bh.b_private = msc;
++ msc->read_bh.b_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated
++ msc->write_bh.b_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated
++ msc->read_bh.b_data = page_address(msc->read_bh.b_page);
++ msc->write_bh.b_data = page_address(msc->write_bh.b_page);
++
++ CATCH(ejected) {
++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: EJECTED block_dev_state: %x", msc->block_dev_state);
++ printk(KERN_INFO"%s: Cannot get device %d %d\n", __FUNCTION__, major, minor);
++ }
++ up(&msc_sem);
++}
++
++/*! msc_close_blockdev - close the device for host
++ */
++
++void msc_close_blockdev(struct msc_private *msc)
++{
++
++ RETURN_IF(msc->block_dev_state == DEVICE_EJECTED);
++
++ down(&msc_sem);
++ msc->block_dev_state = DEVICE_EJECTED;
++ if (msc->bdev)
++ blkdev_put(msc->bdev, BDEV_RAW);
++
++ // XXX this should be a wait for read_bh/write_bh to go to NULL
++ //while (msc->read_bh.b_data || msc->write_bh.b_data) {
++ while (msc->read_pending || msc->write_pending) {
++ printk(KERN_INFO"%s: sleeping on read or write bh\n", __FUNCTION__);
++ sleep_on_timeout(&msc->msc_wq, 20);
++ }
++ __free_page((void *)&msc->read_bh.b_page);
++ msc->read_bh.b_page = NULL;
++ __free_page((void *)&msc->write_bh.b_page);
++ msc->write_bh.b_page = NULL;
++ up(&msc_sem);
++}
++
++
++/* READ 10 COMMAND - read and send data to the host ******************************************** */
++
++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc);
++void msc_block_read_finished(struct buffer_head *bh, int flag);
++
++
++/*! msc_scsi_read_10 - process a read(10) command
++ *
++ * We have received a READ(10) CBW, if transfer length is non-zero
++ * initiate a generic block i/o otherwise send a CSW.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_read_10(struct msc_private *msc, char *name, int op)
++{
++ SCSI_READ_10_COMMAND *command = (SCSI_READ_10_COMMAND *)&msc->command.CBWCB;
++
++ /*
++ * save the CBW information and setup for transmitting data read from the block device
++ */
++ msc->lba = be32_to_cpu(command->LogicalBlockAddress);
++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength);
++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size;
++ msc->command_state = MSC_DATA_IN_READ;
++ msc->io_state = MSC_INACTIVE;
++
++ /*
++ * Start reading blocks to send or simply send the CSW if the host
++ * didn't actually ask for a non-zero length.
++ */
++ return (msc->TransferLength_in_blocks) ? msc_start_reading_block_data(msc->function, msc) :
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/*! msc_start_reading_block_data - start reading data
++ *
++ * Generate a generic block io request to read some data to transmit.
++ *
++ * This function is initially called by msc_scsi_read_10() but can also be called
++ * by msc_urb_sent() if the amount of data requested was too large.
++ *
++ * The function msc_block_read_finished() will be called to actually send the data
++ * to the host when the I/O request is complete.
++ *
++ */
++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc)
++{
++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks);
++ unsigned long flags;
++
++ TRACE_MSG3(MSC,"START READING BLOCK DATA lba: %x blocks: %d %d ",
++ msc->lba, msc->TransferLength_in_blocks, TransferLength_in_blocks);
++
++ /* ensure that device state is ok
++ */
++ if (msc->block_dev_state != DEVICE_INSERTED) {
++ TRACE_MSG0(MSC,"START READ MEDIA NOT PRESENT");
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
++ }
++
++ // XXX an ioctl has requested that pending io be aborted
++ local_irq_save(flags);
++ if (msc->io_state & MSC_ABORT_IO) {
++ msc->io_state &= ~MSC_ABORT_IO;
++ TRACE_MSG0(MSC,"BLOCK READ ABORTED");
++ local_irq_restore(flags);
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
++ }
++ local_irq_restore(flags);
++
++ /* sanity check lba against capacity
++ */
++ if (msc->lba >= msc->capacity) {
++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity);
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
++ }
++
++ /* setup buffer head - msc_block_read_finished() will be called when block i/o is finished
++ */
++ msc->read_bh.b_end_io = msc_block_read_finished;
++ msc->read_bh.b_size = TransferLength_in_blocks * msc->block_size;
++ msc->read_bh.b_rsector = msc->lba;
++ msc->read_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock);
++ msc->io_state |= MSC_BLOCKIO_PENDING;
++ msc->read_pending=1;
++
++ memset(msc->read_bh.b_data, 0x0, msc->read_bh.b_size);
++
++ generic_make_request(READ, &msc->read_bh);
++ generic_unplug_device(blk_get_queue(msc->read_bh.b_rdev));
++ return 0;
++}
++
++
++/*! msc_block_read_finished - called by generic request
++ *
++ * Called when block i/o read is complete, send the data to the host if possible.
++ *
++ * The function msc_urb_sent() will be called when the data is sent to
++ * either send additional data or the CSW as appropriate.
++ *
++ * If more data is required then call msc_start_reading_block_data() to
++ * issue another block i/o to get more data.
++ *
++ * These means that there can be two outstanding actions when this
++ * function completes:
++ *
++ * 1. a transmit urb may be pending, sending the most recently
++ * read data to the host
++ * 2. another block i/o may be pending to read additional data
++ *
++ * This leads to a race condition, if the block i/o finished before the urb
++ * transmit, then we must simply exit. The msc_in_read_10_urb_sent()
++ * function will ensure that this is restarted.
++ */
++void msc_block_read_finished(struct buffer_head *bh, int uptodate)
++{
++ //struct msc_private *msc = bh->b_private;
++ //int TransferLength_in_blocks = bh->b_size / msc->block_size;
++
++ struct msc_private *msc;
++ int TransferLength_in_blocks;
++
++ struct usbd_function_instance *function;
++ struct usbd_urb *tx_urb;
++ unsigned long flags;
++ int rc;
++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
++ u32 crc;
++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
++ int i;
++
++ msc = bh->b_private;
++ TransferLength_in_blocks = bh->b_size / msc->block_size;
++
++ TRACE_MSG1(MSC,"BLOCK READ FINISHED size: %x", bh->b_size);
++
++ #if 0
++ local_irq_save(flags);
++ if (msc->io_state & MSC_IOCTL_WAITING) {
++ wake_up_interruptible(&msc->ioctl_wq);
++ msc->io_state &= ~MSC_IOCTL_WAITING;
++ }
++ local_irq_restore(flags);
++ #endif
++
++ wake_up_interruptible(&msc->ioctl_wq);
++
++ /*
++ * Race condition here, if we have not finished sending the
++ * previous tx_urb then we want to simply exit and let
++ * msc_in_read_10_urb_sent() call us again.
++ *
++ * Ensure that we do not reset BLOCKIO flags if SEND PENDING and
++ * that we do reset BLOCKIO if not SEND PENDING
++ */
++ {
++ unsigned long flags;
++ local_irq_save(flags);
++ if (msc->io_state & MSC_SEND_PENDING) {
++ TRACE_MSG0(MSC,"BLOCK READ SEND PENDING");
++ msc->io_state |= MSC_BLOCKIO_FINISHED;
++ msc->uptodate = uptodate;
++ local_irq_restore(flags);
++ return;
++ }
++ msc->io_state &= ~(MSC_BLOCKIO_PENDING | MSC_BLOCKIO_FINISHED);
++
++ local_irq_restore(flags);
++ }
++
++ /* verify that the I/O completed
++ */
++ if (1 != uptodate) {
++ TRACE_MSG0(MSC,"BLOCK READ FAILED");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
++ /* debug output trace - dump rlba and computed crc
++ */
++ for (i = 0; i < bh->b_size / msc->block_size; i++) {
++ crc = crc32_compute(bh->b_data + (i * msc->block_size), msc->block_size, CRC32_INIT);
++ TRACE_RLBA(bh->b_rsector + i, crc);
++ }
++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
++ /* allocate a tx_urb and copy into it
++ */
++ THROW_IF(!(function = msc->function), error);
++ THROW_IF(!(tx_urb = usbd_alloc_urb (function, BULK_IN, bh->b_size, msc_urb_sent)), error);
++ TRACE_MSG1(MSC,"BLOCK READ FINISHED urb: %p", (int)tx_urb);
++
++ tx_urb->function_privdata = msc;
++ tx_urb->actual_length = tx_urb->buffer_length = bh->b_size;
++ memcpy(tx_urb->buffer, bh->b_data, bh->b_size);
++
++ msc->read_pending=0;
++
++ {
++ unsigned long flags;
++ local_irq_save(flags);
++
++ msc->io_state |= MSC_SEND_PENDING;
++ msc->TransferLength_in_bytes -= bh->b_size;
++ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
++ msc->lba += TransferLength_in_blocks;
++
++ if (!msc->TransferLength_in_blocks) {
++ TRACE_MSG0(MSC,"BLOCK READ FINISHED - IO FINISHED");
++ // set flag so that CSW can be sent
++ msc->io_state |= MSC_DATA_IN_READ_FINISHED;
++ }
++ local_irq_restore(flags);
++ }
++
++ /* dispatch urb - msc_urb_sent() will be called when urb is finished
++ */
++ if ((rc = usbd_start_in_urb (tx_urb))) {
++ TRACE_MSG0(MSC,"BLOCK READ FINISHED FAILED");
++ usbd_free_urb (tx_urb);
++ THROW(error);
++ }
++
++ /* if more data is required then call msc_start_reading_block_data() to
++ * issue another block i/o to get more data.
++ */
++ if (!(msc->io_state & MSC_DATA_IN_READ_FINISHED)) {
++ TRACE_MSG0(MSC,"BLOCK READ SEND RESTARTING");
++ msc_start_reading_block_data(msc->function, msc);
++ }
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"BLOCK READ FINISHED ERROR");
++ // stall?
++ }
++}
++
++/*! msc_in_read_10_urb_sent - called by msc_urb_sent when state is MSC_DATA_IN_READ
++ *
++ * This will process a read_10 urb that has been sent. Specifically if any previous
++ * read_10 block I/O has finished we recall the msc_block_read_finished() function
++ * to transmit another read_10 urb.
++ *
++ * If there is no other pending read_10 to do we create and send a CSW.
++ *
++ * If there is more I/O to do or there is already an outstanding I/O we simply
++ * return after freeing the URB.
++ *
++ * Return non-zero if urb was not disposed of.
++ */
++int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb, struct msc_private *msc)
++{
++ unsigned long flags;
++
++ TRACE_MSG0(MSC,"URB SENT DATA IN");
++
++ /*
++ * Potential race condition here, we may need to restart blockio.
++ */
++ local_irq_save(flags);
++
++ msc->io_state &= ~MSC_SEND_PENDING;
++ msc->data_transferred_in_bytes += tx_urb->actual_length;
++
++ TRACE_MSG1(MSC,"URB SENT DATA IN data transferred: %d", msc->data_transferred_in_bytes);
++
++ if (!tx_urb->actual_length)
++ msc->TransferLength_in_blocks = 0;
++
++ /* XXX We should be checking urb status
++ */
++
++ usbd_free_urb (tx_urb);
++
++ /*
++ * Check to see if we need to send CSW.
++ */
++ if (MSC_DATA_IN_READ_FINISHED & msc->io_state) {
++ TRACE_MSG0(MSC,"URB SENT DATA IN - FINISHED SENDING CSW");
++ local_irq_restore(flags);
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++ }
++ /*
++ * Check to see if there is a block read needs to be finished.
++ */
++ if (MSC_BLOCKIO_FINISHED & msc->io_state) {
++ TRACE_MSG0(MSC,"URB SENT DATA IN - RESTART");
++ local_irq_restore(flags);
++ // XXX uptodate?
++ msc_block_read_finished(&msc->read_bh, msc->uptodate);
++ return 0;
++ }
++ local_irq_restore(flags);
++ return 0;
++}
++
++
++/* WRITE 10 - receive data from host and write to block device ********************************* */
++
++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc);
++void msc_data_written(struct buffer_head *bh, int flag);
++void msc_data_test(struct buffer_head *bh, int flag);
++
++/*! msc_scsi_write_10 - process a write command
++ *
++ * Call either msc_start_receiving_data() or msc_start_sending_csw() as appropriate.
++ * Normally msc_start_receiving_data() is called and it will use msc_start_recv_urb()
++ * to setup a receive urb of the appropriate size.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_write_10(struct msc_private *msc, char *name, int op)
++{
++ SCSI_WRITE_10_COMMAND *command = (SCSI_WRITE_10_COMMAND *)&msc->command.CBWCB;
++
++ /* save the CBW and setup for receiving data to be written to the block device
++ */
++ msc->lba = be32_to_cpu(command->LogicalBlockAddress);
++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength);
++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size;
++
++ msc->command_state = MSC_DATA_OUT_WRITE;
++ msc->io_state = MSC_INACTIVE;
++
++ TRACE_MSG1(MSC,"RECV WRITE lba: %x", msc->lba);
++ TRACE_MSG1(MSC,"RECV WRITE blocks: %d", msc->TransferLength_in_blocks);
++
++ return (msc->TransferLength_in_blocks) ?
++ msc_start_receiving_data(msc->function, msc) :
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/*! msc_start_receiving_data - called to initiate an urb to receive WRITE(10) data
++ *
++ * Initiate a receive urb to receive upto PAGE_SIZE WRITE(10) data. The
++ * msc_recv_urb() function will call msc_recv_out_blocks() to actually
++ * write the data.
++ *
++ * This is called from msc_scsi_write_10() to initiate the WRITE(10) and
++ * called from msc_data_written() to start the next page sized receive
++ * urb.
++ */
++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc)
++{
++ /*
++ * Calculating the length is most of the work we do :-)
++ */
++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks);
++
++ TRACE_MSG1(MSC,"START RECEIVING DATA lba: %x", msc->lba);
++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", msc->TransferLength_in_blocks);
++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", TransferLength_in_blocks);
++ TRACE_MSG1(MSC,"START RECEIVING DATA bytes: %d", TransferLength_in_blocks * msc->block_size);
++
++ THROW_IF(msc->command_state != MSC_DATA_OUT_WRITE, error);
++ THROW_IF(!msc->TransferLength_in_blocks, error);
++
++ msc->io_state |= MSC_RECV_PENDING;
++
++ return msc_start_recv_urb(function, msc, TransferLength_in_blocks * msc->block_size);
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"START RECEIVING DATA ERROR");
++ return -EINVAL;
++ }
++}
++
++
++/*! msc_recv_out_blocks - process received WRITE(10) data by writing to block device
++ *
++ * We get here indirectly from msc_recv_urb() when state is MSC_DATA_OUT_WRITE.
++ *
++ * Dealloc the urb and call Initiate the generic request to write the
++ * received data. The b_end_io function msc_data_written() will send the
++ * CSW.
++ *
++ */
++void msc_recv_out_blocks(struct usbd_urb *rcv_urb, struct msc_private *msc)
++{
++ int TransferLength_in_bytes = rcv_urb->actual_length;
++ int TransferLength_in_blocks = TransferLength_in_bytes / msc->block_size;
++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
++ u32 crc;
++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
++ int i;
++
++ TRACE_MSG1(MSC,"RECV OUT bytes: %d", TransferLength_in_bytes);
++ TRACE_MSG1(MSC,"RECV OUT iostate: %x", msc->io_state);
++ TRACE_MSG1(MSC,"RECV OUT data transferred: %d", msc->data_transferred_in_bytes);
++
++ /*
++ * Race condition here, we may get to here before the previous block
++ * write completed. If so just exit and the msc_data_written()
++ * function will recall us.
++ */
++ {
++ unsigned long flags;
++ local_irq_save(flags);
++ if (msc->io_state & MSC_BLOCKIO_PENDING) {
++ TRACE_MSG0(MSC,"RECV OUT BLOCKIO PENDING");
++ msc->io_state |= MSC_RECV_FINISHED;
++ msc->rcv_urb_finished = rcv_urb;
++ local_irq_restore(flags);
++ return;
++ }
++ msc->io_state &= ~(MSC_RECV_PENDING |MSC_RECV_FINISHED);
++ local_irq_restore(flags);
++ }
++
++ msc->data_transferred_in_bytes += rcv_urb->actual_length;
++
++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
++ for (i = 0; i < TransferLength_in_blocks; i++) {
++ crc = crc32_compute(rcv_urb->buffer + (i * msc->block_size), msc->block_size, CRC32_INIT);
++ TRACE_SLBA(msc->lba + i, crc);
++ }
++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
++
++ msc->write_pending=1;
++
++ memcpy(msc->write_bh.b_data, rcv_urb->buffer, rcv_urb->actual_length);
++
++ usbd_free_urb(rcv_urb);
++
++ /* ensure media state is ok
++ */
++ if (msc->block_dev_state != DEVICE_INSERTED) {
++ TRACE_MSG0(MSC,"START READ MEDIA NOT PRESENT");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ // XXX an ioctl has requested that pending io be aborted
++ if (msc->io_state & MSC_ABORT_IO) {
++ unsigned long flags;
++ local_irq_save(flags);
++ msc->io_state &= ~MSC_ABORT_IO;
++ TRACE_MSG0(MSC,"BLOCK READ ABORTED");
++ local_irq_restore(flags);
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ /* sanity check lba against capacity
++ */
++ if (msc->lba >= msc->capacity) {
++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity);
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ /* additional sanity check for lba - ensure non-zero
++ */
++ if (!msc->TransferLength_in_blocks) {
++ TRACE_MSG0(MSC,"START READ LBA TransferLength_in_blocks zero:");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ /* XXX additional sanity check required here - verify that the transfer
++ * size agrees with what we where expecting, specifically I think
++ * we need to verify that we have received a multiple of the block
++ * size and either a full bulk transfer (so more will come) or
++ * the actual exact amount that the host said it would send.
++ * An error should generate a CSW with a USB_MSC_PHASE_ERROR
++ */
++
++
++ TRACE_MSG1(MSC,"RECV OUT lba: %x", msc->lba);
++ TRACE_MSG1(MSC,"RECV OUT blocks left: %d", msc->TransferLength_in_blocks);
++ TRACE_MSG1(MSC,"RECV OUT blocks current: %d", TransferLength_in_blocks);
++
++ /* Initiate writing the data - msc_data_written() will be called
++ * when finished.
++ */
++ msc->write_bh.b_end_io = msc_data_written;
++
++ /* set address in buffer head
++ */
++ msc->write_bh.b_size = TransferLength_in_bytes;
++ msc->write_bh.b_rsector = msc->lba;
++
++ /* decrement counters and increment address
++ */
++ msc->TransferLength_in_bytes -= TransferLength_in_bytes;
++ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
++ msc->lba += TransferLength_in_blocks;
++ msc->write_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock);
++ msc->io_state |= MSC_BLOCKIO_PENDING;
++
++
++ /* Check current status of TransferLength - if non-zero then we need
++ * to queue another urb to receive more data.
++ */
++ if (!msc->TransferLength_in_blocks)
++ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED;
++ else
++ msc_start_receiving_data(msc->function, msc);
++
++ /* initiate the block i/o request
++ */
++ generic_make_request(WRITE, &msc->write_bh);
++ generic_unplug_device(blk_get_queue(msc->write_bh.b_rdev));
++}
++
++
++/*! msc_data_written - called by generic request
++ *
++ * Called when current block i/o read is finished, restart block i/o or send the CSW.
++ *
++ */
++void msc_data_written(struct buffer_head *bh, int uptodate)
++{
++ struct msc_private *msc = bh->b_private;
++ unsigned long flags;
++ u8 io_state;
++
++ TRACE_MSG4(MSC,"DATA WRITTEN uptodate: %x b_state: %x state: %x io: %x",
++ uptodate, bh->b_state, msc->command_state, msc->io_state);
++
++ TRACE_MSG1(MSC,"DATA WRITTEN lba: %x", msc->lba);
++
++ local_irq_save(flags);
++ io_state = msc->io_state &= ~MSC_BLOCKIO_PENDING;
++ msc->write_pending=0;
++
++ local_irq_restore(flags);
++
++ // XXX an ioctl has waited for io to complete, need to wake it up
++ #if 0
++ local_irq_save(flags);
++ if (msc->io_state & MSC_IOCTL_WAITING) {
++ wake_up_interruptible(&msc->ioctl_wq);
++ msc->io_state &= ~MSC_IOCTL_WAITING;
++ }
++ local_irq_restore(flags);
++ #endif
++
++ wake_up_interruptible(&msc->ioctl_wq);
++
++ /*
++ * verify that the I/O completed
++ */
++ if (1 != uptodate) {
++ TRACE_MSG0(MSC,"BLOCK READ FAILED");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
++ // XXX CHECKME
++ if (msc->rcv_urb_finished) {
++ usbd_free_urb(msc->rcv_urb_finished);
++ msc->rcv_urb_finished = NULL;
++ }
++ return;
++ }
++
++ /*
++ * If there was a rcv_urb that was not processed then we need
++ * to process it now. This is done by simply restarting msc_recv_out()
++ */
++ if ((io_state & MSC_RECV_FINISHED) && msc->rcv_urb_finished) {
++ struct usbd_urb *rcv_urb = msc->rcv_urb_finished;
++ msc->rcv_urb_finished = NULL;
++ TRACE_MSG0(MSC,"DATA WRITTEN RECV FINISHED");
++ msc_recv_out_blocks(rcv_urb, msc);
++ }
++ /*
++ * DATA_IN mode and no more data to write
++ */
++ if (MSC_DATA_OUT_WRITE_FINISHED == msc->command_state) {
++ // finished, send CSW
++ TRACE_MSG0(MSC,"DATA WRITTEN send CSW");
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++ }
++}
++
++extern struct usbd_function_operations msc_function_ops;
++extern struct usbd_function_driver msc_function_driver;
++
++
++/* USB Module init/exit ************************************************************************ */
++
++int msc_io_init_l24(void);
++void msc_io_exit_l24(void);
++
++/*! msc_modinit - module init
++ *
++ */
++static int msc_modinit (void)
++{
++ int rc;
++ struct msc_private *msc = &msc_private;
++
++ printk(KERN_INFO "Copyright (c) 2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n");
++ printk (KERN_INFO "%s vendor_id: %04x product_id: %04x major: %d minor: %d\n", __FUNCTION__,
++ vendor_id, product_id, major, minor);
++
++
++ MSC = otg_trace_obtain_tag();
++
++ if (vendor_id)
++ msc_function_driver.idVendor = cpu_to_le16(vendor_id);
++ if (product_id)
++ msc_function_driver.idProduct = cpu_to_le16(product_id);
++
++ init_waitqueue_head(&msc->msc_wq);
++ init_waitqueue_head(&msc->ioctl_wq);
++
++ msc->block_dev_state = DEVICE_EJECTED;
++
++
++ msc->command_state = MSC_READY;
++ msc->io_state = MSC_INACTIVE;
++
++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
++ make_crc_table();
++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
++
++ TRACE_MSG3(MSC,"PAGE_SHIFT: %x PAGE_SIZE: %x %d", PAGE_SHIFT, PAGE_SIZE, PAGE_SIZE);
++
++ THROW_IF(msc_io_init_l24(), error);
++
++ /* register as usb function driver
++ */
++ THROW_UNLESS ((msc->function = usbd_register_function (&msc_function_driver, "msc", NULL)), error);
++ msc->usb_driver_registered++;
++
++ CATCH(error) {
++ if (msc->usb_driver_registered) {
++ usbd_deregister_function (msc->function);
++ msc->usb_driver_registered = 0;
++ }
++ otg_trace_invalidate_tag(MSC);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++/*! msc_modexit - module cleanup
++ */
++static void msc_modexit (void)
++{
++ struct msc_private *msc = &msc_private;
++
++ /* destroy control io interface
++ */
++ msc_io_exit_l24();
++
++ /* flush io and free page buffers
++ */
++ msc_close_blockdev(msc);
++
++ if (msc->usb_driver_registered)
++ usbd_deregister_function (msc->function);
++
++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
++ free_crc_table();
++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
++ otg_trace_invalidate_tag(MSC);
++}
++
++
++module_init (msc_modinit);
++module_exit (msc_modexit);
++
+diff -uNr linux/drivers/no-otg/functions/msc/msc-original.c linux/drivers/otg/functions/msc/msc-original.c
+--- linux/drivers/no-otg/functions/msc/msc-original.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-original.c 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,2209 @@
++/*
++ * otg/msc_fd/msc.c
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ * Bruce Balden <balden@belcarra.com>
++ *
++ *
++ * This is a Mass Storage Class Function that uses the Bulk Only protocol.
++ *
++ * To use simply load with something like:
++ *
++ * insmod msc_fd.o vendor_id=0xffff product_id=0xffff
++ *
++ * Notes:
++ *
++ * 1. Currently block I/O is done a page at a time. I have not determined if
++ * it is possible to dispatch a multiple page generic request. It should at
++ * least be possible to queue page requests.
++ *
++ * 2. Currently for READ operations we have a maximum of one outstanding
++ * read block I/O and send urb. These are allowed to overlap so that we can
++ * continue to read data while sending data to the host.
++ *
++ * 3. Currently for WRITE operations we have a maximum of one outstanding
++ * recv urb and one outstanding write block I/O. These are allowed to
++ * overlap so that we can continue to receive data from the host while
++ * waiting for writing to complete.
++ *
++ * 4. It would be possible to allow multiple writes to be pending, to the
++ * limit of the page cache, if desired.
++ *
++ * 5. It should be possible to allow multiple outstanding reads to be
++ * pending, to the limit of the page cache, but this potentially could
++ * require dealing with out of order completions of the reads. Essentially a
++ * list of completed buffer heads would be required to hold any completed
++ * buffer heads that cannot be sent prior to another, earlier request being
++ * completed.
++ *
++ * 6. Currently we only support the Bulk Only model. Microsoft states that
++ * further support for the mass storage driver will only be done for devices
++ * that conform to the Bulk Only model.
++ *
++ * 7. Multiple LUN's are not supported, but in theory they could be.
++ *
++ * 8. It should be possible to use the removalble disk model to allow for a
++ * hotplug script to umount locally and signal that the block device is now
++ * available. An insertion event would notify the host that it can now use
++ * the device. Similarily applications could notify us to send a removal
++ * event to the host so that the device can be mounted locally.
++ *
++ * 9. Error handling should be done with STALL but using ZLP seems to also
++ * work. ZLP is usually easier to implement (except possibly on the SA1100.)
++ * We may need to make STALL an option if we find devices (perhaps SA1100)
++ * that cannot reliaby send a ZLP on BULK-IN endpoint.
++ *
++ *
++ * 10. WinXP will match the following:
++ *
++ * USB\Class_08&SubClass_02&Prot_50
++ * USB\Class_08&SubClass_05&Prot_50
++ * USB\Class_08&SubClass_06&Prot_50
++ *
++ * SubClass 02 is MMC or SFF8020I
++ * SubClass 05 is SFF or SFF8070I
++ * SubClass 06 is SCSI
++ *
++ * From the Windows USB Storage FAQ:
++ *
++ * RBC not supported
++ *
++ * SubClass = 6 (SCSI)
++ * CDBs SHOULD NOT be padded to 12 bytes
++ * ModeSense/ModeSelect SHOULD NOT be translated from 1ah/15h to 5ah/55h
++ * should be used for FLASH
++ *
++ * SubClass !=6
++ * CDBs SHOULD be padded to 12 bytes
++ * ModeSense/ModeSelect SHOULD be translated from 1ah/15h to 5ah/55h
++ *
++ * We are using the former, SubClass = 6, and implement the required SCSI operations.
++ *
++ *
++ * TODO
++ *
++ * 1. error handling for block io, e.g. what if using with removable block
++ * device (like CF card) and it is removed.
++ *
++ * 2. Currently we memcpy() data from between the urb buffer and buffer
++ * head page. It should be possible to simply use the page buffer for the
++ * urb.
++ *
++ * 3. Should we offer using fileio as an option? This would allow direct access
++ * to a block device image stored in a normal file or direct access to (for example)
++ * ram disks. It would require implementing a separate file I/O kernel thread to
++ * do the actual I/O.
++ *
++ *
++ * TODO FIXME Bus Interface Notes
++ *
++ * 1. The bus interface driver must correctly implement NAK'ing if not rcv_urb is
++ * present (see au1x00.c or wmmx.c for examples.)
++ *
++ * 2. The bus interface driver must implement USBD_URB_SENDZLP flag (see au1x00.c
++ * for example.)
++ *
++ */
++
++#if 1
++#include <otg/otg-compat.h>
++#include <otg/usbp-chap9.h>
++#include <otg/usbp-func.h>
++#include <otg/otg-trace.h>
++#include <otg/otg-mesg.h>
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/random.h>
++#include <linux/slab.h>
++
++#include <linux/blkdev.h>
++
++
++#include "msc-scsi.h"
++#include "msc.h"
++#include "crc.h"
++
++static __inline__ void TRACE_SENSE(unsigned int sense, unsigned int info)
++{
++ TRACE_MSG2(MSC, "--> SENSE: %06x INFO: %08x", sense, info);
++}
++static __inline__ void TRACE_RLBA(unsigned int lba, unsigned int crc)
++{
++ TRACE_MSG2(MSC, "<-- rlba [%8x %08x]", lba, crc);
++}
++static __inline__ void TRACE_SLBA(unsigned int lba, unsigned int crc)
++{
++ TRACE_MSG2(MSC, "--> slba [%8x %08x]", lba, crc);
++}
++static __inline__ void TRACE_TLBA(unsigned int lba, unsigned int crc)
++{
++ TRACE_MSG2(MSC, "--> tlba [%8x %08x]", lba, crc);
++}
++static __inline__ void TRACE_TAG(unsigned int tag, unsigned int frame)
++{
++ TRACE_MSG2(MSC, "--> TAG: %8x FRAME: %03x", tag, frame);
++}
++static __inline__ void TRACE_CBW(char *msg, int val)
++{
++ TRACE_MSG2(MSC, " --> %s %02x", msg, val);
++}
++static __inline__ void TRACE_RECV(unsigned char *cp)
++{
++ TRACE_MSG8(MSC, "<-- recv [%02x %02x %02x %02x %02x %02x %02x %02x]",
++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
++}
++static __inline__ void TRACE_SENT(unsigned char *cp)
++{
++ TRACE_MSG8(MSC, "--> sent [%02x %02x %02x %02x %02x %02x %02x %02x]",
++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
++}
++
++
++#else
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++
++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com");
++MODULE_LICENSE("PROPRIETARY");
++MODULE_DESCRIPTION ("Belcarra Mass Storage Class Bulk Only Function");
++
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <asm/atomic.h>
++#include <linux/random.h>
++#include <linux/slab.h>
++
++#include <linux/blkdev.h>
++
++#include "usbp-chap9.h"
++#include <usbp-mem.h>
++#include <otg-compat.h>
++#include <usbp-func.h>
++
++USBD_MODULE_INFO ("msc_fd 2.0-beta");
++
++#include "msc-scsi.h"
++#include "msc.h"
++
++#include "trace.h"
++#include "crc.h"
++#endif
++
++//#include "rbc.h"
++
++
++/* Module Parameters ************************************************************************* */
++
++static u32 vendor_id;
++static u32 product_id;
++
++static u32 major;
++static u32 minor;
++
++MODULE_PARM (vendor_id, "i");
++MODULE_PARM (product_id, "i");
++MODULE_PARM (major, "i");
++MODULE_PARM (minor, "i");
++
++MODULE_PARM_DESC (vendor_id, "Device Vendor ID");
++MODULE_PARM_DESC (product_id, "Device Product ID");
++MODULE_PARM_DESC (major, "Device Major");
++MODULE_PARM_DESC (minor, "Device Minor");
++
++/*
++ * MSC Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++#define BULK_OUT 0x00
++#define BULK_IN 0x01
++#define ENDPOINTS 0x02
++
++/*
++ * Mass Storage Class - Bulk Only
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++static u8 msc_data_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, };
++static u8 msc_data_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, };
++static struct usbd_endpoint_descriptor *msc_default[] = {
++ (struct usbd_endpoint_descriptor *) msc_data_1,
++ (struct usbd_endpoint_descriptor *) msc_data_2, };
++u8 msc_indexes[] = { BULK_OUT, BULK_IN, };
++
++
++/* Endpoint, Interface, Configuration and Device descriptions and descriptors
++ */
++static u8 msc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = {
++ 0x09, USB_DT_INTERFACE,
++ 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
++ sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor), // bNumEndpoints
++ MASS_STORAGE_CLASS,
++ MASS_STORAGE_SUBCLASS_SCSI,
++ MASS_STORAGE_PROTO_BULK_ONLY,
++ 0x00,
++};
++
++static struct usbd_alternate_description msc_data_alternate_descriptions[] = {
++ { iInterface: CONFIG_OTG_MSC_INTF,
++ interface_descriptor: (struct usbd_interface_descriptor *)&msc_data_alternate_descriptor,
++ endpoints:sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor *),
++ endpoint_list: msc_default,
++ endpoint_indexes: msc_indexes,
++ },
++};
++
++
++struct usbd_interface_description msc_interfaces[] = {
++ { alternates:sizeof (msc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
++ alternate_list:msc_data_alternate_descriptions,},
++};
++
++u8 msc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = {
++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength
++ sizeof (msc_interfaces) / sizeof (struct usbd_interface_description),
++ 0x01, 0x00, // bConfigurationValue, iConfiguration
++ 0, 0,
++};
++
++struct usbd_configuration_description msc_description[] = {
++ { iConfiguration: CONFIG_OTG_MSC_DESC,
++ configuration_descriptor: (struct usbd_configuration_descriptor *)msc_configuration_descriptor,
++ },
++};
++
++
++static struct usbd_device_descriptor msc_device_descriptor = {
++ bLength: sizeof(struct usbd_device_descriptor),
++ bDescriptorType: USB_DT_DEVICE,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: 0x00,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE),
++};
++
++#ifdef CONFIG_OTG_HIGH_SPEED
++static struct usbd_device_qualifier_descriptor msc_device_qualifier_descriptor = {
++ bLength: sizeof(struct usbd_device_qualifier_descriptor),
++ bDescriptorType: USB_DT_DEVICE_QUALIFIER,
++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
++ bDeviceClass: 0x00,
++ bDeviceSubClass: 0x02,
++ bDeviceProtocol: 0x00,
++ bMaxPacketSize0: 0x00,
++};
++#endif /* CONFIG_OTG_HIGH_SPEED */
++
++
++
++
++static struct usbd_endpoint_request msc_endpoint_requests[ENDPOINTS+1] = {
++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, },
++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, },
++ { 0, },
++};
++
++struct usbd_otg_descriptor msc_otg_descriptor = {
++ bLength : sizeof(struct usbd_otg_descriptor),
++ bDescriptorType: USB_DT_OTG,
++ bmAttributes: 0,
++};
++
++struct usbd_device_description msc_device_description = {
++ device_descriptor: &msc_device_descriptor,
++#ifdef CONFIG_OTG_HIGH_SPEED
++ device_qualifier_descriptor: &msc_device_qualifier_descriptor,
++#endif /* CONFIG_OTG_HIGH_SPEED */
++ otg_descriptor: &msc_otg_descriptor,
++ iManufacturer: CONFIG_OTG_MSC_MANUFACTURER,
++ iProduct: CONFIG_OTG_MSC_PRODUCT_NAME,
++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR,
++#endif
++};
++
++
++#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED
++#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT
++
++#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF
++#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON
++
++#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON
++
++#define DEVICE_PREVENT_REMOVAL 0x0020
++
++
++#define DEF_NUMBER_OF_HEADS 0x10
++#define DEF_SECTORS_PER_TRACK 0x20
++
++
++
++/* MSC ******************************************************************************************** */
++
++static struct msc_private msc_private;
++int msc_interrupts;
++
++int msc_urb_sent (struct usbd_urb *tx_urb, int rc);
++static int msc_recv_urb (struct usbd_urb *urb, int rc);
++
++
++/* Sense Key *********************************************************************************** */
++
++void set_sense_data(struct msc_private *msc, u32 sensedata, u32 info)
++{
++ TRACE_SENSE(sensedata, info);
++ msc->sensedata = sensedata;
++ msc->info = info;
++}
++
++
++static int msc_reset(void)
++{
++ struct msc_private *msc = &msc_private;
++ msc->device_state = MSC_DEVICE_DN;
++ msc->command_state = MSC_READY;
++ msc->io_state = MSC_INACTIVE;
++ msc->block_dev_state = 0;
++ msc->sensedata = 0;
++ msc->info = 0;
++ return 0;
++}
++
++
++/* Generic start recv urb and send csw ********************************************************* */
++
++/* msc_start_recv - queue a receive urb
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_start_recv_urb(struct usbd_function_instance *function, struct msc_private *msc, int size)
++{
++ struct usbd_urb *rcv_urb = NULL;
++ int wMaxPacketSize;
++
++ TRACE_MSG1(MSC, "START RECV URB size: %d", size);
++
++ // ensure that we are a multiple of the endpoint packetsize
++ wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function));
++ if ((size % wMaxPacketSize)) {
++ size += wMaxPacketSize;
++ size = (size / wMaxPacketSize) * wMaxPacketSize;
++ }
++
++ // allocate urb and queue it
++ THROW_IF(!(rcv_urb = usbd_alloc_urb (function, BULK_OUT, size, msc_recv_urb)), error);
++ TRACE_MSG1(MSC, "START RECV urb: %p", (int)rcv_urb);
++
++ rcv_urb->function_privdata = msc;
++ msc->rcv_urb_finished = NULL;
++ THROW_IF(usbd_start_out_urb(rcv_urb), error);
++ return 0;
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"START RECV URB ERROR");
++ if (rcv_urb)
++ usbd_free_urb(rcv_urb);
++ return -EINVAL;
++ }
++}
++
++/* msc_start_sending - start sending a new data or csw urb
++ *
++ * Generate a CSW (Command Status Wrapper) to send to the the host.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_start_sending_csw(struct usbd_function_instance *function, struct msc_private *msc, u8 status)
++{
++ COMMAND_STATUS_WRAPPER *csw;
++ int length = sizeof(COMMAND_STATUS_WRAPPER);
++ struct usbd_urb *tx_urb;
++
++ TRACE_MSG1(MSC,"START SENDING CSW %08x", status);
++
++ msc->command_state = MSC_STATUS;
++
++ THROW_IF(!(tx_urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent)), error);
++ TRACE_MSG1(MSC,"START sending csw urb: %p", (int)tx_urb);
++ tx_urb->actual_length = length;
++ tx_urb->function_privdata = msc;
++
++ // fill in CSW and queue the urb
++ csw = (COMMAND_STATUS_WRAPPER *) tx_urb->buffer;
++ csw->dCSWSignature = CSW_SIGNATURE;
++ csw->dCSWTag = msc->command.dCBWTag;
++ csw->dCSWDataResidue = msc->command.dCBWDataTransferLength - msc->data_transferred_in_bytes;
++ csw->bCSWStatus = status;
++
++ TRACE_MSG1(MSC,"START SENDING CSW data residue: %d", csw->dCSWDataResidue);
++
++ THROW_IF (usbd_start_in_urb (tx_urb), error);
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"START SENDING CSW FAILED");
++ if (tx_urb) usbd_free_urb (tx_urb);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++/* msc_start_sending_csw_failed - starting sending a CSW showing failure
++ *
++ * Sets sensedata and generates a CSW with status set to USB_MSC_FAILED.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_start_sending_csw_failed(struct msc_private *msc, u32 sensedata, u32 info, int status)
++{
++ set_sense_data(msc, sensedata, info);
++ return msc_start_sending_csw(msc->function, msc, status);
++}
++
++
++/* ********************************************************************************************* */
++
++/* msc_alloc_urb - allocate an urb for returning a query
++ *
++ * Returns NULL if there is an error in the USB layer.
++ */
++struct usbd_urb * msc_alloc_urb(struct msc_private *msc, int length)
++{
++ struct usbd_function_instance *function;
++ struct usbd_urb *urb;
++
++ THROW_IF(!(function = msc->function), error);
++ THROW_IF(!(urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent)), error);
++ return urb;
++
++ CATCH(error) {
++ msc->command_state = MSC_READY;
++ return NULL;
++ }
++}
++
++
++/* msc_dispatch_query_urb - dispatch an urb containing query data
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_dispatch_query_urb(struct usbd_urb *urb, struct msc_private *msc, int length, int status)
++{
++ int rc;
++
++ TRACE_MSG1(MSC,"DISPATCH URB len: %d", length);
++
++ // save information in msc and urb
++ //
++ urb->function_privdata = msc;
++ urb->actual_length = msc->TransferLength_in_bytes = length;
++
++ // dispatch urb
++ {
++ unsigned long flags;
++ local_irq_save(flags);
++ if ((rc = usbd_start_in_urb (urb))) {
++
++ TRACE_MSG0(MSC,"DISPATCH URB FAILED");
++ usbd_free_urb (urb);
++
++ // stall?
++ msc->command_state = MSC_READY;
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++ msc->command_state = MSC_QUERY;
++ msc->status = status;
++ local_irq_restore(flags);
++ }
++ return 0;
++}
++
++
++/* msc_dispatch_query_urb_zlp - send a ZLP
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_dispatch_query_urb_zlp(struct msc_private *msc, u32 sensedata, u32 info, int status)
++{
++ struct usbd_urb *urb;
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, 1)));
++ set_sense_data(msc, sensedata, info);
++ urb->flags |= USBD_URB_SENDZLP;
++ return msc_dispatch_query_urb(urb, msc, 0, status);
++}
++
++
++
++/* Block Device ******************************************************************************** */
++
++/* msc_open_blockdev - open the block device specified in msc->major, msc->minor
++ *
++ * Sets appropriate fields to show current status of block device.
++ *
++ * XXX TODO - this needs to be tested against RO and absent devices.
++ *
++ */
++void msc_open_blockdev(struct msc_private *msc)
++{
++ int rc;
++ msc->block_dev_state = DEVICE_EJECTED;
++
++ TRACE_MSG2(MSC, "OPEN BLOCKDEV: Major: %x Minor: %x", major, minor);
++ //printk(KERN_INFO"%s: major: %d minor: %d\n", __FUNCTION__, major, minor);
++
++ /*
++ * Check device information and verify access to the block device.
++ */
++ THROW_IF (!major, ejected);
++
++ msc->dev = MKDEV(major, minor);
++
++ THROW_IF (!(msc->bdev = bdget(kdev_t_to_nr(msc->dev))), ejected);
++
++ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0, BDEV_RAW))) {
++
++ TRACE_MSG0(MSC,"OPEN BLOCKDEV: cannot open RW");
++ //printk(KERN_INFO"%s: cannot open RW\n", __FUNCTION__);
++ THROW_IF ((rc = blkdev_get(msc->bdev, FMODE_READ, 0, BDEV_RAW)), ejected);
++ msc->block_dev_state |= DEVICE_WRITE_PROTECTED;
++ }
++
++ msc->block_dev_state &= ~DEVICE_EJECTED;
++ msc->block_dev_state |= DEVICE_INSERTED;
++
++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: opened block_dev_state: %x", msc->block_dev_state);
++
++ /*
++ * Note that capacity must return the LBA of the last addressable block
++ * c.f. RBC 4.4, RBC 5.3 and notes below RBC Table 6
++ *
++ * The blk_size array contains the number of addressable blocks or N,
++ * capacity is therefore N-1.
++ */
++
++ msc->capacity = (blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1) - 1;
++ msc->block_size = get_hardsect_size(msc->dev);
++ msc->max_blocks = PAGE_SIZE / msc->block_size;
++
++ TRACE_MSG1(MSC,"blk_size: %x", blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1);
++ TRACE_MSG1(MSC,"blk_size: %d", blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1);
++
++ TRACE_MSG1(MSC,"capacity: %x", msc->capacity);
++ TRACE_MSG1(MSC,"capacity: %d", msc->capacity);
++
++ TRACE_MSG1(MSC,"block_size: %x", msc->block_size);
++ TRACE_MSG1(MSC,"block_size: %d", msc->block_size);
++
++ TRACE_MSG1(MSC,"max_blocks: %d", msc->max_blocks);
++
++ printk(KERN_INFO"%s: finis\n", __FUNCTION__);
++
++ CATCH(ejected) {
++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: EJECTED block_dev_state: %x", msc->block_dev_state);
++ printk(KERN_INFO"%s: cannot open R)\n", __FUNCTION__);
++ printk(KERN_INFO"%s: Cannot get device %d %d\n", __FUNCTION__, major, minor);
++ }
++}
++
++/* msc_check_blockdev - check current status of the block device
++ *
++ * Check if the block device is operational, generate a failed CSW if not.
++ *
++ * Returns non-zero if the block device is not available for I/O operations
++ * and a failed CSW has already been sent.
++ */
++int msc_check_blockdev(struct msc_private *msc, int zlp)
++{
++ if (msc->block_dev_state & DEVICE_EJECTED) {
++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_EJECTED");
++
++ (zlp ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed)
++ (msc, SCSI_SENSEKEY_MEDIUM_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
++
++ return -EINVAL;
++ }
++ if (msc->block_dev_state & DEVICE_CHANGE_ON) {
++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_CHANGE_ON");
++ (zlp ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed)
++ (msc, SCSI_SENSEKEY_NOT_READY_TO_CHANGE, msc->lba, USB_MSC_FAILED);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++
++/* READ 10 COMMAND - read and send data to the host ******************************************** */
++
++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc);
++void msc_block_read_finished(struct buffer_head *bh, int flag);
++
++
++/* msc_scsi_read_10 - process a read(10) command
++ *
++ * We have received a READ(10) CBW, if transfer length is non-zero
++ * initiate a generic block i/o otherwise send a CSW.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_read_10(struct msc_private *msc, char *name, int op)
++{
++ SCSI_READ_10_COMMAND *command = (SCSI_READ_10_COMMAND *)&msc->command.CBWCB;
++
++ RETURN_ZERO_IF (msc_check_blockdev(msc, 1));
++
++ /*
++ * save the CBW information and setup for transmitting data read from the block device
++ */
++ msc->lba = be32_to_cpu(command->LogicalBlockAddress);
++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength);
++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size;
++ msc->command_state = MSC_DATA_IN_READ;
++ msc->io_state = MSC_INACTIVE;
++
++ /*
++ * Start reading blocks to send or simply send the CSW if the host
++ * didn't actually ask for a non-zero length.
++ */
++ return (msc->TransferLength_in_blocks) ? msc_start_reading_block_data(msc->function, msc) :
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/* msc_start_reading_block_data - start reading data
++ *
++ * Generate a generic block io request to read some data to transmit.
++ *
++ * This function is initially called by msc_scsi_read_10() but can also be called
++ * by msc_urb_sent() if the amount of data requested was too large.
++ *
++ * The function msc_block_read_finished() will be called to actually send the data
++ * to the host when the I/O request is complete.
++ *
++ */
++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc)
++{
++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks);
++
++ TRACE_MSG1(MSC,"START READING BLOCK DATA lba: %x", msc->lba);
++ TRACE_MSG1(MSC,"START READING BLOCK DATA blocks: %d", msc->TransferLength_in_blocks);
++ TRACE_MSG1(MSC,"START READING BLOCK DATA blocks: %d", TransferLength_in_blocks);
++
++ if (msc->block_dev_state != DEVICE_INSERTED) {
++ TRACE_MSG0(MSC,"START READ MEDIUM NOT PRESENT");
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIUM_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
++ }
++
++ if (msc->lba >= msc->capacity) {
++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity);
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_LOGICAL_BLOCK_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
++ }
++
++ // setup buffer head
++ msc->read_bh.b_size = TransferLength_in_blocks * msc->block_size;
++ msc->read_bh.b_end_io = msc_block_read_finished;
++ msc->read_bh.b_rsector = msc->lba;
++ msc->read_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock);
++ msc->io_state |= MSC_BLOCKIO_PENDING;
++ msc->read_bh.b_data = page_address(msc->read_bh.b_page);
++ memset(msc->read_bh.b_data, 0x0, msc->read_bh.b_size);
++
++ generic_make_request(READ, &msc->read_bh);
++ generic_unplug_device(blk_get_queue(msc->read_bh.b_rdev));
++
++ return 0;
++}
++
++
++/* msc_block_read_finished - called by generic request
++ *
++ * Called when block i/o read is complete, send the data to the host if possible.
++ *
++ * The function msc_urb_sent() will be called when the data is sent to
++ * either send additional data or the CSW as appropriate.
++ *
++ */
++void msc_block_read_finished(struct buffer_head *bh, int uptodate)
++{
++ struct msc_private *msc = bh->b_private;
++ int TransferLength_in_blocks = bh->b_size / msc->block_size;
++ struct usbd_function_instance *function;
++ struct usbd_urb *tx_urb;
++ int rc;
++ u32 crc;
++ int i;
++
++ msc_interrupts++;
++ TRACE_MSG1(MSC,"BLOCK READ FINISHED size: %x", bh->b_size);
++
++ /*
++ * Race condition here, if we have not finished sending the
++ * previous tx_urb then we want to simply exit and let
++ * msc_in_read_10_urb_sent() call us again.
++ *
++ * Ensure that we do not reset BLOCKIO flags if SEND PENDING and
++ * that we do reset BLOCKIO if not SEND PENDING
++ */
++ {
++ unsigned long flags;
++ local_irq_save(flags);
++ if (msc->io_state & MSC_SEND_PENDING) {
++ TRACE_MSG0(MSC,"BLOCK READ SEND PENDING");
++ msc->io_state |= MSC_BLOCKIO_FINISHED;
++ msc->uptodate = uptodate;
++ local_irq_restore(flags);
++ return;
++ }
++ msc->io_state &= ~(MSC_BLOCKIO_PENDING | MSC_BLOCKIO_FINISHED);
++ local_irq_restore(flags);
++ }
++
++ // verify that the I/O completed
++ //
++ if (1 != uptodate) {
++ TRACE_MSG0(MSC,"BLOCK READ FAILED");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ // debug output trace
++ //
++ for (i = 0; i < bh->b_size / msc->block_size; i++) {
++ crc = crc32_compute(bh->b_data + (i * msc->block_size), msc->block_size, CRC32_INIT);
++ TRACE_RLBA(bh->b_rsector + i, crc);
++ }
++
++ // allocate a tx_urb and copy into it
++ //
++ THROW_IF(!(function = msc->function), error);
++ THROW_IF(!(tx_urb = usbd_alloc_urb (function, BULK_IN, bh->b_size, msc_urb_sent)), error);
++ TRACE_MSG1(MSC,"BLOCK READ FINISHED urb: %p", (int)tx_urb);
++
++ tx_urb->function_privdata = msc;
++ tx_urb->actual_length = tx_urb->buffer_length = bh->b_size;
++ memcpy(tx_urb->buffer, bh->b_data, bh->b_size);
++
++ msc->read_bh.b_data = NULL;
++ {
++ unsigned long flags;
++ local_irq_save(flags);
++
++ msc->io_state |= MSC_SEND_PENDING;
++ msc->TransferLength_in_bytes -= bh->b_size;
++ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
++ msc->lba += TransferLength_in_blocks;
++
++ if (!msc->TransferLength_in_blocks) {
++ TRACE_MSG0(MSC,"BLOCK READ FINISHED - IO FINISHED");
++ // set flag so that CSW can be sent
++ msc->io_state |= MSC_DATA_IN_READ_FINISHED;
++ }
++ local_irq_restore(flags);
++ }
++
++ // dispatch urb
++ if ((rc = usbd_start_in_urb (tx_urb))) {
++ TRACE_MSG0(MSC,"BLOCK READ FINISHED FAILED");
++ usbd_free_urb (tx_urb);
++ THROW(error);
++ }
++ if (!(msc->io_state & MSC_DATA_IN_READ_FINISHED)) {
++ TRACE_MSG0(MSC,"BLOCK READ SEND RESTARTING");
++ msc_start_reading_block_data(msc->function, msc);
++ return;
++ }
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"BLOCK READ FINISHED ERROR");
++ // stall?
++ }
++}
++
++/* msc_in_read_10_urb_sent - called by msc_urb_sent when state is MSC_DATA_IN_READ
++ *
++ * This will process a read_10 urb that has been sent. Specifically if any previous
++ * read_10 block I/O has finished we recall the msc_block_read_finished() function
++ * to transmit another read_10 urb.
++ *
++ * If there is no other pending read_10 to do we create and send a CSW.
++ *
++ * If there is more I/O to do or there is already an outstanding I/O we simply
++ * return after freeing the URB.
++ *
++ * Return non-zero if urb was not disposed of.
++ */
++int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb, struct msc_private *msc)
++{
++ unsigned long flags;
++
++ TRACE_MSG0(MSC,"URB SENT DATA IN");
++
++ /*
++ * Potential race condition here, we may need to restart blockio.
++ */
++
++ local_irq_save(flags);
++
++ msc->io_state &= ~MSC_SEND_PENDING;
++ msc->data_transferred_in_bytes += tx_urb->actual_length;
++
++ TRACE_MSG1(MSC,"URB SENT DATA IN data transferred: %d", msc->data_transferred_in_bytes);
++
++ if (!tx_urb->actual_length)
++ msc->TransferLength_in_blocks = 0;
++
++ usbd_free_urb (tx_urb);
++
++ /*
++ * Check to see if we need to send CSW.
++ */
++ if (MSC_DATA_IN_READ_FINISHED & msc->io_state) {
++ TRACE_MSG0(MSC,"URB SENT DATA IN - FINISHED SENDING CSW");
++ local_irq_restore(flags);
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++ }
++ /*
++ * Check to see if there is a block read needs to be finished.
++ */
++ if (MSC_BLOCKIO_FINISHED & msc->io_state) {
++ TRACE_MSG0(MSC,"URB SENT DATA IN - RESTART");
++ local_irq_restore(flags);
++ // XXX uptodate?
++ msc_block_read_finished(&msc->read_bh, msc->uptodate);
++ return 0;
++ }
++ local_irq_restore(flags);
++ return 0;
++}
++
++
++/* WRITE 10 - receive data from host and write to block device ********************************* */
++
++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc);
++void msc_data_written(struct buffer_head *bh, int flag);
++void msc_data_test(struct buffer_head *bh, int flag);
++
++/* msc_scsi_write_10 - process a write command
++ *
++ * Call either msc_start_receiving_data() or msc_start_sending_csw() as appropriate.
++ * Normally msc_start_receiving_data() is called and it will use msc_start_recv_urb()
++ * to setup a receive urb of the appropriate size.
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_write_10(struct msc_private *msc, char *name, int op)
++{
++ SCSI_WRITE_10_COMMAND *command = (SCSI_WRITE_10_COMMAND *)&msc->command.CBWCB;
++
++ RETURN_ZERO_IF (msc_check_blockdev(msc, 0));
++
++ // save the CBW and setup for receiving data to be written to the block device
++ //
++ msc->lba = be32_to_cpu(command->LogicalBlockAddress);
++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength);
++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size;
++
++ msc->command_state = MSC_DATA_OUT_WRITE;
++ msc->io_state = MSC_INACTIVE;
++
++ TRACE_MSG1(MSC,"RECV WRITE lba: %x", msc->lba);
++ TRACE_MSG1(MSC,"RECV WRITE blocks: %d", msc->TransferLength_in_blocks);
++
++ return (msc->TransferLength_in_blocks) ?
++ msc_start_receiving_data(msc->function, msc) :
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/* msc_start_receiving_data - called to initiate an urb to receive WRITE(10) data
++ *
++ * Initiate a receive urb to receive upto PAGE_SIZE WRITE(10) data. The
++ * msc_recv_urb() function will call msc_recv_out_blocks() to actually
++ * write the data.
++ *
++ * This is called from msc_scsi_write_10() to initiate the WRITE(10) and
++ * called from msc_data_written() to start the next page sized receive
++ * urb.
++ *
++ */
++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc)
++{
++ /*
++ * Calculating the length is most of the work we do :-)
++ */
++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks);
++
++ TRACE_MSG1(MSC,"START RECEIVING DATA lba: %x", msc->lba);
++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", msc->TransferLength_in_blocks);
++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", TransferLength_in_blocks);
++ TRACE_MSG1(MSC,"START RECEIVING DATA bytes: %d", TransferLength_in_blocks * msc->block_size);
++
++ THROW_IF(msc->command_state != MSC_DATA_OUT_WRITE, error);
++ THROW_IF(!msc->TransferLength_in_blocks, error);
++
++ msc->io_state |= MSC_RECV_PENDING;
++
++ return msc_start_recv_urb(function, msc, TransferLength_in_blocks * msc->block_size);
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"START RECEIVING DATA ERROR");
++ return -EINVAL;
++ }
++}
++
++
++/* msc_recv_out_blocks - process received WRITE(10) data by writing to block device
++ *
++ * We get here indirectly from msc_recv_urb() when state is MSC_DATA_OUT_WRITE.
++ *
++ * Dealloc the urb and call Initiate the generic request to write the
++ * received data. The b_end_io function msc_data_written() will send the
++ * CSW.
++ *
++ */
++void msc_recv_out_blocks(struct usbd_urb *rcv_urb, struct msc_private *msc)
++{
++ int TransferLength_in_bytes = rcv_urb->actual_length;
++ int TransferLength_in_blocks = TransferLength_in_bytes / msc->block_size;
++ u32 crc;
++ int i;
++
++ TRACE_MSG1(MSC,"RECV OUT bytes: %d", TransferLength_in_bytes);
++ TRACE_MSG1(MSC,"RECV OUT iostate: %x", msc->io_state);
++ TRACE_MSG1(MSC,"RECV OUT data transferred: %d", msc->data_transferred_in_bytes);
++
++ /*
++ * Race condition here, we may get to here before the previous block
++ * write completed. If so just exit and the msc_data_written()
++ * function will recall us.
++ */
++ {
++ unsigned long flags;
++ local_irq_save(flags);
++ if (msc->io_state & MSC_BLOCKIO_PENDING) {
++ TRACE_MSG0(MSC,"RECV OUT BLOCKIO PENDING");
++ msc->io_state |= MSC_RECV_FINISHED;
++ msc->rcv_urb_finished = rcv_urb;
++ local_irq_restore(flags);
++ return;
++ }
++ msc->io_state &= ~(MSC_RECV_PENDING |MSC_RECV_FINISHED);
++ local_irq_restore(flags);
++ }
++
++ msc->data_transferred_in_bytes += rcv_urb->actual_length;
++
++ for (i = 0; i < TransferLength_in_blocks; i++) {
++ crc = crc32_compute(rcv_urb->buffer + (i * msc->block_size), msc->block_size, CRC32_INIT);
++ TRACE_SLBA(msc->lba + i, crc);
++ }
++
++ msc->write_bh.b_data = page_address(msc->write_bh.b_page);
++ memcpy(msc->write_bh.b_data, rcv_urb->buffer, rcv_urb->actual_length);
++
++ usbd_free_urb(rcv_urb);
++
++ if (msc->block_dev_state != DEVICE_INSERTED) {
++ TRACE_MSG0(MSC,"START READ MEDIUM NOT PRESENT");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIUM_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ if (msc->lba >= msc->capacity) {
++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity);
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_LOGICAL_BLOCK_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ if (!msc->TransferLength_in_blocks) {
++ TRACE_MSG0(MSC,"START READ LBA TransferLength_in_blocks zero:");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_LOGICAL_BLOCK_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
++ return;
++ }
++
++ // Initiate writing the data
++
++ TRACE_MSG1(MSC,"RECV OUT lba: %x", msc->lba);
++ TRACE_MSG1(MSC,"RECV OUT blocks left: %d", msc->TransferLength_in_blocks);
++ TRACE_MSG1(MSC,"RECV OUT blocks current: %d", TransferLength_in_blocks);
++
++
++ // set address in buffer head
++ msc->write_bh.b_size = TransferLength_in_bytes;
++ msc->write_bh.b_rsector = msc->lba;
++
++ // decrement counters and increment address
++ msc->TransferLength_in_bytes -= TransferLength_in_bytes;
++ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
++ msc->lba += TransferLength_in_blocks;
++ msc->write_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock);
++ msc->write_bh.b_end_io = msc_data_written;
++ msc->io_state |= MSC_BLOCKIO_PENDING;
++
++
++ // start new urb here
++ if (!msc->TransferLength_in_blocks)
++ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED;
++ else
++ msc_start_receiving_data(msc->function, msc);
++
++ generic_make_request(WRITE, &msc->write_bh);
++ generic_unplug_device(blk_get_queue(msc->write_bh.b_rdev));
++}
++
++
++/* msc_data_written - called by generic request
++ *
++ * Called when current block i/o read is finished, restart block i/o or send the CSW.
++ *
++ */
++void msc_data_written(struct buffer_head *bh, int uptodate)
++{
++ struct msc_private *msc = bh->b_private;
++ unsigned long flags;
++ u8 io_state;
++
++ msc_interrupts++;
++
++ TRACE_MSG4(MSC,"DATA WRITTEN uptodate: %x b_state: %x state: %x io: %x",
++ uptodate, bh->b_state, msc->command_state, msc->io_state);
++
++ TRACE_MSG1(MSC,"DATA WRITTEN lba: %x", msc->lba);
++
++ local_irq_save(flags);
++ io_state = msc->io_state &= ~MSC_BLOCKIO_PENDING;
++ msc->write_bh.b_data = NULL;
++ local_irq_restore(flags);
++
++ /*
++ * verify that the I/O completed
++ */
++ if (1 != uptodate) {
++ TRACE_MSG0(MSC,"BLOCK READ FAILED");
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
++ // XXX CHECKME
++ if (msc->rcv_urb_finished) {
++ usbd_free_urb(msc->rcv_urb_finished);
++ msc->rcv_urb_finished = NULL;
++ }
++ return;
++ }
++ /*
++ * If there was a rcv_urb that was not processed then we need
++ * to process it now. This is done by simply restarting msc_recv_out()
++ */
++ if ((io_state & MSC_RECV_FINISHED) && msc->rcv_urb_finished) {
++ struct usbd_urb *rcv_urb = msc->rcv_urb_finished;
++ msc->rcv_urb_finished = NULL;
++ TRACE_MSG0(MSC,"DATA WRITTEN RECV FINISHED");
++ msc_recv_out_blocks(rcv_urb, msc);
++ }
++ /*
++ * DATA_IN mode and no more data to write
++ */
++ if (MSC_DATA_OUT_WRITE_FINISHED == msc->command_state) {
++ // finished, send CSW
++ TRACE_MSG0(MSC,"DATA WRITTEN send CSW");
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++ }
++}
++
++
++/* SCSI Commands ******************************************************************************* */
++
++/* msc_scsi_inquiry - process an inquiry
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_inquiry(struct msc_private *msc, char *name, int op)
++{
++ SCSI_INQUIRY_COMMAND *command = (SCSI_INQUIRY_COMMAND *)&msc->command.CBWCB;
++ SCSI_INQUIRY_DATA *data;
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_INQUIRY_DATA);
++
++ /*
++ * C.f. SPC2 7.3 INQUIRY command
++ * C.f. Table 46 - Standard INQUIRY data format
++ *
++ * C.f. Table 47 - Peripheral Qualifier
++ *
++ * 000b The specified peripheral device type is currently connected to this
++ * logical unit.....
++ * 001b The device server is capable of of supporting the peripheral device
++ * type on this logical unit. However, the physical device is not currently
++ * connected to this logical unit.....
++ * 010b Reserved
++ * 011b The device server is not capable of supporting a physical device on
++ * this logical unit....
++ *
++ */
++
++ TRACE_MSG4(MSC,"INQUIRY EnableVPD: %02x LogicalUnitNumber: %02x PageCode: %02x AllocLen: %02x",
++ command->EnableVPD, command->LogicalUnitNumber, command->PageCode, command->AllocationLength);
++
++ // XXX THROW_IF(msc->command_state != MSC_READY, error);
++
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_INQUIRY_DATA *)urb->buffer;
++ data->PeripheralQaulifier = msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON) ? 0x1 : 0;
++ data->PeripheralDeviceType = 0x00;
++ data->RMB = 0x1;
++ data->ResponseDataFormat = 0x1;
++ data->AdditionalLength = 0x1f;
++
++ strncpy(data->VendorInformation, CONFIG_OTG_MSC_MANUFACTURER, strlen(CONFIG_OTG_MSC_MANUFACTURER));
++ strncpy(data->ProductIdentification, CONFIG_OTG_MSC_PRODUCT_NAME, strlen(CONFIG_OTG_MSC_PRODUCT_NAME));
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++
++/* old_msc_scsi_read_format_capacity - process a query
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int old_msc_scsi_read_format_capacity(struct msc_private *msc, char *name, int op)
++{
++ // send ZLP then CSW
++ return msc_dispatch_query_urb_zlp(msc, 0, 0, USB_MSC_PASSED);
++}
++
++/* msc_scsi_read_format_capacity - process a query
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_read_format_capacity(struct msc_private *msc, char *name, int op)
++{
++ SCSI_READ_FORMAT_CAPACITY_DATA *data;
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_READ_FORMAT_CAPACITY_DATA);
++ u32 block_num = msc->capacity;
++ u32 block_len;
++
++
++
++ /*
++ * If we don't have a valid block device then simply
++ * send a ZLP.
++ *
++ * XXX VERIFY this is correct thing to do for this situation.
++ */
++ //if (msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON))
++ // return msc_dispatch_query_urb_zlp(msc);
++ RETURN_ZERO_IF (msc_check_blockdev(msc, 1));
++
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_READ_FORMAT_CAPACITY_DATA *) urb->buffer;
++
++ data->CapacityListHeader.CapacityListLength = sizeof(data->CurrentMaximumCapacityDescriptor);
++
++ data->CurrentMaximumCapacityDescriptor.NumberofBlocks = block_num;
++ data->CurrentMaximumCapacityDescriptor.DescriptorCode = 0x03;
++ memcpy(data->CurrentMaximumCapacityDescriptor.BlockLength + 1, &block_len, sizeof(block_len));
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++/* msc_read_capacity - process a read_capacity command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_read_capacity(struct msc_private *msc, char *name, int op)
++{
++ SCSI_READ_CAPACITY_COMMAND *command = (SCSI_READ_CAPACITY_COMMAND *)&msc->command.CBWCB;
++ SCSI_READ_CAPACITY_DATA *data;
++ struct usbd_urb *urb;
++ int length = 8;
++ u32 lba;
++
++ /*
++ * C.f. RBC 5.3
++ */
++
++ /*
++ * If we don't have a valid block device then simply
++ * send a ZLP.
++ *
++ * XXX VERIFY this is correct thing to do for this situation.
++ */
++ //if (msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON))
++ // return msc_dispatch_query_urb_zlp(msc);
++
++ RETURN_ZERO_IF (msc_check_blockdev(msc, 1));
++
++ //memcpy(&lba, &command->LogicalBlockAddress, 4);
++ //lba = be32_to_cpu(lba);
++
++ lba = be32_to_cpu(command->LogicalBlockAddress);
++
++ TRACE_MSG1(MSC,"READ CAPACITY LBA: %d", lba);
++
++ if ((command->PMI > 1) || (!command->PMI && lba)) {
++ TRACE_MSG1(MSC,"READ CAPACITY PMI: %d", command->PMI);
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, lba, USB_MSC_FAILED);
++ }
++
++ // alloc urb
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_READ_CAPACITY_DATA *) urb->buffer;
++
++ data->LastLogicalBlockAddress = cpu_to_be32(msc->capacity);
++ data->BlockLengthInBytes = cpu_to_be32(msc->block_size);
++
++ TRACE_MSG1(MSC,"RECV READ CAPACITY lba: %x", be32_to_cpu(data->LastLogicalBlockAddress));
++ TRACE_MSG1(MSC,"RECV READ CAPACITY block_size: %x", be32_to_cpu(data->BlockLengthInBytes));
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++
++/* msc_scsi_request_sense - process a request_sense command
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_request_sense(struct msc_private *msc, char *name, int op)
++{
++ SCSI_REQUEST_SENSE_COMMAND* command = (SCSI_REQUEST_SENSE_COMMAND *)&msc->command.CBWCB;
++ SCSI_REQUEST_SENSE_DATA *data;
++
++ /*
++ * C.f. SPC2 7.20 REQUEST SENSE command
++ */
++
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_REQUEST_SENSE_DATA);
++
++ // alloc urb
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ data = (SCSI_REQUEST_SENSE_DATA *) urb->buffer;
++ memset(command, 0x0, length);
++ data->ErrorCode = SCSI_ERROR_CURRENT;
++ data->SenseKey = msc->sensedata >> 16;
++ data->AdditionalSenseCode = msc->sensedata >> 8;
++ data->AdditionalSenseCodeQualifier = msc->sensedata;
++ data->Valid = 1;
++
++ set_sense_data(msc, SCSI_SENSEKEY_NO_SENSE, 0);
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++/* msc_scsi_mode_sense - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int old_msc_scsi_mode_sense(struct msc_private *msc, char *name, int op)
++{
++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB;
++ SCSI_MODE_SENSE_DATA *data;
++ int length = sizeof(SCSI_MODE_SENSE_DATA);
++
++ struct usbd_urb *urb;
++ u8 *cp;
++
++
++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x",
++ command->DBD,
++ command->PageControl,
++ command->PageCode,
++ 0);
++
++ length = 8;
++
++ // alloc urb
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++
++ cp = (u8 *) urb->buffer;
++ memset(cp, 0x0, length);
++
++ cp[0] = 0;
++ cp[1] = 0;
++ cp[2] = 0; // 0x80 is writeprotect
++ cp[3] = 0x08;
++ cp[4] = 0;
++ cp[5] = 0;
++ cp[6] = 0;
++ cp[7] = 0;
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++/* msc_scsi_mode_sense - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_mode_sense(struct msc_private *msc, char *name, int op)
++{
++
++ /*
++ * C.f. SPC2 7.8.1 MODE SENSE(6) command
++ */
++
++ static READ_WRITE_ERROR_RECOVERY_PAGE page_01 = {
++ PageCode:0x01,
++ PageLength:0x0A,
++ ReadRetryCount:0x03,
++ WriteRetryCount:0x80,
++ };
++ static FLEXIBLE_DISK_PAGE page_05 = {
++ PageCode:0x05,
++ PageLength:0x1E,
++ TransferRate:__constant_cpu_to_be16(0xFA00),
++ NumberofHeads:0xA0,
++ SectorsperTrack:0x00,
++ DataBytesperSector:__constant_cpu_to_be16(0x0002),
++ NumberofCylinders:__constant_cpu_to_be16(0x0000),
++ MotorOnDelay:0x05,
++ MotorOffDelay:0x1E,
++ MediumRotationRate:__constant_cpu_to_be16(0x6801),
++ };
++ static REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE page_1b = {
++ PageCode:0x1B,
++ PageLength:0x0A,
++ TLUN:0x01,
++ };
++ static TIMER_AND_PROTECT_PAGE page_1c = {
++ PageCode:0x1c,
++ PageLength:0x06,
++ InactivityTimeMultiplier:0x0A,
++ };
++
++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB;
++ SCSI_MODE_SENSE_DATA *data;
++ struct usbd_urb *urb;
++ int length = sizeof(SCSI_MODE_SENSE_DATA);
++
++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x",
++ command->DBD,
++ command->PageControl,
++ command->PageCode,
++ 0);
++
++
++ if (msc->block_dev_state & DEVICE_EJECTED) {
++ u16 sector = htons((unsigned short)msc->block_size);
++ u16 cylinder = 0;
++ page_05.NumberofHeads = 0;
++ page_05.SectorsperTrack = 0;
++ memcpy(&page_05.DataBytesperSector, &sector, sizeof(sector));
++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder));
++ }
++ else {
++ u16 sector = htons((unsigned short)msc->block_size);
++ u32 size = DEF_NUMBER_OF_HEADS * DEF_SECTORS_PER_TRACK * sector;
++ u16 cylinder = htons(msc->capacity / size);
++ page_05.NumberofHeads = DEF_NUMBER_OF_HEADS;
++ page_05.SectorsperTrack = DEF_SECTORS_PER_TRACK;
++ memcpy(&page_05.DataBytesperSector, &sector, sizeof(sector));
++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder));
++
++ }
++
++
++ if (SCSI_MODEPAGE_CONTROL_CURRENT != command->PageControl) {
++ TRACE_MSG2(MSC,"MODE SENSE - requeested other than CONTROL_CURRENT: %d %d",
++ command->PageControl, command->PageCode);
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED);
++ }
++
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length)));
++ data = (SCSI_MODE_SENSE_DATA *) urb->buffer;
++
++ data->ModeParameterHeader.WriteProtect = msc->block_dev_state & DEVICE_WRITE_PROTECTED ? 1 : 0;
++
++ switch (command->PageCode) {
++ case SCSI_MODEPAGE_ERROR_RECOVERY:
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_01);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_01, sizeof(page_01));
++ break;
++ case SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE:
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_05);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_05, sizeof(page_05));
++ break;
++ case SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS:
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1b);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_1b, sizeof(page_1b));
++ break;
++ case SCSI_MODEPAGE_INFORMATION_EXCEPTIONS:
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1c);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages, &page_1c, sizeof(page_1c));
++ break;
++ case SCSI_MODEPAGE_ALL_SUPPORTED:
++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_ALL_PAGES);
++ data->ModeParameterHeader.ModeDataLength = length - 1;
++ memcpy(&data->ModePages.ModeAllPages.ReadWriteErrorRecoveryPage, &page_01, sizeof(page_01));
++ memcpy(&data->ModePages.ModeAllPages.FlexibleDiskPage, &page_05, sizeof(page_05));
++ memcpy(&data->ModePages.ModeAllPages.RemovableBlockAccessCapabilitiesPage, &page_1b, sizeof(page_1b));
++ memcpy(&data->ModePages.ModeAllPages.TimerAndProtectPage, &page_1c, sizeof(page_1c));
++ break;
++ case SCSI_MODEPAGE_CACHING:
++ // see file_storage.c for an example if we want to support this
++ break;
++ }
++
++ TRACE_MSG2(MSC,"LENGTH: %d %d", msc->command.dCBWDataTransferLength, length);
++
++ /*
++ * XXX verify that length is <= 256 bytes, return CHECK_CONDITION if it is
++ */
++ length = MIN(msc->command.dCBWDataTransferLength, length);
++
++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED);
++}
++
++
++/* msc_scsi_test_unit_ready - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_test_unit_ready(struct msc_private *msc, char *name, int op)
++{
++ /*
++ * C.f. SPC2 7.25 TEST UNIT READY command
++ *
++ * Return GOOD, illegal request for bad LUN or NOT READY
++ *
++ */
++ RETURN_EINVAL_IF (msc_check_blockdev(msc, 0));
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/* msc_scsi_prevent_allow - process a request_sense command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_prevent_allow(struct msc_private *msc, char *name, int op)
++{
++ SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL_COMMAND* command = (SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL_COMMAND*)&msc->command.CBWCB;
++
++ /*
++ * C.f. SPC2 7.12 Table 78 PREVENT ALLOW MEDIUM REMOVAL Prevent Field
++ *
++ * 00b Medium removal shall be allowed from both the data transport
++ * element and the attached medium changer (if any).
++ * 01b Medium removal shall be prohibited from the data transport
++ * element but allowed from the attached medium changer (if any).
++ * 10b Medium removal shall be allowed for the data transport element
++ * but prohibited for the attached medium changer.
++ * 11b Medium remval shall be prohibited from both the data transport
++ * element and the attached medium changer
++ *
++ * Prevention shall terminate after 00b or 10b, after a SYNC CACHE or hard reset.
++ */
++
++ TRACE_MSG1(MSC,"PREVENT: %d", command->Prevent);
++
++ RETURN_EINVAL_IF (msc_check_blockdev(msc, 0));
++
++
++ // XXX TODO
++ // this is from storageproto.c, shouldn't we implement something?
++ if (command->Prevent)
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD, msc->lba, USB_MSC_FAILED);
++
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/* msc_scsi_start_stop - process a request_sense command
++ *
++ * C.f. RBC 5.4 and 5.4.2
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_start_stop(struct msc_private *msc, char *name, int op)
++{
++ SCSI_START_STOP_COMMAND* command = (SCSI_START_STOP_COMMAND*)&msc->command.CBWCB;
++
++ TRACE_MSG4(MSC,"START STOP: Immed: %d Power: %x LoEj: %d Start: %d",
++ command->IMMED, command->PowerConditions, command->LoEj, command->Start);
++
++ /*
++ * C.f. 5.4
++ *
++ * IMMED - if set return status immediately after command validation, otherwise
++ * return status as soon operation is completed.
++ *
++ * C.f. 5.4.1 Table 8 POWER CONDITIONS
++ *
++ * 0 - M - no change in power condition
++ * 1 - M - place device in active condition
++ * 2 - M - place device in idle condition
++ * 3 - M - place device in Standby condition
++ * 4 - - reserved
++ * 5 - M - place device in Sleep condition
++ * 6 - - reserved
++ * 7 - 0 - Device Control
++ *
++ * C.f. 5.4.2 Table 9 START STOP control bit definitions
++ *
++ * Power Load/Eject Start
++ * 1-7 x x LoEj and Start Ignored
++ * 0 0 0 Stop the medium
++ * 0 0 1 Make the medium ready
++ * 0 1 0 Unload the medium
++ * 0 1 1 Load the medium
++ *
++ */
++
++
++ RETURN_EINVAL_IF (msc_check_blockdev(msc, 0));
++
++ // XXX TODO
++ // this is from storageproto.c, shouldn't we implement something?
++
++ if (command->Start && command->LoEj)
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD, msc->lba, USB_MSC_FAILED);
++
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/* msc_scsi_verify - process a verify command
++ *
++ * Used by:
++ * win2k
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_verify(struct msc_private *msc, char *name, int op)
++{
++ SCSI_VERIFY_COMMAND *command = (SCSI_VERIFY_COMMAND *)&msc->command.CBWCB;
++ /*
++ * C.f. RBC 5.7 VERIFY command
++ */
++ // XXX This actually should use the read_10 function and when
++ // finished reading simply send the following
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/* msc_scsi_mode_select - process a select command
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_scsi_mode_select(struct msc_private *msc, char *name, int op)
++{
++ //SCSI_MODE_SELECT_COMMAND *command = (SCSI_MODE_SELECT_COMMAND *)&msc->command.CBWCB;
++
++ /*
++ * C.f. SPC2 7.6 MODE SELECT(6) command
++ */
++
++ // if less than correct amount of data return USB_MSC_PHASE_ERROR - see MV
++ //
++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++}
++
++
++/* msc_cmd_unknown - process an unknown command
++ *
++ *
++ * Returns non-zero if there is an error in the USB layer.
++ */
++int msc_cmd_unknown(struct msc_private *msc, char *name, int op)
++{
++ /*
++ struct usbd_urb *urb;
++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, 1))); // XXX +1 ?
++ urb->flags |= USBD_URB_SENDZLP;
++ return msc_dispatch_query_urb(urb, msc, 0);
++ */
++
++ printk(KERN_ERR"%s: NOT IMPLEMENTED %s opcode: %02x\n", __FUNCTION__, name, op);
++
++ // should we send ZLP for unknow commands?
++ // return msc_dispatch_query_urb_zlp(msc);
++
++ // or failed
++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
++}
++
++
++struct rbc_dispatch {
++ u8 op;
++ char *name;
++ int (*rbc_command) (struct msc_private *, char *, int op);
++};
++
++/* Command cross reference
++ *
++ * This is the list of commands observed from each host OS. It is necessarily
++ * incomplete in that we not have reached some condition necessary to have
++ * other commands used.
++ * Win2k WinXP
++ * SCSI_TEST_UNIT_READY yes yes
++ * SCSI_READ_10 yes yes
++ * SCSI_WRITE_10 yes yes
++ * SCSI_READ_CAPACITY yes yes
++ * SCSI_VERIFY yes
++ * SCSI_INQUIRY yes yes
++ * SCSI_MODE_SENSE yes
++ * SCSI_READ_FORMAT_CAPACITY yes yes
++ * SCSI_REQUEST_SENSE
++ * SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL
++ * SCSI_START_STOP
++ * SCSI_MODE_SELECT
++ * SCSI_FORMAT_UNIT
++ *
++ */
++
++struct rbc_dispatch rbc_dispatch_table[] = {
++ { SCSI_TEST_UNIT_READY, "SCSI_TEST_UNIT_READY", msc_scsi_test_unit_ready }, //
++ { SCSI_READ_10, "SCSI_READ_10", msc_scsi_read_10 }, //
++ { SCSI_WRITE_10, "SCSI_WRITE_10", msc_scsi_write_10 }, //
++ { SCSI_READ_CAPACITY, "SCSI_READ_CAPACITY", msc_scsi_read_capacity }, //
++ { SCSI_VERIFY, "SCSI_VERIFY", msc_scsi_verify }, //
++ { SCSI_INQUIRY, "SCSI_INQUIRY", msc_scsi_inquiry }, //
++ { SCSI_MODE_SENSE, "SCSI_MODE_SENSE", msc_scsi_mode_sense }, //
++ { SCSI_READ_FORMAT_CAPACITY, "SCSI_READ_FORMAT_CAPACITY", msc_scsi_read_format_capacity }, //
++ { SCSI_REQUEST_SENSE, "SCSI_REQUEST_SENSE", msc_scsi_request_sense }, //
++ { SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, "SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL", msc_scsi_prevent_allow }, //
++ { SCSI_START_STOP, "SCSI_START_STOP", msc_scsi_start_stop }, //
++ { SCSI_MODE_SELECT, "SCSI_MODE_SELECT", msc_scsi_mode_select },
++ { SCSI_FORMAT_UNIT, "SCSI_FORMAT_UNIT", msc_cmd_unknown },
++
++ { SCSI_READ_6, "SCSI_READ_6", msc_cmd_unknown },
++ { SCSI_WRITE_6, "SCSI_WRITE_6", msc_cmd_unknown },
++ { SCSI_RESERVE, "SCSI_RESERVE", msc_cmd_unknown },
++ { SCSI_RELEASE, "SCSI_RELEASE", msc_cmd_unknown },
++ { SCSI_SEND_DIAGNOSTIC, "SCSI_SEND_DIAGNOSTIC", msc_cmd_unknown },
++ { SCSI_SYNCHRONIZE_CACHE, "SCSI_SYNCHRONIZE_CACHE", msc_cmd_unknown },
++ { SCSI_MODE_SENSE_10, "SCSI_MODE_SENSE_10", msc_cmd_unknown },
++ { SCSI_REZERO_UNIT, "SCSI_REZERO_UNIT", msc_cmd_unknown },
++ { SCSI_SEEK_10, "SCSI_SEEK_10", msc_cmd_unknown },
++ { SCSI_WRITE_AND_VERIFY, "SCSI_WRITE_AND_VERIFY", msc_cmd_unknown },
++ { SCSI_WRITE_12, "SCSI_WRITE_12", msc_cmd_unknown },
++ { SCSI_READ_12, "SCSI_READ_12", msc_cmd_unknown },
++
++ { 0xff, "SCSI_UNKNOWN", msc_cmd_unknown },
++};
++
++
++/* msc_recv_command - process a new CBW
++ *
++ * Return non-zero if urb was not disposed of.
++ */
++int msc_recv_command(struct usbd_urb *urb, struct msc_private *msc)
++{
++ COMMAND_BLOCK_WRAPPER *command = (COMMAND_BLOCK_WRAPPER *)urb->buffer;
++ u8 op = command->CBWCB[0];
++ struct rbc_dispatch *dispatch;
++
++ /*
++ * c.f. section 6.2 - Valid and Meaningful CBW
++ * c.f. section 6.2.1 - Valid CBW
++ *
++ * The CBW was received after the device had sent a CSW or after a
++ * reset XXX check that we only set MSC_READY after reset or sending
++ * CSW.
++ *
++ * The CBW is 31 (1Fh) bytes in length and the bCBWSignature is
++ * equal to 43425355h.
++ */
++ THROW_IF(31 != urb->actual_length, error);
++ THROW_IF(CBW_SIGNATURE != le32_to_cpu(command->dCBWSignature), error);
++
++ /*
++ * c.f. section 6.2.2 - Meaningful CBW
++ *
++ * no reserved bits are set
++ * the bCBWLUN contains a valid LUN supported by the device
++ * both bCBWCBlength and the content of the CBWCB are in accordance with bInterfaceSubClass
++ */
++
++ // XXX checklun etc
++
++ /*
++ * Success
++ */
++ memcpy(&msc->command, command, sizeof(COMMAND_BLOCK_WRAPPER));
++ msc->data_transferred_in_bytes = msc->TransferLength_in_blocks = msc->TransferLength_in_bytes = 0;
++
++ TRACE_TAG(command->dCBWTag, urb->framenum);
++ usbd_free_urb(urb);
++
++ /*
++ * Search using the opcode to find the dispatch function to use and
++ * call it.
++ */
++ for (dispatch = rbc_dispatch_table; dispatch->op != 0xff; dispatch++) {
++ if ((dispatch->op == op)) {
++ TRACE_CBW(dispatch->name, dispatch->op);
++ TRACE_RECV(&(command->CBWCB[1]));
++ if (dispatch->rbc_command(msc, dispatch->name, op))
++ TRACE_MSG0(MSC,"COMMAND ERROR");
++ return 0;
++ }
++ }
++ /* FALL THROUGH if no match is found */
++
++ THROW_IF(msc_cmd_unknown(msc, "CMD_UNKNOWN", op), error);
++ return 0;
++
++ CATCH(error) {
++ TRACE_MSG0(MSC,"RECV CBW ERROR");
++ }
++
++ /*
++ * default behaviour is to stall or send ZLP on BULK-IN endpoint
++ */
++ TRACE_CBW("UNKNOWN COMMAND", op);
++
++ /*
++ * We cannot return error as we have deallocated urb and there is no
++ * other sensible course of action that the usbdcore layer can take
++ * for us.
++ */
++ return 0;
++}
++
++
++/* Sent Function - process a sent urb ********************************************************** */
++
++/* msc_urb_sent - called to indicate URB transmit finished
++ * @urb: pointer to struct usbd_urb
++ * @rc: result
++ *
++ * This is called when an urb is sent. Depending on current state
++ * it may:
++ *
++ * - continue sending data
++ * - send a CSW
++ * - start a recv for a CBW
++ *
++ * This is called from BOTTOM HALF context.
++ *
++ * Return non-zero if urb was not disposed of.
++ */
++int msc_urb_sent (struct usbd_urb *tx_urb, int rc)
++{
++ struct usbd_function_instance *function;
++ struct msc_private *msc = &msc_private;
++
++ msc_interrupts++;
++
++ TRACE_MSG1(MSC,"URB SENT tx_urb: %p", (int)tx_urb);
++ TRACE_MSG1(MSC,"URB SENT function: %p", (int)tx_urb->function_instance);
++
++ RETURN_EINVAL_IF(!(function = tx_urb->function_instance));
++
++ TRACE_MSG1(MSC,"URB SENT status: %p", usbd_get_device_status(function));
++ RETURN_EINVAL_IF(usbd_get_device_status(function) == USBD_CLOSING);
++
++ TRACE_MSG1(MSC,"URB SENT state: %p", usbd_get_device_state(function));
++ RETURN_EINVAL_IF(usbd_get_device_state(function) != STATE_CONFIGURED);
++
++ switch (msc->command_state) {
++ case MSC_DATA_IN_READ:
++ case MSC_DATA_IN_READ_FINISHED:
++ TRACE_MSG0(MSC,"URB SENT READ");
++ return msc_in_read_10_urb_sent(tx_urb, msc);
++ case MSC_QUERY:
++ // finished, send CSW
++ TRACE_MSG0(MSC,"URB SENT QUERY");
++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED);
++ break;
++ case MSC_STATUS:
++ default:
++ // sent a CSW need to receive the next CBW
++ TRACE_MSG0(MSC,"URB SENT STATUS");
++ msc->command_state = MSC_READY;
++ msc_start_recv_urb(msc->function, msc, sizeof(COMMAND_BLOCK_WRAPPER));
++ break;
++ }
++ usbd_free_urb (tx_urb);
++ return 0;
++}
++
++
++/* Receive Function - receiving an urb ********************************************************* */
++
++/* msc_recv_urb - process a received urb
++ *
++ * Return non-zero if urb was not disposed of.
++ */
++static int msc_recv_urb (struct usbd_urb *rcv_urb, int rc)
++{
++ struct msc_private *msc = &msc_private;
++
++ msc_interrupts++;
++
++ RETURN_EINVAL_IF(!msc->connected);
++
++ TRACE_MSG2(MSC, "RECV URB length: %d state: %d", rcv_urb->actual_length, msc->command_state);
++
++ switch(msc->command_state) {
++
++ // ready to start a new transaction
++ case MSC_READY:
++ return msc_recv_command(rcv_urb, msc);
++
++ // we think we are receiving data
++ case MSC_DATA_OUT_WRITE:
++ case MSC_DATA_OUT_WRITE_FINISHED:
++ msc_recv_out_blocks(rcv_urb, msc);
++ return 0;
++
++ // we think we are sending data
++ case MSC_DATA_IN_READ:
++ case MSC_DATA_IN_READ_FINISHED:
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
++ break;
++
++ // we think we are sending status
++ case MSC_STATUS:
++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
++ break;
++
++ // we don't think
++ case MSC_UNKNOWN:
++ default:
++ TRACE_MSG0(MSC,"RECV URB ERROR");
++ }
++ // let caller dispose of urb
++ return -EINVAL;
++}
++
++/* USB Device Functions ************************************************************************ */
++
++/* msc_event_handler - process a device event
++ *
++ * This function is called when an USBD event occurs.
++ *
++ * This is called from INTERRUPT context.
++ */
++void msc_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data)
++{
++ struct msc_private *msc = &msc_private;
++
++ msc_interrupts++;
++
++ TRACE_MSG1(MSC,"EVENT IRQ %d", event);
++
++ switch (event) {
++ case DEVICE_CONFIGURED:
++ TRACE_MSG0(MSC,"EVENT CONFIGURED");
++ msc->connected = 1;
++ msc->command_state = MSC_READY;
++ msc_start_recv_urb(function, msc, sizeof(COMMAND_BLOCK_WRAPPER));
++ break;
++
++ case DEVICE_BUS_INACTIVE:
++ case DEVICE_RESET:
++ case DEVICE_DE_CONFIGURED:
++ TRACE_MSG0(MSC,"EVENT RESET");
++ BREAK_IF(!msc->connected);
++ msc->connected = 0;
++ if (msc->rcv_urb_finished) {
++ usbd_free_urb (msc->rcv_urb_finished);
++ msc->rcv_urb_finished = NULL;
++ }
++ break;
++
++ default:
++ TRACE_MSG0(MSC,"EVENT IGNORED");
++ break;
++ }
++}
++
++
++/* msc_device_request - called to indicate urb has been received
++ *
++ * This function is called when a SETUP packet has been received that
++ * should be handled by the function driver. It will not be called to
++ * process the standard chapter nine defined requests.
++ *
++ * Return non-zero for failure.
++ */
++int msc_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
++{
++ struct msc_private *msc = &msc_private;
++ struct usbd_urb *urb;
++
++ TRACE_MSG0(MSC,"RECV SETUP");
++ msc_interrupts++;
++
++ // verify that this is a usb class request per cdc-acm specification or a vendor request.
++ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR)));
++
++ // determine the request direction and process accordingly
++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
++
++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
++ switch (request->bRequest) {
++ case MSC_BULKONLY_RESET:
++ // XXX TODO FIXME
++ return 0;
++ }
++
++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
++ switch (request->bRequest) {
++ case MSC_BULKONLY_GETMAXLUN:
++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, 1, NULL)));
++ urb->buffer[0] = 0;
++ urb->actual_length = 1;
++ RETURN_ZERO_IF(!usbd_start_in_urb(urb));
++ usbd_free_urb(urb);
++ return -EINVAL;
++ }
++ default:
++ break;
++ }
++ return -EINVAL;
++}
++
++/* msc_function_enable - this is called by the USBD core layer
++ *
++ * This is called to initialize the function when a bus interface driver
++ * is loaded.
++ */
++static int msc_function_enable (struct usbd_function_instance *function)
++{
++ struct msc_private *msc = &msc_private;
++
++ MOD_INC_USE_COUNT;
++
++ // XXX TODO need to verify that serial number is minimum of 12
++
++ msc_interrupts++;
++
++ msc->function = function;
++ msc->command_state = MSC_READY;
++
++ //msc_open_blockdev(msc);
++ return 0;
++}
++
++/* msc_function_disable - this is called by the USBD core layer
++ *
++ * This is called to close the function when a bus interface driver
++ * is unloaded.
++ */
++static void msc_function_disable (struct usbd_function_instance *function)
++{
++ struct msc_private *msc = &msc_private;
++
++ msc_interrupts++;
++
++ TRACE_MSG0(MSC,"FUNCTION EXIT");
++
++ msc->function = NULL;
++
++ MOD_DEC_USE_COUNT;
++}
++
++static struct usbd_function_operations function_ops = {
++ event_handler: msc_event_handler,
++ device_request: msc_device_request,
++ function_enable: msc_function_enable,
++ function_disable: msc_function_disable,
++};
++
++static struct usbd_function_driver function_driver = {
++ name: "msc-bulkonly",
++ fops:&function_ops,
++ device_description:&msc_device_description,
++ bNumConfigurations:sizeof (msc_description) / sizeof (struct usbd_configuration_description),
++ configuration_description:msc_description,
++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID),
++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID),
++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE),
++ bNumInterfaces:sizeof (msc_interfaces) / sizeof (struct usbd_interface_description),
++ interface_list:msc_interfaces,
++ endpointsRequested: ENDPOINTS,
++ requestedEndpoints: msc_endpoint_requests,
++};
++
++
++/* USB Module init/exit ************************************************************************ */
++
++/*
++ * msc_modinit - module init
++ *
++ */
++static int msc_modinit (void)
++{
++ int rc;
++ struct msc_private *msc = &msc_private;
++
++ printk(KERN_INFO "Copyright (c) 2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n");
++ printk (KERN_INFO "%s vendor_id: %04x product_id: %04x major: %d minor: %d\n", __FUNCTION__,
++ vendor_id, product_id, major, minor);
++
++ if (vendor_id)
++ function_driver.idVendor = cpu_to_le16(vendor_id);
++ if (product_id)
++ function_driver.idProduct = cpu_to_le16(product_id);
++
++ init_waitqueue_head(&msc->msc_wq);
++#if 0
++ /*
++ * Check device information and verify access to the block device.
++ */
++ RETURN_EINVAL_IF(!major && !minor);
++ msc->dev = MKDEV(major, minor);
++ if(!(msc->bdev = bdget(kdev_t_to_nr(msc->dev)))) {
++ printk(KERN_INFO"%s: Cannot find device %d %d\n", __FUNCTION__, major, minor);
++ return -EINVAL;
++ }
++ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0, BDEV_RAW))) {
++ printk(KERN_INFO"%s: Cannot get device %d %d\n", __FUNCTION__, major, minor);
++ return -EINVAL;
++ }
++
++ /*
++ * Note that capacity must return the LBA of the last addressable block
++ * c.f. RBC 4.4, RBC 5.3 and notes below RBC Table 6
++ *
++ * The blk_size array contains the number of addressable blocks or N,
++ * capacity is therefore N-1.
++ */
++
++ msc->capacity = (blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1) - 1;
++ msc->block_size = get_hardsect_size(msc->dev);
++ msc->max_blocks = PAGE_SIZE / msc->block_size;
++#endif
++
++ msc->block_dev_state = DEVICE_EJECTED;
++
++ msc_open_blockdev(msc);
++
++ /*
++ * setup generic buffer_head
++ */
++
++ msc->read_bh.b_rdev = msc->dev;
++ msc->read_bh.b_private = msc;
++ msc->read_bh.b_page = alloc_page(GFP_NOIO);
++
++ msc->write_bh.b_rdev = msc->dev;
++ msc->write_bh.b_private = msc;
++ msc->write_bh.b_page = alloc_page(GFP_NOIO);
++
++ msc->command_state = MSC_READY;
++ msc->io_state = MSC_INACTIVE;
++
++ msc_trace_init("msctrace", &msc_private);
++ make_crc_table();
++
++ TRACE_MSG1(MSC,"PAGE_SHIFT: %x", PAGE_SHIFT);
++ TRACE_MSG1(MSC,"PAGE_SIZE: %x", PAGE_SIZE);
++ TRACE_MSG1(MSC,"PAGE_SIZE: %d", PAGE_SIZE);
++
++ // register as usb function driver
++ THROW_IF (usbd_register_function (&function_driver, NULL), error);
++ msc->usb_driver_registered++;
++
++ CATCH(error) {
++ if (msc->usb_driver_registered) {
++ usbd_deregister_function (&function_driver);
++ msc->usb_driver_registered = 0;
++ }
++ return -EINVAL;
++ }
++ return 0;
++}
++
++/* msc_modexit - module cleanup
++ */
++static void msc_modexit (void)
++{
++ struct msc_private *msc = &msc_private;
++
++ if (msc->bdev) {
++ blkdev_put(msc->bdev, BDEV_RAW);
++ }
++ // XXX this should be a wait for read_bh/write_bh to go to NULL
++ while (msc->read_bh.b_data) {
++ printk(KERN_INFO"%s: sleeping on read bh\n", __FUNCTION__);
++ sleep_on_timeout(&msc->msc_wq, 20);
++ }
++
++ while (msc->write_bh.b_data) {
++ printk(KERN_INFO"%s: sleeping on read bh\n", __FUNCTION__);
++ sleep_on_timeout(&msc->msc_wq, 20);
++ }
++
++ printk(KERN_INFO"%s: freeing read bh\n", __FUNCTION__);
++ __free_page((void *)&msc->read_bh.b_page);
++ msc->read_bh.b_page = NULL;
++
++ printk(KERN_INFO"%s: freeing write bh\n", __FUNCTION__);
++ __free_page((void *)&msc->write_bh.b_page);
++ msc->write_bh.b_page = NULL;
++
++ if (msc->usb_driver_registered) {
++ usbd_deregister_function (&function_driver);
++ }
++
++ msc_trace_exit("msctrace");
++ free_crc_table();
++}
++
++
++module_init (msc_modinit);
++module_exit (msc_modexit);
+diff -uNr linux/drivers/no-otg/functions/msc/msc-scsi.h linux/drivers/otg/functions/msc/msc-scsi.h
+--- linux/drivers/no-otg/functions/msc/msc-scsi.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc-scsi.h 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,795 @@
++/*
++ * otg/msc_fd/msc_scsi.h - mass storage protocol library header
++ *
++ * Copyright(c) 2004, Belcarra
++ *
++ * Adapated from work:
++ *
++ * Copyright (c) 2003 Lineo Solutions, Inc.
++ *
++ * Written by Shunnosuke kabata
++ *
++ * 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.
++ *
++ */
++
++
++/*
++ *
++ * Documents
++ * Universal Serial Bus Mass Storage Class - Specification Overview
++ * Universal Serial Bus Mass Storage Class - Bulk-Only Transport
++ * T10/1240-D - Information technology - Reduced Block Commands
++ *
++ * Notes
++ *
++ * 1. Reduced Block Command set C.f. Table 2 RBC
++ *
++ * Command Support
++ * Name OpCode Fixed Removable Reference
++ * Format Unit 04h O O RBC
++ * Inquiry 12h M M SPC-2
++ * Mode Select (6) 15h M M SPC-2
++ * Mode Sense (6) 1Ah M M SPC-2
++ * Perisstent Reserve In 5Eh O O SPC-2
++ * Persistent Reserve Out 5Fh O O SPC-2
++ * Prevent/Allow Medium Removal 1Eh N/A M SPC-2
++ * Read (10) 28h M M RBC
++ * Read Capacity 25h M M RBC
++ * Reelase (6) 17h O O SPC-2
++ * Request Sense 03h O O SPC-2
++ * Reserve (6) 16h O O SPC-2
++ * Start Stop Unit 1Bh M M RBC
++ * Synchronize Cache 35h O O RBC
++ * Test Unit Ready 00h M M SPC-2
++ * Verify (10) 2Fh M M RBC
++ * Write (10) 2Ah M M RBC
++ * Write Buffer 3Bh M O SPC-2
++ *
++ * 2. Other commands seen?
++ * Sh MV FS
++ * SCSI_REZERO_UNIT 0x01 no
++ * SCSI_READ_6 0x08 yes
++ * SCSI_WRITE_6 0x0a yes
++ * SCSI_SEND_DIAGNOSTIC 0x1d no yes
++ * SCSI_READ_FORMAT_CAPACITY 0x23 yes yes
++ * SCSI_WRITE_AND_VERIFY 0x2e no
++ * SCSI_SEEK_10 0x2b no
++ * SCSI_MODE_SELECT_10 0x55 no yes yes
++ * SCSI_READ_12 0xa8 no yes
++ * SCSI_WRITE_12 0xaa no yes
++ *
++ *
++ * 3. Status - C.f. Chapter 5 - RBC
++ *
++ * Check Condition 02h
++ *
++ *
++ * 4. Sense Keys - C.f. Chapter 5 - RBC
++ *
++ * Not Ready 02h
++ * Media Error 03h
++ * Illegal Request 05h
++ * Unit Attention 06h
++ *
++ * 5. ASC/ASCQ - C.f. Chapter 5 - RBC
++ *
++ * Logical Unit Not Ready 04h
++ * Logical Unit Not Ready, Format in Progress 04h,04h
++ * Invalid Command Operation Code 20h
++ * Invalide Field in CDB 24h
++ * Format Command Failed 31h,01h
++ * Status Notification / Power Management Class Event 38h,02h
++ * Status Notification / Media Class Event 38h,04h
++ * Status Notification / Device Busy Class Event 38h,06h
++ * Low Power Condition Active 5Eh,00
++ * Power Condition Change to Active 5Eh,41h
++ * Power Condition Change to Idle 5Eh,42h
++ * Power Condition Change to Standby 5Eh,43h
++ * Power Condition Change to Sleep 5Eh,45h
++ * Power Condition Change to Device Control 5Eh,47h
++ *
++ * 6. ASCQ - C.f. Chapter 5 - RBC
++ *
++ *
++ *
++ * 7. Sense Keys C.f. Chapter 5 - RBC
++ *
++ * Command Status Sense Key ASC/ASCQ
++ *
++ * 5.1 Format Unit
++ * Format progress Check Condition Not Ready, Logical Unit Note Ready, Format in Progress
++ * Sueccsful completion Check Condition Unit Attention, Status Notification / Media Class Event
++ * Failure Check Condition Media error, Format Command Failed
++ *
++ * 5.2 Read (10)
++ *
++ * 5.3 Read Capacity
++ * No Media Check Condition Not Ready, Logical Unit Not Ready
++ *
++ * 5.4 Start Stop Unit
++ * Power Consumption Check Condition Illegal Request,Low Power Condition Active
++ *
++ * 5.5 Synchronize Cache Check Condition Illegal Request,Invalid Command Operation Code
++ *
++ * 5.6 Write (10
++ *
++ * 5.7 Verify
++ *
++ * 5.8 Mode
++ *
++ * 6.1 Inquiry
++ *
++ * 6.2 Mode Select (6)
++ * Cannot Save Check Condition Illegal Request,Invalid Field in CDB
++ *
++ * 6.3 Mode Sense (6)
++ *
++ * 6.4 Prevent Allow Medium Removal
++ *
++ * 6.5 Request Sense
++ *
++ * 6.6 Test Unit Ready
++ *
++ * 6.7 Write Buffer
++ *
++ * 7.1 Unit Attention
++ * Power Contition change Check Condition Unit Attention, Power Condition Change Notification
++ *
++ * 7.4.1 Event Status Sense
++ *
++ * Power Management Event Check Condition Unit Attention, Event Status Notification / Power Managment
++ * Media Class Event Check Condition Unit Attention, Event Status Notification / Media Class
++ * Device Busy Event Check Condition Unit Attention, Event Status Notification / Device Busy Class
++ *
++ * 7.4.6 Removable Medium Device Initial Response
++ * Ready Check Condition Unit Attention, Event Status Notification / Media Class Event
++ * Power Check Condition Unit Attention, Event Status Notification / Power Management Class Event
++ */
++
++#ifndef _MSCSCSIPROTO_H_
++#define _MSCSCSIPROTO_H_
++
++
++/*
++ * Class Specific Requests - C.f. MSC BO Chapter 3
++ */
++
++#define MSC_BULKONLY_RESET 0xff
++#define MSC_BULKONLY_GETMAXLUN 0xfe
++
++
++/*
++ * Class Code
++ */
++
++#define MASS_STORAGE_CLASS 0x08
++
++/*
++ * MSC - Specification Overview
++ *
++ * SubClass Codes - C.f MSC Table 2.1
++ */
++
++#define MASS_STORAGE_SUBCLASS_RBC 0x01
++#define MASS_STORAGE_SUBCLASS_SFF8020I 0x02
++#define MASS_STORAGE_SUBCLASS_QIC157 0x03
++#define MASS_STORAGE_SUBCLASS_UFI 0x04
++#define MASS_STORAGE_SUBCLASS_SFF8070I 0x05
++#define MASS_STORAGE_SUBCLASS_SCSI 0x06
++
++/*
++ * Protocol - C.f MSC Table 3.1
++ */
++
++#define MASS_STORAGE_PROTO_CBI_WITH_COMP 0x00
++#define MASS_STORAGE_PROTO_CBI_NO_COMP 0x01
++#define MASS_STORAGE_PROTO_BULK_ONLY 0x50
++
++/*
++ * SCSI Command
++*/
++
++#define SCSI_TEST_UNIT_READY 0x00
++#define SCSI_REQUEST_SENSE 0x03
++#define SCSI_FORMAT_UNIT 0x04
++#define SCSI_INQUIRY 0x12
++#define SCSI_MODE_SELECT 0x15 // aka MODE_SELECT_6
++#define SCSI_MODE_SENSE 0x1a // aka MODE_SENSE_6
++#define SCSI_START_STOP 0x1b
++#define SCSI_PREVENT_ALLOW_MEDIA_REMOVAL 0x1e
++#define SCSI_READ_FORMAT_CAPACITY 0x23
++#define SCSI_READ_CAPACITY 0x25
++#define SCSI_READ_10 0x28
++#define SCSI_WRITE_10 0x2a
++#define SCSI_VERIFY 0x2f
++
++#define SCSI_READ_6 0x08
++#define SCSI_WRITE_6 0x0a
++#define SCSI_RESERVE 0x16
++#define SCSI_RELEASE 0x17
++#define SCSI_SEND_DIAGNOSTIC 0x1d
++#define SCSI_SYNCHRONIZE_CACHE 0x35
++#define SCSI_MODE_SENSE_10 0x5a
++
++#define SCSI_REZERO_UNIT 0x01
++#define SCSI_REASSIGN_BLOCKS 0x07
++#define SCSI_COPY 0x18
++#define SCSI_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
++#define SCSI_WRITE_AND_VERIFY 0x2e
++#define SCSI_PREFETCH 0x34
++#define SCSI_READ_DEFECT_DATA 0x37
++#define SCSI_COMPARE 0x39
++#define SCSI_COPY_AND_VERIFY 0x3a
++#define SCSI_WRITE_BUFFER 0x3b
++#define SCSI_READ_BUFFER 0x3c
++#define SCSI_READ_LONG 0x3e
++#define SCSI_WRITE_LONG 0x3f
++#define SCSI_CHANGE_DEFINITION 0x40
++#define SCSI_WRITE_SAME 0x41
++#define SCSI_LOG_SELECT 0x4c
++#define SCSI_LOG_SENSE 0x4d
++#define SCSI_XD_WRITE 0x50
++#define SCSI_XP_WRITE 0x51
++#define SCSI_XD_READ 0x52
++#define SCSI_MODE_SELECT_10 0x55
++#define SCSI_RESERVE_10 0x56
++#define SCSI_RELEASE_10 0x57
++#define SCSI_MODE_SELECT_10 0x55
++#define SCSI_XD_WRITE_EXTENDED 0x80
++#define SCSI_REBUILD 0x81
++#define SCSI_REGENERATE 0x82
++
++#define SCSI_SEEK_10 0x2b
++#define SCSI_WRITE_AND_VERIFY 0x2e
++#define SCSI_WRITE_12 0xaa
++#define SCSI_READ_12 0xa8
++
++/*
++ * Private
++ */
++#define SCSI_PRIVATE_PCS 0xff
++
++/*
++ * SCSI Command Parameter
++ */
++
++#define CBW_SIGNATURE 0x43425355 /* USBC */
++#define CSW_SIGNATURE 0x53425355 /* USBS */
++
++#define PRODUCT_REVISION_LEVEL "1.00"
++
++/*
++ * Command Block Status Values - C.f MSC BO Table 5.3
++ */
++
++#define USB_MSC_PASSED 0x00 // good
++#define USB_MSC_FAILED 0x01 // bad
++#define USB_MSC_PHASE_ERROR 0x02 // we want to be reset
++
++
++
++
++/*
++ * SCSI Sense
++ * SenseKey
++ * AdditionalSenseCode
++ * SenseCodeQualifier
++ */
++
++#define SCSI_ERROR_CURRENT 0x70
++#define SCSI_ERROR_DEFERRED 0x07
++
++/*
++ * SCSI Sense Keys
++ */
++
++#define SK_NO_SENSE 0x00
++#define SK_RECOVERED_ERROR 0x01
++#define SK_NOT_READY 0x02
++#define SK_MEDIA_ERROR 0x03
++#define SK_HARDWARE_ERROR 0x04
++#define SK_ILLEGAL_REQUEST 0x05
++#define SK_UNIT_ATTENTION 0x06
++#define SK_DATA_PROTECT 0x07
++#define SK_BLANK_CHECK 0x08
++#define SK_COPY_ABORTED 0x0a
++#define SK_ABORTED_COMMAND 0x0b
++#define SK_VOLUME_OVERFLOW 0x0d
++#define SK_MISCOMPARE 0x0e
++
++/*
++ * 5. ASC/ASCQ - C.f. Chapter 5 - RBC
++ */
++
++#define SK(SenseKey,ASC,ASCQ) ((SenseKey<<16) | (ASC << 8) | (ASCQ))
++
++#define SCSI_SENSEKEY_NO_SENSE SK(SK_NO_SENSE, 0x00,0x00) // 0x000000
++
++#define SCSI_FAILURE_PREDICTION_THRESHOLD_EXCEEDED SK(SK_RECOVERED_ERROR, 0x5d,0x00) // 0x015d00
++
++#define SCSI_SENSEKEY_LOGICAL_UNIT_NOT_READY SK(SK_NOT_READY, 0x04,0x00) // 0x020400
++#define SCSI_SENSEKEY_FORMAT_IN_PROGRESS SK(SK_NOT_READY, 0x04,0x04) // 0x020404
++#define SCSI_SENSEKEY_MEDIA_NOT_PRESENT SK(SK_NOT_READY, 0x3a,0x00) // 0x023a00
++
++#define SCSI_SENSEKEY_WRITE_ERROR SK(SK_MEDIA_ERROR, 0x0c,0x02) // 0x030c02
++#define SCSI_SENSEKEY_UNRECOVERED_READ_ERROR SK(SK_MEDIA_ERROR, 0x11,0x00) // 0x031100
++#define SCSI_FORMAT_COMMAND_FAILED SK(SK_MEDIA_ERROR, 0x31,0x01) // 0x033101
++
++#define SCSI_SENSEKEY_COMMUNICATION_FAILURE SK(SK_HARDWARE_ERROR, 0x08,0x00) // 0x040800
++
++#define SCSI_SENSEKEY_INVALID_COMMAND SK(SK_ILLEGAL_REQUEST, 0x20,0x00) // 0x052000
++#define SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE SK(SK_ILLEGAL_REQUEST, 0x21,0x00) // 0x052100
++#define SCSI_SENSEKEY_INVALID_FIELD_IN_CDB SK(SK_ILLEGAL_REQUEST, 0x24,0x00) // 0x052400
++#define SCSI_SENSEKEY_LOGICAL_UNIT_NOT_SUPPORTED SK(SK_ILLEGAL_REQUEST, 0x25,0x00) // 0x052500
++#define SCSI_SENSEKEY_SAVING_PARAMETERS_NOT_SUPPORTED SK(SK_ILLEGAL_REQUEST, 0x39,0x00) // 0x053900
++#define SCSI_MEDIA_REMOVAL_PREVENTED SK(SK_ILLEGAL_REQUEST, 0x53,0x02) // 0x055302
++
++#define SCSI_SENSEKEY_NOT_READY_TO_READY_CHANGE SK(SK_UNIT_ATTENTION, 0x28,0x00) // 0x062800
++#define SCSI_SENSEKEY_RESET_OCCURRED SK(SK_UNIT_ATTENTION, 0x29,0x00) // 0x062900
++
++#define SCSI_SENSEKEY_STATUS_NOTIFICATION_POWER_CLASS SK(SK_UNIT_ATTENTION, 0x38,0x02) // 0x063802
++#define SCSI_SENSEKEY_STATUS_NOTIFICATION_MEDIA_CLASS SK(SK_UNIT_ATTENTION, 0x38,0x04) // 0x063804
++#define SCSI_SENSEKEY_STATUS_NOTIFICATION_DEVICE_BUSY SK(SK_UNIT_ATTENTION, 0x38,0x06) // 0x063806
++
++#define SCSI_SENSEKEY_LOW_POWER_CONDITION_ACTIVE SK(SK_UNIT_ATTENTION, 0x5e,0x00) // 0x065e00
++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_ACTIVE SK(SK_UNIT_ATTENTION, 0x5e,0x41) // 0x065e41
++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_IDLE SK(SK_UNIT_ATTENTION, 0x5e,0x42) // 0x065e42
++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_STANDBY SK(SK_UNIT_ATTENTION, 0x5e,0x43) // 0x065e43
++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_SLEEP SK(SK_UNIT_ATTENTION, 0x5e,0x45) // 0x065e45
++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_DEVICE SK(SK_UNIT_ATTENTION, 0x5e,0x47) // 0x065e47
++
++
++#define SCSI_SENSEKEY_WRITE_PROTECTED SK(SK_DATA_PROTECT, 0x27,0x00) // 0x072700
++
++
++/*
++ * Mode Page Code and Page Control
++ */
++#define SCSI_MODEPAGE_CONTROL_CURRENT 0x0
++#define SCSI_MODEPAGE_CONTROL_CHANGEABLE 0x1
++#define SCSI_MODEPAGE_CONTROL_DEFAULT 0x2
++#define SCSI_MODEPAGE_CONTROL_SAVED 0x3
++
++#define SCSI_MODEPAGE_UNIT_ATTENTION 0x00
++#define SCSI_MODEPAGE_ERROR_RECOVERY 0x01
++#define SCSI_MODEPAGE_DISCONNNECT_RECONNECT 0x02
++#define SCSI_MODEPAGE_FORMAT 0x03
++#define SCSI_MODEPAGE_RIGID_DRIVE_GEOMETRY 0x04
++#define SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE 0x05 // check
++#define SCSI_MODEPAGE_VERIFY_ERROR_RECOVERY 0x07
++#define SCSI_MODEPAGE_CACHING 0x08
++#define SCSI_MODEPAGE_CONTROL_MODE 0x0a
++#define SCSI_MODEPAGE_NOTCH_AND_PARTITION 0x0c
++#define SCSI_MODEPAGE_POWER_CONDITION 0x0d
++#define SCSI_MODEPAGE_XOR 0x10
++#define SCSI_MODEPAGE_CONTROL_MODE_ALIAS 0x1a
++#define SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS 0x1b// check
++#define SCSI_MODEPAGE_INFORMATION_EXCEPTIONS 0x1c
++#define SCSI_MODEPAGE_ALL_SUPPORTED 0x3f
++
++
++
++/*
++ * Command Block Wrapper / Command Status Wrapper
++ */
++
++/*
++ * Command Block Wrapper
++ */
++typedef struct{
++ unsigned long dCBWSignature;
++ unsigned long dCBWTag;
++ unsigned long dCBWDataTransferLength;
++ u8 bmCBWFlags;
++ u8 bCBWLUN:4,
++ Reserved:4;
++ u8 bCBWCBLength:5,
++ Reserved2:3;
++ u8 CBWCB[16];
++} __attribute__((packed)) COMMAND_BLOCK_WRAPPER;
++
++/*
++ * Command Status Wrapper
++ */
++typedef struct{
++ unsigned long dCSWSignature;
++ unsigned long dCSWTag;
++ unsigned long dCSWDataResidue;
++ u8 bCSWStatus;
++} __attribute__((packed)) COMMAND_STATUS_WRAPPER;
++
++/*
++ * SCSI Command
++ */
++
++/*
++ * INQUIRY
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 EnableVPD:1,
++ Reserved1:4,
++ LogicalUnitNumber:3;
++ u8 PageCode;
++ u8 Reserved2;
++ u8 AllocationLength;
++ u8 Reserved3;
++ u8 Reserved4;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 Reserved7;
++ u8 Reserved8;
++ u8 Reserved9;
++} __attribute__((packed)) SCSI_INQUIRY_COMMAND;
++
++typedef struct{
++ u8 PeripheralDeviceType:5,
++ PeripheralQaulifier:3;
++ u8 Reserved2:7,
++ RMB:1;
++ u8 ANSIVersion:3,
++ ECMAVersion:3,
++ ISOVersion:2;
++ u8 ResponseDataFormat:4,
++ Reserved3:4;
++ u8 AdditionalLength;
++ u8 Reserved4;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 VendorInformation[8];
++ u8 ProductIdentification[16];
++ u8 ProductRevisionLevel[4];
++} __attribute__((packed)) SCSI_INQUIRY_DATA;
++
++/*
++ * READ FORMAT CAPACITY
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 Reserved1:5,
++ LogicalUnitNumber:3;
++ u8 Reserved2;
++ u8 Reserved3;
++ u8 Reserved4;
++ u8 Reserved5;
++ u8 Reserved6;
++ u16 AllocationLength;
++ u8 Reserved7;
++ u8 Reserved8;
++ u8 Reserved9;
++} __attribute__((packed)) SCSI_READ_FORMAT_CAPACITY_COMMAND;
++
++typedef struct{
++ struct{
++ u8 Reserved1;
++ u8 Reserved2;
++ u8 Reserved3;
++ u8 CapacityListLength;
++ } __attribute__((packed)) CapacityListHeader;
++
++ struct{
++ u32 NumberofBlocks;
++ u8 DescriptorCode:2,
++ Reserved1:6;
++ u8 BlockLength[3];
++ } __attribute__((packed)) CurrentMaximumCapacityDescriptor;
++
++} __attribute__((packed)) SCSI_READ_FORMAT_CAPACITY_DATA;
++
++/*
++ * READ FORMAT CAPACITY
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 RelAdr:1,
++ Reserved1:4,
++ LogicalUnitNumber:3;
++ u32 LogicalBlockAddress;
++ u8 Reserved2;
++ u8 Reserved3;
++ u8 PMI:1,
++ Reserved4:7;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 Reserved7;
++} __attribute__((packed)) SCSI_READ_CAPACITY_COMMAND;
++
++typedef struct{
++ u32 LastLogicalBlockAddress;
++ u32 BlockLengthInBytes;
++} __attribute__((packed)) SCSI_READ_CAPACITY_DATA;
++
++/*
++ * REQUEST SENSE
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 Reserved1:5,
++ LogicalUnitNumber:3;
++ u8 Reserved2;
++ u8 Reserved3;
++ u8 AllocationLength;
++ u8 Reserved4;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 Reserved7;
++ u8 Reserved8;
++ u8 Reserved9;
++ u8 Reserved10;
++} __attribute__((packed)) SCSI_REQUEST_SENSE_COMMAND;
++
++typedef struct{
++ u8 ErrorCode:7,
++ Valid:1;
++ u8 Reserved1;
++ u8 SenseKey:4,
++ Reserved2:4;
++ u32 Information;
++ u8 AdditionalSenseLength;
++ u8 Reserved3[4];
++ u8 AdditionalSenseCode;
++ u8 AdditionalSenseCodeQualifier;
++ u8 Reserved4;
++ u8 Reserved5[3];
++} __attribute__((packed)) SCSI_REQUEST_SENSE_DATA;
++
++/*
++ * READ(10)
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 RelAdr:1,
++ Reserved1:2,
++ FUA:1,
++ DPO:1,
++ LogicalUnitNumber:3;
++ u32 LogicalBlockAddress;
++ u8 Reserved2;
++ u16 TransferLength;
++ u8 Reserved3;
++ u8 Reserved4;
++ u8 Reserved5;
++} __attribute__((packed)) SCSI_READ_10_COMMAND;
++
++/*
++ * MODE SENSE
++ */
++typedef struct{
++ u8 OperationCode;
++ u8 Reserved1:3,
++ DBD:1,
++ Reserved2:1,
++ LogicalUnitNumber:3;
++ u8 PageCode:6,
++ PageControl:2; // PC
++ u8 Reserved3;
++ u8 Reserved4;
++ u8 Reserved5;
++ u8 Reserved6;
++ u16 ParameterListLength;
++ u8 Reserved7;
++ u8 Reserved8;
++ u8 Reserved9;
++} __attribute__((packed)) SCSI_MODE_SENSE_COMMAND;
++
++typedef struct{
++ u8 ModeDataLength;
++ u8 MediumTypeCode;
++ u8 Reserved1:4,
++ DPOFUA:1,
++ Reserved2:2,
++ WriteProtect:1;
++ u8 Reserved3;
++} __attribute__((packed)) MODE_PARAMETER_HEADER;
++
++typedef struct{
++ u8 PageCode:6,
++ Reserved1:1,
++ PS:1;
++ u8 PageLength;
++ u8 DCR:1,
++ Reserved2:1,
++ PER:1,
++ Reserved3:1,
++ RC:1,
++ Reserved4:1,
++ Reserved5:1,
++ AWRE:1;
++ u8 ReadRetryCount;
++ u8 Reserved6[4];
++ u8 WriteRetryCount;
++ u8 Reserved7[3];
++} __attribute__((packed)) READ_WRITE_ERROR_RECOVERY_PAGE;
++
++typedef struct{
++ u8 PageCode:6,
++ Reserved1:1,
++ PS:1;
++ u8 PageLength;
++ u16 TransferRate;
++ u8 NumberofHeads;
++ u8 SectorsperTrack;
++ u16 DataBytesperSector;
++ u16 NumberofCylinders;
++ u8 Reserved2[9];
++ u8 MotorOnDelay;
++ u8 MotorOffDelay;
++ u8 Reserved3[7];
++ u16 MediumRotationRate;
++ u8 Reserved4;
++ u8 Reserved5;
++} __attribute__((packed)) FLEXIBLE_DISK_PAGE;
++
++typedef struct{
++ u8 PageCode:6,
++ Reserved1:1,
++ PS:1;
++ u8 PageLength;
++ u8 Reserved2:6,
++ SRFP:1,
++ SFLP:1;
++ u8 TLUN:3,
++ Reserved3:3,
++ SML:1,
++ NCD:1;
++ u8 Reserved4[8];
++} __attribute__((packed)) REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE;
++
++typedef struct{
++ u8 PageCode:6,
++ Reserved1:1,
++ PS:1;
++ u8 PageLength;
++ u8 Reserved2;
++ u8 InactivityTimeMultiplier:4,
++ Reserved3:4;
++ u8 SWPP:1,
++ DISP:1,
++ Reserved4:6;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 Reserved7;
++} __attribute__((packed)) TIMER_AND_PROTECT_PAGE;
++
++typedef struct{
++ READ_WRITE_ERROR_RECOVERY_PAGE ReadWriteErrorRecoveryPage;
++ FLEXIBLE_DISK_PAGE FlexibleDiskPage;
++ REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE RemovableBlockAccessCapabilitiesPage;
++ TIMER_AND_PROTECT_PAGE TimerAndProtectPage;
++} __attribute__((packed)) MODE_ALL_PAGES;
++
++typedef struct{
++ MODE_PARAMETER_HEADER ModeParameterHeader;
++ union{
++ READ_WRITE_ERROR_RECOVERY_PAGE ReadWriteErrorRecoveryPage;
++ FLEXIBLE_DISK_PAGE FlexibleDiskPage;
++ REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE RemovableBlockAccessCapabilitiesPage;
++ TIMER_AND_PROTECT_PAGE TimerAndProtectPage;
++ MODE_ALL_PAGES ModeAllPages;
++ } __attribute__((packed)) ModePages;
++} __attribute__((packed)) SCSI_MODE_SENSE_DATA;
++
++/*
++ * TEST UNIT READY
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 Reserved1:5,
++ LogicalUnitNumber:3;
++ u8 Reserved2;
++ u8 Reserved3;
++ u8 Reserved4;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 Reserved7;
++ u8 Reserved8;
++ u8 Reserved9;
++ u8 Reserved10;
++ u8 Reserved11;
++} __attribute__((packed)) SCSI_TEST_UNIT_READY_COMMAND;
++
++/*
++ * PREVENT-ALLOW MEDIA REMOVAL
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 Reserved1:5,
++ LogicalUnitNumber:3;
++ u8 Reserved2;
++ u8 Reserved3;
++ u8 Prevent:2,
++ Reserved4:6;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 Reserved7;
++ u8 Reserved8;
++ u8 Reserved9;
++ u8 Reserved10;
++ u8 Reserved11;
++} __attribute__((packed)) SCSI_PREVENT_ALLOW_MEDIA_REMOVAL_COMMAND;
++
++/*
++ * START-STOP UNIT
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 IMMED:1,
++ Reserved1:4,
++ LogicalUnitNumber:3;
++ u8 Reserved2;
++ u8 Reserved3;
++ u8 Start:1,
++ LoEj:1,
++ Reserved4:2,
++ PowerConditions:4;
++ u8 Reserved5;
++ u8 Reserved6;
++ u8 Reserved7;
++ u8 Reserved8;
++ u8 Reserved9;
++ u8 Reserved10;
++ u8 Reserved11;
++} __attribute__((packed)) SCSI_START_STOP_COMMAND;
++
++/*
++ * WRITE(10)
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 RelAdr:1,
++ Reserved1:2,
++ FUA:1,
++ DPO:1,
++ LogicalUnitNumber:3;
++ u32 LogicalBlockAddress;
++ u8 Reserved2;
++ u16 TransferLength;
++ u8 Reserved3;
++ u8 Reserved4;
++ u8 Reserved5;
++} __attribute__((packed)) SCSI_WRITE_10_COMMAND;
++
++/*
++ * VERIFY
++ */
++
++typedef struct{
++ u8 OperationCode;
++ u8 RelAdr:1,
++ ByteChk:1,
++ Reserved1:1,
++ Reserved2:1,
++ DPO:1,
++ LogicalUnitNumber:3;
++ u32 LogicalBlockAddress;
++ u8 Reserved3;
++ u16 VerificationLength;
++ u8 Reserved4;
++ u8 Reserved5;
++ u8 Reserved6;
++} __attribute__((packed)) SCSI_VERIFY_COMMAND;
++
++#endif /* _MSCSCSIPROTO_H_ */
++
+diff -uNr linux/drivers/no-otg/functions/msc/msc.h linux/drivers/otg/functions/msc/msc.h
+--- linux/drivers/no-otg/functions/msc/msc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/msc.h 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,141 @@
++/*
++ * otg/function/msc/msc.h - Mass Storage Class
++ *
++ * Copyright (c) 2003, 2004 Belcarra
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>
++ * Bruce Balden <balden@belcarra.com>
++ *
++ */
++/*!
++ * @defgroup MSCFunction Mass Storage
++ * @ingroup functiongroup
++ */
++/*!
++ * @file otg/functions/msc/msc.h
++ * @brief Mass Storage Driver private defines
++ *
++ *
++ * @ingroup MSCFunction
++ */
++
++#ifndef MSC_H
++#define MSC_H 1
++
++extern otg_tag_t msc_fd_trace_tag;
++#define MSC msc_fd_trace_tag
++
++/*
++ * Command/Data/Status Flow
++ * C.f. 5 - Figure 1
++ */
++
++typedef enum msc_state {
++ MSC_READY,
++ MSC_DATA_OUT_WRITE,
++ MSC_DATA_OUT_WRITE_FINISHED,
++ MSC_DATA_IN_READ,
++ MSC_DATA_IN_READ_FINISHED,
++ MSC_STATUS,
++ MSC_QUERY,
++ MSC_PHASE_ERROR,
++ MSC_UNKNOWN
++} msc_state_t;
++
++
++/*
++ * Device Transfer state
++ * C.F. Table 6.1
++ */
++
++typedef enum msc_device_state {
++ MSC_DEVICE_DN, // The device intends to transfer no data
++ MSC_DEVICE_DI, // The device intends to send data to the host
++ MSC_DEVICE_DO, // The device intents to received data from the host
++} msc_device_state_t;
++
++#define MSC_INACTIVE 0x0000
++#define MSC_BLOCKIO_PENDING 0x0001
++#define MSC_BLOCKIO_FINISHED 0x0002
++#define MSC_RECV_PENDING 0x0010
++#define MSC_RECV_FINISHED 0x0020
++#define MSC_SEND_PENDING 0x0040
++#define MSC_SEND_FINISHED 0x0080
++#define MSC_IOCTL_WAITING 0x0100 // there is an ioctl call waiting for I/O completion
++#define MSC_ABORT_IO 0x0200 // please abort current i/o
++
++#if 0
++struct SPC_inquiry_cdb {
++ u8 OperationCode; /* 12H */
++ u8 EnableVPD:1;
++ u8 CmdSupportData:1;
++ u8 Reserved0:6;
++ u8 PageCode;
++ u8 Reserved1;
++ u8 AllocationLen;
++ u8 Control;
++} __attribute__((packed));
++#endif
++
++struct msc_private {
++ struct usbd_function_instance *function;
++
++ int usb_driver_registered; // non-zero if usb function registered
++ unsigned char connected; // non-zero if connected to host (configured)
++
++ struct usbd_urb *rcv_urb_finished;
++
++ struct buffer_head read_bh;
++ struct buffer_head write_bh;
++
++ u16 read_pending;
++ u16 write_pending;
++
++ msc_device_state_t device_state;
++ msc_state_t command_state; // current command state
++ u16 io_state; // current IO state
++
++ COMMAND_BLOCK_WRAPPER command;
++
++ u32 lba; // next lba to read/write from
++ u32 transfer_blocks;
++ u32 TransferLength_in_blocks; // amount of transfer remaining
++ u32 TransferLength_in_bytes; // amount of transfer remaining
++ u32 data_transferred_in_bytes; // amount of data actually transferred
++
++ int major;
++ int minor;
++ kdev_t dev;
++ struct block_device *bdev;
++ u32 block_size;
++ u32 capacity;
++ u32 max_blocks;
++
++ int uptodate;
++
++ wait_queue_head_t msc_wq;
++ wait_queue_head_t ioctl_wq;
++
++ u32 status;
++ u32 block_dev_state;
++ u32 sensedata;
++ u32 info;
++};
++
++
++/*
++ * MSC Configuration
++ *
++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
++ */
++
++#define BULK_OUT 0x00
++#define BULK_IN 0x01
++#define ENDPOINTS 0x02
++
++extern struct usbd_function_operations function_ops;
++extern struct usbd_function_driver function_driver;
++
++
++#endif
+diff -uNr linux/drivers/no-otg/functions/msc/scripts/test1 linux/drivers/otg/functions/msc/scripts/test1
+--- linux/drivers/no-otg/functions/msc/scripts/test1 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/scripts/test1 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,37 @@
++#!/bin/sh
++#
++# Basic Start/Stop test
++#
++#
++# 1. Wait for connection
++# 2. loop making block device available for 10 second intervals
++# 3. exit if disconnected
++#
++
++MAJOR=7
++MINOR=0
++
++TIMEOUT=5
++
++set -x
++
++# wait for connection
++msc_check connect
++
++while sleep 1
++do
++
++ # make block device available
++ msc_check start $MAJOR $MINOR
++
++ sleep $TIMEOUT
++
++ # make block device un-available
++ msc_check stop
++
++ sleep $TIMEOUT
++
++ msc_check connected || break
++
++done
++
+diff -uNr linux/drivers/no-otg/functions/msc/scripts/test2 linux/drivers/otg/functions/msc/scripts/test2
+--- linux/drivers/no-otg/functions/msc/scripts/test2 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/scripts/test2 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,35 @@
++#!/bin/sh
++#
++# Basic Start/Stop test on connect/disconnect.
++#
++# Loop
++# 1. wait for connection
++# 2. making block device available for 10 seconds
++# 3. wait for disconnect
++# 4. continue
++#
++
++MAJOR=7
++MINOR=0
++
++TIMEOUT=5
++
++set -x
++
++while sleep 1
++do
++ # wait for connection
++ msc_check connect
++
++ # make block device available
++ msc_check start $MAJOR $MINOR
++
++ sleep $TIMEOUT
++
++ # make block device un-available
++ msc_check stop
++
++ msc_check disconnect
++
++done
++
+diff -uNr linux/drivers/no-otg/functions/msc/scripts/test3 linux/drivers/otg/functions/msc/scripts/test3
+--- linux/drivers/no-otg/functions/msc/scripts/test3 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/scripts/test3 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,46 @@
++#!/bin/sh
++#
++# Force connect/disconnect with start/stop.
++#
++# 1. loop making block device available for 10 second intervals
++# 2. exit if disconnected
++# 3. start
++# 4. stop
++# 5. force disconnect
++#
++
++MAJOR=7
++MINOR=0
++
++TIMEOUT=5
++
++set -x
++
++while sleep 1
++do
++
++ # bus request
++ otgd bus_req
++
++ # wait for connection
++ msc_check connect
++
++ # make block device available
++ msc_check start $MAJOR $MINOR
++
++ sleep $TIMEOUT
++
++ # make block device un-available
++ msc_check stop
++
++ sleep $TIMEOUT
++
++ # bus request/
++ otgd bus_req/
++
++ msc_check disconnect
++
++ sleep $TIMEOUT
++
++done
++
+diff -uNr linux/drivers/no-otg/functions/msc/scripts/test4 linux/drivers/otg/functions/msc/scripts/test4
+--- linux/drivers/no-otg/functions/msc/scripts/test4 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/scripts/test4 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,47 @@
++#!/bin/sh
++
++# Force connect/disconnect without start/stop.
++#
++#
++# 1. loop making block device available for 10 second intervals
++# 2. exit if disconnected
++# 3. force disconnect
++# 4. force connect
++#
++
++MAJOR=7
++MINOR=0
++
++TIMEOUT=5
++
++set -x
++
++# make block device available
++msc_check start $MAJOR $MINOR
++
++
++while sleep 1
++do
++
++ # bus request
++ otgd bus_req
++
++ # wait for connection
++ msc_check connect
++
++ sleep $TIMEOUT
++
++ sleep $TIMEOUT
++
++ # bus request/
++ otgd bus_req/
++
++ msc_check disconnect
++
++ sleep $TIMEOUT
++
++done
++
++# make block device un-available
++msc_check stop
++
+diff -uNr linux/drivers/no-otg/functions/msc/scripts/test5 linux/drivers/otg/functions/msc/scripts/test5
+--- linux/drivers/no-otg/functions/msc/scripts/test5 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/scripts/test5 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,47 @@
++#!/bin/sh
++#
++# Force connect/disconnect with start/stop.
++#
++# 1. loop making block device available for 10 second intervals
++# 2. exit if disconnected
++# 3. start
++# 4. force disconnect
++# 5. stop
++# 6. force connect
++#
++
++MAJOR=7
++MINOR=0
++
++TIMEOUT=5
++
++set -x
++
++while sleep 1
++do
++
++ # bus request
++ otgd bus_req
++
++ # wait for connection
++ msc_check connect
++
++ # make block device available
++ msc_check start $MAJOR $MINOR
++
++ sleep $TIMEOUT
++
++ # bus request/
++ otgd bus_req/
++
++ # make block device un-available
++ msc_check stop
++
++ sleep $TIMEOUT
++
++ msc_check disconnect
++
++ sleep $TIMEOUT
++
++done
++
+diff -uNr linux/drivers/no-otg/functions/msc/trace.c linux/drivers/otg/functions/msc/trace.c
+--- linux/drivers/no-otg/functions/msc/trace.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/otg/functions/msc/trace.c 2006-09-01 21:41:28.000000000 +0200
+@@ -0,0 +1,340 @@
++/*
++ * otg/msc_fd/trace.c
++ *
++ * Copyright (c) 2004 Belcarra
++ *
++ * Adapted from earlier work:
++ * Copyright (c) 2002, 2003 Belcarra
++ * Copyright (c) 2002 Lineo
++ *
++ * By:
++ * Stuart Lynne <sl@belcarra.com>,
++ * Tom Rushworth <tbr@belcarra.com>,
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/interrupt.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <linux/proc_fs.h>
++#include <linux/vmalloc.h>
++
++#include <asm/atomic.h>
++#include <asm/io.h>
++
++#include <linux/proc_fs.h>
++
++#include <linux/netdevice.h>
++#include <linux/pci.h>
++#include <linux/cache.h>
++
++
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/pgtable.h>
++#include <asm/pgalloc.h>
++
++#include <usbp-chap9.h>
++#include <usbp-mem.h>
++#include <usbp-func.h>
++#include "msc-scsi.h"
++#include "msc.h"
++#include "trace.h"
++
++
++static struct msc_private *msc_private;
++int msc_trace_first;
++int msc_trace_next;
++msc_trace_t *msc_traces;
++
++extern int msc_interrupts;
++
++
++#if defined(CONFIG_OTG_MSC_REGISTER_TRACE) && defined(CONFIG_PROC_FS)
++
++msc_trace_t *MSC_TRACE_NEXT(msc_trace_types_t msc_trace_type)
++{
++ msc_trace_t *p;
++
++ p = msc_traces + msc_trace_next;
++
++ if (msc_private) {
++ p->ticks = usbd_ticks(msc_private->function);
++ p->sofs = usbd_framenum(msc_private->function);
++ }
++ p->interrupts = msc_interrupts;
++ p->msc_trace_type = msc_trace_type;
++
++ msc_trace_next++;
++ msc_trace_next = (msc_trace_next == TRACE_MAX) ? 0 : msc_trace_next;
++
++ if (msc_trace_next == msc_trace_first) {
++ msc_trace_first++;
++ msc_trace_first = (msc_trace_first == TRACE_MAX) ? 0 : msc_trace_first;
++ }
++
++ return p;
++}
++
++/* Proc Filesystem *************************************************************************** */
++
++/* *
++ * msc_trace_proc_read - implement proc file system read.
++ * @file
++ * @buf
++ * @count
++ * @pos
++ *
++ * Standard proc file system read function.
++ */
++static ssize_t msc_trace_proc_read (struct file *file, char *buf, size_t count, loff_t * pos)
++{
++ unsigned long page;
++ int len = 0;
++ int index;
++ int oindex;
++ int previous;
++
++ MOD_INC_USE_COUNT;
++ // get a page, max 4095 bytes of data...
++ if (!(page = get_free_page (GFP_KERNEL))) {
++ MOD_DEC_USE_COUNT;
++ return -ENOMEM;
++ }
++
++ len = 0;
++ oindex = index = (*pos)++;
++
++ if (index == 0)
++ len += sprintf ((char *) page + len, " Index Ints Ticks\n");
++
++
++ index += msc_trace_first;
++ if (index >= TRACE_MAX)
++ index -= TRACE_MAX;
++
++ previous = (index) ? (index - 1) : (TRACE_MAX - 1);
++
++
++ if (
++ ((msc_trace_first < msc_trace_next) && (index >= msc_trace_first) && (index < msc_trace_next)) ||
++ ((msc_trace_first > msc_trace_next) && ((index < msc_trace_next) || (index >= msc_trace_first)))
++ )
++ {
++
++ u64 ticks = 0;
++
++ msc_trace_t *p = msc_traces + index;
++ unsigned char *cp;
++ unsigned int *ip;
++ int skip = 0;
++
++ if (oindex > 0) {
++ msc_trace_t *o = msc_traces + previous;
++
++ if (o->ticks)
++ ticks = (p->ticks > o->ticks) ? (p->ticks - o->ticks) : (o->ticks - p->ticks) ;
++
++ if (o->interrupts != p->interrupts)
++ skip++;
++
++ }
++
++ //printk(KERN_INFO"index: %d interrupts: %d\n", index, p->interrupts);
++ len += sprintf ((char *) page + len, "%s%6d %8d ", skip?"\n":"", index, p->interrupts);
++
++ if (ticks > 1024*1024)
++ len += sprintf ((char *) page + len, "%8dM ", ticks>>20);
++ else
++ len += sprintf ((char *) page + len, "%8d ", ticks);
++
++ len += sprintf ((char *) page + len, "%6d ", (int)p->sofs);
++
++ switch (p->msc_trace_type) {
++ case msc_trace_msg:
++ len += sprintf ((char *) page + len, " -- ");
++ len += sprintf ((char *) page + len, p->trace.msg.msg);
++ break;
++
++ case msc_trace_w:
++ len += sprintf ((char *) page + len, " --> ");
++ len += sprintf ((char *) page + len, "[%8x] W %s", p->trace.msg32.val, p->trace.msg32.msg);
++ break;
++
++ case msc_trace_r:
++ len += sprintf ((char *) page + len, "<-- ");
++ len += sprintf ((char *) page + len, "[%8x] R %s", p->trace.msg32.val, p->trace.msg32.msg);
++ break;
++
++ case msc_trace_msg32:
++ len += sprintf ((char *) page + len, " -- ");
++ len += sprintf ((char *) page + len, p->trace.msg32.msg, p->trace.msg32.val);
++ break;
++
++ case msc_trace_msg16:
++ len += sprintf ((char *) page + len, " -- ");
++ len += sprintf ((char *) page + len, p->trace.msg16.msg, p->trace.msg16.val0, p->trace.msg16.val1);
++ break;
++
++ case msc_trace_msg8:
++ len += sprintf ((char *) page + len, " -- ");
++ len += sprintf ((char *) page + len, p->trace.msg8.msg,
++ p->trace.msg8.val0, p->trace.msg8.val1, p->trace.msg8.val2, p->trace.msg8.val3);
++ break;
++
++ case msc_trace_setup:
++ cp = (unsigned char *)&p->trace.setup;
++ len += sprintf ((char *) page + len,
++ " -- request [%02x %02x %02x %02x %02x %02x %02x %02x]",
++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
++ break;
++
++ case msc_trace_recv:
++ case msc_trace_sent:
++ cp = (unsigned char *)&p->trace.sent;
++ len += sprintf ((char *) page + len,
++ "%s %s [%02x %02x %02x %02x %02x %02x %02x %02x]",
++ ( p->msc_trace_type == msc_trace_recv)?"<-- ":" -->",
++ ( p->msc_trace_type == msc_trace_recv)?"recv":"sent",
++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
++ break;
++ case msc_trace_rlba:
++ ip = (unsigned int *)&p->trace.ints;
++ len += sprintf ((char *) page + len,
++ "%s %s [%8x %08x]",
++ "<-- ", "rlba", ip[0], ip[1]);
++ break;
++ case msc_trace_slba:
++ case msc_trace_tlba:
++ ip = (unsigned int *)&p->trace.ints;
++ len += sprintf ((char *) page + len,
++ "%s %s [%8x %08x]", " -->",
++ ( p->msc_trace_type == msc_trace_tlba)?"tlba":"slba",
++ ip[0], ip[1]);
++ break;
++
++ case msc_trace_tag:
++ ip = (unsigned int *)&p->trace.ints;
++ len += sprintf ((char *) page + len,
++ "%s TAG: %8x FRAME: %03x", " -->",
++ ip[0], ip[1]);
++ break;
++
++ case msc_trace_sense:
++ ip = (unsigned int *)&p->trace.ints;
++ len += sprintf ((char *) page + len,
++ "%s SENSE: %06x INFO: %08x", " -->",
++ ip[0], ip[1]);
++ break;
++
++ case msc_trace_cbw:
++ len += sprintf ((char *) page + len, " --> ");
++ len += sprintf ((char *) page + len, "%s %02x", p->trace.msg32.msg, p->trace.msg32.val);
++ break;
++ }
++ len += sprintf ((char *) page + len, "\n");
++ }
++
++ if ((len > count) || (len == 0))
++ len = -EINVAL;
++ else if (len > 0 && copy_to_user (buf, (char *) page, len))
++ len = -EFAULT;
++
++ free_page (page);
++ MOD_DEC_USE_COUNT;
++ return len;
++}
++
++/* *
++ * msc_trace_proc_write - implement proc file system write.
++ * @file
++ * @buf
++ * @count
++ * @pos
++ *
++ * Proc file system write function, used to signal monitor actions complete.
++ * (Hotplug script (or whatever) writes to the file to signal the completion
++ * of the script.) An ugly hack.
++ */
++static ssize_t msc_trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos)
++{
++ return count;
++}
++
++static struct file_operations msc_trace_proc_operations_functions = {
++ read:msc_trace_proc_read,
++ write:msc_trace_proc_write,
++};
++
++
++/**
++ * msc_trace_init
++ *
++ * Return non-zero if not successful.
++ */
++int msc_trace_init (char *name,