--- 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 , + * Stuart Lynne , + * Bruce Balden + */ +/*! + * @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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#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 , + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @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 , + * Stuart Lynne , + * Bruce Balden + * + * + */ +/*! + * @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 , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @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 , + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @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 +#include +#include + +MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com"); + +MOD_DESCRIPTION ("Belcarra CDC-ACM Function"); +EMBED_LICENSE(); + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#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 , + * Stuart Lynne , + * Bruce Balden + */ +/*! + * @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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#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 , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @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 , + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @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 +#include +#include + +MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com"); + +MOD_DESCRIPTION ("Belcarra CDC-ACM Function"); +EMBED_LICENSE(); + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#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 , + * Stuart Lynne , + * Bruce Balden + */ +/*! + * @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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#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 , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @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 , + * Stuart Lynne , + * Bruce Balden + */ +/*! + * @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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#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 , + * Stuart Lynne , + * Bruce Balden + */ +/*! + * @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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#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 , + * Stuart Lynne , + * Bruce Balden + * + */ + +/*! + * @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 +#include +#include + +MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com"); + +MOD_DESCRIPTION ("Belcarra TTY Function"); +EMBED_LICENSE(); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "tty.h" +#include +#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 , + * Stuart Lynne , + * Bruce Balden + * + * + */ +/*! + * @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 , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @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 + * + * + * 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 +#include +#include +#include + +#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 +#include + +// 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 + * + * 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 +#include +#include + +#include +//#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) +#include +#include +#include +#include +#include +#include +#endif + +#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) +#include +#include +#include +#endif + +#if defined(CONFIG_ARCH_SAMSUNG) +#include +#include +#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 , + * Tom Rushworth , + * Bruce Balden + * + * + */ + +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "usbp-chap9.h" +#include +#include +#include + +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 , + * Tom Rushworth , + * + */ + +#include +#include +#include + +#include +//#include +#include +#include +//#include +#include + +#include +#include +//#include +//#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + + +#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) +#include +#include +#include +#include +#include +#include +#endif + +#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) +#include +#include +#include +#endif + +#if defined(CONFIG_ARCH_SAMSUNG) +#include +#include +#endif + +#include +#include +#include +#include +#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 , + * Bruce Balden , + * Tom Rushworth , + * + */ + +#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 , + * Bruce Balden + * + */ +/*! + * @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 +#include + +#include +#include +#include + +#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 , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @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 +#include +#include +#include +#include + +#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 , + * Stuart Lynne , + * + * 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 +#include + +/* + * 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 , + * Bruce Balden + * + */ +/*! + * @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 +#include + +#include +#include +#include +#include + +#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 , + * Bruce Balden + * + */ +/*! + * @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 +#include + +#include +#include +#include +#include + +#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 , + * Bruce Balden + * + */ +/*! + * @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 +#include + +#include +#include +#include + +#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 , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @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 + * Stuart Lynne + * Bruce Balden + * + */ + + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//#include +//#include +//#include +//#include + +#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 + * Stuart Lynne + * Bruce Balden + * Ted Powell + * + * + */ + + +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 , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#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 , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#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, §or, 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, §or, 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 + * Bruce Balden + * + */ + +#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 , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/functions/msc/msc-io-l24.c + * @brief + * + * NOTES + * + * TODO + * + * 1. implement prevent removal command. + * + * @ingroup MSCFunction + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#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 + * Bruce Balden + * + */ +/*! + * @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 , + * Tom Rushworth , + * Bruce Balden + * + * + */ + +/*! + * @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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#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 , + * Tom Rushworth , + * Bruce Balden + * + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#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 +#include +#include +#include + +MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MODULE_LICENSE("PROPRIETARY"); +MODULE_DESCRIPTION ("Belcarra Mass Storage Class Bulk Only Function"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usbp-chap9.h" +#include +#include +#include + +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, §or, 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, §or, 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 + * Bruce Balden + * + */ +/*! + * @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 , + * Tom Rushworth , + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include + + + +#include +#include +#include +#include + +#include +#include +#include +#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, struct msc_private *msc) +{ + printk(KERN_INFO"%s:\n", __FUNCTION__); + if (!(msc_traces = vmalloc(sizeof(msc_trace_t) * TRACE_MAX))) { + printk(KERN_ERR"%s: malloc failed %p %d\n", __FUNCTION__, msc_traces, sizeof(msc_trace_t) * TRACE_MAX); + return -EINVAL; + } + memset(msc_traces, 0, sizeof(msc_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 = &msc_trace_proc_operations_functions; + } + } + printk(KERN_INFO"%s: OK\n", __FUNCTION__); + msc_private = msc; + return 0; +} + +/** + * udc_release_io - release UDC io region + */ +void msc_trace_exit (char *name) +{ + msc_private = NULL; + { + unsigned long flags; + local_irq_save (flags); + remove_proc_entry (name, NULL); + if (msc_traces) { + msc_trace_t *p = msc_traces; + msc_traces = NULL; + vfree(p); + } + local_irq_restore (flags); + } +} + + +#else +int msc_trace_init (void) +{ + return 0; +} + +void msc_trace_exit (char *) +{ + return; +} +#endif + diff -uNr linux/drivers/no-otg/functions/msc/trace.h linux/drivers/otg/functions/msc/trace.h --- linux/drivers/no-otg/functions/msc/trace.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/msc/trace.h 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,303 @@ +/* + * otg/msc_fd/trace.h + * + * Copyright (c) 2004 Belcarra + * + * Adapted from earlier work: + * Copyright (c) 2002, 2003 Belcarra + * Copyright (c) 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + * + */ + + +typedef enum msc_trace_int_types { + msc_trace_int_udc, msc_trace_int_ep0, msc_trace_int_in, msc_trace_int_out, msc_trace_int_int +} msc_trace_int_types_t; + +typedef struct msc_trace_int { + u32 last; + u32 total; + u32 samples; +} msc_trace_int_t; + + +typedef enum msc_trace_types { + msc_trace_setup, msc_trace_msg, msc_trace_msg32, msc_trace_msg16, + msc_trace_msg8, msc_trace_recv, msc_trace_sent, msc_trace_w, msc_trace_r, + msc_trace_rlba, msc_trace_slba, msc_trace_tlba, msc_trace_tag, + msc_trace_sense, msc_trace_cbw +} msc_trace_types_t; + + +typedef struct msc_trace_regs32 { + u32 reg; + char * msg; +} msc_trace_regs32_t; + + +typedef struct msc_trace_msg { + char *msg; +} msc_trace_msg_t; + +typedef struct msc_trace_msg32 { + u32 val; + char *msg; +} msc_trace_msg32_t; + +typedef struct msc_trace_msg16 { + u16 val0; + u16 val1; + char *msg; +} msc_trace_msg16_t; + +typedef struct msc_trace_msg8 { + u8 val0; + u8 val1; + u8 val2; + u8 val3; + char *msg; +} msc_trace_msg8_t; + + +typedef struct trace { + msc_trace_types_t msc_trace_type; + char *function; + u32 interrupts; + u64 ticks; + u64 sofs; + + union { + msc_trace_msg_t msg; + msc_trace_msg8_t msg8; + msc_trace_msg16_t msg16; + msc_trace_msg32_t msg32; + + struct usbd_device_request setup; + unsigned char recv[8]; + unsigned char sent[8]; + + unsigned int ints[2]; + + } trace; + +} msc_trace_t; + + +#define TRACE_MAX 30000 + +extern int msc_trace_first; +extern int msc_trace_next; + +extern msc_trace_int_t *msc_trace_ints; +extern msc_trace_t *msc_traces; + +#ifdef CONFIG_OTG_MSC_REGISTER_TRACE + +msc_trace_t *MSC_TRACE_NEXT(msc_trace_types_t msc_trace_type); + +static __inline__ void TRACE_SETUP(struct usbd_device_request *setup) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_setup); + p->msc_trace_type = msc_trace_setup; + memcpy(&p->trace.setup, setup, sizeof(struct usbd_device_request)); + } +} + +static __inline__ void TRACE_MSG(char *msg) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg); + p->trace.msg.msg = msg; + } +} + +static __inline__ void TRACE_W(char *msg, u32 val) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_w); + p->trace.msg32.val = val; + p->trace.msg32.msg = msg; + } +} + +static __inline__ void TRACE_R(char *msg, u32 val) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_r); + p->trace.msg32.val = val; + p->trace.msg32.msg = msg; + } +} + +static __inline__ void TRACE_MSG32(char *msg, u32 val) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg32); + p->trace.msg32.val = val; + p->trace.msg32.msg = msg; + } +} + +static __inline__ void TRACE_MSG16(char *msg, u16 val0, u16 val1) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg16); + p->trace.msg16.val0 = val0; + p->trace.msg16.val1 = val1; + p->trace.msg16.msg = msg; + } +} + +static __inline__ void TRACE_MSG8(char *msg, u8 val0, u8 val1, u8 val2, u8 val3) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg8); + p->trace.msg8.val0 = val0; + p->trace.msg8.val1 = val1; + p->trace.msg8.val2 = val2; + p->trace.msg8.val3 = val3; + p->trace.msg8.msg = msg; + } +} + +static __inline__ void TRACE_RECV(unsigned char *cp) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_recv); + memcpy(&p->trace.recv, cp, 8); + } +} + +static __inline__ void TRACE_RECVN(unsigned char *cp, int bytes) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_recv); + memset(&p->trace.recv, 0, 8); + memcpy(&p->trace.recv, cp, bytes); + } +} + +static __inline__ void TRACE_SENT(unsigned char *cp) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_sent); + memcpy(&p->trace.sent, cp, 8); + } +} + + +static __inline__ void TRACE_RLBA(unsigned int lba, unsigned int crc) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_rlba); + p->trace.ints[0] = lba; + p->trace.ints[1] = crc; + } +} + +static __inline__ void TRACE_SLBA(unsigned int lba, unsigned int crc) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_slba); + p->trace.ints[0] = lba; + p->trace.ints[1] = crc; + } +} + +static __inline__ void TRACE_TLBA(unsigned int lba, unsigned int crc) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_tlba); + p->trace.ints[0] = lba; + p->trace.ints[1] = crc; + } +} + +static __inline__ void TRACE_TAG(unsigned int tag, unsigned int frame) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_tag); + p->trace.ints[0] = tag; + p->trace.ints[1] = frame; + } +} + +static __inline__ void TRACE_SENSE(unsigned int sense, unsigned int info) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_sense); + p->trace.ints[0] = sense; + p->trace.ints[1] = info; + } +} + +static __inline__ void TRACE_CBW(char *msg, int val) +{ + if (msc_traces) { + msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_cbw); + p->trace.msg32.val = val; + p->trace.msg32.msg = msg; + } +} + +#else + +static __inline__ void TRACE_SETUP(struct usbd_device_request *setup) +{ +} + +static __inline__ void TRACE_IRQS(u32 cr, u32 sr) +{ +} + +static __inline__ void TRACE_RECV(unsigned char *cp) +{ +} + +static __inline__ void TRACE_SENT(unsigned char *cp) +{ +} + +static __inline__ void TRACE_W(char *msg, u32 val) +{ +} + +static __inline__ void TRACE_R(char *msg, u32 val) +{ +} + +static __inline__ void TRACE_MSG(char *msg) +{ +} + +static __inline__ void TRACE_MSG32(char *msg, u32 val) +{ +} + +static __inline__ void TRACE_MSG16(char *msg, u16 val0, u16 val1) +{ +} + +static __inline__ void TRACE_TLBA(char *msg, u16 val0, u16 val1) +{ +} + +static __inline__ void TRACE_TAG(char *msg, u16 val0, u16 val1) +{ +} + +static __inline__ void TRACE_SENSE(char *msg, u8 val0, u8 val1, u8 val2, u8 vale) +{ +} + +#endif + +int msc_trace_init (char *str, struct msc_private *); +void msc_trace_exit (char *str); + diff -uNr linux/drivers/no-otg/functions/network/Config.in linux/drivers/otg/functions/network/Config.in --- linux/drivers/no-otg/functions/network/Config.in 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Config.in 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,70 @@ +# +# Network Function +# +# Copyright (C) 2002-2004 Belcarra +# + +mainmenu_option next_comment +comment "USB Peripheral Function Driver - Network" + +dep_tristate ' Network Function Driver' CONFIG_OTG_NETWORK $CONFIG_OTG + +if [ "$CONFIG_OTG_NETWORK" != "n" ]; then + + hex 'VendorID (hex value)' CONFIG_OTG_NETWORK_VENDORID "15ec" + hex 'ProductID (hex value)' CONFIG_OTG_NETWORK_PRODUCTID "e003" + hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_NETWORK_BCDDEVICE "0100" + + string 'iManufacturer (string)' CONFIG_OTG_NETWORK_MANUFACTURER "Belcarra" + string 'iProduct (string)' CONFIG_OTG_NETWORK_PRODUCT_NAME "Belcarra BLAN Device" + + choice "Mode" "\ + MDLM-BLAN-Networking-for-personal-devices CONFIG_OTG_NETWORK_BLAN \ + MDLM-SAFE-Networking-for-Bridge-Routers CONFIG_OTG_NETWORK_SAFE \ + CDC-Networking-for-Bridge-Routers CONFIG_OTG_NETWORK_CDC \ + Failsafe-BASIC-Networking-testing-only CONFIG_OTG_NETWORK_BASIC \ + Failsafe-BASIC2-Networking-testing-only CONFIG_OTG_NETWORK_BASIC2 \ + Experimental-Ethernet-Mode-Networking-testing-only CONFIG_OTG_NETWORK_EEM \ + " MDLM-BLAN-Networking-for-personal-devices + + if [ "$CONFIG_OTG_NETWORK_BLAN" = "y" ]; then + source drivers/otg/functions/network/Config.in-blan + fi + + if [ "$CONFIG_OTG_NETWORK_SAFE" = "y" ]; then + source drivers/otg/functions/network/Config.in-safe + fi + + if [ "$CONFIG_OTG_NETWORK_CDC" = "y" ]; then + source drivers/otg/functions/network/Config.in-cdc + fi + + if [ "$CONFIG_OTG_NETWORK_BASIC" = "y" ]; then + source drivers/otg/functions/network/Config.in-basic + fi + + if [ "$CONFIG_OTG_NETWORK_BASIC2" = "y" ]; then + source drivers/otg/functions/network/Config.in-basic2 + fi + + if [ "$CONFIG_OTG_NETWORK_EEM" = "y" ]; then + source drivers/otg/functions/network/Config.in-eem + fi + + comment '' + comment 'Testing Options' + bool 'Start Single Urb Test' CONFIG_OTG_NETWORK_START_SINGLE + bool 'EP0 Test' CONFIG_OTG_NETWORK_EP0TEST + dep_bool "Hotplug-config" CONFIG_OTG_NETWORK_HOTPLUG $CONFIG_HOTPLUG + +else + define_bool CONFIG_OTG_NETWORK_BLAN n + define_bool CONFIG_OTG_NETWORK_SAFE n + define_bool CONFIG_OTG_NETWORK_CDC n + define_bool CONFIG_OTG_NETWORK_BASIC n + define_bool CONFIG_OTG_NETWORK_BASIC2 n + define_bool CONFIG_OTG_NETWORK_EEM n +fi + +endmenu + diff -uNr linux/drivers/no-otg/functions/network/Config.in-basic linux/drivers/otg/functions/network/Config.in-basic --- linux/drivers/no-otg/functions/network/Config.in-basic 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Config.in-basic 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,17 @@ +# +# Network Function +# +# Copyright (C) 2002-2003 Belcarra +# + +mainmenu_option next_comment +comment "BASIC Networking Configuration" + + #bool " Failsafe BASIC Networking mode" CONFIG_OTG_NETWORK_BASIC + if [ "$CONFIG_OTG_NETWORK_BASIC" = "y" ]; then + comment 'BASIC Configuration' + string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_BASIC_DESC "BASIC Net Cfg" + string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_BASIC_INTF "Data Intf" + fi + +endmenu diff -uNr linux/drivers/no-otg/functions/network/Config.in-basic2 linux/drivers/otg/functions/network/Config.in-basic2 --- linux/drivers/no-otg/functions/network/Config.in-basic2 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Config.in-basic2 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,13 @@ +# +# Network Function +# +# Copyright (C) 2002-2003 Belcarra +# + +mainmenu_option next_comment + comment "BASIC2 Networking Configuration" + string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_BASIC2_DESC "BASIC Net Cfg" + string 'Comm Interface iInterface (string)' CONFIG_OTG_NETWORK_BASIC2_COMM_INTF "Comm Intf" + string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_BASIC2_DATA_INTF "Data Intf" + +endmenu diff -uNr linux/drivers/no-otg/functions/network/Config.in-blan linux/drivers/otg/functions/network/Config.in-blan --- linux/drivers/no-otg/functions/network/Config.in-blan 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Config.in-blan 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,38 @@ +# +# Network Function +# +# Copyright (C) 2002-2003 Belcarra +# + +mainmenu_option next_comment + comment "MBLM-BLAN Networking Configuration" + + string 'iConfiguration (string)' CONFIG_OTG_NETWORK_BLAN_DESC "BLAN Net Cfg" + string 'iInterface (string)' CONFIG_OTG_NETWORK_BLAN_INTF "Comm/Data Intf" + + + comment 'Special Framing Options' + bool "CRC" CONFIG_OTG_NETWORK_BLAN_CRC + if [ "$CONFIG_OTG_NETWORK_BLAN_CRC" = "y" ]; then + + bool "Pad Before CRC (to wMaxPacketSize-1)" CONFIG_OTG_NETWORK_BLAN_PADBEFORE + bool "Pad After CRC" CONFIG_OTG_NETWORK_BLAN_PADAFTER + if [ "$CONFIG_OTG_NETWORK_BLAN" = "y" -a "$CONFIG_OTG_NETWORK_BLAN_PADAFTER" = "y" ]; then + int 'Pad multiple' CONFIG_OTG_NETWORK_BLAN_PADBYTES "8" + fi + bool "Fermat Randomizer" CONFIG_OTG_NETWORK_BLAN_FERMAT + fi + + comment 'USBLAN Options' + bool "Do not Set Time" CONFIG_OTG_NETWORK_BLAN_DO_NOT_SETTIME + bool "Request Hostname" CONFIG_OTG_NETWORK_BLAN_HOSTNAME + bool "Infrastructure Device" CONFIG_OTG_NETWORK_BLAN_NONBRIDGED + bool "Data Notification OK" CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK + int 'Pad multiple' CONFIG_OTG_NETWORK_BLAN_PADBYTES "8" + int 'Polling Interval ' CONFIG_OTG_NETWORK_BLAN_INTERVAL "4" + bool "Set iProduct from IPADDR" CONFIG_OTG_NETWORK_BLAN_IPADDR + bool "Auto-config" CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG + comment '' + + +endmenu diff -uNr linux/drivers/no-otg/functions/network/Config.in-cdc linux/drivers/otg/functions/network/Config.in-cdc --- linux/drivers/no-otg/functions/network/Config.in-cdc 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Config.in-cdc 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,16 @@ +# +# Network Function +# +# Copyright (C) 2002-2003 Belcarra +# + +mainmenu_option next_comment + comment "CDC Networking Configuration" + + string 'iConfiguration (string)' CONFIG_OTG_NETWORK_CDC_DESC "SAFE Net Cfg" + string 'Comm Interface iInterface (string)' CONFIG_OTG_NETWORK_CDC_COMM_INTF "Comm Intf" + string 'Data (diabled) iInterface (string)' CONFIG_OTG_NETWORK_CDC_NODATA_INTF "Data (Disabled) Intf" + string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_CDC_DATA_INTF "Data Intf" + + +endmenu diff -uNr linux/drivers/no-otg/functions/network/Config.in-eem linux/drivers/otg/functions/network/Config.in-eem --- linux/drivers/no-otg/functions/network/Config.in-eem 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Config.in-eem 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,17 @@ +# +# Network Function +# +# Copyright (C) 2002-2003 Belcarra +# + +mainmenu_option next_comment +comment "EEM Networking Configuration" + + #bool " Experimental Ethernet Mode" CONFIG_OTG_NETWORK_EEM + if [ "$CONFIG_OTG_NETWORK_EEM" = "y" ]; then + comment 'EEM Configuration' + string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_EEM_DESC "EEM Net Cfg" + string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_EEM_INTF "Data Intf" + fi + +endmenu diff -uNr linux/drivers/no-otg/functions/network/Config.in-safe linux/drivers/otg/functions/network/Config.in-safe --- linux/drivers/no-otg/functions/network/Config.in-safe 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Config.in-safe 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,21 @@ +# +# Network Function +# +# Copyright (C) 2002-2003 Belcarra +# + +mainmenu_option next_comment + comment "MDLM-SAFE Networking Configuration" + + string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_SAFE_DESC "SAFE Net Cfg" + string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_SAFE_INTF "Data Intf" + bool " Do not Set Time" CONFIG_OTG_NETWORK_SAFE_DO_NOT_SETTIME + bool " CRC" CONFIG_OTG_NETWORK_SAFE_CRC + if [ "$CONFIG_OTG_NETWORK_SAFE_CRC" = "y" ]; then + bool "Pad Before CRC (to wMaxPacketSize-1)" CONFIG_OTG_NETWORK_SAFE_PADBEFORE + fi + bool "Infrastructure Device" CONFIG_OTG_NETWORK_SAFE_NONBRIDGED + comment '' + + +endmenu diff -uNr linux/drivers/no-otg/functions/network/Kconfig linux/drivers/otg/functions/network/Kconfig --- linux/drivers/no-otg/functions/network/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Kconfig 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,131 @@ +menu "OTG Network Function" + depends on OTG + +config OTG_NETWORK + tristate " Network Function Driver" + depends on OTG + +menu "OTG Network Function options" + depends on OTG_NETWORK + +config OTG_NETWORK_VENDORID + hex "VendorID (hex value)" + depends on OTG && OTG_NETWORK + default "0x15ec" + +config OTG_NETWORK_PRODUCTID + hex "ProductID (hex value)" + depends on OTG && OTG_NETWORK + default "0xe003" + +config OTG_NETWORK_BCDDEVICE + hex "bcdDevice (binary-coded decimal)" + depends on OTG && OTG_NETWORK + default "0x0100" + +config OTG_NETWORK_MANUFACTURER + string "iManufacturer (string)" + depends on OTG && OTG_NETWORK + default "Belcarra" + +config OTG_NETWORK_PRODUCT_NAME + string "iProduct (string)" + depends on OTG && OTG_NETWORK + default "Belcarra BLAN Device" + +config OTG_NETWORK_BLAN_DESC + string " iConfiguration (string)" + depends on OTG && OTG_NETWORK + default "BLAN Net Cfg" + +config OTG_NETWORK_MANUFACTURER + string "iManufacturer (string)" + depends on OTG && OTG_NETWORK + default "Belcarra" + +config OTG_NETWORK_PRODUCT_NAME + string "iProduct (string)" + depends on OTG && OTG_NETWORK + default "Belcarra BLAN Device" + +config OTG_NETWORK_BLAN_DESC + string " iConfiguration (string)" + depends on OTG && OTG_NETWORK + default "BLAN Net Cfg" + +config OTG_NETWORK_BLAN_INTF + string " iInterface (string)" + depends on OTG && OTG_NETWORK + default "Comm/Data Intf" + +config OTG_NETWORK_BLAN_PADBYTES + int " Pad multiple" + default "8" + +config OTG_NETWORK_SAFE_DESC + string " Data Interface iConfiguration (string)" + depends on OTG && OTG_NETWORK + default "SAFE Net Cfg" + +config OTG_NETWORK_SAFE_INTF + string " Data Interface iInterface (string)" + depends on OTG && OTG_NETWORK + default "Data Intf" + +config OTG_NETWORK_CDC_DESC + string " iConfiguration (string)" + depends on OTG && OTG_NETWORK + default "SAFE Net Cfg" + +config OTG_NETWORK_CDC_COMM_INTF + string " Comm Interface iInterface (string)" + depends on OTG && OTG_NETWORK + default "Comm Intf" + +config OTG_NETWORK_CDC_NODATA_INTF + string " Data (diabled) iInterface (string)" + depends on OTG && OTG_NETWORK + default "Data (Disabled) Intf" + +config OTG_NETWORK_CDC_DATA_INTF + string " Data Interface iInterface (string)" + depends on OTG && OTG_NETWORK + default "Data Intf" + +config OTG_NETWORK_BASIC_DESC + string " Data Interface iConfiguration (string)" + depends on OTG && OTG_NETWORK + default "BASIC Net Cfg" + +config OTG_NETWORK_BASIC_INTF + string " Data Interface iInterface (string)" + depends on OTG && OTG_NETWORK + default "Data Intf" + +config OTG_NETWORK_BASIC2_DESC + string " Data Interface iConfiguration (string)" + depends on OTG && OTG_NETWORK + default "BASIC Net Cfg" + +config OTG_NETWORK_BASIC2_COMM_INTF + string " Comm Interface iInterface (string)" + depends on OTG && OTG_NETWORK + default "Comm Intf" + +config OTG_NETWORK_BASIC2_DATA_INTF + string " Data Interface iInterface (string)" + depends on OTG && OTG_NETWORK + default "Data Intf" + +config OTG_NETWORK_START_SINGLE + bool " Start Single Urb Test" + depends on OTG && OTG_NETWORK + default n + +config OTG_NETWORK_EP0TEST + bool " EP0 Test" + depends on OTG && OTG_NETWORK + default n + +endmenu +endmenu diff -uNr linux/drivers/no-otg/functions/network/Makefile linux/drivers/otg/functions/network/Makefile --- linux/drivers/no-otg/functions/network/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Makefile 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,70 @@ +# +# Network Function Driver +# +# Copyright (C) 2002-2003 Belcarra + + +O_TARGET := network_target.o +list-multi := network_fd.o + +network_fd-objs := net-l24-os.o net-fd.o basic.o basic2.o blan.o cdc.o safe.o fermat.o + +# Objects that export symbols. +export-objs := net-l24-os.o net-fd.o + + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_OTG_NETWORK) += network_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. + + +OTG_DIR=$(TOPDIR)/drivers/otg +NETWORK_DIR=$(OTG_DIR)/functions/network +include $(TOPDIR)/Rules.make +EXTRA_CFLAGS += -Wno-unused -Wstrict-prototypes -Wno-format +EXTRA_CFLAGS += -I$(NETWORK_DIR) -I$(OTG_DIR) +EXTRA_CFLAGS_nostdinc += -Wstrict-prototypes -Wno-unused -Wno-format +EXTRA_CFLAGS_nostdinc += -I$(NETWORK_DIR) -I$(OTG_DIR) + +# Link rules for multi-part drivers. + +network_fd.o: $(network_fd-objs) + $(LD) -r -o $@ $(network_fd-objs) + +# dependencies: + +net-fd.o: net-fd.h net-os.h network.h +net-l24-os.o: net-fd.h net-os.h network.h + + diff -uNr linux/drivers/no-otg/functions/network/Makefile-l26 linux/drivers/otg/functions/network/Makefile-l26 --- linux/drivers/no-otg/functions/network/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/Makefile-l26 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,22 @@ +# +# Network Function Driver +# +# Copyright (C) 2002-2004 Belcarra Technologies Corp + + +OTG=$(TOPDIR)/drivers/otg +NETWORKD=$(OTG)/functions/network +OTGCORE_DIR=$(OTG)/otgcore +USBDCORE_DIR=$(OTG)/usbdcore +EXTRA_CFLAGS += -I$(NETWORKD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) +EXTRA_CFLAGS_nostdinc += -I$(NETWORKD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) + +network_fd-objs := network.o basic.o basic2.o blan.o cdc.o safe.o fermat.o +obj-$(CONFIG_OTG_NETWORK) += network_fd.o + + +# Link rules for multi-part drivers. + +network_fd.o: $(network_fd-objs) + $(LD) -r -o $@ $(network_fd-objs) + diff -uNr linux/drivers/no-otg/functions/network/TODO.txt linux/drivers/otg/functions/network/TODO.txt --- linux/drivers/no-otg/functions/network/TODO.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/TODO.txt 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,32 @@ +NETWORK TODO List Stuart Lynne +Belcarra Tue Aug 24 21:36:09 PDT 2004 + + +1. NETWORK documentation + + +2. BLAN + - data notification + - timeout to resend data notification after XX mS + +3. CDC + - filters + + +4. RNDIS + - should we add back in? + + +5. NOTIFICATIONS + + NETWORK_CONNECTION + CONNECTION_SPEED_CHANGE + + + +I suspect that we should make the data notification a runtime option, +the host driver would "enable" this after a certain number of devices +are plugged in (say 5 or more, some configurable number.) This would +be done by sending a vendor device request to the peripheral to tell +the network driver that it needs to use this mechanism. + diff -uNr linux/drivers/no-otg/functions/network/basic.c linux/drivers/otg/functions/network/basic.c --- linux/drivers/no-otg/functions/network/basic.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/basic.c 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,247 @@ +/* + * otg/functions/network/basic.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + * + */ +/*! + * @file otg/functions/network/basic.c + * @brief This file implements the required descriptors to implement + * a basic network device with a single interface. + * + * The BASIC network driver implements a very simple descriptor set. + * A single interface with two BULK data endpoints and a optional + * INTERRUPT endpoint. + * + * @ingroup NetworkFunction + */ + + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "network.h" + +#define NTT network_fd_trace_tag + +#ifdef CONFIG_OTG_NETWORK_BASIC +/* USB BASIC Configuration ******************************************************************** */ + +/* BASIC Communication Interface Class descriptors + */ +/*! BULK OUT data endpoint descriptor + */ +static struct usbd_endpoint_descriptor basic_data_1 = { + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_OUT, + bmAttributes: BULK, + wMaxPacketSize: 0, + bInterval: 0, +}; +/*! BULK IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor basic_data_2 =// { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; +{ + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: BULK, + wMaxPacketSize: 0, + bInterval: 0x0, +}; + +/*! INTERRUPT IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor basic_comm_1 = //{ 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; +{ + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: INTERRUPT, + wMaxPacketSize: 0, + bInterval: 0xa, +}; + +/*! Endpoint descriptor list + */ +static struct usbd_endpoint_descriptor *basic_default[] = { &basic_data_1, + &basic_data_2, + &basic_comm_1, }; +u8 basic_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; + +/*! Data Interface Descriptor + */ +static struct usbd_interface_descriptor basic_data_alternate_descriptor = { + bLength:sizeof(struct usbd_interface_descriptor), + bDescriptorType:USB_DT_INTERFACE, + bInterfaceNumber: 0x00, + bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting + sizeof (basic_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints + bInterfaceClass:LINEO_CLASS, + bInterfaceSubClass:LINEO_SUBCLASS_BASIC_NET, + bInterfaceProtocol:LINEO_BASIC_NET_CRC, + iInterface:0x00, +}; + +/*! Data Alternate Interface Description List + */ +static struct usbd_alternate_description basic_data_alternate_descriptions[] = { + { iInterface: CONFIG_OTG_NETWORK_BASIC_INTF, + interface_descriptor: (struct usbd_interface_descriptor *)&basic_data_alternate_descriptor, + endpoints:sizeof (basic_default) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list: basic_default, + endpoint_indexes: basic_indexes, + }, +}; + + +/* BASIC Data Interface Alternate descriptions and descriptors + */ + +/* BASIC Interface descriptions and descriptors + */ +/*! Interface Description List + */ +struct usbd_interface_description basic_interfaces[] = { + { alternates:sizeof (basic_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), + alternate_list:basic_data_alternate_descriptions,}, +}; + +/* BASIC Configuration descriptions and descriptors + */ +/*! Configuration Descriptor + */ +u8 basic_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { + 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength + sizeof (basic_interfaces) / sizeof (struct usbd_interface_description), + 0x01, 0x00, // bConfigurationValue, iConfiguration + 0, 0, +}; + +/*! Configuration Description List + */ +struct usbd_configuration_description basic_description[] = { + { iConfiguration: CONFIG_OTG_NETWORK_BASIC_DESC, + configuration_descriptor: (struct usbd_configuration_descriptor *)basic_configuration_descriptor, + }, + +}; + +/* BASIC Device Description + */ +/*! Device Descriptor + */ +static struct usbd_device_descriptor basic_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_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), +}; + +#ifdef CONFIG_OTG_HIGH_SPEED +/*! High Speed Device Qualifier Descriptor + */ +static struct usbd_device_qualifier_descriptor basic_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 basic_endpoint_requests[ENDPOINTS+1] = { + { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, + { 0, }, +}; + +/*! OTG Descriptor + */ +static struct usbd_otg_descriptor basic_otg_descriptor = { + bLength : sizeof(struct usbd_otg_descriptor), + bDescriptorType: USB_DT_OTG, + bmAttributes: 0, +}; + +/*! Device Description + */ +struct usbd_device_description basic_device_description = { + device_descriptor: &basic_device_descriptor, +#ifdef CONFIG_OTG_HIGH_SPEED + device_qualifier_descriptor: &basic_device_qualifier_descriptor, +#endif /* CONFIG_OTG_HIGH_SPEED */ + otg_descriptor: &basic_otg_descriptor, + iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, + iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, +#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) + iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, +#endif +}; + + +/*! basic_init + * @param function The function instance + */ +void basic_init (struct usbd_function_instance *function) +{ + basic_data_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; +} + +/*! function driver description + */ +struct usbd_function_driver basic_function_driver = { + name: "network-BASIC", + fops: &net_fd_function_ops, + device_description: &basic_device_description, + bNumConfigurations: sizeof (basic_description) / sizeof (struct usbd_configuration_description), + configuration_description: basic_description, + idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), + bNumInterfaces:sizeof (basic_interfaces) / sizeof (struct usbd_interface_description), + interface_list:basic_interfaces, + endpointsRequested: ENDPOINTS, + requestedEndpoints: basic_endpoint_requests, +}; +#endif /* CONFIG_OTG_NETWORK_BASIC */ + diff -uNr linux/drivers/no-otg/functions/network/basic2.c linux/drivers/otg/functions/network/basic2.c --- linux/drivers/no-otg/functions/network/basic2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/basic2.c 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,320 @@ +/* + * otg/functions/network/basic2.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + * + */ +/*! + * @file otg/functions/network/basic2.c + * @brief This file implements the required descriptors to implement + * a basic network device with two interfaces. + * + * The BASIC2 network driver implements a very simple descriptor set. + * A data interface with two BULK data endpoints and comm interface with + * an INTERRUPT endpoint. + * + * @ingroup NetworkFunction + */ + + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "network.h" + +#define NTT network_fd_trace_tag + +#ifdef CONFIG_OTG_NETWORK_BASIC2 +/* USB BASIC Configuration ******************************************************************** */ + +/* + * This provides a slight amplification of the basic configuration, it moves the + * interrupt endpoint (if available) to a separate interface, so that it is similiar + * to the cdc configuration + */ + +/* BASIC Communication Interface Class descriptors + */ +//static u8 basic2_data_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; +//static u8 basic2_data_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; +//static u8 basic2_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; + +/*! BULK OUT data endpoint descriptor + */ +static struct usbd_endpoint_descriptor basic2_data_1 = { + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_OUT, + bmAttributes: BULK, + wMaxPacketSize: 0, + bInterval: 0, +}; +/*! BULK IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor basic2_data_2 = { + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: BULK, + wMaxPacketSize: 0, + bInterval: 0x0, +}; + +/*! INTERRUPT IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor basic2_comm_1 = { + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: INTERRUPT, + wMaxPacketSize: 0, + bInterval: 0xa, +}; + +/*! Endpoint descriptor list + */ +static struct usbd_endpoint_descriptor *basic2_default[] = { + &basic2_data_1, + &basic2_data_2, + &basic2_comm_1, }; +u8 basic2_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; + +static struct usbd_endpoint_descriptor *basic2_comm_endpoints[] = { + (struct usbd_endpoint_descriptor *) &basic2_comm_1, }; + +/*! Endpoint descriptor list + */ +static struct usbd_endpoint_descriptor *basic2_data_endpoints[] = { + (struct usbd_endpoint_descriptor *) &basic2_data_1, + (struct usbd_endpoint_descriptor *) &basic2_data_2, }; + +u8 basic2_comm_indexes[] = { INT_IN, }; +u8 basic2_data_indexes[] = { BULK_OUT, BULK_IN, }; + + +/* BASIC2 Data Interface Alternate endpoints + */ + + +#ifndef COMMUNICATIONS_NETWORK_SUBCLASS +#define COMMUNICATIONS_NETWORK_SUBCLASS 0 +#endif + +#ifndef VENDOR_PROTOCOL +#define VENDOR_PROTOCOL 0xFF +#endif +/*! Comm Interface Descriptor + */ +static struct usbd_interface_descriptor basic2_comm_alternate_descriptor = { + bLength:sizeof(struct usbd_interface_descriptor), + bDescriptorType:USB_DT_INTERFACE, + bInterfaceNumber: 0x00, + bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting + sizeof (basic2_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints + bInterfaceClass:COMMUNICATIONS_INTERFACE_CLASS, + bInterfaceSubClass:COMMUNICATIONS_NETWORK_SUBCLASS, + bInterfaceProtocol:VENDOR_PROTOCOL, + iInterface:0x00, +}; + +/*! Data Interface Descriptor + */ +static struct usbd_interface_descriptor basic2_data_alternate_descriptor = { + bLength:sizeof(struct usbd_interface_descriptor), + bDescriptorType:USB_DT_INTERFACE, + bInterfaceNumber: 0x01, + bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting + sizeof (basic2_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints + bInterfaceClass:DATA_INTERFACE_CLASS, + bInterfaceSubClass:COMMUNICATIONS_NO_SUBCLASS, + bInterfaceProtocol:COMMUNICATIONS_NO_PROTOCOL, + iInterface:0x00, +}; + +/*! Comm Alternate Interface Description List + */ +static struct usbd_alternate_description basic2_comm_alternate_descriptions[] = { + { iInterface: CONFIG_OTG_NETWORK_BASIC2_COMM_INTF, + interface_descriptor: (struct usbd_interface_descriptor *)&basic2_comm_alternate_descriptor, + endpoints:sizeof (basic2_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list:basic2_comm_endpoints, + endpoint_indexes:basic2_comm_indexes, + }, +}; + + +/*! Data Alternate Interface Description List + */ +static struct usbd_alternate_description basic2_data_alternate_descriptions[] = { + { iInterface: CONFIG_OTG_NETWORK_BASIC2_DATA_INTF, + interface_descriptor: (struct usbd_interface_descriptor *)&basic2_data_alternate_descriptor, + endpoints:sizeof (basic2_data_endpoints) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list: basic2_data_endpoints, + endpoint_indexes: basic2_data_indexes, + }, +}; + + +/* BASIC Data Interface Alternate descriptions and descriptors + */ + +/* BASIC Interface descriptions and descriptors + */ +/*! Interface Description List + */ +static struct usbd_interface_description basic2_interfaces[] = { + { alternates:sizeof (basic2_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description), + alternate_list:basic2_comm_alternate_descriptions,}, + + { alternates:sizeof (basic2_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), + alternate_list:basic2_data_alternate_descriptions,}, +}; + + +/* BASIC Configuration descriptions and descriptors + */ +/*! Configuration Descriptor + */ +u8 basic2_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { + 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength + sizeof (basic2_interfaces) / sizeof (struct usbd_interface_description), + 0x01, 0x00, // bConfigurationValue, iConfiguration + 0, 0, +}; + +/*! Configuration Description List + */ +struct usbd_configuration_description basic2_description[] = { + { iConfiguration: CONFIG_OTG_NETWORK_BASIC2_DESC, + configuration_descriptor: (struct usbd_configuration_descriptor *)basic2_configuration_descriptor, + }, + +}; + +/* BASIC Device Description + */ +/*! Device Descriptor + */ +static struct usbd_device_descriptor basic2_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_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), +}; + +#ifdef CONFIG_OTG_HIGH_SPEED +/*! High Speed Device Qualifier Descriptor + */ +static struct usbd_device_qualifier_descriptor basic2_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 basic2_endpoint_requests[ENDPOINTS+1] = { + { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, + { 0, }, +}; + +/*! OTG Descriptor + */ +static struct usbd_otg_descriptor basic2_otg_descriptor = { + bLength : sizeof(struct usbd_otg_descriptor), + bDescriptorType: USB_DT_OTG, + bmAttributes: 0, +}; +/*! Device Description + */ +struct usbd_device_description basic2_device_description = { + device_descriptor: &basic2_device_descriptor, +#ifdef CONFIG_OTG_HIGH_SPEED + device_qualifier_descriptor: &basic2_device_qualifier_descriptor, +#endif /* CONFIG_OTG_HIGH_SPEED */ + otg_descriptor: &basic2_otg_descriptor, + iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, + iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, +#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) + iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, +#endif +}; + + +/*! basic2_init + * @param function The function instance + */ +void basic2_init (struct usbd_function_instance *function) +{ + TRACE_MSG0(NTT,"entered"); + + basic2_comm_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 1 : 0; + + TRACE_MSG2(NTT,"alternate: %p endpoints: %d", + basic2_data_alternate_descriptions, + basic2_data_alternate_descriptions->endpoints + ); + + TRACE_MSG0(NTT,"exited"); +} + + +/*! function driver description + */ +struct usbd_function_driver basic2_function_driver = { + name: "network-BASIC2", + fops: &net_fd_function_ops, + device_description: &basic2_device_description, + bNumConfigurations: sizeof (basic2_description) / sizeof (struct usbd_configuration_description), + configuration_description: basic2_description, + idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), + bNumInterfaces:sizeof (basic2_interfaces) / sizeof (struct usbd_interface_description), + interface_list:basic2_interfaces, + endpointsRequested: ENDPOINTS, + requestedEndpoints: basic2_endpoint_requests, +}; +#endif /* CONFIG_OTG_NETWORK_BASIC2 */ + diff -uNr linux/drivers/no-otg/functions/network/blan.c linux/drivers/otg/functions/network/blan.c --- linux/drivers/no-otg/functions/network/blan.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/blan.c 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,403 @@ +/* + * otg/functions/network/blan.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + * + */ +/*! + * @file otg/functions/network/blan.c + * @brief This file implements the required descriptors to implement + * a BLAN network device with a single interface. + * + * The BLAN network driver implements the BLAN protocol descriptors. + * + * The BLAN protocol is designed to support smart devices that want + * to create a virtual network between them host and other similiar + * devices. + * + * @ingroup NetworkFunction + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "network.h" + +#define NTT network_fd_trace_tag + +#ifdef CONFIG_OTG_NETWORK_BLAN +/* USB BLAN Configuration ******************************************************************** */ + +/* + * BLAN Ethernet Configuration + */ + +/* Communication Interface Class descriptors + */ + +/*! BULK OUT data endpoint descriptor + */ +static struct usbd_endpoint_descriptor blan_data_1 = { + .bLength = sizeof(struct usbd_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = BULK, + .wMaxPacketSize = 0, + .bInterval = 0, +}; +/*! BULK IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor blan_data_2 =// { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; +{ + .bLength = sizeof(struct usbd_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = BULK, + .wMaxPacketSize = 0, + .bInterval = 0x0, +}; + +/*! INTERRUPT IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor blan_comm_1 = //{ 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; +{ + .bLength = sizeof(struct usbd_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = INTERRUPT, + .wMaxPacketSize = 0, +#if defined (CONFIG_OTG_NETWORK_BLAN_INTERVAL) + .bInterval = CONFIG_OTG_NETWORK_BLAN_INTERVAL, +#else + .bInterval = 0x08, +#endif /* defined (CONFIG_OTG_NETWORK_BLAN_INTERVAL) */ +}; + +//static u8 blan_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; +static struct usbd_class_header_function_descriptor blan_class_1 = { + .bFunctionLength = 0x05, /* Length */ + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_HEADER, + .bcdCDC = __constant_cpu_to_le16(0x0110) /*Version */ +}; +//static u8 blan_class_2[] = { 0x15, CS_INTERFACE, USB_ST_MDLM, 0x00, 0x01, /* bcdVersion, bcdVersion */ +// 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70, /* bGUID */ +static struct usbd_class_mdlm_descriptor blan_class_2 = { + .bFunctionLength = 0x15, + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_MDLM, + .bcdVersion = __constant_cpu_to_le16(0x0100), + .bGUID = { + 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70, /* bGUID */ + 0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37, /* bGUID */ }, +}; + + + +//static u8 blan_class_3[] = { 0x07, CS_INTERFACE, USB_ST_MDLMD, 0x01, 0x00, 0x00, 0x00, }; +static struct usbd_class_blan_descriptor blan_class_3 = { + .bFunctionLength = 0x07, + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_MDLMD, + .bGuidDescriptorType = 0x01, + .bmNetworkCapabilities = 0x00, + .bmDataCapabilities = 0x00, + .bPad = 0x00, +}; + +//static u8 blan_class_4[] = { 0x0d, CS_INTERFACE, USB_ST_ENF, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */ +// 0x00, 0x00, 0x00 , }; +static struct usbd_class_ethernet_networking_descriptor blan_class_4 = { + .bFunctionLength = 0x0d, + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_ENF, + .iMACAddress = 0x00, + .bmEthernetStatistics = 0x00, + .wMaxSegmentSize = 0x05ea, /* 1514 maximum frame size */ + .wNumberMCFilters = 0x00, + .bNumberPowerFilters = 0x00 , +}; + +//static u8 blan_class_5[] = { 0x07, CS_INTERFACE, USB_ST_NCT, 0x00, 0x00, 0x00, 0x00, }; +static struct usbd_class_network_channel_descriptor blan_class_5 = { + .bFunctionLength = 0x07, + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_NCT, + .bEntityId = 0, + .iName = 0, + .bChannelIndex = 0, + .bPhysicalInterface = 0, +}; + + +/*! Endpoint descriptor list + */ +static struct usbd_endpoint_descriptor *blan_alt_endpoints[] = { + (struct usbd_endpoint_descriptor *) &blan_data_1, + (struct usbd_endpoint_descriptor *) &blan_data_2, + (struct usbd_endpoint_descriptor *) &blan_comm_1, }; + +u8 blan_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; + +static struct usbd_generic_class_descriptor *blan_comm_class_descriptors[] = { + (struct usbd_generic_class_descriptor *) &blan_class_1, + (struct usbd_generic_class_descriptor *) &blan_class_2, + (struct usbd_generic_class_descriptor *) &blan_class_3, + (struct usbd_generic_class_descriptor *) &blan_class_4, + (struct usbd_generic_class_descriptor *) &blan_class_5, }; + + +/*! Data Interface Descriptor + */ +static struct usbd_interface_descriptor blan_alternate_descriptor = { + .bLength = 0x09, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, // bInterfaceNumber, bAlternateSetting + .bNumEndpoints = sizeof (blan_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints + .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS, + .bInterfaceSubClass = COMMUNICATIONS_MDLM_SUBCLASS, + .bInterfaceProtocol = COMMUNICATIONS_NO_PROTOCOL, + .iInterface = 0x00, +}; + +/*! Data Alternate Interface Description List + */ +static struct usbd_alternate_description blan_alternate_descriptions[] = { + { + .iInterface = CONFIG_OTG_NETWORK_BLAN_INTF, + .interface_descriptor = (struct usbd_interface_descriptor *)&blan_alternate_descriptor, + .classes = sizeof (blan_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), + .class_list = blan_comm_class_descriptors, + .endpoints = sizeof (blan_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), + .endpoint_list = blan_alt_endpoints, + .endpoint_indexes = blan_alt_indexes, + }, +}; +/* Interface descriptions and descriptors + */ +/*! Interface Description List + */ +static struct usbd_interface_description blan_interfaces[] = { + { + .alternates = sizeof (blan_alternate_descriptions) / sizeof (struct usbd_alternate_description), + .alternate_list = blan_alternate_descriptions, + }, +}; + + +/* Configuration descriptions and descriptors + */ + +/*! Configuration Descriptor + */ +static struct usbd_configuration_descriptor blan_configuration_descriptor = { + .bLength = 0x09, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0x00, // wLength + .bNumInterfaces = sizeof (blan_interfaces) / sizeof (struct usbd_interface_description), + .bConfigurationValue = 0x01, + .iConfiguration = 0x00, // bConfigurationValue, iConfiguration + .bmAttributes = 0, + .bMaxPower = 0, +}; + +/*! Configuration Description List + */ +struct usbd_configuration_description blan_description[] = { + { + .iConfiguration = CONFIG_OTG_NETWORK_BLAN_DESC, + .configuration_descriptor = &blan_configuration_descriptor, + }, +}; + +/* Device Description + */ + +/*! Device Descriptor + */ +static struct usbd_device_descriptor blan_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_NETWORK_VENDORID), + .idProduct = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), +}; + +#ifdef CONFIG_OTG_HIGH_SPEED +/*! High Speed Device Qualifier Descriptor + */ +static struct usbd_device_qualifier_descriptor blan_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 blan_endpoint_requests[ENDPOINTS+1] = { + { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, + { 0, }, +}; + +/*! OTG Descriptor + */ +static struct usbd_otg_descriptor blan_otg_descriptor = { + .bLength = sizeof(struct usbd_otg_descriptor), + .bDescriptorType = USB_DT_OTG, + .bmAttributes = 0, +}; + +/*! Device Description + */ +struct usbd_device_description blan_device_description = { + .device_descriptor = &blan_device_descriptor, +#ifdef CONFIG_OTG_HIGH_SPEED + .device_qualifier_descriptor = &blan_device_qualifier_descriptor, +#endif /* CONFIG_OTG_HIGH_SPEED */ + .otg_descriptor = &blan_otg_descriptor, + .iManufacturer = CONFIG_OTG_NETWORK_MANUFACTURER, + .iProduct = CONFIG_OTG_NETWORK_PRODUCT_NAME, +#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) + .iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR, +#endif +}; + + +/*! blan_init + * @param function The function instance + */ +void blan_init (struct usbd_function_instance *function) +{ + struct usbd_class_ethernet_networking_descriptor *ethernet; + struct usbd_class_network_channel_descriptor *channel; + + int len = 0; + char buf[255]; + + buf[0] = 0; + + blan_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; + + // Update the iMACAddress field in the ethernet descriptor + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) + char address_str[14]; + snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x", + local_dev_addr[0], local_dev_addr[1], local_dev_addr[2], + local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]); +#else + char address_str[20]; + sprintf(address_str, "%02x%02x%02x%02x%02x%02x", + local_dev_addr[0], local_dev_addr[1], local_dev_addr[2], + local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]); +#endif + TRACE_MSG0(NTT,"alloc mac string"); + if ((ethernet = &blan_class_4)) + ethernet->iMACAddress = usbd_alloc_string(address_str); + TRACE_MSG0(NTT,"alloc mac string done"); + } + TRACE_MSG0(NTT,"alloc channel string"); + if ((channel = &blan_class_5)) + channel->iName = usbd_alloc_string(system_utsname.nodename); + TRACE_MSG0(NTT,"alloc channel string done"); + +#ifdef CONFIG_OTG_NETWORK_BLAN_PADBYTES + blan_class_3.bPad = CONFIG_OTG_NETWORK_BLAN_PADBYTES; + len += sprintf(buf + len, "PADBYTES: %02x ", blan_class_3.bPad); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_PADBEFORE + blan_class_3.bmDataCapabilities |= BMDATA_PADBEFORE; + len += sprintf(buf + len, "PADBEFORE: %02x ", blan_class_3.bmDataCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_PADAFTER + blan_class_3.bmDataCapabilities |= BMDATA_PADAFTER; + len += sprintf(buf + len, "PADAFTER: %02x ", blan_class_3.bmDataCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_CRC + blan_class_3.bmDataCapabilities |= BMDATA_CRC; + len += sprintf(buf + len, "CRC: %02x ", blan_class_3.bmDataCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT + blan_class_3.bmDataCapabilities |= BMDATA_FERMAT; + len += sprintf(buf + len, "FERMAT: %02x ", blan_class_3.bmDataCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_HOSTNAME + blan_class_3.bmDataCapabilities |= BMDATA_HOSTNAME; + len += sprintf(buf + len, "HOSTNAME: %02x ", blan_class_3.bmDataCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_NONBRIDGED + blan_class_3.bmNetworkCapabilities |= BMNETWORK_NONBRIDGED; + len += sprintf(buf + len, "NONBRIDGE: %02x ", blan_class_3.bmNetworkCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK + blan_class_3.bmNetworkCapabilities |= BMNETWORK_DATA_NOTIFY_OK; + len += sprintf(buf + len, "DATA NOTIFY: %02x ", blan_class_3.bmNetworkCapabilities); +#endif + if (strlen(buf)) + TRACE_MSG1(NTT,"%s", buf); +} + +/*! function driver description + */ +struct usbd_function_driver blan_function_driver = { + .name = "network-BLAN", + .fops = &net_fd_function_ops, + .device_description = &blan_device_description, + .bNumConfigurations = sizeof (blan_description) / sizeof (struct usbd_configuration_description), + .configuration_description = blan_description, + .idVendor = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), + .idProduct = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), + .bNumInterfaces = sizeof (blan_interfaces) / sizeof (struct usbd_interface_description), + .interface_list = blan_interfaces, + .endpointsRequested = ENDPOINTS, + .requestedEndpoints = blan_endpoint_requests, +}; +#endif /* CONFIG_OTG_NETWORK_BLAN */ + diff -uNr linux/drivers/no-otg/functions/network/cdc.c linux/drivers/otg/functions/network/cdc.c --- linux/drivers/no-otg/functions/network/cdc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/cdc.c 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,376 @@ +/* + * otg/functions/network/cdc.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + * + * The CDC network driver implements the standard CDC Ethernet descriptors. + * + * The CDC protocol is suitable for infrastructure devices that + * are implementing a bridged or routed connection betwen an + * external network and the USB Host. + * + */ +/*! + * @file otg/functions/network/cdc.c + * @brief This file implements the required descriptors to implement + * a CDC network device with two interfaces. + * + * @ingroup NetworkFunction + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#include "network.h" + +#define NTT network_fd_trace_tag + + +#ifdef CONFIG_OTG_NETWORK_CDC +/* USB CDC Configuration ********************************************************************* */ + +/* CDC Communication Interface Class descriptors + */ +/*! BULK OUT data endpoint descriptor + */ +static struct usbd_endpoint_descriptor cdc_data_1 = { + bLength: 0x07, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIR_OUT, + bmAttributes: BULK, + wMaxPacketSize: __constant_cpu_to_le16(0x00), + bInterval: 0x00, +}; + +/*! BULK IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor cdc_data_2 = { + bLength: 0x07, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIR_IN, + bmAttributes: BULK, + wMaxPacketSize: __constant_cpu_to_le16(0x00), + bInterval: 0x00, +}; + +/*! INTERRUPT IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor cdc_comm_1 = { + bLength: 0x07, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIR_IN, + bmAttributes: INTERRUPT, + wMaxPacketSize: __constant_cpu_to_le16(0x00), + bInterval: 0x0a, +}; + + + + +//static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; + +static struct usbd_class_header_function_descriptor cdc_class_1 = { + bFunctionLength: 0x05, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: USB_ST_HEADER, + bcdCDC: __constant_cpu_to_le16(0x0110), +}; + + +//static u8 cdc_class_2[] = { +// 0x0d, CS_INTERFACE, USB_ST_ENF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */ +// 0x00, 0x00, 0x00 , }; +static struct usbd_class_ethernet_networking_descriptor cdc_class_2 = { + bFunctionLength: 0x0D, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: USB_ST_HEADER, + iMACAddress: 0x00, + bmEthernetStatistics: 0x00, + wMaxSegmentSize: __constant_cpu_to_le16(0x05ea), + wNumberMCFilters: 0, + bNumberPowerFilters: 0, +}; + +//static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface, bSlaveInterface */}; + +static struct usbd_class_union_function_descriptor cdc_class_3 = +{ + bFunctionLength: 0x05, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: USB_ST_UF, + bMasterInterface: 0x00, + bSlaveInterface0: {1}, + +}; + +static usbd_class_descriptor_t *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, +}; + +/*! Endpoint descriptor list + */ +static usbd_endpoint_descriptor_t *cdc_data_endpoints[] = { + (struct usbd_endpoint_descriptor *) &cdc_data_1, + (struct usbd_endpoint_descriptor *) &cdc_data_2, +}; +static usbd_endpoint_descriptor_t *cdc_comm_endpoints[] = { + (usbd_endpoint_descriptor_t *) &cdc_comm_1, }; + +u8 cdc_comm_indexes[] = { INT_IN, }; +u8 cdc_data_indexes[] = { BULK_OUT, BULK_IN, }; + + +/*! Comm Interface Descriptor + */ +static struct usbd_interface_descriptor cdc_comm_alternate_descriptor = { + bLength: sizeof(struct usbd_interface_descriptor), + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0x00, + bAlternateSetting: 0x00, + bNumEndpoints: sizeof(cdc_comm_endpoints)/sizeof(usbd_endpoint_descriptor_t *), + bInterfaceClass: COMMUNICATIONS_INTERFACE_CLASS, + bInterfaceSubClass: COMMUNICATIONS_ENCM_SUBCLASS, + bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, + iInterface: 0x00 +}; + +/*! No Data Interface Descriptor + */ +static struct usbd_interface_descriptor cdc_nodata_alternate_descriptor = { + bLength: sizeof(struct usbd_interface_descriptor), + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0x01, + bAlternateSetting: 0x00, + bNumEndpoints: 0, + bInterfaceClass: DATA_INTERFACE_CLASS, + bInterfaceSubClass: COMMUNICATIONS_NO_SUBCLASS, + bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, + iInterface: 0x00 +}; + +/*! Data Interface Descriptor + */ +static struct usbd_interface_descriptor cdc_data_alternate_descriptor = { + bLength: sizeof(struct usbd_interface_descriptor), + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0x01, + bAlternateSetting: 0x01, + bNumEndpoints: sizeof(cdc_comm_endpoints)/sizeof(usbd_endpoint_descriptor_t *), + bInterfaceClass: DATA_INTERFACE_CLASS, + bInterfaceSubClass: COMMUNICATIONS_NO_SUBCLASS, + bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, + iInterface: 0x00 +}; + +/*! Comm Alternate Interface Description List + */ +static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = { + { + iInterface: CONFIG_OTG_NETWORK_CDC_COMM_INTF, + interface_descriptor: &cdc_comm_alternate_descriptor, + classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), + class_list: cdc_comm_class_descriptors, + + endpoints:sizeof (cdc_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list: cdc_comm_endpoints, + endpoint_indexes: cdc_comm_indexes, + }, +}; + + +/*! Data Alternate Interface Description List + */ +static struct usbd_alternate_description cdc_data_alternate_descriptions[] = { + { + iInterface: CONFIG_OTG_NETWORK_CDC_NODATA_INTF, + interface_descriptor: (struct usbd_interface_descriptor *)&cdc_nodata_alternate_descriptor, + }, + { + iInterface: CONFIG_OTG_NETWORK_CDC_DATA_INTF, + interface_descriptor: &cdc_data_alternate_descriptor, + endpoints:sizeof (cdc_data_endpoints) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list: cdc_data_endpoints, + endpoint_indexes: cdc_data_indexes, + }, +}; + +/* Interface descriptions and descriptors + */ +/*! Interface Description 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 descriptions and descriptors + */ + +/*! Configuration Descriptor + */ +static struct usbd_configuration_descriptor cdc_configuration_descriptor = { + bLength: sizeof(struct usbd_configuration_descriptor), + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength:0, + bNumInterfaces:sizeof(cdc_interfaces)/sizeof(struct usbd_interface_description *), + bConfigurationValue: 0x01, + iConfiguration: 0x00, + bmAttributes:0, + bMaxPower:0, +}; + +/*! Configuration Description List + */ +static struct usbd_configuration_description cdc_description[] = { + { iConfiguration: CONFIG_OTG_NETWORK_CDC_DESC, + configuration_descriptor: &cdc_configuration_descriptor, + }, +}; + + + +/* Device Description + */ + +/*! Device Descriptor + */ +static struct usbd_device_descriptor cdc_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_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), +}; + +#ifdef CONFIG_OTG_HIGH_SPEED +/*! High Speed Device Qualifier Descriptor + */ +static struct usbd_device_qualifier_descriptor cdc_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 cdc_endpoint_requests[ENDPOINTS+1] = { + { 1, 1, 1, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 1, 1, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, + { 0, }, +}; + +/*! OTG Descriptor + */ +static struct usbd_otg_descriptor cdc_otg_descriptor = { + bLength : sizeof(struct usbd_otg_descriptor), + bDescriptorType: USB_DT_OTG, + bmAttributes: 0, +}; + +/*! Device Description + */ +struct usbd_device_description cdc_device_description = { + device_descriptor: &cdc_device_descriptor, +#ifdef CONFIG_OTG_HIGH_SPEED + device_qualifier_descriptor: &cdc_device_qualifier_descriptor, +#endif /* CONFIG_OTG_HIGH_SPEED */ + otg_descriptor: &cdc_otg_descriptor, + iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, + iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, +#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) + iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, +#endif +}; + + + +/*! cdc_init + * @param function The function instance + */ +void cdc_init (struct usbd_function_instance *function) +{ + struct usbd_class_ethernet_networking_descriptor *ethernet; + + cdc_comm_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 1 : 0; + + // Update the iMACAddress field in the ethernet descriptor + { + char address_str[14]; + snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x", + remote_dev_addr[0], remote_dev_addr[1], remote_dev_addr[2], + remote_dev_addr[3], remote_dev_addr[4], remote_dev_addr[5]); + + if ((ethernet = &cdc_class_2)) { + if (ethernet->iMACAddress) { + usbd_free_string_descriptor(ethernet->iMACAddress); + } + ethernet->iMACAddress = usbd_alloc_string(address_str); + } + } +} + + +/*! function driver description + */ +struct usbd_function_driver cdc_function_driver = { + name: "network-CDC", + fops: &net_fd_function_ops, + device_description: &cdc_device_description, + bNumConfigurations: sizeof (cdc_description) / sizeof (struct usbd_configuration_description), + configuration_description: cdc_description, + idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), + bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), + interface_list:cdc_interfaces, + endpointsRequested: ENDPOINTS, + requestedEndpoints: cdc_endpoint_requests, +}; +#endif /* CONFIG_OTG_NETWORK_CDC */ + diff -uNr linux/drivers/no-otg/functions/network/eem.c linux/drivers/otg/functions/network/eem.c --- linux/drivers/no-otg/functions/network/eem.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/eem.c 2006-09-01 21:41:28.000000000 +0200 @@ -0,0 +1,247 @@ +/* + * otg/functions/network/eem.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + * + */ +/*! + * @file otg/functions/network/eem.c + * @brief This file implements the required descriptors to implement + * a eem network device with a single interface. + * + * The BASIC network driver implements a very simple descriptor set. + * A single interface with two BULK data endpoints and a optional + * INTERRUPT endpoint. + * + * @ingroup NetworkFunction + */ + + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "network.h" + +#define NTT network_fd_trace_tag + +#ifdef CONFIG_OTG_NETWORK_BASIC +/* USB BASIC Configuration ******************************************************************** */ + +/* BASIC Communication Interface Class descriptors + */ +/*! BULK OUT data endpoint descriptor + */ +static struct usbd_endpoint_descriptor eem_data_1 = { + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_OUT, + bmAttributes: BULK, + wMaxPacketSize: 0, + bInterval: 0, +}; +/*! BULK IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor eem_data_2 =// { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; +{ + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: BULK, + wMaxPacketSize: 0, + bInterval: 0x0, +}; + +/*! INTERRUPT IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor eem_comm_1 = //{ 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; +{ + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: INTERRUPT, + wMaxPacketSize: 0, + bInterval: 0xa, +}; + +/*! Endpoint descriptor list + */ +static struct usbd_endpoint_descriptor *eem_default[] = { &eem_data_1, + &eem_data_2, + &eem_comm_1, }; +u8 eem_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; + +/*! Data Interface Descriptor + */ +static struct usbd_interface_descriptor eem_data_alternate_descriptor = { + bLength:sizeof(struct usbd_interface_descriptor), + bDescriptorType:USB_DT_INTERFACE, + bInterfaceNumber: 0x00, + bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting + sizeof (eem_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints + bInterfaceClass:LINEO_CLASS, + bInterfaceSubClass:LINEO_SUBCLASS_BASIC_NET, + bInterfaceProtocol:LINEO_BASIC_NET_CRC, + iInterface:0x00, +}; + +/*! Data Alternate Interface Description List + */ +static struct usbd_alternate_description eem_data_alternate_descriptions[] = { + { iInterface: CONFIG_OTG_NETWORK_BASIC_INTF, + interface_descriptor: (struct usbd_interface_descriptor *)&eem_data_alternate_descriptor, + endpoints:sizeof (eem_default) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list: eem_default, + endpoint_indexes: eem_indexes, + }, +}; + + +/* BASIC Data Interface Alternate descriptions and descriptors + */ + +/* BASIC Interface descriptions and descriptors + */ +/*! Interface Description List + */ +struct usbd_interface_description eem_interfaces[] = { + { alternates:sizeof (eem_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), + alternate_list:eem_data_alternate_descriptions,}, +}; + +/* BASIC Configuration descriptions and descriptors + */ +/*! Configuration Descriptor + */ +u8 eem_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { + 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength + sizeof (eem_interfaces) / sizeof (struct usbd_interface_description), + 0x01, 0x00, // bConfigurationValue, iConfiguration + 0, 0, +}; + +/*! Configuration Description List + */ +struct usbd_configuration_description eem_description[] = { + { iConfiguration: CONFIG_OTG_NETWORK_BASIC_DESC, + configuration_descriptor: (struct usbd_configuration_descriptor *)eem_configuration_descriptor, + }, + +}; + +/* BASIC Device Description + */ +/*! Device Descriptor + */ +static struct usbd_device_descriptor eem_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_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), +}; + +#ifdef CONFIG_OTG_HIGH_SPEED +/*! High Speed Device Qualifier Descriptor + */ +static struct usbd_device_qualifier_descriptor eem_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 eem_endpoint_requests[ENDPOINTS+1] = { + { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, + { 0, }, +}; + +/*! OTG Descriptor + */ +static struct usbd_otg_descriptor eem_otg_descriptor = { + bLength : sizeof(struct usbd_otg_descriptor), + bDescriptorType: USB_DT_OTG, + bmAttributes: 0, +}; + +/*! Device Description + */ +struct usbd_device_description eem_device_description = { + device_descriptor: &eem_device_descriptor, +#ifdef CONFIG_OTG_HIGH_SPEED + device_qualifier_descriptor: &eem_device_qualifier_descriptor, +#endif /* CONFIG_OTG_HIGH_SPEED */ + otg_descriptor: &eem_otg_descriptor, + iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, + iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, +#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) + iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, +#endif +}; + + +/*! eem_init + * @param function The function instance + */ +void eem_init (struct usbd_function_instance *function) +{ + eem_data_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; +} + +/*! function driver description + */ +struct usbd_function_driver eem_function_driver = { + name: "network-BASIC", + fops: &net_fd_function_ops, + device_description: &eem_device_description, + bNumConfigurations: sizeof (eem_description) / sizeof (struct usbd_configuration_description), + configuration_description: eem_description, + idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), + bNumInterfaces:sizeof (eem_interfaces) / sizeof (struct usbd_interface_description), + interface_list:eem_interfaces, + endpointsRequested: ENDPOINTS, + requestedEndpoints: eem_endpoint_requests, +}; +#endif /* CONFIG_OTG_NETWORK_BASIC */ + diff -uNr linux/drivers/no-otg/functions/network/fermat.c linux/drivers/otg/functions/network/fermat.c --- linux/drivers/no-otg/functions/network/fermat.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/fermat.c 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,140 @@ +/* + * otg/functions/network/fermat.c - Network Function Driver + * + * Copyright (c) 2003, 2004 Belcarra + * + * By: + * Bruce Balden + * + * + * 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. + * + */ +/*! + * @file otg/functions/network/fermat.c + * @brief This implements a special data munging function that + * randomizes data. This is a very specific fix for a device + * that had trouble with runs of zeros. + * + * @ingroup NetworkFunction + */ + +#include +#include +#include +#include + +#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT + +#include "fermat.h" + +#ifndef 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 +#include + +// 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_NETWORK_BLAN_FERMAT */ + diff -uNr linux/drivers/no-otg/functions/network/fermat.h linux/drivers/otg/functions/network/fermat.h --- linux/drivers/no-otg/functions/network/fermat.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/fermat.h 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,46 @@ +/* + * otg/functions/network/fermat.h - Network Function Driver + * + * Copyright (c) 2003, 2004 Belcarra + * + * By: + * Bruce Balden + * + * 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. + * + * + */ +/*! + * @file otg/functions/network/fermat.h + * @brief Fermat related data structures. + * + * + * @ingroup NetworkFunction + */ + +#ifndef FERMAT_DEFINED +#define 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/network/net-fd.c linux/drivers/otg/functions/network/net-fd.c --- linux/drivers/no-otg/functions/network/net-fd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/net-fd.c 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,1463 @@ +/* + * otg/functions/network/net-fd.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Tom Rushworth + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + * + */ +/*! + * @file otg/functions/network/net-fd.c + * @brief The lower edge (USB Device Function) implementation of + * the Network Function Driver. This performs the core protocol + * handling and data encpasulation. + * + * This implements the lower edge (USB Device) layer of the Network Function + * Driver. Specifically the data encapsulation, envent and protocol handlers. + * + * + * + * This network function driver intended to interoperate with + * Belcarra's USBLAN Class drivers. + * + * These are available for Windows, Linux and Mac OSX. For more + * information and to download a copy for testing: + * + * http://www.belcarra.com/usblan/ + * + * When configured for CDC it can also work with any CDC Class driver. + * + * + * @ingroup NetworkFunction + */ + + +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +//#include +#include + +#include +#include + +#include "network.h" +#include "net-fd.h" +#include "net-os.h" +#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT +#include "fermat.h" +#endif + +otg_tag_t network_fd_trace_tag; +#define NTT network_fd_trace_tag + +#if defined(CONFIG_OTG_NETWORK_CDC) +void cdc_init(struct usbd_function_instance *); +extern struct usbd_function_driver cdc_function_driver; +#endif + +#ifdef CONFIG_OTG_NETWORK_EEM +void eem_init(struct usbd_function_instance *); +extern struct usbd_function_driver eem_function_driver; +#endif + +#ifdef CONFIG_OTG_NETWORK_BASIC +void basic_init(struct usbd_function_instance *); +extern struct usbd_function_driver basic_function_driver; +#endif + +#ifdef CONFIG_OTG_NETWORK_BASIC2 +void basic2_init(struct usbd_function_instance *); +extern struct usbd_function_driver basic2_function_driver; +#endif + +#ifdef CONFIG_OTG_NETWORK_SAFE +void safe_init(struct usbd_function_instance *); +extern struct usbd_function_driver safe_function_driver; +#endif + +#ifdef CONFIG_OTG_NETWORK_BLAN +void blan_init(struct usbd_function_instance *); +extern struct usbd_function_driver blan_function_driver; +#endif + +static char ip_addr_str[20]; +u32 ip_addr; +u32 router_ip; +u32 network_mask; +u32 dns_server_ip; + +//_________________________________________________________________________________________________ + +/* + * If the following are defined we implement the crc32_copy routine using + * Duff's device. This will unroll the copy loop by either 4 or 8. Do not + * use these without profiling to test if it actually helps on any specific + * device. + */ +#undef CONFIG_OTG_NETWORK_CRC_DUFF4 +#undef CONFIG_OTG_NETWORK_CRC_DUFF8 + +static u32 *network_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) ^ network_crc32_table[((val) ^ (c)) & 0xff]) + +//_________________________________________________________________________________________________ +// crc32_copy + +/*! make_crc_table + * Generate the crc32 table + * + * @return non-zero if malloc fails + */ +STATIC int make_crc_table(void) +{ + u32 n; + RETURN_ZERO_IF(network_crc32_table); + RETURN_ENOMEM_IF(!(network_crc32_table = (u32 *)ckmalloc(256*4, GFP_KERNEL))); + for (n = 0; n < 256; n++) { + int k; + u32 c = n; + for (k = 0; k < 8; k++) { + c = (c & 1) ? (CRC32_POLY ^ (c >> 1)) : (c >> 1); + } + network_crc32_table[n] = c; + } + return 0; +} + +#if !defined(CONFIG_OTG_NETWORK_CRC_DUFF4) && !defined(CONFIG_OTG_NETWORK_CRC_DUFF8) +/*! crc32_copy + * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so. + * + * @param dst Pointer to the destination memory area. + * @param src Pointer to the source memory area. + * @param len Number of bytes to copy. + * @param val Starting value for the CRC FCS. + * + * @return Final value of the CRC FCS. + * + * @sa crc32_pad + */ +static u32 __inline__ crc32_copy (u8 *dst, u8 *src, int len, u32 val) +{ + for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = *src++)); + return val; +} + +#else /* DUFFn */ + +/*! crc32_copy + * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so. + * + * @param dst Pointer to the destination memory area. + * @param src Pointer to the source memory area. + * @param len Number of bytes to copy. + * @param val Starting value for the CRC FCS. + * + * @return Final value of the CRC FCS. + * + * @sa crc32_pad + */ +static u32 crc32_copy (u8 *dst, u8 *src, int len, u32 val) +{ +#if defined(CONFIG_OTG_NETWORK_CRC_DUFF8) + int n = (len + 7) / 8; + switch (len % 8) +#elif defined(CONFIG_OTG_NETWORK_CRC_DUFF4) + int n = (len + 3) / 4; + switch (len % 4) +#endif + { + case 0: do { + val = COMPUTE_FCS (val, *dst++ = *src++); +#if defined(CONFIG_OTG_NETWORK_CRC_DUFF8) + case 7: + val = COMPUTE_FCS (val, *dst++ = *src++); + case 6: + val = COMPUTE_FCS (val, *dst++ = *src++); + case 5: + val = COMPUTE_FCS (val, *dst++ = *src++); + case 4: + val = COMPUTE_FCS (val, *dst++ = *src++); +#endif + case 3: + val = COMPUTE_FCS (val, *dst++ = *src++); + case 2: + val = COMPUTE_FCS (val, *dst++ = *src++); + case 1: + val = COMPUTE_FCS (val, *dst++ = *src++); + } while (--n > 0); + } + return val; +} +#endif /* DUFFn */ + + +//_________________________________________________________________________________________________ +// crc32_pad + +/*! crc32_pad - pad and calculate crc32 + * + * @return CRC FCS + */ +static u32 __inline__ crc32_pad (u8 *dst, int len, u32 val) +{ + for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = '\0')); + return val; +} + +//_________________________________________________________________________________________________ +// net_fd_send_int +// + +/*! net_fd_urb_sent_int - callback for completed INT URB + * + * Handles notification that an urb has been sent (successfully or otherwise). + * + * @return non-zero for failure. + */ +STATIC int net_fd_urb_sent_int (struct usbd_urb *urb, int urb_rc) +{ + unsigned long flags; + int rc = -EINVAL; + struct usb_network_private *npd = urb->function_privdata; + + TRACE_MSG3(NTT,"urb: %p npd: %p urb_rc: %d", urb, npd, urb_rc); + + local_irq_save(flags); + npd->int_urb = NULL; + usbd_free_urb (urb); + local_irq_restore(flags); + return 0; +} + +/*! net_fd_send_int_blan - send an interrupt notification response + * + * Generates a response urb on the notification (INTERRUPT) endpoint. + * + * This is called from either a scheduled task or from the process context + * that calls network_open() or network_close(). + * This must be called with interrupts locked out as net_fd_event_handler can + * change the NETWORK_ATTACHED status + * + */ +STATIC void net_fd_send_int_blan(struct usb_network_private *npd, int connected, int data) +{ + struct usbd_urb *urb; + struct cdc_notification_descriptor *cdc; + int rc; + struct usbd_function_instance *function = (npd?npd->function:NULL); + + TRACE_MSG2(NTT,"npd=%p function=%p",npd,function); + + do { + BREAK_IF(!function); + + + BREAK_IF(npd->network_type != network_blan); + BREAK_IF(!npd->have_interrupt); + + BREAK_IF(!(npd->flags & NETWORK_ATTACHED)); + + TRACE_MSG3(NTT,"connected: %d network: %d %d", connected, + npd->network_type, network_blan); + + BREAK_IF(usbd_get_device_status(function) != USBD_OK); + + //if (npd->int_urb) { + // printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, npd->int_urb); + // usbd_cancel_urb_irq(npd->int_urb); + // npd->int_urb = NULL; + //} + + BREAK_IF(!(urb = usbd_alloc_urb (function, INT_IN, + sizeof(struct cdc_notification_descriptor), net_fd_urb_sent_int))); + + urb->actual_length = sizeof(struct cdc_notification_descriptor); + memset(urb->buffer, 0, sizeof(struct cdc_notification_descriptor)); + urb->function_privdata = npd; + + cdc = (struct cdc_notification_descriptor *)urb->buffer; + + cdc->bmRequestType = 0xa1; + + if (data) { + cdc->bNotification = 0xf0; + cdc->wValue = 1; + } + else { + cdc->bNotification = 0x00; + cdc->wValue = connected ? 0x01 : 0x00; + } + cdc->wIndex = 0x00; // XXX interface - check that this is correct + + + npd->int_urb = urb; + TRACE_MSG1(NTT,"int_urb: %p", urb); + BREAK_IF (!(rc = usbd_start_in_urb (urb))); + + TRACE_MSG1(NTT,"usbd_start_in_urb failed err: %x", rc); + printk(KERN_ERR"%s: usbd_start_in_urb failed err: %x\n", __FUNCTION__, rc); + urb->function_privdata = NULL; + npd->int_urb = NULL; + usbd_free_urb (urb); + + } while(0); +} + +//_________________________________________________________________________________________________ +// net_fd_start_xmit + +/*! net_fd_urb_sent_bulk - callback for completed BULK xmit URB + * + * Handles notification that an urb has been sent (successfully or otherwise). + * + * @param urb Pointer to the urb that has been sent. + * @param urb_rc Result code from the send operation. + * + * @return non-zero for failure. + */ +STATIC int net_fd_urb_sent_bulk (struct usbd_urb *urb, int urb_rc) +{ + unsigned long flags; + void *buff_ctx; + int rc = -EINVAL; + + TRACE_MSG2(NTT,"urb: %p urb_rc: %d", urb, urb_rc); + + local_irq_save(flags); + do { + + BREAK_IF(!urb); + buff_ctx = urb->function_privdata; + TRACE_MSG2(NTT,"urb: %p buff_ctx: %p", urb, buff_ctx); + urb->function_privdata = NULL; + + BREAK_IF(net_os_xmit_done(urb->function_instance,buff_ctx,urb_rc)); + + usbd_free_urb (urb); + rc = 0; + + } while (0); + local_irq_restore(flags); + return rc; +} + +/*! net_fd_start_xmit - start sending a buffer + * + * Called with net_os_mutex_enter()d. + * + * @return: 0 if all OK + * -EINVAL, -EUNATCH, -ENOMEM + * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above) + * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped. + */ +STATIC int net_fd_start_xmit (struct usb_network_private *npd, u8 *buff, int len, void *buff_ctx) +{ + struct usbd_function_instance *function = (npd?npd->function:NULL); + struct usbd_urb *urb = NULL; + int rc; + int in_pkt_sz; + u8 *cp; + u32 crc; + TRACE_MSG2(NTT,"npd=%p function=%p",npd,function); + +#if 0 + printk(KERN_INFO"%s: %s len: %d encap: %d\n", __FUNCTION__, net_device->name, len, npd->encapsulation); + printk(KERN_INFO"start_xmit: len: %x data: %p\n", len, buff); + { + u8 *cp = buff; + int i; + for (i = 0; i < len; i++) { + if ((i%32) == 0) { + printk("\ntx[%2x] ", i); + } + printk("%02x ", *cp++); + } + printk("\n"); + } +#endif + + if (!(npd->flags & NETWORK_ATTACHED)) { + return -EUNATCH; + } + if (usbd_get_device_status(function) != USBD_OK) { + return -EINVAL; + } + +#if defined(CONFIG_OTG_NETWORK_CDC) + // verify interface is enabled - non-zero altsetting means data is enabled + if (!usbd_interface_AltSetting(function, DATA_INTF)) { + return -EINVAL; + } +#endif + in_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_IN, usbd_high_speed(function)); + + if (npd->encapsulation != simple_crc) { + TRACE_MSG0(NTT,"unknown encapsulation"); + printk(KERN_ERR"%s: unknown encapsulation\n", __FUNCTION__); + // Since we are now only really using one, just fix it. + npd->encapsulation = simple_crc; + } + //TRACE_MSG0(NTT,"SIMPLE_CRC"); + // allocate urb 5 bytes larger than required + if (!(urb = usbd_alloc_urb (function, BULK_IN, len + 5 + 4 + in_pkt_sz, net_fd_urb_sent_bulk ))) { + u8 epa = usbd_endpoint_bEndpointAddress(function, BULK_IN, usbd_high_speed(function)); + TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa); + printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", __FUNCTION__, len, epa); + return -ENOMEM; + } + + switch (npd->network_type) { + case network_eem: + cp = urb->buffer + 2; + urb->actual_length = len + 2; + break; + default: + cp = urb->buffer; + urb->actual_length = len; + break; + } + // copy and crc len bytes + crc = crc32_copy(cp, buff, len, CRC32_INIT); + + switch (npd->network_type) { + case network_eem: + break; + default: + if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz - 4)) { + + #undef CONFIG_OTG_NETWORK_PADBYTE + #ifdef CONFIG_OTG_NETWORK_PADBYTE + // add a pad byte if required to ensure a short packet, usbdnet driver + // will correctly handle pad byte before or after CRC, but the MCCI driver + // wants it before the CRC. + crc = crc32_pad(urb->buffer + urb->actual_length, 1, crc); + urb->actual_length++; + #else /* CONFIG_OTG_NETWORK_PADBYTE */ + urb->flags |= USBD_URB_SENDZLP; + TRACE_MSG2(NTT,"setting ZLP: urb: %p flags: %x", urb, urb->flags); + #endif /* CONFIG_OTG_NETWORK_PADBYTE */ + } + break; + } + // munge and append crc + crc = ~crc; + urb->buffer[urb->actual_length++] = crc & 0xff; + urb->buffer[urb->actual_length++] = (crc >> 8) & 0xff; + urb->buffer[urb->actual_length++] = (crc >> 16) & 0xff; + urb->buffer[urb->actual_length++] = (crc >> 24) & 0xff; + // End of CRC processing + // + switch (npd->network_type) { + case network_eem: + break; + default: + break; + } + + TRACE_MSG3(NTT,"urb=%p buff_ctx=%p priv=%p",urb,buff_ctx,urb->function_privdata); + urb->function_privdata = (void *) buff_ctx; +#if 0 + printk(KERN_INFO"start_xmit: len: %d : %d data: %p\n", skb->len, urb->actual_length, urb->buffer); + { + u8 *cp = urb->buffer; + int i; + for (i = 0; i < urb->actual_length; i++) { + if ((i%32) == 0) { + printk("\ntx[%2x] ", i); + } + printk("%02x ", *cp++); + } + printk("\n"); + } +#endif +#if defined(CONFIG_OTG_NETWORK_BLAN_FERMAT) + if (npd->fermat) { + fermat_encode(urb->buffer, urb->actual_length); + } +#endif + TRACE_MSG1(NTT,"sending urb: %p", urb); + if ((rc = usbd_start_in_urb (urb))) { + + TRACE_MSG1(NTT,"FAILED: %d", rc); + printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc); + urb->function_privdata = NULL; + usbd_free_urb (urb); + + return(rc); + } + #if 0 + { + static int xmit_count = 0; + if (xmit_count++ == 100) { + TRACE_MSG1(NTT, "halt test: %02x", BULK_IN); + usbd_halt_endpoint(function, BULK_IN); + } + } + #endif + + TRACE_MSG0(NTT,"OK"); + return 0; +} + + +//_________________________________________________________________________________________________ + +/*! net_fd_recv_urb - callback to process a received URB + * + * @return non-zero for failure. + */ +STATIC int net_fd_recv_urb(struct usbd_urb *urb, int rc) +{ + struct usbd_function_instance *function = urb->function_instance; + struct usb_network_private *npd = urb->function_privdata; + void *buff_ctx = NULL; + u8 *buff; + int crc_bad = 0; + int trim = 0; + int len; + int out_pkt_sz; + u32 crc; + +#if 0 + printk(KERN_INFO"%s: urb: %p len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, + urb, urb->actual_length, npd->maxtransfer, npd->encapsulation); + + { + u8 *cp = urb->buffer; + int i; + for (i = 0; i < urb->actual_length; i++) { + if ((i%32) == 0) { + printk("\n[%2x] ", i); + } + printk("%02x ", *cp++); + } + printk("\n"); + } +#endif + if (!urb || !urb->function_instance || !npd || !npd->function || + urb->function_instance != npd->function) { + TRACE_MSG4(NTT,"urb=%p npd=%p u->f=%p n->f=%p", + urb,(urb?npd:NULL),(urb?urb->function_instance:NULL), + ((urb&&npd)?npd->function:NULL)); + } + + THROW_IF(urb->status != RECV_OK, error); + + out_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function)); + // There is only one working encapsulation. + if (npd->encapsulation != simple_crc) { + npd->encapsulation = simple_crc; + } + + len = urb->actual_length; + trim = 0; + buff_ctx = net_os_alloc_buff(npd, &buff, len); + THROW_IF((NULL == buff_ctx), error); + +#if defined(CONFIG_OTG_NETWORK_BLAN_PADAFTER) + { + /* This version simply checks for a correct CRC along the + * entire packet. Some UDC's have trouble with some packet + * sizes, this allows us to add pad bytes after the CRC. + */ + + u8 *src = urb->buffer; + int copied; + + // XXX this should work, but the MIPS optimizer seems to get it wrong.... + //copied = (len < out_pkt_sz) ? 0 : ((len / out_pkt_sz) - 1) * out_pkt_sz; + + if (len < out_pkt_sz*2) + copied = 0; + else { + int pkts = ((len - out_pkt_sz) / out_pkt_sz); + copied = (pkts - 1) * out_pkt_sz; + } + + len -= copied; + crc = CRC32_INIT; + for (; copied-- > 0 ; crc = COMPUTE_FCS (crc, *buff++ = *src++)); + + for (; (len-- > 0) && (CRC32_GOOD != crc); crc = COMPUTE_FCS (crc, *buff++ = *src++)); + + trim = len + 4; + + if (CRC32_GOOD != crc) { + TRACE_MSG1(NTT,"AAA frame: %03x", urb->framenum); + THROW_IF(npd->crc, crc_error); + } + else + npd->crc = 1; + } +#else + /* + * The CRC can be sent in two ways when the size of the transfer + * ends up being a multiple of the packetsize: + * + * | + * | case 1 + * | case 2 + * | case 3 + * | | case 4 + * | + * + * This complicates CRC checking, there are four scenarios: + * + * 1. length is 1 more than multiple of packetsize with a trailing byte + * 2. length is 1 more than multiple of packetsize + * 3. length is multiple of packetsize + * 4. none of the above + * + * Finally, even though we always compute CRC, we do not actually throw + * things away until and unless we have previously seen a good CRC. + * This allows backwards compatibility with hosts that do not support + * adding a CRC to the frame. + * + */ + + // test if 1 more than packetsize multiple + if (1 == (len % out_pkt_sz)) { + + // copy and CRC up to the packetsize boundary + crc = crc32_copy(buff, urb->buffer, len - 1, CRC32_INIT); + buff += len - 1; + + // if the CRC is good then this is case 1 + if (CRC32_GOOD != crc) { + + crc = crc32_copy(buff, urb->buffer + len - 1, 1, crc); + buff += 1; + + if (CRC32_GOOD != crc) { + //crc_errors[len%64]++; + TRACE_MSG2(NTT,"A CRC error %08x %03x", crc, urb->framenum); + THROW_IF(npd->crc, crc_error); + } + else + npd->crc = 1; + } + else + npd->crc = 1; + } + else { + crc = crc32_copy(buff, urb->buffer, len, CRC32_INIT); + buff += len; + + if (CRC32_GOOD != crc) { + //crc_errors[len%64]++; + TRACE_MSG2(NTT,"B CRC error %08x %03x", crc, urb->framenum); + THROW_IF(npd->crc, crc_error); + } + else + npd->crc = 1; + } + // trim IFF we are paying attention to crc + if (npd->crc) + trim = 4; +#endif + // catch a simple error, just increment missed error and general error + CATCH(error) { + TRACE_MSG4(NTT,"CATCH(error) urb: %p status: %d len: %d function: %p", + urb, urb->status, urb->actual_length, function); + // catch a CRC error + CATCH(crc_error) { + crc_bad = 1; +#if 0 + printk(KERN_INFO"%s: urb: %p status: %d len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, + urb, urb->status, urb->actual_length, npd->maxtransfer, + npd->encapsulation); + + { + u8 *cp = urb->buffer; + int i; + for (i = 0; i < urb->actual_length; i++) { + if ((i%32) == 0) { + printk("\n[%2x] ", i); + } + printk("%02x ", *cp++); + } + printk("\n"); + } +#endif + } + } + net_os_recv_buff(npd,buff_ctx,crc_bad,trim); + TRACE_MSG1(NTT,"restart: %p", urb); + return (usbd_start_out_urb (urb)); +} +/*! net_fd_recv_urb_eem - callback to process a received URB + * + * @return non-zero for failure. + */ +STATIC int net_fd_recv_urb_eem(struct usbd_urb *urb, int rc) +{ + return 0; +} + +//_________________________________________________________________________________________________ +// net_fd_device_request +// +/*! net_fd_urb_received_ep0 - callback for sent URB + * + * Handles notification that an urb has been sent (successfully or otherwise). + * + * @return non-zero for failure. + */ +STATIC int net_fd_urb_received_ep0 (struct usbd_urb *urb, int urb_rc) +{ + TRACE_MSG2(NTT,"urb: %p status: %d", urb, urb->status); + + printk(KERN_INFO"%s:\n", __FUNCTION__); + RETURN_EINVAL_IF (RECV_OK != urb->status); + + // TRACE_MSG1(NTT,"%s", urb->buffer); // QQSV is this really a NUL-terminated string??? + + printk(KERN_INFO"%s: ok\n", __FUNCTION__); + return -EINVAL; // caller will de-allocate +} + +/*! net_fd_device_request - process a received SETUP URB + * + * Processes a received setup packet and CONTROL WRITE data. + * Results for a CONTROL READ are placed in urb->buffer. + * + * @return non-zero for failure. + */ +STATIC int net_fd_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) +{ + struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); + struct usbd_urb *urb; + int index; + + // Verify that this is a USB Class request per CDC 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_VENDOR: + + switch (request->bRequest) { + case MCCI_ENABLE_CRC: + if (make_crc_table()) + return -EINVAL; + npd->encapsulation = simple_crc; + return 0; + + case BELCARRA_PING: + TRACE_MSG1(NTT,"H2D VENDOR IP: %08x", ip_addr); + if ((npd->network_type == network_blan)) + net_os_send_notification_later(npd); + break; + +#if !defined(CONFIG_OTG_NETWORK_BLAN_DO_NOT_SETTIME) || !defined(CONFIG_OTG_NETWORK_SAFE_DO_NOT_SETTIME) + case BELCARRA_SETTIME: + { +#if defined(LINUX24) + struct timeval tv; +#else + struct timespec tv; +#endif + memset(&tv, 0, sizeof(tv)); + + // wIndex and wLength contain RFC868 time - seconds since midnight 1 jan 1900 + + tv.tv_sec = ntohl( request->wValue << 16 | request->wIndex); + // tv.tv_usec = 0; + + // convert to Unix time - seconds since midnight 1 jan 1970 + + tv.tv_sec -= RFC868_OFFSET_TO_EPOCH; + + TRACE_MSG1(NTT,"H2D VENDOR TIME: %08x", tv.tv_sec); + + // set the time + do_settimeofday(&tv); + } break; +#endif + case BELCARRA_SETIP: + ip_addr = ntohl( request->wValue << 16 | request->wIndex); + // XXX need to get in correct order here + npd->local_dev_addr[2] = (ip_addr >> 24) & 0xff; + npd->local_dev_addr[3] = (ip_addr >> 16) & 0xff; + npd->local_dev_addr[4] = (ip_addr >> 8) & 0xff; + npd->local_dev_addr[5] = (ip_addr >> 0) & 0xff; +#ifdef CONFIG_OTG_NETWORK_BLAN_IPADDR + snprintf(ip_addr_str, sizeof(ip_addr_str), "%d.%d.%d.%d", + (ip_addr >> 24) & 0xff, (ip_addr >> 16) & 0xff, + (ip_addr >> 8) & 0xff, (ip_addr) & 0xff); + index = usbd_realloc_string( + npd->function_driver->device_description->device_descriptor->iProduct, ip_addr_str); +#endif /* CONFIG_OTG_NETWORK_IPADDR */ + break; + + case BELCARRA_SETMSK: + network_mask = ntohl( request->wValue << 16 | request->wIndex); + break; + + case BELCARRA_SETROUTER: + router_ip = ntohl( request->wValue << 16 | request->wIndex); + break; + + case BELCARRA_SETDNS: + dns_server_ip = ntohl( request->wValue << 16 | request->wIndex); + break; +#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT + case BELCARRA_SETFERMAT: + npd->fermat = 1; + break; +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_HOSTNAME + case BELCARRA_HOSTNAME: + TRACE_MSG0(NTT,"HOSTNAME"); + RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, le16_to_cpu(request->wLength), + net_fd_urb_received_ep0) )); + RETURN_ZERO_IF(!usbd_start_out_urb(urb)); // return if no error + usbd_free_urb(urb); // de-alloc if error + return -EINVAL; +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK + case BELCARRA_DATA_NOTIFY: + TRACE_MSG0(NTT,"DATA NOTIFY"); + npd->data_notify = 1; + return -EINVAL; +#endif + } + return 0; +#if 0 + case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR: + urb->actual_length = 0; + switch (request->bRequest) { + case BELCARRA_GETMAC: + { + // copy and free the original buffer + memcpy(urb->buffer, npd->local_dev_addr, ETH_ALEN); + urb->actual_length = ETH_ALEN; + return 0; + } + } +#endif + return 0; + default: + break; + } + return -EINVAL; +} +//_________________________________________________________________________________________________ + +#ifdef CONFIG_OTG_NETWORK_START_SINGLE +#define NETWORK_START_URBS 1 +#else +#define NETWORK_START_URBS 2 +#endif + +typedef enum mesg { + mesg_unknown, + mesg_configured, + mesg_reset, +} mesg_t; +mesg_t net_last_mesg; + +char * net_messages[3] = { + "", + "Network Configured", + "Network Reset", +}; + +/*! net_check_mesg + */ +void net_check_mesg(mesg_t curr_mesg) +{ + RETURN_UNLESS(net_last_mesg != curr_mesg); + net_last_mesg = curr_mesg; + otg_message(net_messages[curr_mesg]); +} + + + +/*! net_fd_start_recv - start recv urb(s) + */ +STATIC void net_fd_start_recv(struct usbd_function_instance *function) +{ + struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); + int i; + for (i = 0; i < NETWORK_START_URBS; i++) { + struct usbd_urb *urb; + BREAK_IF(!(urb = usbd_alloc_urb(function, BULK_OUT, + usbd_endpoint_transferSize(function, BULK_OUT, usbd_high_speed(function)), + net_fd_recv_urb))); + TRACE_MSG5(NTT,"i: %d urb=%p priv=%p npd=%p function=%p", + i, urb, urb->function_privdata, npd, function); + + urb->function_privdata = npd; + if (usbd_start_out_urb(urb)) { + urb->function_privdata = NULL; + usbd_free_urb(urb); + } + } +} + +/*! net_fd_start_recv_eem - start recv urb(s) + */ +STATIC void net_fd_start_recv_eem(struct usbd_function_instance *function) +{ +} + +/*! net_fd_event_handler - Processes a USB event. + */ +STATIC void net_fd_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) +{ + struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); + + switch (event) { + + case DEVICE_RESET: + case DEVICE_DESTROY: + case DEVICE_BUS_INACTIVE: + case DEVICE_DE_CONFIGURED: + TRACE_MSG1(NTT,"RESET/DESTROY/BUS_INACTIVE/DE_CONFIGURED %08x",ip_addr); + net_check_mesg(mesg_reset); + { + // Return if argument is null. + + // XXX flush + + npd->flags &= ~NETWORK_ATTACHED; + npd->int_urb = NULL; + + // Disable our net-device. + // Apparently it doesn't matter if we should do this more than once. + + net_os_carrier_off(npd); + + // If we aren't already tearing things down, do it now. + if (!(npd->flags & NETWORK_DESTROYING)) { + npd->flags |= NETWORK_DESTROYING; + //npd->device = NULL; + } + } + npd->crc = 0; + break; + + case DEVICE_CONFIGURED: + case DEVICE_BUS_ACTIVITY: + TRACE_MSG1(NTT,"CONFIGURED/BUS_ACTIVITY %08x",ip_addr); + net_check_mesg(mesg_configured); + npd->flags |= NETWORK_ATTACHED; + if ((npd->network_type == network_blan) && (npd->flags & NETWORK_OPEN)) + net_os_send_notification_later(npd); + net_os_carrier_on(npd); + (npd->network_type == network_eem) ? net_fd_start_recv_eem : net_fd_start_recv(function); + break; + + case DEVICE_SET_INTERFACE: + // XXX if CDC then we can check device->alternates[1] and see if we should + // enable/disable data flow. + // XXX verify ep0.c SET_CONFIGURATION and SET_INTERFACE implmentation are + // complete before using this + break; + + default: + return; + } + // Let the OS layer know, if it's interested. + net_os_config(npd); + net_os_hotplug(npd); +} + + +/*! net_fd_endpoint_cleared - + */ +STATIC void net_fd_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress, int wIndex) +{ + TRACE_MSG2(NTT, "bEndpointAddress: %02x wIndex: %02x", bEndpointAddress, wIndex); +} + + +//_________________________________________________________________________________________________ + +/*! net_fd_function_enable - enable the function driver + * + * Called for usbd_function_enable() from usbd_register_device() + */ + +STATIC int net_fd_function_enable (struct usbd_function_instance *function) +{ + struct usb_network_private *npd; +#if 0 + /* This is the first time we've seen the function instance + (it's allocated by the usbdcore), so we need to let the OS + layer see it and initialize the privdata pointer. */ + net_os_enable(function); + npd = (struct usb_network_private *) (function->privdata); + TRACE_MSG0(NTT,"semaphore DOWN"); + net_os_mutex_enter(npd); +#else + npd = (struct usb_network_private *) (function->privdata); + TRACE_MSG0(NTT,"semaphore DOWN"); + net_os_mutex_enter(npd); + net_os_enable(function); +#endif + _MOD_INC_USE_COUNT; // QQQ Should this be _before_ the mutex_enter()? + TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); + + // set the network device address from the local device address + // Now done in net_os_enable() above. + // memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); + + npd->have_interrupt = usbd_endpoint_bEndpointAddress(function, INT_IN, usbd_high_speed(function)) ? 1 : 0; + + npd->flags |= NETWORK_ENABLED; + +#if defined(CONFIG_OTG_NETWORK_CDC) + cdc_init(function); +#endif /* CONFIG_OTG_NETWORK_CDC */ + +#ifdef CONFIG_OTG_NETWORK_EEM + eem_init(function); +#endif + +#ifdef CONFIG_OTG_NETWORK_BASIC + basic_init(function); +#endif + +#ifdef CONFIG_OTG_NETWORK_BASIC2 + basic2_init(function); +#endif + +#ifdef CONFIG_OTG_NETWORK_SAFE + safe_init(function); +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN + blan_init(function); +#endif + net_os_mutex_exit(npd); + TRACE_MSG0(NTT,"semaphore UP"); + return 0; +} + +/*! net_fd_function_disable - disable the function driver + * + */ +STATIC void net_fd_function_disable (struct usbd_function_instance *function) +{ + struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); + TRACE_MSG0(NTT,"semaphore DOWN"); + net_os_mutex_enter(npd); + npd->flags &= ~NETWORK_ENABLED; + _MOD_DEC_USE_COUNT; // QQQ Should this be _after_ the up()? + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); + net_os_mutex_exit(npd); + TRACE_MSG0(NTT,"semaphore UP"); +} + +/*! net_fd_function_ops - operations table for network function driver + */ +struct usbd_function_operations net_fd_function_ops = { + device_request: net_fd_device_request, + event_handler: net_fd_event_handler, + function_enable: net_fd_function_enable, + function_disable: net_fd_function_disable, + endpoint_cleared: net_fd_endpoint_cleared, +}; + +//______________________________________module_init and module_exit________________________________ + +/*! hexdigit - + * + * Converts characters in [0-9A-F] to 0..15, characters in [a-f] to 42..47, and all others to 0. + */ +static u8 hexdigit (char c) +{ + return isxdigit (c) ? (isdigit (c) ? (c - '0') : (c - 'A' + 10)) : 0; +} + +/*! set_address - + */ +static void set_address(char *mac_address_str, u8 *dev_addr) +{ + int i; + if (mac_address_str && strlen(mac_address_str)) { + for (i = 0; i < ETH_ALEN; i++) { + dev_addr[i] = + hexdigit (mac_address_str[i * 2]) << 4 | + hexdigit (mac_address_str[i * 2 + 1]); + } + } + else { + get_random_bytes(dev_addr, ETH_ALEN); + dev_addr[0] = (dev_addr[0] & 0xfe) | 0x02; + } +} + +/*! macstrtest - + */ +static int macstrtest(char *mac_address_str) +{ + int l = 0; + + if (mac_address_str) { + l = strlen(mac_address_str); + } + return ((l != 0) && (l != 12)); +} + +/*! select_network_type + * + */ +STATIC network_type_t select_network_type(struct usb_network_params *p) +{ + /* + * Figure out what network mode to operate in. + */ + network_type_t network_type = network_unknown; + /* + * Step 1. Look to see if network_type is specified by something like module params. + */ +#if defined(CONFIG_OTG_NETWORK_CDC) + p->cdc_capable = 1; + if (p->cdc) { + TRACE_MSG0(NTT,"cdc"); + network_type = network_cdc; + } +#endif +#ifdef CONFIG_OTG_NETWORK_EEM + p->eem_capable = 1; + if (p->eem) { + THROW_IF (network_type != network_unknown, select_error); + TRACE_MSG0(NTT,"eem"); + network_type = network_eem; + } +#endif +#ifdef CONFIG_OTG_NETWORK_BASIC + p->basic_capable = 1; + if (p->basic) { + THROW_IF (network_type != network_unknown, select_error); + TRACE_MSG0(NTT,"basic"); + network_type = network_basic; + } +#endif +#ifdef CONFIG_OTG_NETWORK_BASIC2 + p->basic2_capable = 1; + if (p->basic2) { + THROW_IF (network_type != network_unknown, select_error); + TRACE_MSG0(NTT,"basic2"); + network_type = network_basic2; + } +#endif +#ifdef CONFIG_OTG_NETWORK_SAFE + p->safe_capable = 1; + if (p->safe) { + THROW_IF (network_type != network_unknown, select_error); + TRACE_MSG0(NTT,"safe"); + network_type = network_safe; + } +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN + p->blan_capable = 1; + if (p->blan) { + THROW_IF (network_type != network_unknown, select_error); + TRACE_MSG0(NTT,"blan"); + network_type = network_blan; + } +#endif + + /* + * Step 2. If nothing was specified by parameter, default to + * to the first available (i.e. compiled) of: + * CDC, BASIC, BASIC2, SAFE, BLAN. + */ +#if defined(CONFIG_OTG_NETWORK_CDC) + if (network_type == network_unknown) { + TRACE_MSG0(NTT,"unknown => cdc"); + network_type = network_cdc; + } +#endif +#if defined(CONFIG_OTG_NETWORK_EEM) + if (network_type == network_unknown) { + TRACE_MSG0(NTT,"unknown => eem"); + network_type = network_eem; + } +#endif +#if defined(CONFIG_OTG_NETWORK_BASIC) + if (network_type == network_unknown) { + TRACE_MSG0(NTT,"unknown => basic"); + network_type = network_basic; + } +#endif +#if defined(CONFIG_OTG_NETWORK_BASIC2) + if (network_type == network_unknown) { + TRACE_MSG0(NTT,"unknown => basic2"); + network_type = network_basic2; + } +#endif +#if defined(CONFIG_OTG_NETWORK_SAFE) + if (network_type == network_unknown) { + TRACE_MSG0(NTT,"unknown => safe"); + network_type = network_safe; + } +#endif +#if defined(CONFIG_OTG_NETWORK_BLAN) + if (network_type == network_unknown) { + TRACE_MSG0(NTT,"unknown => blan"); + network_type = network_blan; + } +#endif + CATCH(select_error) { + network_type == network_unknown; + } + return(network_type); +} + +/*! select_descriptors + * + */ +STATIC struct usbd_function_driver * select_descriptors(network_type_t network_type) +{ + // select the function driver descriptors based on network_type + + switch (network_type) { + +#if defined(CONFIG_OTG_NETWORK_CDC) + case network_cdc: + TRACE_MSG0(NTT,"fd=cdc"); + return(&cdc_function_driver); +#endif +#if defined(CONFIG_OTG_NETWORK_EEM) + case network_eem: + TRACE_MSG0(NTT,"fd=eem"); + return(&eem_function_driver); +#endif +#if defined(CONFIG_OTG_NETWORK_BASIC) + case network_basic: + TRACE_MSG0(NTT,"fd=basic"); + return(&basic_function_driver); +#endif +#if defined(CONFIG_OTG_NETWORK_BASIC2) + case network_basic2: + TRACE_MSG0(NTT,"fd=basic2"); + return(&basic2_function_driver); +#endif +#if defined(CONFIG_OTG_NETWORK_SAFE) + case network_safe: + TRACE_MSG1(NTT,"fd=safe bNumConfigurations: %d", + safe_function_driver.bNumConfigurations); + return(&safe_function_driver); +#endif +#if defined(CONFIG_OTG_NETWORK_BLAN) + case network_blan: + TRACE_MSG1(NTT,"fd=blan bNumConfigurations: %d", + blan_function_driver.bNumConfigurations); + return(&blan_function_driver); +#endif + default: + break; + } + return(NULL); +} + +/*! net_fd_init - function driver usb part intialization + * + * @return non-zero for failure. + */ +STATIC int net_fd_init(struct usb_network_private *npd, char *info_str) +{ + struct usb_network_params *p = npd->params; + + printk(KERN_INFO "Copyright (c) 2002-2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n"); + NTT = otg_trace_obtain_tag(); + + /* + * Figure out what network mode to operate in. + */ + if (network_unknown == (npd->network_type = select_network_type(p)) || + NULL == (npd->function_driver = select_descriptors(npd->network_type))) { + printk(KERN_INFO"configuration selection error"); + otg_trace_invalidate_tag(NTT); + return -EINVAL; + } + +#ifdef CONFIG_OTG_NETWORK_ALLOW_SETID + TRACE_MSG2(NTT,"checking idVendor: %04x idProduct: %04x", p->vendor_id, p->product_id); + + if (p->vendor_id) + npd->function_driver->idVendor = cpu_to_le16(p->vendor_id); + + if (p->product_id) + npd->function_driver->idProduct = cpu_to_le16(p->product_id); +#endif + printk(KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", + __FUNCTION__, info_str, + le16_to_cpu(npd->function_driver->idVendor), + le16_to_cpu(npd->function_driver->idProduct)); + TRACE_MSG3(NTT,"%s vendor_id: %04x product_id: %04x", info_str, + le16_to_cpu(npd->function_driver->idVendor), + le16_to_cpu(npd->function_driver->idProduct)); + +#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT + TRACE_MSG0(NTT,"fermat"); + fermat_init(); +#endif + +#ifdef CONFIG_OTG_NETWORK_EP0TEST + /* + * ep0test - test that bus interface can do ZLP on endpoint zero + * + * This will artificially force iProduct string descriptor to be + * exactly the same as the endpoint zero packetsize. When the host + * requests this string it will request it not knowing the strength + * and will use a max length of 0xff. The bus interface driver must + * send a ZLP to terminate the transaction. + * + * The iProduct descriptor is used because both the Linux and + * Windows usb implmentations fetch this in a default enumeration. + * + */ + //UNLESS (ep0test) + // ep0test = usbd_endpoint_zero_wMaxPacketsize(function); + + if (ep0test) { + switch (ep0test) { + case 8: npd->function_driver->device_description->iProduct = "012"; break; + case 16: npd->function_driver->device_description->iProduct = "0123456"; break; + case 32: npd->function_driver->device_description->iProduct = "0123456789abcde"; break; + case 64: npd->function_driver->device_description->iProduct = "0123456789abcdef0123456789abcde"; break; + default: printk(KERN_ERR"%s: ep0test: bad value: %d, must be one of 8, 16, 32 or 64\n", + __FUNCTION__, ep0test); return -EINVAL; + break; + } + TRACE_MSG1(NTT,"ep0test: iProduct set to: %s", + npd->function_driver->device_description->iProduct); + printk(KERN_INFO"%s: ep0test: iProduct set to: %s\n", __FUNCTION__, + npd->function_driver->device_description->iProduct); + } + else { + TRACE_MSG0(NTT,"ep0test: not set"); + printk(KERN_INFO"%s: ep0test: not set\n", __FUNCTION__); + } +#endif /* CONFIG_OTG_NETWORK_EP0TEST */ + + +#ifdef CONFIG_OTG_NETWORK_LOCAL_MACADDR + if (NULL == p->local_mac_address_str) { + /* There is a configured override, and it has NOT been + overridden in turn by a module load time parameter, so use it. */ + p->local_mac_address_str = CONFIG_OTG_NETWORK_LOCAL_MACADDR; + } +#endif +#ifdef CONFIG_OTG_NETWORK_REMOTE_MACADDR + if (NULL == p->remote_mac_address_str) { + /* There is a configured override, and it has NOT been + overridden in turn by a module load time parameter, so use it. */ + p->remote_mac_address_str = CONFIG_OTG_NETWORK_REMOTE_MACADDR; + } +#endif + if ((macstrtest(p->local_mac_address_str) || macstrtest(p->remote_mac_address_str))) { + TRACE_MSG2(NTT,"bad size %s %s", p->local_mac_address_str, p->remote_mac_address_str); + otg_trace_invalidate_tag(NTT); + return -EINVAL; + } + + set_address(p->local_mac_address_str, npd->local_dev_addr); + set_address(p->remote_mac_address_str, npd->remote_dev_addr); + npd->encapsulation = simple_crc; + + if (make_crc_table() || + NULL == (npd->function = usbd_register_function(npd->function_driver, "net-fd", NULL))) { + if (network_crc32_table) { + lkfree(network_crc32_table); + network_crc32_table = NULL; + } + otg_trace_invalidate_tag(NTT); + return -EINVAL; + } + npd->function->privdata = (void *) npd; + + return 0; + +} + +//_____________________________________________________________________________ + +/*! net_fd_term - driver exit + * + * Cleans up the module. Deregisters the function driver and destroys the network object. + */ +STATIC void net_fd_term(struct usb_network_private *npd) +{ + if (NULL != npd->function) { + usbd_deregister_function(npd->function); + npd->function = NULL; + } + if (network_crc32_table) { + lkfree(network_crc32_table); + network_crc32_table = NULL; + } + otg_trace_invalidate_tag(NTT); +} + +//_________________________________________________________________________________________________ + + +/*! net_fd_usb_ops operations + */ +struct net_usb_services net_fd_usb_ops = { + .initialize_usb_part = net_fd_init, + .terminate_usb_part = net_fd_term, + .send_int_notification = net_fd_send_int_blan, + .start_xmit = net_fd_start_xmit, +}; diff -uNr linux/drivers/no-otg/functions/network/net-fd.h linux/drivers/otg/functions/network/net-fd.h --- linux/drivers/no-otg/functions/network/net-fd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/net-fd.h 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,48 @@ +/* + * otg/functions/network/net-fd.h - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Tom Rushworth + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + * + */ +/*! + * @file otg/functions/network/net-fd.h + * @brief These are the functions exported by the USB specific parts + * (i.e. net-fd.c and net-cl.c) for use in the OS specific + * layers (e.g. net-l24-os.c). + * + * + * @ingroup NetworkFunction + */ +#ifndef NET_FD_H +#define NET_FD_H 1 + +struct net_usb_services { + int (*initialize_usb_part)(struct usb_network_private *npd, char *info_str); + void (*terminate_usb_part)(struct usb_network_private *npd); + void (*send_int_notification)(struct usb_network_private *npd, int connected, int data); + int (*start_xmit)(struct usb_network_private *npd, __u8 *buff, int len, void *buff_ctx); +}; + +extern struct net_usb_services net_fd_usb_ops; + +#endif diff -uNr linux/drivers/no-otg/functions/network/net-l24-os.c linux/drivers/otg/functions/network/net-l24-os.c --- linux/drivers/no-otg/functions/network/net-l24-os.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/net-l24-os.c 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,1395 @@ +/* + * otg/functions/network/net-l24-os.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra Technologies + * + * By: + * Tom Rushworth + * Stuart Lynne + * Bruce Balden + * + * 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. + * + */ +/*! + * @file otg/functions/network/net-l24-os.c + * @brief The Linux 2.4 OS specific upper edge (network interface) + * implementation for the Network Function Driver. + * + * This file implements a standard Linux network driver interface and + * the standard Linux 2.4 module init and exit functions. + * + * If compiled into the kernel, this driver can be used with NFSROOT to + * provide the ROOT filesystem. Please note that the kernel NFSROOT support + * (circa 2.4.20) can have problems if there are multiple interfaces. So + * it is best to ensure that there are no other network interfaces compiled + * in. + * + * + * @ingroup NetworkFunction + */ + + +/* OS-specific #includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Networking #includes from OS */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* Belcarra public interfaces */ +#include +#include +#include +//#include +#include + +/* Belcarra private interfaces SUBJECT TO CHANGE WITHOUT NOTICE */ +#include "network.h" +#include "net-os.h" +#include "net-fd.h" + +MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com, balden@belcarra.com"); + +EMBED_LICENSE(); + +MOD_DESCRIPTION ("USB Network Function"); + + + +EMBED_USBD_INFO ("network_fd 2.0-beta"); + + +#define NTT network_fd_trace_tag + +wait_queue_head_t usb_netif_wq; +#ifdef CONFIG_OTG_NET_NFS_SUPPORT +int usb_is_configured; +#endif + +/* Module Parameters ************************************************************************* */ + +STATIC struct usb_network_params params; + +#ifdef CONFIG_OTG_NETWORK_ALLOW_SETID +// override vendor ID +static int vendor_id; +MOD_PARM (vendor_id, "i"); +MOD_PARM_DESC (vendor_id, "vendor id"); + +// override product ID +static int product_id; +MOD_PARM (product_id, "i"); +MOD_PARM_DESC (product_id, "product id"); +#endif + +// override local mac address +static char *local_mac_address_str; +MOD_PARM (local_mac_address_str, "s"); +MOD_PARM_DESC (local_mac_address_str, "Local MAC"); + +// override remote mac address +static char *remote_mac_address_str; +MOD_PARM (remote_mac_address_str, "s"); +MOD_PARM_DESC (remote_mac_address_str, "Remote MAC"); + +#ifdef CONFIG_OTG_NETWORK_EP0TEST +static int ep0test; +MOD_PARM (ep0test, "i"); +MOD_PARM_DESC (ep0test, "Test EP0 String Handling - set to ep0 packetsize [8,16,32,64]"); +#endif + +static int zeroconf; +MOD_PARM (zeroconf, "i"); +MOD_PARM_DESC (zeroconf, "Use usbz%d for network name"); + +#if defined(CONFIG_OTG_NETWORK_CDC) +static int cdc; +MOD_PARM (cdc, "i"); +MOD_PARM_DESC (cdc, "Enable CDC mode"); +#endif + +#ifdef CONFIG_OTG_NETWORK_BASIC +static int basic; +MOD_PARM (basic, "i"); +MOD_PARM_DESC (basic, "Enable BASIC mode"); +#endif + +#ifdef CONFIG_OTG_NETWORK_BASIC2 +static int basic2; +MOD_PARM (basic2, "i"); +MOD_PARM_DESC (basic2, "Enable BASIC2 mode"); +#endif + +#ifdef CONFIG_OTG_NETWORK_SAFE +static int safe; +MOD_PARM (safe, "i"); +MOD_PARM_DESC (safe, "Enable SAFE mode"); +#endif + +#ifdef CONFIG_OTG_NETWORK_BLAN +static int blan; +MOD_PARM (blan, "i"); +MOD_PARM_DESC (blan, "Enable BLAN mode"); +#endif + +/* End of Module Parameters ****************************************************************** */ + + +u8 local_dev_addr[ETH_ALEN]; + +u8 remote_dev_addr[ETH_ALEN]; +static u8 zeros[ETH_ALEN]; + +extern u32 ip_addr; +extern u32 router_ip; +extern u32 network_mask; +extern u32 dns_server_ip; + +/* Prevent overlapping of bus administrative functions: + * + * network_function_enable + * network_function_disable + * network_hard_start_xmit + */ +DECLARE_MUTEX(usbd_network_sem); + + +struct net_device Network_net_device; +struct usb_network_private Usb_network_private; + +void notification_schedule_bh (void); +int network_urb_sent_bulk (struct usbd_urb *urb, int urb_rc); +int network_urb_sent_int (struct usbd_urb *urb, int urb_rc); + + +//_________________________________________________________________________________________________ + +/* + * Synchronization + * + * + * Notification bottom half + * + * This is a scheduled task that will send an interrupt notification. Because it + * is called from the task scheduler context it needs to verify that the device is + * still usable. + * + * static int network_send_int_blan(struct usbd_function_instance *, int ) + * static void notification_bh (void *) + * void notification_schedule_bh (void) + * + * + * Netdevice functions + * + * These are called by the Linux network layer. They must be protected by irq locks + * if necessary to prevent interruption by IRQ level events. + * + * int network_init (struct net_device *net_device) + * void network_uninit (struct net_device *net_device) + * int network_open (struct net_device *net_device) + * int network_stop (struct net_device *net_device) + * struct net_device_stats *network_get_stats (struct net_device *net_device) + * int network_set_mac_addr (struct net_device *net_device, void *p) + * void network_tx_timeout (struct net_device *net_device) + * int network_set_config (struct net_device *net_device, struct ifmap *map) + * int network_stop (struct net_device *net_device) + * int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device) + * int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd) + * + * + * Data bottom half functions + * + * These are called from the bus bottom half handler. + * + * static int network_recv (struct usb_network_private *, struct net_device *, struct sk_buff *) + * int network_recv_urb (struct usbd_urb *) + * int network_urb_sent (struct usbd_urb *, int ) + * + * + * Hotplug bottom half: + * + * This is a scheduled task that will send do a hotplug call. Because it is + * called from the task scheduler context it needs to verify that the + * device is still usable. + * + * static int hotplug_attach (u32 ip, u32 mask, u32 router, int attach) + * static void hotplug_bh (void *data) + * void net_os_hotplug (void) + * + * + * Irq level functions: + * + * These are called at interrupt time do process or respond to USB setup + * commands. + * + * int network_device_request (struct usbd_device_request *) + * void network_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) + * + * + * Enable and disable functions: + * + * void network_function_enable (struct usbd_function_instance *, struct usbd_function_instance *) + * void network_function_disable (struct usbd_function_instance *function) + * + * + * Driver initialization and exit: + * + * static int network_create (struct usb_network_private *) + * static void network_destroy (struct usb_network_private *) + * + * int network_modinit (void) + * void network_modexit (void) + */ + + +//_______________________________USB part Functions_________________________________________ + +/* + * net_os_mutex_enter - enter mutex region + */ +void net_os_mutex_enter(struct usb_network_private *npd) +{ + down(&usbd_network_sem); +} + +/* + * net_os_mutex_exit - exit mutex region + */ +void net_os_mutex_exit(struct usb_network_private *npd) +{ + up(&usbd_network_sem); +} + +/* notification_bh - Bottom half handler to send a notification status + * + * Send a notification with open/close status + * + * It should not be possible for this to be called more than once at a time + * as it is only called via schedule_task() which protects against a second + * invocation. + */ +STATIC void notification_bh (void *data) +{ + struct usb_network_private *npd = (struct usb_network_private *) data; + unsigned long flags; + local_irq_save(flags); + (*(npd->fd_ops->send_int_notification))(npd, npd->flags & NETWORK_OPEN, 0); + local_irq_restore(flags); + //_MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); +} + +/* notification_schedule_bh - schedule a call for notification_bh + */ +void net_os_send_notification_later(struct usb_network_private *npd) +{ +#ifdef LINUX24 + //LINUX 2.4 is the only supported OS to need the USE_COUNT + //_MOD_INC_USE_COUNT; + TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); + if (!SCHEDULE_WORK(npd->notification_bh)) { + //_MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); + } +#else + SCHEDULE_WORK(npd->notification_bh); + //XXX No provision for failure of schedule .... +#endif +} + +/*! net_os_xmit_done - called from USB part when a transmit completes, good or bad. + * tx_rc is SEND_FINISHED_ERROR, SEND_CANCELLED or... + * buff_ctx is the pointer passed to fd_ops->start_xmit(). + * @return non-zero only if network does not exist, ow 0. + */ +int net_os_xmit_done(struct usbd_function_instance *function, void *buff_ctx, int tx_rc) +{ + struct sk_buff *skb = (struct sk_buff *) buff_ctx; + struct usb_network_private *npd; + struct net_device *net_device; + int rc = 0; + + npd = (function ? (struct usb_network_private *) (function->privdata) : NULL); + if (skb) { + if (NULL != npd && 0 == (rc = !(npd->flags & NETWORK_CREATED))) { + switch (tx_rc) { + case SEND_FINISHED_ERROR: + npd->stats.tx_errors++; + npd->stats.tx_dropped++; + break; + case SEND_CANCELLED: + npd->stats.tx_errors++; + npd->stats.tx_carrier_errors++; + break; + default: + break; + } + + npd->avg_queue_entries += npd->queued_entries; + npd->queued_entries--; + npd->samples++; + npd->jiffies += jiffies - *(time_t *) (&skb->cb); + npd->queued_bytes -= skb->len; + if (NULL != (net_device = npd->net_dev) && + netif_queue_stopped(net_device)) { + netif_wake_queue(net_device); + npd->restarts++; + } + } + dev_kfree_skb_any(skb); + } + return rc; +} + +/*! net_os_alloc_buf + */ +void *net_os_alloc_buff(struct usb_network_private *npd, u8 **cp, int n) +{ + struct sk_buff *skb; + // allocate skb of appropriate length, reserve 2 to align ip + if (!(skb = dev_alloc_skb(n+2))) { + *cp = NULL; + return(NULL); + } + skb_reserve(skb, 2); + *cp = skb_put(skb, n); + return((void*) skb); +} + +/*! network_recv - function to process an received data URB + * + * Passes received data to the network layer. Passes skb to network layer. + * + * @return non-zero for failure. + */ +STATIC __inline__ int network_recv (struct usb_network_private *npd, + struct net_device *net_device, struct sk_buff *skb) +{ + int rc; +#if 0 + printk(KERN_INFO"%s: len: %x head: %p data: %p tail: %p\n", __FUNCTION__, + skb->len, skb->head, skb->data, skb->tail); + { + u8 *cp = skb->data; + int i; + for (i = 0; i < skb->len; i++) { + if ((i%32) == 0) { + printk("\nrx[%2x] ", i); + } + printk("%02x ", *cp++); + } + printk("\n"); + } +#endif + + // refuse if no device present + if (!netif_device_present (net_device)) { + TRACE_MSG0(NTT,"device not present"); + printk(KERN_INFO"%s: device not present\n", __FUNCTION__); + return -EINVAL; + } + + // refuse if no carrier + if (!netif_carrier_ok (net_device)) { + TRACE_MSG0(NTT,"no carrier"); + printk(KERN_INFO"%s: no carrier\n", __FUNCTION__); + return -EINVAL; + } + + // refuse if the net device is down + if (!(net_device->flags & IFF_UP)) { + TRACE_MSG1(NTT,"not up net_dev->flags: %x", net_device->flags); + //printk(KERN_INFO"%s: not up net_dev->flags: %x\n", __FUNCTION__, net_device->flags); + //npd->stats.rx_dropped++; + return -EINVAL; + } + + skb->dev = net_device; + skb->pkt_type = PACKET_HOST; + skb->protocol = eth_type_trans (skb, net_device); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + //TRACE_MSG4(NTT,"len: %x head: %p data: %p tail: %p", + // skb->len, skb->head, skb->data, skb->tail); + + + // pass it up to kernel networking layer + if ((rc = netif_rx (skb))) { + TRACE_MSG1(NTT,"netif_rx rc: %d", rc); + } + // QQSV - should be else case for stats? + npd->stats.rx_bytes += skb->len; + npd->stats.rx_packets++; + + return 0; +} + +/*! net_os_recv_buff - forward a received URB, or clean up after a bad one. + * buff_ctx == NULL --> just accumulate stats (count 1 bad buff) + * crc_bad != 0 --> count a crc error and free buff_ctx/skb + * trim --> amount to trim from valid skb + */ +void net_os_recv_buff(struct usb_network_private *npd, void *buff_ctx, int crc_bad, int trim) +{ + struct sk_buff *skb = (struct sk_buff *) buff_ctx; + + if (NULL == skb) { + /* Lower layer never got around to allocating an skb, but + needs to count a packet it can't forward. */ + npd->stats.rx_frame_errors++; + npd->stats.rx_errors++; + } else { + // There is an skb, either forward it or free it. + if (crc_bad) { + npd->stats.rx_crc_errors++; + npd->stats.rx_errors++; + } else if ((npd->flags & NETWORK_ATTACHED) && NULL != npd->net_dev) { + // All good, forward it. + if (trim) { + skb_trim(skb, skb->len - trim); + } + // all done if network_recv() == 0, ow fall through to error handling + RETURN_IF(!network_recv(npd, npd->net_dev, skb)); + // Network layer wouldn't take it. + // rx_dropped gets counted below, any other counter we need? + } + // Something wrong, free the skb + dev_kfree_skb_any (skb); + } + // The received buffer didn't get forwarded, so... + npd->stats.rx_dropped++; +} + +/*! net_os_enable + * + */ +void net_os_enable(struct usbd_function_instance *function) +{ + //struct usb_network_private *npd = &Usb_network_private; + struct usb_network_private *npd = (struct usb_network_private *) function->privdata; +#if 0 + /* This is the first time we've gotten access to the function instance, + and it's an allocated structure. Fill in the links to our private data. */ + function->privdata = (void *) npd; + npd->function = function; +#endif + TRACE_MSG3(NTT,"npd=%p function=%p, f->p=%p",npd,function,function->privdata); + + // set the network device address from the local device address + memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); +} + +/*! net_os_disable + * + */ +extern void net_os_disable(struct usbd_function_instance *function) +{ +#if 0 + struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); + function->privdata = NULL; + npd->function = NULL; +#endif +} + +/* + * + */ +void net_os_carrier_on(struct usb_network_private *npd) +{ + netif_carrier_on(npd->net_dev); + netif_wake_queue(npd->net_dev); +#ifdef CONFIG_OTG_NET_NFS_SUPPORT + if (!usb_is_configured) { + wake_up(&usb_netif_wq); + usb_is_configured = 1; + } +#endif + +} + +/* + * + */ +void net_os_carrier_off(struct usb_network_private *npd) +{ + netif_stop_queue(npd->net_dev); + netif_carrier_off(npd->net_dev); +} + + +//_______________________________Network Layer Functions____________________________________ + +/* + * In general because the functions are called from an independant layer it is necessary + * to verify that the device is still ok and to lock interrupts out to prevent in-advertant + * closures while in progress. + */ + +/* network_init - + * + * Initializes the specified network device. + * + * Returns non-zero for failure. + */ +STATIC int network_init (struct net_device *net_device) +{ + TRACE_MSG0(NTT,"no-op"); + return 0; +} + + +/* network_uninit - + * + * Uninitializes the specified network device. + */ +STATIC void network_uninit (struct net_device *net_device) +{ + TRACE_MSG0(NTT,"no-op"); + return; +} + + +/* network_open - + * + * Opens the specified network device. + * + * Returns non-zero for failure. + */ +STATIC int network_open (struct net_device *net_device) +{ + struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); + unsigned long flags; + + _MOD_INC_USE_COUNT; + TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); + npd->flags |= NETWORK_OPEN; + netif_wake_queue (net_device); + + local_irq_save(flags); + (*(npd->fd_ops->send_int_notification))(npd, 1, 0); + local_irq_restore(flags); + +#ifdef CONFIG_OTG_NET_NFS_SUPPORT + if (!usb_is_configured) { + if (!in_interrupt()) { + TRACE_MSG0(NTT,"Please replug USB cable and then ifconfig host interface.\n"); + printk(KERN_ERR"Please replug USB cable and then ifconfig host interface.\n"); + interruptible_sleep_on(&usb_netif_wq); + } + else { + TRACE_MSG_0(NTT,"Warning! In interrupt\n"); + printk(KERN_ERR"Warning! In interrupt\n"); + } + } +#endif + return 0; +} + + +/* network_stop - + * + * Stops the specified network device. + * + * Returns non-zero for failure. + */ +STATIC int network_stop (struct net_device *net_device) +{ + struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); + unsigned long flags; + + TRACE_MSG0(NTT,"-"); + + //netif_stop_queue (net_device); + + npd->flags &= ~NETWORK_OPEN; + + local_irq_save(flags); + (*(npd->fd_ops->send_int_notification))(npd, 0, 0); + local_irq_restore(flags); + + _MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); + return 0; +} + + +/* network_get_stats - + * + * Gets statistics from the specified network device. + * + * Returns pointer to net_device_stats structure with the required information. + */ +STATIC struct net_device_stats *network_get_stats (struct net_device *net_device) +{ + struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); + return &npd->stats; +} + + +/* network_set_mac_addr + * + * Sets the MAC address of the specified network device. Fails if the device is in use. + * + * Returns non-zero for failure. + */ +STATIC int network_set_mac_addr (struct net_device *net_device, void *p) +{ + struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); + struct sockaddr *addr = p; + unsigned long flags; + + TRACE_MSG0(NTT,"--"); + + RETURN_EBUSY_IF(netif_running (net_device)); + local_irq_save(flags); + memcpy(net_device->dev_addr, addr->sa_data, net_device->addr_len); + memcpy(npd->local_dev_addr, npd->net_dev->dev_addr, ETH_ALEN); + local_irq_restore(flags); + return 0; +} + + +/* network_tx_timeout - + * + * Tells the specified network device that its current transmit attempt has timed out. + */ +STATIC void network_tx_timeout (struct net_device *net_device) +{ + struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); +#if 0 + //printk(KERN_ERR"%s:\n", __FUNCTION__); + npd->stats.tx_errors++; + npd->stats.tx_dropped++; + usbd_cancel_urb_irq (npd->bus, NULL); // QQSV +#endif +#if 0 + // XXX attempt to wakeup the host... + if ((npd->network_type == network_blan) && (npd->flags & NETWORK_OPEN)) { + notification_schedule_bh(); + } +#endif +} + + +/** network_set_config - + * + * Sets the specified network device's configuration. Fails if the device is in use. + * + * map An ifmap structure containing configuration values. + * Those values which are non-zero/non-null update the corresponding fields + * in net_device. + * + * Returns non-zero for failure. + */ +STATIC int network_set_config (struct net_device *net_device, struct ifmap *map) +{ + RETURN_EBUSY_IF(netif_running (net_device)); + if (map->base_addr) + net_device->base_addr = map->base_addr; + if (map->mem_start) + net_device->mem_start = map->mem_start; + if (map->irq) + net_device->irq = map->irq; + return 0; +} + + +/* network_change_mtu - + * + * Sets the specified network device's MTU. Fails if the new value is larger and + * the device is in use. + * + * Returns non-zero for failure. + */ +STATIC int network_change_mtu (struct net_device *net_device, int mtu) +{ + struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); + + TRACE_MSG0(NTT,"-"); + + RETURN_EBUSY_IF(netif_running (net_device)); + RETURN_EBUSY_IF(mtu > npd->mtu); + + npd->mtu = mtu; + return 0; +} + +//_________________________________________________________________________________________________ +// network_hard_start_xmit + +/* + * net_os_start_xmit - start sending an skb, with usbd_network_sem already held. + * + * Return non-zero (1) if busy. QQSV - this code always returns 0. + */ +STATIC __inline__ int net_os_start_xmit (struct sk_buff *skb, struct net_device *net_device) +{ + struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); + int rc; + + if (!netif_carrier_ok(net_device)) { + dev_kfree_skb_any (skb); + npd->stats.tx_dropped++; + //netif_stop_queue(net_device); // XXX + // XXX this is what we should do, blows up on some 2.4.20 kernels + // return(NET_XMIT_DROP); + return(0); + } + + // stop queue, it will be restarted only when we are ready for another skb + netif_stop_queue(net_device); + npd->stopped++; + + // Set the timestamp for tx timeout + net_device->trans_start = jiffies; + + // XXX request IN, should start a timer to resend this. + if (npd->data_notify) + (*(npd->fd_ops->send_int_notification))(npd, 0, 1); + + rc = (*(npd->fd_ops->start_xmit))(npd,skb->data,skb->len,(void*)skb); + switch (rc) { + case 0: + TRACE_MSG1(NTT,"OK: %d", rc); + // update some stats + npd->queued_entries++; + npd->queued_bytes += skb->len; + npd->stats.tx_packets++; + npd->stats.tx_bytes += skb->len; + + // Should we restart network queue + if ((npd->queued_entries < npd->max_queue_entries) && + (npd->queued_bytes < npd->max_queue_bytes)) + netif_wake_queue (net_device); + + break; + + case -EINVAL: + case -EUNATCH: + TRACE_MSG1(NTT,"not attached, send failed: %d", rc); + printk(KERN_ERR"%s: not attached, send failed: %d\n", __FUNCTION__, rc); + npd->stats.tx_errors++; + npd->stats.tx_carrier_errors++; + netif_wake_queue(net_device); + break; + + case -ENOMEM: + TRACE_MSG1(NTT,"no mem, send failed: %d", rc); + printk(KERN_ERR"%s: no mem, send failed: %d\n", __FUNCTION__, rc); + npd->stats.tx_errors++; + npd->stats.tx_fifo_errors++; + netif_wake_queue(net_device); + break; + + case -ECOMM: + TRACE_MSG2(NTT,"comm failure, send failed: %d %p", rc, net_device); + printk(KERN_ERR"%s: comm failure, send failed: %d %p\n", __FUNCTION__, rc, net_device); + // Leave the IF queue stopped. + npd->stats.tx_dropped++; + break; + + } + if (0 != rc) { + dev_kfree_skb_any (skb); + // XXX this is what we should do, blows up on some 2.4.20 kernels + // return(NET_XMIT_DROP); + return(0); + } + + return 0; +} + +/* + * network_hard_start_xmit - start sending an skb. Called by the OS network layer. + * + * Return non-zero (1) if busy. QQSV - this code always returns 0. + */ +STATIC int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device) +{ + int rc; + down(&usbd_network_sem); + rc = net_os_start_xmit(skb,net_device); + up(&usbd_network_sem); + return rc; +} + + +/* network_do_ioctl - perform an ioctl call + * + * Carries out IOCTL commands for the specified network device. + * + * rp Points to an ifreq structure containing the IOCTL parameter(s). + * cmd The IOCTL command. + * + * Returns non-zero for failure. + */ +STATIC int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd) +{ + return -ENOIOCTLCMD; +} + +//_________________________________________________________________________________________________ + +struct net_device Network_net_device = { + get_stats: network_get_stats, + tx_timeout: network_tx_timeout, + do_ioctl: network_do_ioctl, + set_config: network_set_config, + set_mac_address: network_set_mac_addr, + hard_start_xmit: network_hard_start_xmit, + change_mtu: network_change_mtu, + init: network_init, + uninit: network_uninit, + open: network_open, + stop: network_stop, + priv: &Usb_network_private, +}; + +//_________________________________________________________________________________________________ + +#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG +/* sock_ioctl - perform an ioctl call to inet device + */ +int sock_ioctl(u32 cmd, struct ifreq *ifreq) +{ + int rc = 0; + mm_segment_t save_get_fs = get_fs(); + TRACE_MSG1(NTT, "cmd: %x", cmd); + set_fs(get_ds()); + rc = devinet_ioctl(cmd, ifreq); + set_fs(save_get_fs); + return rc; +} + +/* sock_addr - setup a socket address for specified interface + */ +static int sock_addr(char * ifname, u32 cmd, u32 s_addr) +{ + struct ifreq ifreq; + struct sockaddr_in *sin = (void *) &(ifreq.ifr_ifru.ifru_addr); + + TRACE_MSG2(NTT, "ifname: %s addr: %x", ifname, ntohl(s_addr)); + + memset(&ifreq, 0, sizeof(ifreq)); + strcpy(ifreq.ifr_ifrn.ifrn_name, ifname); + + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = s_addr; + + return sock_ioctl(cmd, &ifreq); +} + + +/* sock_flags - set flags for specified interface + */ +static int sock_flags(char * ifname, u16 oflags, u16 sflags, u16 rflags) +{ + int rc = 0; + struct ifreq ifreq; + + TRACE_MSG4(NTT, "ifname: %s oflags: %x s_flags: %x r_flags: %x", ifname, oflags, sflags, rflags); + + memset(&ifreq, 0, sizeof(ifreq)); + strcpy(ifreq.ifr_ifrn.ifrn_name, ifname); + + oflags |= sflags; + oflags &= ~rflags; + ifreq.ifr_flags = oflags; + + TRACE_MSG1(NTT, "-> ifr_flags: %x ", ifreq.ifr_flags); + + THROW_IF ((rc = sock_ioctl(SIOCSIFFLAGS, &ifreq)), error); + + TRACE_MSG1(NTT, "<- ifr_flags: %x ", ifreq.ifr_flags); + + CATCH(error) { + TRACE_MSG1(NTT, "ifconfig: cannot get/set interface flags (%d)", rc); + return rc; + } + return rc; +} + +/* network_attach - configure interface + * + * This will use socket calls to configure the interface to the supplied + * ip address and make it active. + */ +STATIC int network_attach(struct usb_network_private *npd, u32 ip, u32 mask, u32 router, int attach) +{ + int err = 0; + + if (attach) { + TRACE_MSG5(NTT, "ATTACH npd: %x ip: %08x mask: %08x router: %08x attach: %d", npd, ip, mask, router, attach); + u16 oflags = (npd && npd->net_dev) ? npd->net_dev->flags : 0; + + if (npd->net_dev) + memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); + + /* setup ip address, netwask, and broadcast address */ + if (ip) { + THROW_IF ((err = sock_addr("usbl0", SIOCSIFADDR, htonl(ip))), error); + if (mask) { + THROW_IF ((err = sock_addr("usbl0", SIOCSIFNETMASK, htonl(mask))), error); + THROW_IF ((err = sock_addr("usbl0", SIOCSIFBRDADDR, htonl(ip | ~mask))), error); + } + /* bring the interface up */ + THROW_IF ((err = sock_flags("usbl0", oflags, IFF_UP, 0)), error); + } + } + else { + TRACE_MSG5(NTT, "DETACH npd: %x ip: %08x mask: %08x router: %08x attach: %d", npd, ip, mask, router, attach); + u16 oflags = (npd && npd->net_dev) ? npd->net_dev->flags : 0; + /* bring the interface down */ + THROW_IF ((err = sock_flags("usbl0", oflags, 0, IFF_UP)), error); + } + + CATCH(error) { + TRACE_MSG1(NTT, "ifconfig: cannot configure interface (%d)", err); + return err; + } + return 0; +} + +/* network_config - configure network + * + * Check connected status and load/unload as appropriate. + * + */ +STATIC void config_bh (void *data) +{ + struct usb_network_private *npd = (struct usb_network_private *) data; + struct usbd_function_instance *function = npd->function; + + TRACE_MSG5(NTT, "function: %x enabled: %x status: %x state: %x npd->config_status: %d", + function, + npd->flags & NETWORK_ENABLED, + usbd_get_device_status(function), + usbd_get_device_state(function), + npd->config_status); + + if ((npd->flags & NETWORK_ENABLED ) && (function && (USBD_OK == usbd_get_device_status(function)) + && (STATE_CONFIGURED == usbd_get_device_state(function)) + && ip_addr + )) + { + TRACE_MSG2(NTT,"BUS state: %d status: %d", usbd_get_device_state(function), usbd_get_device_status(function)); + if (config_attached != npd->config_status) { + TRACE_MSG0(NTT,"ATTACH"); + npd->config_status = config_attached; + network_attach (npd, ip_addr, network_mask, router_ip, 1); + } + return; + } + + if (config_detached != npd->config_status) { + TRACE_MSG0(NTT,"DETACH"); + npd->config_status = config_detached; + network_attach (npd, ip_addr, network_mask, router_ip, 0); + } + //_MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); +} + +/* + * net_os_config - schedule a call to config bottom half + */ +void net_os_config(struct usb_network_private *npd) +{ + //_MOD_INC_USE_COUNT; + TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); + if (!SCHEDULE_WORK(npd->config_bh)) { + //_MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); + } +} +#else /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ +void net_os_config(struct usb_network_private *npd) +{ + TRACE_MSG0(NTT, "NOT CONFIGURED"); + // Do nothing, config not configured. +} +#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ +//______________________________________ Hotplug Functions ________________________________________ + +#ifdef CONFIG_OTG_NETWORK_HOTPLUG + +#define AGENT "network_fd" + +/* hotplug_attach - call hotplug + */ +STATIC int hotplug_attach(struct usb_network_private *npd, u32 ip, u32 mask, u32 router, int attach) +{ + static int count = 0; + char *argv[3]; + char *envp[10]; + char ifname[20+12 + IFNAMSIZ]; + int i; + char count_str[20]; + + RETURN_EINVAL_IF(!hotplug_path[0]); + + argv[0] = hotplug_path; + argv[1] = AGENT; + argv[2] = 0; + + sprintf (ifname, "INTERFACE=%s", npd->net_dev->name); + sprintf (count_str, "COUNT=%d", count++); + + i = 0; + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[i++] = ifname; + + + if (attach) { + unsigned char *cp; + char ip_str[20+32]; + char mask_str[20+32]; + char router_str[20+32]; + u32 nh; + + nh = htonl(ip); + cp = (unsigned char*) &nh; + sprintf (ip_str, "IP=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); + + nh = htonl(mask); + cp = (unsigned char*) &nh; + sprintf (mask_str, "MASK=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); + + nh = htonl(router); + cp = (unsigned char*) &nh; + sprintf (router_str, "ROUTER=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); + + //TRACE_MSG3(NTT, "attach %s %s %s", ifname, ip_str, count_str); + + envp[i++] = "ACTION=attach"; + envp[i++] = ip_str; + envp[i++] = mask_str; + envp[i++] = router_str; + + TRACE_MSG3(NTT, "attach ip: %08x mask: %08x router: %08x", ip, mask, router); + } + else { + //TRACE_MSG2(NTT,"detach %s %s", ifname, count_str); + envp[i++] = "ACTION=detach"; + TRACE_MSG3(NTT, "detach ip: %08x mask: %08x router: %08x", ip, mask, router); + } + + envp[i++] = count_str; + envp[i++] = 0; + + return call_usermodehelper (argv[0], argv, envp); +} + + +/* hotplug_bh - bottom half handler to call hotplug script to signal ATTACH or DETACH + * + * Check connected status and load/unload as appropriate. + * + * It should not be possible for this to be called more than once at a time + * as it is only called via schedule_task() which protects against a second + * invocation. + */ +STATIC void hotplug_bh (void *data) +{ + //struct usb_network_private *network_private = &Usb_network_private; + struct usb_network_private *npd = (struct usb_network_private *) data; + struct usbd_function_instance *function = NULL; + + UNLESS (npd) { + //_MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "NPD NULL DEC: %d", MOD_IN_USE); + return; + } + + function = npd->function; + + TRACE_MSG2(NTT, "function: %x npd->hotplug_status: %d", function, npd->hotplug_status); + + + if ((npd->flags & NETWORK_ENABLED ) && (function && (USBD_OK == usbd_get_device_status(function)) + && (STATE_CONFIGURED == usbd_get_device_state(function)))) + { + TRACE_MSG2(NTT,"BUS state: %d status: %d", usbd_get_device_state(function), usbd_get_device_status(function)); + if (hotplug_attached != npd->hotplug_status) { + TRACE_MSG0(NTT,"ATTACH"); + npd->hotplug_status = hotplug_attached; + hotplug_attach (npd, ip_addr, network_mask, router_ip, 1); + } + return; + } + + if (hotplug_detached != npd->hotplug_status) { + TRACE_MSG0(NTT,"DETACH"); + npd->hotplug_status = hotplug_detached; + hotplug_attach (npd, ip_addr, network_mask, router_ip, 0); + } + //_MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); +} + +/* + * net_os_hotplug - schedule a call to hotplug bottom half + */ +void net_os_hotplug(struct usb_network_private *npd) +{ + //_MOD_INC_USE_COUNT; + TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); + if (!SCHEDULE_WORK(npd->hotplug_bh)) { + //_MOD_DEC_USE_COUNT; + TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); + } +} +#else +void net_os_hotplug(struct usb_network_private *npd) +{ + TRACE_MSG0(NTT, "NOT CONFIGURED"); + // Do nothing, hotplug not configured. +} +#endif /* CONFIG_OTG_NETWORK_HOTPLUG */ + +//_________________________________________________________________________________________________ + +/* network_create - create and initialize network private structure + * + * Returns non-zero for failure. + */ +STATIC int network_create(struct usb_network_private *npd) +{ + TRACE_MSG0(NTT,"entered"); + + // Set some fields to generic defaults and register the network device with the kernel networking code + + memset(&npd->stats, 0, sizeof npd->stats); + + ether_setup(npd->net_dev); + RETURN_EINVAL_IF (register_netdev(npd->net_dev)); + npd->flags |= NETWORK_REGISTERED; + + netif_stop_queue(npd->net_dev); + netif_carrier_off(npd->net_dev); + npd->net_dev->flags &= ~IFF_UP; + + npd->flags |= NETWORK_CREATED; + + npd->maxtransfer = MAXFRAMESIZE + 4 + 64; + + TRACE_MSG0(NTT,"finis"); + return 0; +} + +/* network_destroy - destroy network private struture + * + * Destroys the network interface referenced by the global variable @c Network_net_device. + */ +STATIC void network_destroy(struct usb_network_private *npd) +{ + if (npd->flags & NETWORK_REGISTERED) { + netif_stop_queue(npd->net_dev); + netif_carrier_off(npd->net_dev); + unregister_netdev(npd->net_dev); + } + npd->flags = 0; +} + + +//______________________________________module_init and module_exit________________________________ + +#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG + +/*! network_proc_read_ip - implement proc file system read. + * Standard proc file system read function. + */ +static ssize_t network_proc_read_ip (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + char *bp; + int index = (*pos)++; + + RETURN_ZERO_IF(index); + + // get a page, max 4095 bytes of data... + RETURN_ENOMEM_UNLESS ((page = GET_KERNEL_PAGE())); + + bp = (char *) page; + + len = sprintf(bp, "%d.%d.%d.%d\n", + (router_ip >> 24) & 0xff, + (router_ip >> 16) & 0xff, + (router_ip >> 8) & 0xff, + (router_ip) & 0xff + ); + + if (copy_to_user(buf, (char *) page, len)) + len = -EFAULT; + + free_page (page); + return len; +} + +static struct file_operations otg_message_proc_switch_functions = { + read: network_proc_read_ip, +}; + +#endif + + +/* network_modinit - driver intialization + * + * Returns non-zero for failure. + */ +STATIC int network_modinit (void) +{ + // Pickup and link together the various global structs. + struct usb_network_private *npd = &Usb_network_private; + #ifdef CONFIG_PROC_FS + struct proc_dir_entry *p; + #endif /* CONFIG_PROC_FS */ + npd->fd_ops = &net_fd_usb_ops; + npd->params = ¶ms; + npd->net_dev = &Network_net_device; + npd->net_dev->priv = (void *) npd; +#ifdef CONFIG_OTG_NETWORK_ALLOW_SETID + params.vendor_id = vendor_id; + params.product_id = product_id; +#endif + params.local_mac_address_str = local_mac_address_str; + params.remote_mac_address_str = remote_mac_address_str; +#ifdef CONFIG_OTG_NETWORK_EP0TEST + params.ep0test = ep0test; +#endif + params.zeroconf = zeroconf; +#if defined(CONFIG_OTG_NETWORK_CDC) + params.cdc = cdc; +#endif +#ifdef CONFIG_OTG_NETWORK_BASIC + params.basic = basic; +#endif +#ifdef CONFIG_OTG_NETWORK_BASIC2 + params.basic2 = basic2; +#endif +#ifdef CONFIG_OTG_NETWORK_SAFE + params.safe = safe; +#endif +#ifdef CONFIG_OTG_NETWORK_BLAN + params.blan = blan; +#endif + // That should be it for reference to globals except for network_modexit(). + + init_waitqueue_head(&usb_netif_wq); + PREPARE_WORK_ITEM(npd->notification_bh, notification_bh,npd); +#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG + PREPARE_WORK_ITEM(npd->config_bh, config_bh, npd); + #ifdef CONFIG_PROC_FS + if ((p = create_proc_entry ("network_ip", 0666, 0))) + p->proc_fops = &otg_message_proc_switch_functions; + #endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ +#ifdef CONFIG_OTG_NETWORK_HOTPLUG + PREPARE_WORK_ITEM(npd->hotplug_bh, hotplug_bh, npd); +#endif /* CONFIG_OTG_NETWORK_HOTPLUG */ + + if (0 != (*(npd->fd_ops->initialize_usb_part))(npd,"linux network_fd")) { + // If the usb part fails, there are no trace functions. + return -EINVAL; + } + + memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); + /* QQSV - this is very strange, since npd->network type can have + a number of different values. setting zeroconf is not going to + be quite as obvious as it might at first seem. */ + strncpy(npd->net_dev->name, ((npd->network_type == zeroconf) ? "usbz0" : + (network_blan ? "usbl0" : "usbb0")), 6); + + if (network_create(npd)) { + (*(npd->fd_ops->terminate_usb_part))(npd); + return -EINVAL; + } + + return 0; +} + +//_____________________________________________________________________________ + +/* network_modexit - driver exit + * + * Cleans up the module. Deregisters the function driver and destroys the network object. + */ +STATIC void network_modexit (void) +{ + struct usb_network_private *npd = &Usb_network_private; + + #ifdef CONFIG_PROC_FS + remove_proc_entry("network_ip", NULL); + #endif /* CONFIG_PROC_FS */ + + while (PENDING_WORK_ITEM(npd->notification_bh)) { + // TRACE_MSG pointless here, module will be gon before we can look at it. + printk(KERN_ERR"%s: waiting for notificationhotplug bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } +#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG + while (PENDING_WORK_ITEM(npd->config_bh)) { + printk(KERN_ERR"%s: waiting for config bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } +#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ + +#ifdef CONFIG_OTG_NETWORK_HOTPLUG + while (PENDING_WORK_ITEM(npd->hotplug_bh)) { + printk(KERN_ERR"%s: waiting for hotplug bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } +#endif + /* Which order to tear down the two parts? No matter which goes first, + the other could still try to send data through the now-torn-down part, + so go with usb last, because it has the otg_trace cleanup. */ + network_destroy(npd); + if (npd->fd_ops) + (*(npd->fd_ops->terminate_usb_part))(npd); +} + +//_________________________________________________________________________________________________ + +module_init (network_modinit); +module_exit (network_modexit); + diff -uNr linux/drivers/no-otg/functions/network/net-os.h linux/drivers/otg/functions/network/net-os.h --- linux/drivers/no-otg/functions/network/net-os.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/net-os.h 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,107 @@ +/* + * otg/functions/network/net-os.h + * + * Copyright (c) 2003, 2004 Belcarra + * + * By: + * Tom Rushworth , + * Stuart Lynne , + * Bruce Balden + */ + +/*! + * @file otg/functions/network/net-os.h + * @brief Structures and function definitions required for implementation + * of the OS specific upper edge (network interface) for the Network + * Function Driver. + * + * A USB network driver is composed of two pieces: + * 1) An OS specific piece that handles creating and operating + * a network device for the given OS. + * 2) A USB specific piece that interfaces either with the host + * usbcore layer, or with the device usbdcore layer. + * + * If the USB piece interfaces with the host usbcore layer you get + * a network class driver. If the USB piece interfaces with the usbdcore + * layer you get a network function driver. + * + * This file describes the functions exported by the various net-*-os.c + * files (implementing (1)) for use in net-fd.c (2). + * + * + * @ingroup NetworkFunction + */ + +#ifndef NET_OS_H +#define NET_OS_H 1 + +/* + * net_os_mutex_enter - enter mutex region + */ +extern void net_os_mutex_enter(struct usb_network_private *npd); + +/* + * net_os_mutex_exit - exit mutex region + */ +extern void net_os_mutex_exit(struct usb_network_private *npd); + +/* + * net_os_send_notification_later - schedule a callback to the USB part to send + * an INT notification. + */ +extern void net_os_send_notification_later(struct usb_network_private *npd); + +/* + * net_os_xmit_done - called from USB part when a transmit completes, good or bad. + * tx_rc is SEND_FINISHED_ERROR, SEND_CANCELLED or... + * buff_ctx is the pointer passed to fd_ops->start_xmit(). + * Return non-zero only if network does not exist, ow 0. + */ +extern int net_os_xmit_done(struct usbd_function_instance *function, void *buff_ctx, int tx_rc); + +/* + * + */ +extern void *net_os_alloc_buff(struct usb_network_private *npd, u8 **cp, int n); + +/* + * net_os_recv_buff - forward a received URB, or clean up after a bad one. + * buff_ctx == NULL --> just accumulate stats (count 1 bad buff) + * crc_bad != 0 --> count a crc error and free buff_ctx/skb + * trim --> amount to trim from valid buffer (i.e. shorten + * from amount requested in net_os_alloc_buff(..... n). + * This will be called from interrupt context. + */ +extern void net_os_recv_buff(struct usb_network_private *npd, void *buff_ctx, int crc_bad, int trim); + +/* + * net_os_config - schedule a network hotplug event if the OS supports hotplug. + */ +extern void net_os_config(struct usb_network_private *npd); + +/* + * net_os_hotplug - schedule a network hotplug event if the OS supports hotplug. + */ +extern void net_os_hotplug(struct usb_network_private *npd); + +/* + * + */ +extern void net_os_enable(struct usbd_function_instance *function); + +/* + * + */ +extern void net_os_disable(struct usbd_function_instance *function); + +/* + * + */ +extern void net_os_carrier_on(struct usb_network_private *npd); + +/* + * + */ +extern void net_os_carrier_off(struct usb_network_private *npd); + +#endif diff -uNr linux/drivers/no-otg/functions/network/network.h linux/drivers/otg/functions/network/network.h --- linux/drivers/no-otg/functions/network/network.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/network.h 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,348 @@ +/* + * otg/functions/network/network.h - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Tom Rushworth + * Chris Lynne + * Stuart Lynne + * Bruce Balden + * + * 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. + */ +/*! + * @defgroup NetworkFunction Network + * @ingroup functiongroup + */ +/*! + * @file otg/functions/network/network.h + * @brief Network Function Driver private defines + * + * This defines the internal and private structures required + * by the Network Function Driver. + * + * @ingroup NetworkFunction + */ + +/*! This is a test + */ +#ifndef NETWORK_FD_H +#define NETWORK_FD_H 1 + +#include + +extern otg_tag_t network_fd_trace_tag; + +// Some platforms need to get rid of "static" when using GDB or looking at ksyms +//#define STATIC +#define STATIC static + +typedef enum network_encapsulation { + simple_net, simple_crc, +} network_encapsulation_t; + +typedef enum network_config_status { + config_unkown, + config_attached, + config_detached +} network_config_status_t; + +typedef enum network_hotplug_status { + hotplug_unkown, + hotplug_attached, + hotplug_detached +} network_hotplug_status_t; + +typedef enum network_type { + network_unknown, + network_blan, + network_safe, + network_cdc, + network_basic, + network_basic2, + network_eem, +} network_type_t; + +struct usb_network_params { + // enabling switchs + u32 cdc; + u32 basic; + u32 basic2; + u32 safe; + u32 blan; + // capability flags + u32 cdc_capable; + u32 basic_capable; + u32 basic2_capable; + u32 safe_capable; + u32 blan_capable; + // overrides + u32 vendor_id; + u32 product_id; + char *local_mac_address_str; + char *remote_mac_address_str; + // other switches + u32 ep0test; + u32 zeroconf; +}; + +#define CONFIG_OTG_NETWORK_ALLOW_SETID 1 + +#define NETWORK_CREATED 0x01 +#define NETWORK_REGISTERED 0x02 +#define NETWORK_DESTROYING 0x04 +#define NETWORK_ENABLED 0x08 +#define NETWORK_ATTACHED 0x10 +#define NETWORK_OPEN 0x20 + + +#define MCCI_ENABLE_CRC 0x03 +#define BELCARRA_GETMAC 0x04 + +#define BELCARRA_SETTIME 0x04 +#define BELCARRA_SETIP 0x05 +#define BELCARRA_SETMSK 0x06 +#define BELCARRA_SETROUTER 0x07 +#define BELCARRA_SETDNS 0x08 +#define BELCARRA_PING 0x09 +#define BELCARRA_SETFERMAT 0x0a +#define BELCARRA_HOSTNAME 0x0b + + +// RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT +#define RFC868_OFFSET_TO_EPOCH 0x83AA7E80 + +struct usb_network_private { + + struct net_device_stats stats; /* network device statistics */ + + int flags; + struct net_device *net_dev; + struct usbd_function_instance *function; + struct usbd_function_driver *function_driver; + struct usb_network_params *params; + struct net_usb_services *fd_ops; + unsigned int maxtransfer; + rwlock_t rwlock; + + network_config_status_t config_status; + network_hotplug_status_t hotplug_status; + network_type_t network_type; + + int state; + + int mtu; + int crc; +#if defined(CONFIG_OTG_NETWORK_BLAN_FERMAT) + int fermat; +#endif + + unsigned int stopped; + unsigned int restarts; + + unsigned int max_queue_entries; + unsigned int max_queue_bytes; + + unsigned int queued_entries; + unsigned int queued_bytes; + + time_t avg_queue_entries; + + time_t jiffies; + unsigned long samples; + + int have_interrupt; + + int data_notify; + + struct usbd_urb *int_urb; + + network_encapsulation_t encapsulation; + + struct WORK_ITEM notification_bh; +#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG + struct WORK_ITEM config_bh; +#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ +#ifdef CONFIG_HOTPLUG + struct WORK_ITEM hotplug_bh; +#endif /* CONFIG_HOTPLUG */ + __u8 local_dev_addr[ETH_ALEN]; + __u8 remote_dev_addr[ETH_ALEN]; +}; + +// XXX this needs to be co-ordinated with rndis.c maximum's +#define MAXFRAMESIZE 2000 + +#if !defined(CONFIG_OTG_MANUFACTURER) + #define CONFIG_OTG_MANUFACTURER "Belcarra" +#endif + + +#if !defined(CONFIG_OTG_SERIAL_NUMBER_STR) + #define CONFIG_OTG_SERIAL_NUMBER_STR "" +#endif + +/* + * Lineo specific + */ + +#define VENDOR_SPECIFIC_CLASS 0xff +#define VENDOR_SPECIFIC_SUBCLASS 0xff +#define VENDOR_SPECIFIC_PROTOCOL 0xff + +/* + * Lineo Classes + */ +#define LINEO_CLASS 0xff + +#define LINEO_SUBCLASS_BASIC_NET 0x01 +#define LINEO_SUBCLASS_BASIC_SERIAL 0x02 + +/* + * Lineo Protocols + */ +#define LINEO_BASIC_NET_CRC 0x01 +#define LINEO_BASIC_NET_CRC_PADDED 0x02 + +#define LINEO_BASIC_SERIAL_CRC 0x01 +#define LINEO_BASIC_SERIAL_CRC_PADDED 0x02 + + +/* + * endpoint and interface indexes + */ +#define BULK_OUT 0x00 +#define BULK_IN 0x01 +#define INT_IN 0x02 +#define ENDPOINTS 0x03 + +#define COMM_INTF 0x00 +#define DATA_INTF 0x01 + + +/* bmDataCapabilities */ +#define BMDATA_CRC 0x01 +#define BMDATA_PADBEFORE 0x02 +#define BMDATA_PADAFTER 0x04 +#define BMDATA_FERMAT 0x08 +#define BMDATA_HOSTNAME 0x10 + +/* bmNetworkCapabilities */ +#define BMNETWORK_SET_PACKET_OK 0x01 +#define BMNETWORK_NONBRIDGED 0x02 +#define BMNETWORK_DATA_NOTIFY_OK 0x04 + + +/* + * BLAN Data Plane + */ +//#define CONFIG_OTG_NETWORK_PADBYTES 8 +//#define CONFIG_OTG_NETWORK_PADAFTER 1 +//#undef CONFIG_OTG_NETWORK_PADBEFORE +//#define CONFIG_OTG_NETWORK_CRC 1 + + +extern __u8 network_requested_endpoints[ENDPOINTS+1]; +extern __u16 network_requested_transferSizes[ENDPOINTS+1]; +extern struct usb_network_private Usb_network_private; +extern __u8 local_dev_addr[ETH_ALEN]; +extern __u8 remote_dev_addr[ETH_ALEN]; + +extern struct usbd_function_operations net_fd_function_ops; + +struct usbd_class_safe_networking_mdlm_descriptor { + __u8 bFunctionLength; // 0x06 + __u8 bDescriptorType; // 0x24 + __u8 bDescriptorSubtype; // 0x13 + __u8 bGuidDescriptorType; // 0x00 + __u8 bmNetworkCapabilities; + __u8 bmDataCapabilities; +} __attribute__ ((packed)); + +struct usbd_class_blan_networking_mdlm_descriptor { + __u8 bFunctionLength; // 0x07 + __u8 bDescriptorType; // 0x24 + __u8 bDescriptorSubtype; // 0x13 + __u8 bGuidDescriptorType; // 0x01 + __u8 bmNetworkCapabilities; + __u8 bmDataCapabilities; + __u8 bPad; +} __attribute__ ((packed)); + + + + +#define NETWORK_CREATED 0x01 +#define NETWORK_REGISTERED 0x02 +#define NETWORK_DESTROYING 0x04 +#define NETWORK_ENABLED 0x08 +#define NETWORK_ATTACHED 0x10 +#define NETWORK_OPEN 0x20 + + +#define MCCI_ENABLE_CRC 0x03 +#define BELCARRA_GETMAC 0x04 + +#define BELCARRA_SETTIME 0x04 +#define BELCARRA_SETIP 0x05 +#define BELCARRA_SETMSK 0x06 +#define BELCARRA_SETROUTER 0x07 +#define BELCARRA_SETDNS 0x08 +#define BELCARRA_PING 0x09 +#define BELCARRA_SETFERMAT 0x0a +#define BELCARRA_HOSTNAME 0x0b +#define BELCARRA_DATA_NOTIFY 0x0c + +#define RFC868_OFFSET_TO_EPOCH 0x83AA7E80 // RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT + + +extern __u32 *network_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) ^ network_crc32_table[((val) ^ (c)) & 0xff]) + + +#if 0 +#if !defined(CONFIG_OTG_NETWORK_CRC_DUFF4) && !defined(CONFIG_OTG_NETWORK_CRC_DUFF8) +/** + * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so. + * + * dst Pointer to the destination memory area. + * src Pointer to the source memory area. + * len Number of bytes to copy. + * val Starting value for the CRC FCS. + * + * Returns Final value of the CRC FCS. + * + * @sa crc32_pad + */ +static __u32 __inline__ crc32_copy (__u8 *dst, __u8 *src, int len, __u32 val) +{ + for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = *src++)); + return val; +} + +#endif /* DUFFn */ +#endif + + + + +#endif /* NETWORK_FD_H */ diff -uNr linux/drivers/no-otg/functions/network/safe.c linux/drivers/otg/functions/network/safe.c --- linux/drivers/no-otg/functions/network/safe.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/functions/network/safe.c 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,362 @@ +/* + * otg/functions/network/safe.c - Network Function Driver + * + * Copyright (c) 2002, 2003, 2004 Belcarra + * + * By: + * Thomas Rushworth + * Stuart Lynne + * Bruce Balden + * + * 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. + * + * + * The SAFE network driver implements the SAFE protocol descriptors. + * + * The SAFE protocol is suitable for infrastructure devices that + * are implementing a bridged or routed connection betwen an + * external network and the USB Host. + * + * + */ +/*! + * @file otg/functions/network/safe.c + * @brief This file implements the required descriptors to implement + * a SAFE network device with one interface. + * + * @ingroup NetworkFunction + */ + +#include +#include + + +#include +#include +#include +#include + +#include +#include + +#include "network.h" + +#define NTT network_fd_trace_tag + +#ifdef CONFIG_OTG_NETWORK_SAFE +/* USB SAFE Configuration ********************************************************** */ + +/* + * SAFE Ethernet Configuration + */ + +/* Communication Interface Class descriptors + */ + +/*! BULK OUT data endpoint descriptor + */ +static struct usbd_endpoint_descriptor safe_data_1 = { + bLength: 0x07, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIR_OUT, + bmAttributes: BULK, + wMaxPacketSize: __constant_cpu_to_le16(0), + bInterval: 0, +}; +/*! BULK IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor safe_data_2 = +{ + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: BULK, + wMaxPacketSize: __constant_cpu_to_le16(0), + bInterval: 0x0, +}; + +/*! INTERRUPT IN data endpoint descriptor + */ +static struct usbd_endpoint_descriptor safe_comm_1 = +{ + bLength:sizeof(struct usbd_endpoint_descriptor), + bDescriptorType:USB_DT_ENDPOINT, + bEndpointAddress:USB_DIR_IN, + bmAttributes: INTERRUPT, + wMaxPacketSize: __constant_cpu_to_le16(0), + bInterval: 0xa, +}; + +#if 0 +static struct usbd_endpoint_descriptor *basic_default[] = { &basic_data_1, + &basic_data_2, + &basic_comm_1, }; +u8 basic_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; +#endif + +#if 0 +static u8 safe_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; +static u8 safe_class_2[] = { 0x15, CS_INTERFACE, USB_ST_MDLM, 0x00, 0x01, /* bcdVersion, bcdVersion */ + + 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, /* bGUID */ + 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, /* bGUID */ }; + +static u8 safe_class_3[] = { 0x06, CS_INTERFACE, USB_ST_MDLMD, 0x00, 0x00, 0x00, /* bDetailData */ }; + +static u8 safe_class_4[] = { 0x0d, CS_INTERFACE, USB_ST_ENF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */ + 0x00, 0x00, 0x00 , }; +#else +static struct usbd_class_header_function_descriptor safe_class_1 = { + bFunctionLength:0x05, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: USB_ST_HEADER, + bcdCDC: __constant_cpu_to_le16(0x0110), //0x10, 0x01, +}; + +static struct usbd_class_mdlm_descriptor safe_class_2 = { + bFunctionLength: 0x15, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: USB_ST_MDLM, + bcdVersion: __constant_cpu_to_le16( 0x0100), //0x00, 0x01, + bGUID: { + 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, /* bGUID */ + 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, /* bGUID */ }, +}; + +static struct usbd_class_safe_descriptor safe_class_3 = { + bFunctionLength: 0x06, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: USB_ST_MDLMD, + bGuidDescriptorType: 0x00, + bmNetworkCapabilities: 0x00, + bmDataCapabilities: 0x00, /* bDetailData */ +}; + +static struct usbd_class_ethernet_networking_descriptor safe_class_4 = { + bFunctionLength: 0x0d, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: USB_ST_ENF, + iMACAddress: 0x0, + bmEthernetStatistics: __constant_cpu_to_le32(0x00), + wMaxSegmentSize: __constant_cpu_to_le16(0x5ea), + wNumberMCFilters: __constant_cpu_to_le16(0x00), + bNumberPowerFilters: 0, +}; + +#endif + +/*! Endpoint descriptor list + */ +static struct usbd_endpoint_descriptor *safe_alt_endpoints[] = { + &safe_data_1, &safe_data_2, &safe_comm_1, }; + +static struct usbd_generic_class_descriptor *safe_comm_class_descriptors[] = { + (struct usbd_generic_class_descriptor *) &safe_class_1, + (struct usbd_generic_class_descriptor *) &safe_class_2, + (struct usbd_generic_class_descriptor *) &safe_class_3, + (struct usbd_generic_class_descriptor *) &safe_class_4, }; + +u8 safe_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; + +/*! Data Interface Descriptor + */ +static struct usbd_interface_descriptor safe_alternate_descriptor = { + bLength: 0x09, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0x00, + bAlternateSetting: 0x00, + bNumEndpoints: sizeof(safe_alt_endpoints)/sizeof(struct usbd_endpoint_descriptor *), + bInterfaceClass: COMMUNICATIONS_INTERFACE_CLASS, + bInterfaceSubClass: COMMUNICATIONS_MDLM_SUBCLASS, + bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, + iInterface: 0x00, +}; + +/*! Data Alternate Interface Description List + */ +static struct usbd_alternate_description safe_alternate_descriptions[] = { + { + iInterface: CONFIG_OTG_NETWORK_SAFE_INTF, + interface_descriptor: &safe_alternate_descriptor, + classes: sizeof (safe_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), + class_list: safe_comm_class_descriptors, + endpoints:sizeof (safe_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list: safe_alt_endpoints, + endpoint_indexes: safe_alt_indexes, + }, +}; + +/* Interface descriptions and descriptors + */ +/*! Interface Description List + */ +static struct usbd_interface_description safe_interfaces[] = { + { + alternates:sizeof (safe_alternate_descriptions) / sizeof (struct usbd_alternate_description), + alternate_list:safe_alternate_descriptions, }, +}; + + +/* Configuration descriptions and descriptors + */ + +/*! Configuration Descriptor + */ +static struct usbd_configuration_descriptor safe_configuration_descriptor = { + bLength: 0x09, + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength: __constant_cpu_to_le16(0x0000), //0x00, 0x00, // wLength + bNumInterfaces:sizeof (safe_interfaces) / sizeof (struct usbd_interface_description), + bConfigurationValue:0x01, + iConfiguration: 0x00, // bConfigurationValue, iConfiguration + bmAttributes: 0, + bMaxPower:0, +}; + +/*! Configuration Description List + */ +struct usbd_configuration_description safe_description[] = { + { + iConfiguration: CONFIG_OTG_NETWORK_SAFE_DESC, + configuration_descriptor: &safe_configuration_descriptor, + }, +}; + +/* Device Description + */ +/*! Device Descriptor + */ +static struct usbd_device_descriptor safe_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_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), +}; + +#ifdef CONFIG_OTG_HIGH_SPEED +/*! High Speed Device Qualifier Descriptor + */ +static struct usbd_device_qualifier_descriptor safe_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 safe_endpoint_requests[ENDPOINTS+1] = { + { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, + { 0, }, +}; + +/*! OTG Descriptor + */ +static struct usbd_otg_descriptor safe_otg_descriptor = { + bLength : sizeof(struct usbd_otg_descriptor), + bDescriptorType: USB_DT_OTG, + bmAttributes: 0, +}; + +/*! Device Description + */ +struct usbd_device_description safe_device_description = { + device_descriptor: &safe_device_descriptor, +#ifdef CONFIG_OTG_HIGH_SPEED + device_qualifier_descriptor: &safe_device_qualifier_descriptor, +#endif /* CONFIG_OTG_HIGH_SPEED */ + otg_descriptor: &safe_otg_descriptor, + iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, + iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, +#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) + iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, +#endif +}; + +/*! safe_init + * @param function The function instance + */ +void safe_init (struct usbd_function_instance *function) +{ + struct usbd_class_ethernet_networking_descriptor *ethernet; + int len = 0; + char buf[255]; + + buf[0] = 0; + + safe_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; + + // Update the iMACAddress field in the ethernet descriptor + { + char address_str[14]; + snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x", + remote_dev_addr[0], remote_dev_addr[1], remote_dev_addr[2], + remote_dev_addr[3], remote_dev_addr[4], remote_dev_addr[5]); + if ((ethernet = &safe_class_4)) { + if (ethernet->iMACAddress) { + usbd_free_string_descriptor(ethernet->iMACAddress); + } + ethernet->iMACAddress = usbd_alloc_string(address_str); + } + } + +#ifdef CONFIG_OTG_NETWORK_SAFE_PADBEFORE + safe_class_3.bmNetworkCapabilities |= BMDATA_PADBEFORE; + len += sprintf(buf + len, "PADBEFORE: %02x ", safe_class_3.bmNetworkCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_SAFE_CRC + safe_class_3.bmNetworkCapabilities |= BMDATA_CRC; + len += sprintf(buf + len, "CRC: %02x ", safe_class_3.bmNetworkCapabilities); +#endif +#ifdef CONFIG_OTG_NETWORK_SAFE_NONBRIDGED + safe_class_3.bmNetworkCapabilities |= BMNETWORK_NONBRIDGED; + len += sprintf(buf + len, "NONBRIDGE: %02x ",safe_class_3.bmDataCapabilities); +#endif + if (strlen(buf)) + TRACE_MSG1(NTT,"%s", buf); + +} + +/*! function driver description + */ +struct usbd_function_driver safe_function_driver = { + name: "network-SAFE", + fops: &net_fd_function_ops, + device_description: &safe_device_description, + bNumConfigurations: sizeof (safe_description) / sizeof (struct usbd_configuration_description), + configuration_description: safe_description, + idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), + idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), + bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), + bNumInterfaces:sizeof (safe_interfaces) / sizeof (struct usbd_interface_description), + interface_list:safe_interfaces, + endpointsRequested: ENDPOINTS, + requestedEndpoints: safe_endpoint_requests, +}; +#endif /* CONFIG_OTG_NETWORK_SAFE */ + diff -uNr linux/drivers/no-otg/ocd/Config.in linux/drivers/otg/ocd/Config.in --- linux/drivers/no-otg/ocd/Config.in 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/Config.in 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,15 @@ + +# +# Copyright (c) 2004 Belcarra +# +# + + mainmenu_option next_comment + comment 'OTG Host Controller Driver (HCD)' + dep_tristate ' USB Host Controller Driver' CONFIG_OTG_HCD $CONFIG_OTG_HOSTCORE + hex 'VendorID (hex value)' CONFIG_OTG_ROOTHUB_VENDORID "15ec" + hex 'ProductID (hex value)' CONFIG_OTG_ROOTHUB_PRODUCTID "f010" + hex 'ProductID (hex value)' CONFIG_OTG_ROOTHUB_BCDDEVICE "0100" + + + endmenu diff -uNr linux/drivers/no-otg/ocd/Makefile linux/drivers/otg/ocd/Makefile --- linux/drivers/no-otg/ocd/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/Makefile 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,44 @@ +# +# 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 := ocd_target.o + +subdir-$(CONFIG_OTG_AU1550) += au1x00 +subdir-$(CONFIG_OTG_AU1X00) += au1x00 +subdir-$(CONFIG_OTG_ISP1301_OMAP_H2) += isp1301 +subdir-$(CONFIG_OTG_ISP1301_MAINSTONE) += isp1301 + +subdir-$(CONFIG_OTG_MAX3353E_DB1550) += max3353e + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Bus Interface Drivers +ocd-obj-$(CONFIG_OTG_AU1550) += au1x00/au1x00_pcd_drv.o +ocd-obj-$(CONFIG_OTG_AU1X00) += au1x00/au1x00_pcd_drv.o + +# Platform +ocd-obj-$(CONFIG_OTG_ISP1301_OMAP_H2) += isp1301/isp1301_target.o +ocd-obj-$(CONFIG_OTG_ISP1301_MAINSTONE) += isp1301/isp1301_target.o + +ocd-obj-$(CONFIG_OTG_MAX3353E_DB1550) += max3353e/max3353e_target.o + +obj-y += $(ocd-obj-y) + +include $(TOPDIR)/Rules.make +EXTRA_CFLAGS += -Wno-format -Wall diff -uNr linux/drivers/no-otg/ocd/Makefile-l26 linux/drivers/otg/ocd/Makefile-l26 --- linux/drivers/no-otg/ocd/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/Makefile-l26 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,29 @@ +# +# Belcarra OTG - On-The-Go +# +# Copyright (c) 2004 Belcarra Technologies Corp + +EXTRA_CFLAGS += -Wno-format -Wall + +# Bus Interface Drivers +#subdir-$(CONFIG_OTG_AU1X00) += au1x00 +#subdir-$(CONFIG_OTG_LH7A400) += lh7a400 +#subdir-$(CONFIG_OTG_MX1) += mx1 +#subdir-$(CONFIG_OTG_MX2) += mx2 +#subdir-$(CONFIG_OTG_NC2280) += nc2280 +#subdir-$(CONFIG_OTG_PXA) += pxa +#subdir-$(CONFIG_OTG_SA1100) += sa1100 +#subdir-$(CONFIG_OTG_SMDK2500) += smdk2500 +#subdir-$(CONFIG_OTG_SUPERH) += superh +#subdir-$(CONFIG_OTG_SX2) += sx2 +#subdir-$(CONFIG_OTG_WMMX) += wmmx + +obj-m += scma11-evb/ +obj-m += omap/ +obj-m += isp1301/ + +obj-y += scma11-evb/ +obj-y += omap/ +obj-y += isp1301/ + + diff -uNr linux/drivers/no-otg/ocd/README linux/drivers/otg/ocd/README --- linux/drivers/no-otg/ocd/README 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/README 2006-09-01 21:41:30.000000000 +0200 @@ -0,0 +1,54 @@ +otg/pcd + + +Linux Build support: + + Makefile + Makefile-l24 + Makefile-l26 + + README + +PCD support files: + + otg-pcd/pcd.c + otg-pcd/pcd-init-l24.c + otg-pcd/pcd-ocd-init-l24.c + otg-pcd/tr-init-l24.c + +TCD support files: + + otg-tcd/tcd.c + otg-tcd/tcd-init-l24.c + +HCD support files; + + otg-hcd/hcd-init-l24.c + otg-hcd/hcd-l26.c + otg-hcd/hcd-rh.c + otg-hcd/hcd.c + +Architecture Specific Drivers: + + lh7a400 + mx1 + nc2280 + pxa + sa1100 + smdk2500 + superh + sx2 + +Device Specific Drivers: + + atlas + isp1301 + max3353e + +Platform Specific Drivers + + au1x00 + bvd + mx2 + omap + diff -uNr linux/drivers/no-otg/ocd/au1x00/Makefile linux/drivers/otg/ocd/au1x00/Makefile --- linux/drivers/no-otg/ocd/au1x00/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/au1x00/Makefile 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,111 @@ +# +# Makefile for the kernel USBD (device not host) drivers. +# +# Copyright (c) 2002 Belcarra + +# Subdirs. +# This is a bit complex, because some subdirs are for +# proprietary code, and are simply not present in a +# general distribution. + +# The all-CAPS *_DIRS get nuked in the new versions +# of Rules.make, so use only the subdir-* methods. +subdir-y := +subdir-m := +subdir-n := +subdir- := + +# The target object and module list name. + +O_TARGET := au1x00_pcd_drv.o + +# Objects that export symbols. + +export-objs := + +# Multipart objects. + +#au1x00_otg-objs := au1x00.o usbd-bi.o trace.o +db1550_tr-objs := tr-init-l24.o au1550.o pcd.o +db1550_dr-objs := tr-init-l24.o au1550.o pcd.o +#au1550_tr-objs := tr-init-l24.o au1550.o pcd.o +au1x00_tr-objs := tr-init-l24.o au1x00.o pcd.o + +# Optional parts of multipart objects. + +# Object file lists. + +#obj-y := usbd-bi.o +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_OTG_AU1550_DB1500_TR) += db1550_tr.o +obj-$(CONFIG_OTG_AU1550_DB1550_TR) += db1550_tr.o +obj-$(CONFIG_OTG_AU1550_DB1500_DR) += db1550_dr.o + +#obj-$(CONFIG_OTG_AU1550) += au1550_tr.o +obj-$(CONFIG_OTG_AU1X00) += au1x00_tr.o + +# Object files in subdirectories + + +# 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 + +OTG=$(TOPDIR)/drivers/otg +OCD_DIR=$(OTG)/ocd +INCLUDE_DIRS = -I$(OTG) +EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} +EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} + +I2C=$(OTG)/ocd/otg-i2c +PCD=$(OTG)/ocd/otg-pcd +vpath %.c $(USBDCORE_DIR) $(OCD_DIR) $(PCD) $(I2C) + + +# Link rules for multi-part drivers. + +db1550_tr.o: $(db1550_tr-objs) + $(LD) -r -o $@ $(db1550_tr-objs) + +db1550_dr.o: $(db1550_tr-objs) + $(LD) -r -o $@ $(au1550_tr-objs) + +#au1550_tr.o: $(au1550_tr-objs) +# $(LD) -r -o $@ $(au1550_tr-objs) + +au1x00_tr.o: $(au1x00_tr-objs) + $(LD) -r -o $@ $(au1x00_tr-objs) + +# dependencies: + +# local + + diff -uNr linux/drivers/no-otg/ocd/au1x00/NOTES.txt linux/drivers/otg/ocd/au1x00/NOTES.txt --- linux/drivers/no-otg/ocd/au1x00/NOTES.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/au1x00/NOTES.txt 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,62 @@ + +Notes + +1. EP0 - packetsize of 8 does not work, this means that PIO cannot be used for IN (transmit). +DMA must be used. Only 16, 32 and 64 are usable. + +Update: even though we couldn't program the endpoint size to 8, we just set it to 8 in the +device descriptor and things work ok. This works and we don't need or use DMA for either +direction for EP0. + +2. For each BULK transfer the first N packets must be sent with the full packetsize and the +last must be sent as a short packet. The packetsize must be set before enabling the DMA. + +3. For IN endpoints an interrupt will be generated for EVERY IN packet including ones that +will be NAK'd. To reduce overhead the interrupt should only be enabled when there is data +being sent and we need to know when it has been sent. + +Update: for new silicon this is not true, the UDC will interrupt less often and will NAK +until the interrupt is cleared. + +4. The AU1x00 auto-acks many setup commands and is very sensitive to latency of the irq +handler processing the interrupt and clearing the ep0 fifo. Like the pxa we need to spool the +requests and process later when we receive a request for data. There is a special test in +the bulk data receive handler to check if there are spooled requests to process. + +Update: this has been mostly mitigated by reducing all upper layer processing in recv_setup, +event_irq etc so that they complete in less than a milli-second. This simplifies things and +we can now pass UUT tests. + +5. There is a time sensitive problem in au_in_epn(). Without a udelay(8) sending large +packets will eventually stall interrupts. + +Update: this is not required for new silicon. + +6. When receiving data (Bulk OUT) the UDC will not start NAKing until and unless the receive +FIFO is full. This means that there is no reliable way to determine the end of one packet and +the beginning of the next if there is any undue latency in handling the interrupt for the +first packet. + +The Belcarra Windows and Macintosh network class drivers have an option that can be used +to pad all outgoing packets to a multiple of 8 bytes. This helps eliminate this problem. + +Update: this is not a problem with new silicon, it NAK's until the interrupt is cleared. + +8. The original AU1x00 UDC generated an interrupt for EVERY IN packet. If we do not have +any data we need to disable the interrupt for the endpoint to eliminate the overhead +for processing them. The interrupt must be re-enabled when DMA is finished and we +actually want to know that the endpoint has finished. + +Leaving the interrupt enabled also interfers with the DMA process. It is not apparant +why. + +Update: This has been fixed on the new silicon but it doesn't hurt to continue to do this. + +9. The DMA functions replace the equivalent routines in au1100_dma.h except that they pass +the actual struct dma_chan pointer instead of the channel number. The bounds checking and +table lookup to derive this information amounts to a substantial increase in code size and +latency which can simply be avoided by using the structure address directly and/or doing the +equivalent i/o directly. + +10. The Au1x00 does not support REMOTE WAKEUP + diff -uNr linux/drivers/no-otg/ocd/au1x00/au1550.c linux/drivers/otg/ocd/au1x00/au1550.c --- linux/drivers/no-otg/ocd/au1x00/au1550.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/au1x00/au1550.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,1451 @@ +/* + * otg/ocd/au1x00/au1x00.c -- USB Device Controller driver. + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + */ +/*! + * @file otg/ocd/au1x00/au1550.c + * @brief AU1X00 Header + * + * This file contains the private defines and structures for the AU1X00 + * Drivers. + * + * @ingroup AU1X00 + */ + + +#include +#include "au1x00.h" + +#include +#include +#include + +#if defined(CONFIG_SOC_AU1550) +#include +#else +#error "Wrong AU1X00_MDA" +#endif + +#include + +#ifdef CONFIG_MIPS_FREEHAND +#include +#include /* for reading serial number */ +#endif + + +#undef CHECK_LATENCY +#undef RECORD_LATENCY +#undef MAX_INTR_LOOP_STATS //10 +#if defined(MAX_INTR_LOOP_STATS) +static u32 interrupt_loop_stats[MAX_INTR_LOOP_STATS+1]; +#endif +#ifdef RECORD_LATENCY +#define CP0_COUNTS 50 +u32 cp0_counts[CP0_COUNTS]; +u32 cp0_record; +#endif +u32 cp0_count; +unsigned int udc_saw_bus_activity; +int au_halt_dma_expired; +u32 au1x00_inten; + +u8 au1x00_config_bulk[25] = { + 0x04, (USB_ENDPOINT_CONTROL << 4) | (EP0_PACKETSIZE & 0x380) >> 7, // 1 + (EP0_PACKETSIZE & 0x7F) << 1, 0x00, 0x00, + 0x24, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 6 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x02, + 0x34, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 11 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x03, + 0x44, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 16 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x04, + 0x54, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 21 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x05, +}; + +/* ********************************************************************************************* */ +/* Note - The endpoints names are confusing. To simplify mapping of the interrupt request lines + * we use a numbering of the physical endpoints of 0-5, which also matches the fifo numbering + * (see the config block). + * + * Physical FIFO Name Direction Logical + * 0 0 EP0 OUT 0 + * 1 1 EP0 IN 0 + * 2 2 EP1 IN 81 + * 3 3 EP2 IN 82 + * 4 4 EP3 OUT 3 + * 5 5 EP4 OUT 4 + * + * The ep_regs array maps a physical endpoint number to the registers required to access the udc + * for that endpoint. Note that ep0 is always accessed via 0. The epl2p array maps the logical + * addresses back to the physical number. The epp2l array maps the physical number to the + * logical endpoint address + */ +struct ep_regs ep_regs[6] = { + + { rd: USBD_EP0RD, rds: USBD_EP0RDSTAT, /*rx_id: DMA_ID_USBDEV_EP0_RX,*/ cs: USBD_EP0CS, rx_str: "EP0 OUT RD", + wr: USBD_EP0WR, wrs: USBD_EP0WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX0, tx_str: "EP0 IN WR",}, + + { rds: 0, wrs: 0, indma: -1, outdma: -1, }, // ep0 + { wr: NUSBD_EP1WR, wrs: NUSBD_EP1WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX1, cs: NUSBD_EP1CS, tx_str: "EP1 IN WR",}, + { wr: NUSBD_EP2WR, wrs: NUSBD_EP2WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX2, cs: NUSBD_EP2CS, tx_str: "EP2 IN WR",}, + + { rd: NUSBD_EP3RD, rds: NUSBD_EP3RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX3, cs: NUSBD_EP3CS, rx_str: "EP3 OUT RD",}, + { rd: NUSBD_EP4RD, rds: NUSBD_EP4RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX4, cs: NUSBD_EP4CS, rx_str: "EP4 OUT RD",}, +}; +u8 epl2p[6] = { 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, }; // map logical to physical +u8 epp2l[6] = { 0x00, 0x00, 0x81, 0x82, 0x03, 0x04, }; // map physical to logical + +static __inline__ void au_fifo_read(struct ep_regs *ep, unsigned char * cp, int bytes) +{ + u32 rd = ep->rd; + for (; bytes--; *cp++ = au_readl(rd)); +} + +static __inline__ void au_fifo_write(int ep, unsigned char * cp, int bytes) +{ + u32 wr = ep_regs[ep].wr; + for (; bytes--; au_writel(*cp++, wr)); + ep_regs[ep].last = bytes; +} + +void __inline__ au_inten(u32 inten, char *msg) +{ + TRACE_MSG2(PCD, "%s %08x", msg, inten); + au1x00_inten = inten; + au_writel(au1x00_inten, USBD_INTEN); +} + +void __inline__ au_epn_interrupt_enable(int epn) +{ + au_inten(au1x00_inten | (1 << epn), "epn enable"); +} + +void __inline__ au_epn_interrupt_disable(int epn) +{ + au_inten(au1x00_inten & ~(1 << epn), "epn disable"); + au_writel((1 << epn) , USBD_INTSTAT); +} + +static void __inline__ send_zlp(unsigned char epn) +{ + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep_regs[epn].wrs); + au_writel(0 << 1, ep_regs[epn].cs); + au_writel(0, ep_regs[epn].wr); +} +/* ********************************************************************************************* */ +#define AU_DMA_HALT_POLL 0x1000 + +#define DDMA_DESCCMD_ARB (1<<15) +#define DDMA_DESCCMD_DW (16) +#define DDMA_DESCCMD_DW_N(n) ((n&3)<chan_ptr;; + au1x_ddma_desc_t *dp; + + au1xxx_dbdma_stop(chan); + au_halt_dma_expired++; + + dp = ctp->cur_ptr; // ensure that descriptor is stopped + dp->dscr_cmd0 &= ~DSCR_CMD0_V; + +} + +static void __inline__ au_start_dma_in(int indma, u32 tx_id, __u8 *bp, int len) +{ + chan_tab_t *ctp = *((chan_tab_t **)indma); + au1x_ddma_desc_t *dp = ctp->cur_ptr; + volatile au1x_dma_chan_t *cp = ctp->chan_ptr;; + + TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x MEM2USB", + indma, dp, dp->dscr_cmd0, dp->dscr_cmd1, + dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); + + dp->dscr_source0 = virt_to_phys(bp); + dp->dscr_source1 = DDMA_DESCSRC_STRIDE_STS_4 | DDMA_DESCSRC_STRIDE_SAM_INC; + + dp->dscr_cmd1 = len; + dp->dscr_cmd0 = + DSCR_CMD0_SID(DSCR_CMD0_ALWAYS) | + DSCR_CMD0_DID(indma) + ; + TRACE_MSG1(PCD, "cmd0: %08x SID/DID", dp->dscr_cmd0); + + dp->dscr_cmd0 |= + DDMA_DESCCMD_SW_BYTE | + DDMA_DESCCMD_DW_BYTE + ; + TRACE_MSG1(PCD, "cmd0: %08x SW/DW", dp->dscr_cmd0); + + dp->dscr_cmd0 = DSCR_CMD0_V | + DSCR_CMD0_SID(DSCR_CMD0_ALWAYS) | + DSCR_CMD0_DID(tx_id) | + DDMA_DESCCMD_SW_BYTE | + DDMA_DESCCMD_DW_BYTE | + DSCR_CMD0_ARB | + DSCR_CMD0_DN | + DSCR_CMD0_CV + ; + + dp->dscr_dest1 = DDMA_DESCDST_STRIDE_DTS_4 | DDMA_DESCSRC_STRIDE_SAM_STATIC; + + ctp->cur_ptr = dp; + cp->ddma_desptr = virt_to_phys(ctp->cur_ptr); + cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */ + au_sync(); + cp->ddma_dbell = 0xffffffff; /* Make it go */ + au_sync(); + + TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x MEM2USB start", + indma, dp, dp->dscr_cmd0, dp->dscr_cmd1, + dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); +} + +static void __inline__ au_start_dma_out(unsigned int outdma, u32 rx_id, __u8 *bp, int wMaxPacketSize) +{ + chan_tab_t *ctp = *((chan_tab_t **)outdma); + au1x_ddma_desc_t *dp = ctp->cur_ptr; + volatile au1x_dma_chan_t *cp = ctp->chan_ptr;; + + + dma_cache_inv((unsigned long) bp, wMaxPacketSize); + + TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x USB2MEM", + outdma, dp, dp->dscr_cmd0, dp->dscr_cmd1, + dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); + + // if (!au1xxx_dbdma_put_dest(outdma, bp, wMaxPacketSize)) + + dp->dscr_dest0 = virt_to_phys(bp); + dp->dscr_dest1 = DDMA_DESCDST_STRIDE_DTS_4 | DDMA_DESCDST_STRIDE_DAM_INC; + + dp->dscr_cmd1 = wMaxPacketSize; + dp->dscr_cmd0 = DSCR_CMD0_V | + DSCR_CMD0_SID(rx_id) | + DSCR_CMD0_DID(DSCR_CMD0_ALWAYS) | + DDMA_DESCCMD_SW_BYTE | + DDMA_DESCCMD_DW_BYTE | + DSCR_CMD0_ARB | + DSCR_CMD0_SN | + DSCR_CMD0_CV + ; + + dp->dscr_source1 = DDMA_DESCSRC_STRIDE_STS_4 | DDMA_DESCSRC_STRIDE_SAM_STATIC; + + ctp->cur_ptr = dp; + cp->ddma_desptr = virt_to_phys(ctp->cur_ptr); + cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */ + au_sync(); + cp->ddma_dbell = 0xffffffff; /* Make it go */ + au_sync(); + + TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x USB2MEM start", + outdma, dp, dp->dscr_cmd0, dp->dscr_cmd1, + dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); +} + +/* ********************************************************************************************* */ +static struct usbd_urb *au_rcv_complete_ep0_irq(struct usbd_endpoint_instance *endpoint, int len, int urb_bad) +{ + struct usbd_urb *rcv_urb = endpoint->rcv_urb; + if (rcv_urb && !urb_bad) { + int i; + u8 *cp = rcv_urb->buffer + rcv_urb->actual_length; + for (i = 0 ; i < len; i+= 8) + TRACE_MSG8(PCD, "ARCV %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] + ); + + } + return pcd_rcv_complete_irq(endpoint, len, urb_bad); +} + +/* au_start_in_ep0 - start transmit + */ +static void au_start_in_ep0 (struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_urb *urb = endpoint->tx_urb; + int last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); + TRACE_MSG2(PCD, "START IN EP0 SENT: %d SENDING: %d", endpoint->sent, last); + RETURN_IF ((urb->actual_length - endpoint->sent) <= 0); + au_writel(last << 1, ep->cs); // XXX + au_fifo_write(0, urb->buffer + endpoint->sent, last); + endpoint->last = last; +} + +/* au_in_ep0 - called to service an endpoint zero IN interrupt, data sent + */ +static void au_in_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + u32 cs; + struct usbd_urb *tx_urb; + int last; + TRACE_MSG1(PCD, "EP0 IN: tx_urb: %p", (int)endpoint->tx_urb); + if ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) { // clear stall if present + TRACE_MSG1(PCD, "CLEAR STALL %d", 0); + cs &= ~USBDEV_CS_STALL; + au_writel(cs, ep->cs); + return; + } + if (!(tx_urb = pcd_tx_complete_irq(endpoint, 0))) { // wait for setup if no more data + endpoint->state = WAIT_FOR_SETUP; + return; + } + TRACE_MSG4(PCD, "EP0 IN actual: %d last: %d sent: %d flags: %x", endpoint->tx_urb->actual_length, + endpoint->last, endpoint->sent, endpoint->tx_urb->flags); + if (pcd_tx_sendzlp(endpoint)) { // check if tx_urb we have is finished + TRACE_MSG0(PCD, "EP0 IN BULK - sending ZLP"); + tx_urb->flags &= ~USBD_URB_SENDZLP; + send_zlp(0); + pcd_tx_complete_irq(endpoint, 0); + return; + } + if (tx_urb->actual_length > endpoint->sent) { + if ((tx_urb->actual_length - endpoint->sent) < endpoint->wMaxPacketSize) + TRACE_MSG1(PCD, "EP0 IN starting short packet %d", tx_urb->actual_length - endpoint->sent); + else + TRACE_MSG1(PCD, "EP0 IN LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); + au_start_in_ep0(endpoint, ep); + } +} + +/* au_out_ep0 - called to service an endpoint zero OUT interrupt, data received + */ +static void au_out_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_device_request request; + int i; + u32 cs; + u32 bytes; + TRACE_MSG0(PCD, "EP0 OUT"); + cs = au_readl(ep->cs); // check if host aborted transfer and flush the write fifo + bytes = au_readl(ep->rds) & USBDEV_FSTAT_FCNT_MASK; + if (endpoint->state == DATA_STATE_RECV) { + struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); + TRACE_MSG1(PCD, "EP0 OUT: RECV: rcv_urb: %x", (int) rcv_urb); + if (rcv_urb) { + au_fifo_read(ep, rcv_urb->buffer + rcv_urb->actual_length, bytes); + if (au_rcv_complete_ep0_irq(endpoint, bytes, 0)) + return; + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + endpoint->state = WAIT_FOR_SETUP; + send_zlp(0); + return; + } + endpoint->state = WAIT_FOR_SETUP; + } + pcd_tx_cancelled_irq(endpoint); + pcd_rcv_cancelled_irq(endpoint); + au_fifo_read(ep, (u8 *)&request, bytes); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); + if (bytes != 8) { + TRACE_MSG1(PCD, "ERROR SETUP SET not eight bytes: %d", bytes); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); + return; + } + TRACE_MSG4(PCD, "SETUP bmRequestType: %02x bRequest %02x state: %d status: %d", request.bmRequestType, + request.bRequest, pcd_instance->bus->device_state, pcd_instance->bus->status); + switch (request.bRequest) { // we need to simply ignore any of these + case USB_REQ_SET_ADDRESS: // Fake a bus reset IFF not state default and then process normally + BREAK_IF (pcd_instance->bus->device_state == STATE_DEFAULT); + udc_saw_bus_activity = 0; + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); + //usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); + break; + case USB_REQ_GET_DESCRIPTOR: // Fake a bus reset IFF suspended and then process normally + BREAK_IF (STATE_SUSPENDED != pcd_instance->bus->device_state); + udc_saw_bus_activity = 0; + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); + //usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); + break; + } + if (pcd_recv_setup_irq(pcd_instance, &request)) { + TRACE_MSG1(PCD, "ep0 STALL %d", cs); + au_writel(USBDEV_CS_STALL, USBD_EP0CS); + return; + } + if (((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) && le16_to_cpu (request.wLength)) { + TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); + endpoint->state = DATA_STATE_RECV; + return; + } + if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); + if ((request.bmRequestType & ~USB_REQ_DIRECTION_MASK)) { + TRACE_MSG1(PCD, "ep0 Class or Vendor, send ZLP %d", cs); + send_zlp(0); + return; + } + } + TRACE_MSG1(PCD, "ep0 Class D2H request %04x", le16_to_cpu(request.wLength)); +} +/* ********************************************************************************************* */ +/* au_start_in_bulk - start transmit + * The au1x00 will start to send when the first byte is loaded into the FIFO, either by + * DMA or PIO. The packetsize must be set first. + */ +static void au_start_in_bulk (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_urb *urb = endpoint->tx_urb; + u8 *bp = urb->buffer + endpoint->sent; + int last; + int i; + u32 *lp; + TRACE_MSG1(PCD, "START IN BULK %d", epn); + RETURN_IF (!urb || (( (urb->actual_length - endpoint->sent) == 0) && !(urb->flags & USBD_URB_SENDZLP))); + last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); + TRACE_MSG2(PCD, "START IN BULK sent: %d last:%d", endpoint->sent, last); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX + if (!last) { + if (endpoint->tx_urb->flags & USBD_URB_SENDZLP) { + TRACE_MSG0(PCD, "START IN BULK - zending ZLP"); + send_zlp(epn); + endpoint->tx_urb->flags &= ~USBD_URB_SENDZLP; + } + return; + } + else if (8 >= last) { + au_writel(last << 1, ep->cs); + au_fifo_write(epn, bp, last); + return; + } + + dma_cache_wback_inv((unsigned long) bp, last); + //au_writel(last << 1, ep->cs); + au_start_dma_in(ep->indma, ep->tx_id, bp, last); + au_writel(last << 1, ep->cs); +} + +static void au_in_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb; + int rc = 0; + u32 cs = au_readl(ep->cs); + u32 wrs = au_readl(ep->wrs); + u32 residue = au1xxx_get_dma_residue(ep->indma); + TRACE_MSG3(PCD, "BULK IN EPN - cs: %x wrs: %x residue:%d", cs, wrs, residue); + if (wrs) { // check for underflow or overflow + rc = 1; + if (wrs & USBDEV_FSTAT_UF) { + TRACE_MSG2(PCD, "BULK IN EPN - UF epn %d wrs: %x", epn, wrs); + rc = 1; // set rc to indicate an error + } + if (wrs & USBDEV_FSTAT_OF) + TRACE_MSG2(PCD, "BULK IN EPN - OF epn %d wrs: %x", epn, wrs); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // flush the fifo + } + if (cs & USBDEV_CS_NAK) { + RETURN_IF (ep->last && ((wrs&0x1f) == ep->last)); + rc = 1; + } + if (cs & USBDEV_CS_STALL) { // clear stall if present + TRACE_MSG1(PCD, "BULK IN EPN - CLEAR STALL %d", epn); + cs &= ~USBDEV_CS_STALL; + au_writel(cs, ep->cs); + return; + } + TRACE_MSG4(PCD, "BULK IN EPN epn: %d rc: %d last: %d sent: %d", epn, rc, endpoint->last, endpoint->sent); + + if ((tx_urb = pcd_tx_complete_irq(endpoint, rc))) { + if ((tx_urb->actual_length > endpoint->sent) || (endpoint->tx_urb->flags & USBD_URB_SENDZLP)) { + au_start_in_bulk(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); + TRACE_MSG1(PCD, "BULK IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); + return; + } + } + au_epn_interrupt_disable(epn); // disable interrupts +} + +/* au_start_in_iso - start transmit + * The au1x00 will start to send when the first byte is loaded into the FIFO, either by DMA or + * PIO. The packetsize must be set first. + */ +static void au_start_in_iso (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_urb *urb = endpoint->tx_urb; + unsigned char *bp = urb->buffer + endpoint->sent; + int last; + TRACE_MSG2(PCD, "START IN ISO actual: %d sent: %d", urb->actual_length, endpoint->sent); + RETURN_IF ((urb->actual_length - endpoint->sent) == 0); + last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); + TRACE_MSG2(PCD, "START IN ISO last: %d packetSize: %d", last, endpoint->wMaxPacketSize); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX + au_epn_interrupt_enable (epn); + dma_cache_wback_inv((unsigned long) bp, last); + au_writel(last << 1, ep->cs); + au_start_dma_in(ep->indma, ep->tx_id, bp, last); +} + +static void au_in_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb = pcd_tx_complete_irq(endpoint, 0); + u32 cs = au_readl(ep->cs); + u32 wrs = au_readl(ep->wrs); + TRACE_MSG2(PCD, "ISO IN EPN - cs: %x wrs: %x", cs, wrs); + TRACE_MSG4(PCD, "ISO IN EPN epn: %d last: %d sent: %d", epn, endpoint->last, endpoint->sent, 0); + ep->last = 0; + if ((tx_urb = endpoint->tx_urb) && (tx_urb->actual_length > endpoint->sent)) { + au_start_in_iso(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); + /* XXX magic delay - without this large packets will eventually stall the transmit + * XXX and all traffic in both directions will stop. + */ + TRACE_MSG1(PCD, "ISO IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); + return; + } + + TRACE_MSG0(PCD, "ISO IN EPN - nothing to send"); + au_epn_interrupt_disable(epn); // disable interrupts +} +/* ********************************************************************************************* */ + +static struct usbd_urb *au_rcv_complete_epn_irq(struct usbd_endpoint_instance *endpoint, int len, int urb_bad) +{ + return pcd_rcv_complete_irq(endpoint, len, urb_bad); +} + + +static void au_start_out_bulk(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + TRACE_MSG2(PCD, "START OUT BULK %d actual: %d pcktSize: %d", epn, endpoint->wMaxPacketSize); + if (!endpoint->rcv_urb) { + TRACE_MSG0(PCD, "START OUT BULK DISABLE"); + au_epn_interrupt_disable(epn); + return; + } + if (endpoint->rcv_error) { + TRACE_MSG0(PCD, "START OUT BULK reseting rcv_error"); + endpoint->rcv_error = 0; + } + au_start_dma_out(ep->outdma, ep->rx_id, + endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); +} + +static void au_out_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + int bytes = 0; + int residue = 0; + u32 *lp = NULL; + + struct usbd_urb *urb; + struct usbd_urb *completed_urb = NULL; + u32 cs; + u32 rds; + u32 nrds; + int i; + + cs = au_readl(ep->cs); + rds = au_readl(ep->rds); + + // XXX it appears that this is zero if 64 bytes where received!!! + + // get dest ring buffer + au1xxx_dbdma_get_dest(ep->outdma, (void **)&lp, &bytes); + + au_halt_dma(ep->outdma); + residue = endpoint->wMaxPacketSize - au1xxx_get_dma_residue(ep->outdma); + + TRACE_MSG5(PCD, "BULK OUT CS: %04x RD: %04x bytes: %d residue: %d DMA %s", + cs, rds, bytes, residue, lp ? "FINISHED" : "ACTIVE"); + + /* check for NAK + */ + if (cs & USBDEV_CS_NAK) { + TRACE_MSG0(PCD, "NAK bytes: 0"); + bytes = 0; + } + + /* with dbdma residue will either be zero or non-zero and less than packetsize, if + * zero then a complete transfer of packetsize was performed. + */ + else if (!residue && !rds) { + TRACE_MSG0(PCD, "BULK OUT CS: forcing bytes: 64"); + bytes = endpoint->wMaxPacketSize; + } + else { + TRACE_MSG0(PCD, "BULK OUT CS: using residue bytes: 64"); + bytes = residue; + } + + + if (!(urb = pcd_rcv_next_irq(endpoint))) { + TRACE_MSG0(PCD, "BULK OUT EPN - no rcv_urb"); + au_epn_interrupt_disable(epn); + return; + } + + TRACE_MSG4(PCD, "BULK OUT CS: %04x RD: %04x actual: %d DMA bytes: %d", cs, rds, urb->actual_length, bytes); + + if (bytes) + dma_cache_inv((u32) urb->buffer + urb->actual_length, bytes); + + /* The original AU1X00 UDC design will continue to receive data as long as there is room + * in the FIFO. We cannot tell when we are at the end of a packet and/or have the start + * of a new one. + * + * There are only two scenarios that are guaranteed (almost) to be correct: + * + * 64 bytes of data from DMA, empty fifo, continue Bulk OUT < 60 bytes of data and + * < 4 bytes in fifo, end Bulk OUT. + * + * There may be a third scenario that is ok: + * + * 0 bytes dma, 0 bytes in fifo, NAK + * + * Everything else is an error. In all cases we assume that it is safer to drop data + * than to accept it in error. This allows CRC or size protected encapsulations to + * notice bulk transfers received with errors. + * + * In general none of the policies or strategies are able to cope with all errors + * without missing errors and dropping good data. The intent is to minimize the amount + * of potentially bad data getting to the function driver while minimizing the amount of + * good data that is dropped. + * + * The new silicon mitigates this problem for non control endpoints because it will NAK + * additional data until the interrupt service flag is reset. + * + * Start with generic error tests, OF, UF or NAK indicate an error we cannot recover + * from, start flushing until end of current bulk transfer (wait for a short packet) + * + * XXX The following needs to be tested and streamlined for au1550, much of the extra + * testing done for older silicon cab probably be eliminated. For example the extra read + * of nrds to check for overrun in the FIFO should not be required. + * + */ + if (rds & (USBDEV_FSTAT_OF | USBDEV_FSTAT_UF)) { + TRACE_MSG2(PCD, "BULK OUT FLUSHING %d length: %d", bytes, urb->actual_length); + THROW(start_flushing); + } + rds = rds & USBDEV_FSTAT_FCNT_MASK; + nrds = au_readl(ep->rds); + + /* full size packet received, check that we are not flushing and that the FIFO + * does not have any data. If there is data in the FIFO we may not be able to + * restart DMA in time, so start flushing + */ + if (endpoint->wMaxPacketSize == bytes) { + TRACE_MSG2(PCD, "BULK OUT 64 BYTES %d length: %d", bytes, urb->actual_length); + if (endpoint->rcv_error) { + TRACE_MSG4(PCD, "FULL PACKET bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + if ((nrds > 6) && (nrds < 8) ) { + TRACE_MSG4(PCD, "FIFO not empty bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING nrds > 6 < 8", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + if (!urb->actual_length) + TRACE_MSG4(PCD, "PACKET ok bytes: %d rds: %d nrds: %d cp0: %d ACCEPTING 64 bytes", + bytes, rds, nrds, cp0_count); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + au_rcv_complete_epn_irq(endpoint, endpoint->wMaxPacketSize, 0); + if (cs & USBDEV_CS_NAK) + if (nrds && (nrds < 8) ) { + TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING CS_NAK", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + } + /* a nak'd packet may be ok to ignore IFF the FIFO is empty(?) or completely full. + */ + else if ((cs & USBDEV_CS_NAK) && (!nrds || (nrds == 8)) ) { + TRACE_MSG2(PCD, "BULK OUT NAK BYTES %d length: %d", bytes, urb->actual_length); + if (endpoint->rcv_error) { + TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + } + /* short packet by DMA, additional data for this packet in FIFO, more than 3 + * bytes is probably an error. + */ + else { + TRACE_MSG2(PCD, "BULK OUT < 64 BYTES %d length: %d", bytes, urb->actual_length); + if ((cs & USBDEV_CS_NAK) && nrds && (nrds < 8) ) { + TRACE_MSG4(PCD, "BULK OUT NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + if (nrds > 4) { + TRACE_MSG4(PCD, "BULK OUT SHORT PACKET by DMA full FIFO bytes: %d rds: %d nrds: %d cp0: %d START FLUSH", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + TRACE_MSG4(PCD, "BULK OUT SHORT bytes: %d rds: %d nrds: %d cp0: %d reading fifo", bytes, rds, nrds, cp0_count); + au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, nrds); + bytes += nrds; + TRACE_MSG1(PCD, "BULK OUT < 64 BYTES %d", bytes); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + if (!endpoint->rcv_error) { + TRACE_MSG0(PCD, "BULK OUT COMPLETED URB"); + au_rcv_complete_epn_irq(endpoint, bytes, 0); + } + else { + TRACE_MSG0(PCD, "BULK OUT - FLUSHING URB - reseting rcv_error"); + endpoint->rcv_error = 0; + } + } + CATCH(start_flushing) { + TRACE_MSG0(PCD, "BULK OUT - START FLUSHING URB"); + endpoint->rcv_error = 1; + endpoint->rcv_urb->actual_length = 0; + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + bytes = 0; + } + TRACE_MSG0(PCD, "BULK OUT - RESTARTING"); + au_start_out_bulk(epn, endpoint, ep); +} + +static void au_start_out_iso(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + RETURN_IF(!endpoint->rcv_urb); + au_start_dma_out(ep->outdma, ep->rx_id, + endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); +} + +static void au_out_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + int bytes = 0; + struct usbd_urb *urb; + u32 cs; + u32 rds; + au_halt_dma(ep->outdma); + cs = au_readl(ep->cs); + rds = au_readl(ep->rds); + if (!endpoint) { + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + return; + } + if (!(urb = endpoint->rcv_urb)) { + TRACE_MSG2(PCD, "ISO OUT EPN - rcv_urb was NULL bytes: %d rds: %d", bytes, rds); + au_rcv_complete_epn_irq(endpoint, bytes, 1); + TRACE_MSG0(PCD, "ISO OUT setting rcv_error"); + } + bytes = endpoint->wMaxPacketSize - au1xxx_get_dma_residue(ep->outdma); + rds = rds & USBDEV_FSTAT_FCNT_MASK; + au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, rds); + bytes += rds; + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + au_rcv_complete_epn_irq(endpoint, bytes, 0); + au_start_out_iso(epn, endpoint, ep); +} +/* ********************************************************************************************* */ +/* au_tx_dma_done - TX DMA interrupt handler + */ +static void au_tx_dma_done(int irq, void *dev_id, struct pt_regs *regs) +{ + int epn = (int) dev_id; + struct usbd_endpoint_instance *endpoint = pcd_instance->bus->endpoint_array + epn; + struct ep_regs *ep = &ep_regs[epn]; + int residue; + ocd_ops.interrupts++; + RETURN_IF (!epn); + au_halt_dma(ep->indma); + ep->last = 0; +} + +/* au_int_req - usb interrupt handler + */ +static void au_int_req (int irq, void *dev_id, struct pt_regs *regs) +{ + u32 intstat; + struct ep_regs *ep; +#if defined(MAX_INTR_LOOP_STATS) + u32 loopcount = 0; +#endif +#ifdef RECORD_LATENCY + cp0_count = (read_c0_count(CP0_COUNT) - cp0_record) >> 9; + if (cp0_count < CP0_COUNTS) + cp0_counts[cp0_count]++; +#endif +#ifdef CHECK_LATENCY + u32 cp0_count = read_c0_count(CP0_COUNT); +#endif + ocd_ops.interrupts++; + TRACE_MSG2(PCD, "INT - device: %d status: %d", pcd_instance->bus->device_state, pcd_instance->bus->status); +#if 0 + if (ocd_ops.interrupts > 2000) { + TRACE_MSG0(PCD, "UDC_INT call udc_disable_interrupts"); + au_inten(0, "DISABLING"); + return; + } +#endif + while (( intstat = au_readl(USBD_INTSTAT) & au1x00_inten)) { // read and reset interrupt status register + + int epn; +#if 1 + for (epn = 2; epn < 6; epn++) { + CONTINUE_IF (!(intstat & (1 << epn))); +#else + // NOT TESTED + u32 local_intstat = intstat; + TRACE_MSG2(PCD, "INTSTAT: %04x %04x", intstat, au_readl(USBD_INTEN)); + while (local_intstat & 0x3f) { + int epn = 31 - au_clz(local_intstat); + local_intstat &= ~(1<bus, DEVICE_BUS_ACTIVITY, 0); + au_inten(0x0033/*|USBDEV_INT_SOF*/, "int SOF"); + } + ep = &ep_regs[epn]; + switch(ep->eptype) { + case USB_DIR_IN | USB_ENDPOINT_BULK: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: + //TRACE_MSG2(PCD, "BULK IN %d %02x", epn, ep->eptype); + au_in_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: + //TRACE_MSG2(PCD, "ISO IN %d %02x", epn, ep->eptype); + au_in_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + case USB_DIR_OUT | USB_ENDPOINT_BULK: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: + //TRACE_MSG2(PCD, "BULK OUT %d %02x", epn, ep->eptype); + au_out_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: + //TRACE_MSG2(PCD, "ISO OUT %d %04d", epn, au_readl(NUSBD_FRAMENUM)); + au_out_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + } + } + au_writel(intstat, USBD_INTSTAT); // Only clear the interrupt(s) AFTER servicing OUT + /* even though we disable the bulk-in interrupt (endpoint 2) prior to enabling + * DMA we always see one additional interrupt that is a NAK on that endpoint. + */ + CONTINUE_IF(!(intstat & au1x00_inten)); + /* handle control endpoint and suspend interrupt + */ + if (intstat & ( ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 5) | USBDEV_INT_SOF))) { + if (intstat & (1 << 0)) + au_out_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); + if (intstat & (1 << 1)) + au_in_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); + if (intstat & USBDEV_INT_SOF) + if (USBD_SUSPENDED == pcd_instance->bus->status) { + TRACE_MSG0(PCD, "SUS - ACTIVITY"); + udc_saw_bus_activity++; + } + } +#if defined(MAX_INTR_LOOP_STATS) + loopcount += 1; // Gather stats on how many times this loop is performed. +#endif + } +#if defined(MAX_INTR_LOOP_STATS) + interrupt_loop_stats[MIN(loopcount, MAX_INTR_LOOP_STATS)]++; +#endif +#if defined(CHECK_LATENCY) + TRACE_MSG1(PCD, "USB IRQ - %d", read_c0_count(CP0_COUNT) - cp0_count); +#endif +#if defined(RECORD_LATENCY) + cp0_record = read_c0_count(CP0_COUNT); +#endif +} + +/* au_int_sus -suspend interrupt handler + */ +static void au_int_sus (int irq, void *dev_id, struct pt_regs *regs) +{ + ocd_ops.interrupts++; + TRACE_MSG2(PCD, "SUS - INACTIVE device: %d status: %d", pcd_instance->bus->device_state, pcd_instance->bus->status); + switch(pcd_instance->bus->status) { + case USBD_OPENING: + case USBD_OK: + au_inten(0x0033, "suspend"); + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_BUS_INACTIVE, 0); + break; + default: + break; + } +} +/* ********************************************************************************************* */ +/* + * au_connect - enable pullup resistor + * Turn on the USB connection by enabling the pullup resistor. + * + * au_disconnect - disable pullup resistor + * Turn off the USB connection by disabling the pullup resistor. + */ + +#if defined(CONFIG_MIPS_FREEHAND_NATIVE) +#warning "Old code for Freehand" +void au_connect (struct pcd_instance *pcd) +{ + au1000gpio_set(GPIO01); +} + +void au_disconnect (struct pcd_instance *pcd) +{ + au1000gpio_clear(GPIO01); +} +#elif defined(CONFIG_AMX_MORDOR) +void au_connect (struct pcd_instance *pcd) +{ + au_writel(0x00000008, SYS_OUTPUTSET); +} + +void au_disconnect (struct pcd_instance *pcd) +{ + au_writel(0x00000008, SYS_OUTPUTCLR); +} +#elif defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_MTX2) +void au_connect (struct pcd_instance *pcd) +{ + au_writel(au_readl(GPIO2_DIR) | 0x100, GPIO2_DIR); + au_writel(0x01000100, GPIO2_OUTPUT); + au_sync(); +} +void au_disconnect (struct pcd_instance *pcd) +{ + au_writel(au_readl(GPIO2_DIR) | 0x100, GPIO2_DIR); + au_writel(0x01000000, GPIO2_OUTPUT); + au_sync(); +} +#else +#warning "Please define au_connect / au_disconnect" +void au_connect (struct pcd_instance *pcd) +{ +} +void au_disconnect (struct pcd_instance *pcd) +{ +} +#endif + +#undef read_c0_COUNT +#ifndef read_c0_count +#define read_c0_count() read_32bit_cp0_register(CP0_COUNT) +#endif +/* ********************************************************************************************* */ +/* au_ticks - get current ticks +* */ +u64 au_ticks (void) +{ + return read_c0_count(); +} + +/* au_elapsed - return micro-seconds between two tick values + */ +u64 au_elapsed(u64 *t1, u64 *t2) +{ + u64 ticks = (*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1); + ticks = (u32)((u32) ticks / (u32) CONFIG_OTG_AU1X00_SCLOCK); + return ticks; +} + +/* au_framenum - get current framenum + */ +u16 au_framenum (void) +{ + return au_readl(NUSBD_FRAMENUM); +} + + +/* ********************************************************************************************* */ +/* au_start_endpoint_in - start transmit + */ +void au_start_endpoint_in(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + int epn = epl2p[endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { + case USB_ENDPOINT_CONTROL: + au_in_ep0(endpoint, ep); + break; + case USB_ENDPOINT_BULK: + case USB_ENDPOINT_INTERRUPT: + au_start_in_bulk(epn, endpoint, ep); + break; + case USB_ENDPOINT_ISOCHRONOUS: + au_start_in_iso(epn, endpoint, ep); + break; + } + au_epn_interrupt_enable(epn); +} + +/* au_start_endpoint_out - start receive + */ +void au_start_endpoint_out(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + int epn = epl2p[endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { + case USB_ENDPOINT_CONTROL: + break; + case USB_ENDPOINT_BULK: + case USB_ENDPOINT_INTERRUPT: + au_start_out_bulk(epn, endpoint, ep); + au_epn_interrupt_enable(epn); + break; + case USB_ENDPOINT_ISOCHRONOUS: + au_start_out_iso(epn, endpoint, ep); + break; + } +} + +/* + * au_pcd_endpoint_halted() - is endpoint halted + */ +static int +au_pcd_endpoint_halted (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ +int cs; + int epn = epl2p[endpoint->bEndpointAddress&0xf]; + int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; + struct ep_regs *ep = &ep_regs[epn]; + + TRACE_MSG2(PCD, "epn: %02x dir: %x", epn, dir); + return ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) ? TRUE : FALSE; +} + +/* au_pcd_halt_endpoint - halt endpoint + */ +int au_pcd_halt_endpoint(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int flag) +{ + int epn = epl2p[endpoint->bEndpointAddress&0xf]; + int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; + struct ep_regs *ep = &ep_regs[epn]; + + TRACE_MSG3(PCD, "epn: %02x dir: %x flag: %d", epn, dir, flag); + + au_writel(au_readl(ep->cs) & USBDEV_CS_STALL, ep->cs); + return 0; +} + + + +void au_cancel_in_irq(struct pcd_instance *pcd, struct usbd_urb *urb) +{ + int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + au_in_bulk(epn, ep, urb->endpoint); +} + +void au_cancel_out_irq(struct pcd_instance *pcd, struct usbd_urb *urb) +{ + int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + au_out_bulk(epn, ep, urb->endpoint); +} + +/* au_serial_init - set a serial number if available + */ +int au_serial_init (struct pcd_instance *pcd) +{ +#if defined(CONFIG_MIPS_FREEHAND) + int length; + long data[16]; /* yeah a hack, but we KNOW it's 16 */ + struct i2c_client *client; + char chData[16]; + int i; + + if (!(client = getFreeHandEepromClient())) { + printk(KERN_INFO"eeprom not ready when au_serial_init called\n"); + return -EINVAL; + } + eeprom_contents(client, SENSORS_PROC_REAL_READ, EEPROM_SYSCTL1, &length, (long *)data); + + /* serial number is first 9 longs. But each long is just an ASCII char + * convert this to a string and then extract a 4 byte value from it + */ + for (i = 0; i < 9; chData[i] = (char)data[i], i++); /* trunc, is ok */ + chData[9] = 0; /* terminate string */ + printk(KERN_INFO"%s: %s\n", __FUNCTION__, chData); + pcd_instance->bus->serial_number_str = lstrdup(chData); + return 0; +#else + return -EINVAL; +#endif +} + + +/* au_start - start session + */ +void au_start (struct pcd_instance *pcd) +{ + au_inten(0x0033/*|USBDEV_INT_SOF*/, "START"); // Only enable receive interrupts. +} + +/* au_stop - stop session + */ +void au_stop (struct pcd_instance *pcd) +{ + //au_inten(0, "STOP"); +} + +int au_assign_endpoint( u8 physicalEndpoint, int used[6], struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, + u16 wMaxPacketSize, u16 transferSize) +{ + struct ep_regs *ep = &ep_regs[physicalEndpoint]; + RETURN_EINVAL_IF(used[physicalEndpoint]); + endpoint_map->bEndpointAddress[0] = epp2l[physicalEndpoint]; + endpoint_map->physicalEndpoint[0] = physicalEndpoint; + endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; + endpoint_map->transferSize[0] = transferSize; + endpoint_map->bmAttributes[0] = bmAttributes; + used[physicalEndpoint]++; + ep->eptype = bmAttributes & 0x83; + return 0; +} + +int au_request_endpoints(struct pcd_instance *pcd, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, + struct usbd_endpoint_request *requestedEndpoints) +{ + struct usbd_device_description *device_description; + int i, j; + int used[UDC_MAX_ENDPOINTS]; + memset(used, 0, sizeof(used)); + for (j = 0, i = 0; i < endpointsRequested; i++, j++) { + struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; + u8 bmAttributes = requestedEndpoints[i].bmAttributes; + u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; + RETURN_EINVAL_UNLESS(j < UDC_MAX_ENDPOINTS); + switch(bmAttributes) { + case USB_DIR_OUT | USB_ENDPOINT_BULK: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, 0x40, transferSize)); + CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, 0x40, transferSize)); + break; + case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: + CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, transferSize, transferSize)); + CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, transferSize, transferSize)); + break; + case USB_DIR_IN | USB_ENDPOINT_BULK: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, 0x40, transferSize)); + CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, 0x40, transferSize)); + break; + case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: + CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, transferSize, transferSize)); + CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, transferSize, transferSize)); + break; + } + CONTINUE_IF(bmAttributes & USB_ENDPOINT_OPT); + return -EINVAL; + } + return 0; +} + +struct usbd_endpoint_map *au_endpoint_map_array; +int au_endpointsRequested; + +int au_set_endpoints2(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array); +int au_set_endpoints(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) +{ + au_endpointsRequested = endpointsRequested; + au_endpoint_map_array = endpoint_map_array; + return au_set_endpoints2(pcd, au_endpointsRequested, au_endpoint_map_array); +} + +int au_set_endpoints2(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) +{ + int i, j; + u8 config[25]; + u8 *cp; + memcpy(config, au1x00_config_bulk, sizeof(config)); + for (i = 0; i < endpointsRequested; i++) { + struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; + int epreq = endpoint_map->bmAttributes[0]; + int eptype = epreq & USB_ENDPOINT_MASK; + int epdir = epreq & USB_ENDPOINT_DIR_MASK ? 0x8 : 0; + int epsize = endpoint_map->wMaxPacketSize[0]; + int epaddr = epp2l[endpoint_map->physicalEndpoint[0]]; + CONTINUE_IF(!endpoint_map->physicalEndpoint[0] || (endpoint_map->physicalEndpoint[0] > 5)); + cp = config + ((endpoint_map->physicalEndpoint[0] - 1) * 5); + cp[0] = (epaddr & 0xf) << 4 | 0x4; + cp[1] = (eptype << 4) | epdir | (epsize & 0x380) >> 7; + cp[2] = (epsize & 0x7F) << 1; + } + TRACE_MSG0(PCD, "Disable interrupts"); + au_inten(0, "SET ENDPOINTS"); // disable interrupts + au_writel(0x0002, USBD_ENABLE); // reset controller + udelay(100); + au_writel(0x0003, USBD_ENABLE); // enable controller + udelay(100); + for (cp = config, i = 0; i < 25; i++, au_writel(*cp++, USBD_CONFIG)); // feed the config into the UDC + return 0; +} + +/* au_loc_conn - used to enable or disable peripheral connecting to bus + * + */ +void au_loc_conn(struct otg_instance *otg, u8 flag) +{ + struct pcd_instance *pcd = (struct pcd_instance *)otg->pcd; + TRACE_MSG0(PCD, "--"); + TRACE_MSG1(PCD, "PCD: %x", (int)pcd); + switch (flag) { + case SET: + TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_SET - Enable DP PULLUP"); + au_set_endpoints2(pcd, au_endpointsRequested, au_endpoint_map_array); + au_inten(0x0033/*|USBDEV_INT_SOF*/, "START"); // Only enable receive interrupts. + au_connect(pcd); + break; + + case RESET: + TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET - Disable DP PULLUP"); + au_disconnect(pcd); + au_inten(0, "RESET ENDPOINTS"); // disable interrupts + au_writel(0x0000, USBD_ENABLE); // reset controller + break; + } +} + + +/* ********************************************************************************************* */ + + +static u32 request_dma(int ep, int src, int dest, char *str, void dma_done(int , void *, struct pt_regs *)) +{ + u32 dma; + + chan_tab_t *ctp; + volatile au1x_dma_chan_t *cp; + au1x_ddma_desc_t *dp; + + + if ((dma = au1xxx_dbdma_chan_alloc(src, dest, dma_done, (void *)ep)) < 0) { + printk(KERN_INFO"request_io[%d] dma: %x src: %d dest: %d %s FAILED\n", ep, dma, src, dest, str); + return -1; + } + + if(au1xxx_dbdma_ring_alloc(dma, 1) == 0) { + printk(KERN_INFO"request_io[%d] Ring Buffer Allocation for dma: %x src: %d dest: %d %s FAILED\n", + ep, dma, src, dest, str); + return -1; + } + + ctp = *((chan_tab_t **)dma); + cp = ctp->chan_ptr;; + dp = ctp->put_ptr; + dp->dscr_cmd0 &= ~DSCR_CMD0_V; + + TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %04x dst: %08x %08x DBDMA", + dma, dp, dp->dscr_cmd0, dp->dscr_cmd1, + dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); + +#if 0 + // XXX copy static components from au_start_out + // transmit - mem to usb + if (src == DBDMA_MEM_CHAN) { + TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %04x dst: %08x %08x MEM2USB", + dma, dp, dp->dscr_cmd0, dp->dscr_cmd1, + dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); + } +#endif +#if 0 + // XXX copy static components from au_start_in + // receive - usb to mem + if (dest == DBDMA_MEM_CHAN) { + TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x USB2MEM", + dma, dp, dp->dscr_cmd0, dp->dscr_cmd1, + dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); + } +#endif + TRACE_MSG5(PCD, "request_io[%d] dma: %x src: %d dest: %d %s SUCCEDED", ep, dma, src, dest, str); + return dma; +} + +/* au_mod_init + */ +int au_mod_init (void) +{ + int dev = request_irq (AU1000_USB_DEV_REQ_INT, au_int_req, SA_INTERRUPT, UDC_NAME "UDC Req", NULL); + int sus = request_irq (AU1000_USB_DEV_SUS_INT, au_int_sus, SA_INTERRUPT, UDC_NAME "UDC Sus", NULL); + u32 cp0_prid = read_c0_prid(); + int i; + + + if (dev || sus) { // check if either request irq failed + if (!dev) free_irq (AU1000_USB_DEV_REQ_INT, NULL); // free irqs that might have + if (!sus) free_irq (AU1000_USB_DEV_SUS_INT, NULL); // been successfully allocated + return -EINVAL; + } + for (i = 0; i < 6; i++) { + ep_regs[i].indma = ep_regs[i].tx_id ? + request_dma(i, DBDMA_MEM_CHAN, ep_regs[i].tx_id, ep_regs[i].tx_str, au_tx_dma_done) : -1; + ep_regs[i].outdma = ep_regs[i].rx_id ? + request_dma(i, ep_regs[i].rx_id, DBDMA_MEM_CHAN, ep_regs[i].rx_str, NULL) : -1; + } + switch (cp0_prid & CP0_PRID_SOC_MASK) { + case CP0_PRID_AU1000: + case CP0_PRID_AU1100: + case CP0_PRID_AU1500: + return -EINVAL; + case CP0_PRID_AU1550: + TRACE_MSG1(PCD, "AU1550 cp0_prid: %08x\n", cp0_prid); + printk(KERN_INFO"%s: AU1550 cp0_prid: %08x\n", __FUNCTION__, cp0_prid); + break; + default: + printk(KERN_INFO"%s: UNKNOWN CPU cp0_prid: %08x UNKNOWN UDC\n", __FUNCTION__, cp0_prid); + break; + } + return 0; +} + +/* au_mod_exit + */ +void au_mod_exit (void) +{ + int j; + au_writel(0x0000, USBD_ENABLE); + for (j = 0; j < 6; j++) { + struct ep_regs *ep = &ep_regs[j]; + if (ep->indma != -1) { + au1xxx_dbdma_chan_free(ep->indma); + ep->indma = -1; + } + if (ep->outdma != -1) { + au1xxx_dbdma_chan_free(ep->outdma); + ep->outdma = -1; + } + } + free_irq (AU1000_USB_DEV_REQ_INT, NULL); + free_irq (AU1000_USB_DEV_SUS_INT, NULL); +#if defined(MAX_INTR_LOOP_STATS) + { + u32 lc; + for (lc = 0; lc <= MAX_INTR_LOOP_STATS; lc++) + if (interrupt_loop_stats[lc]) + printk(KERN_ERR "%s: interrupt loopcount[%02u] %9u\n", __FUNCTION__, lc,interrupt_loop_stats[lc]); + printk(KERN_INFO"%s: halt_dma_expired: %d\n", __FUNCTION__, au_halt_dma_expired); + } +#endif +#ifdef RECORD_LATENCY + { + int i; + for (i = 0; i < CP0_COUNTS; i++) + if (cp0_counts[i]) + printk(KERN_INFO"%s: cp0_counts[%d] %d\n", __FUNCTION__, i, cp0_counts[i]); + } +#endif +} + +/* ********************************************************************************************* */ +struct usbd_pcd_ops usbd_pcd_ops = { + max_endpoints: UDC_MAX_ENDPOINTS, + ep0_packetsize: EP0_PACKETSIZE, + name: UDC_NAME, + start: au_start, + stop: au_stop, + start_endpoint_in: au_start_endpoint_in, + start_endpoint_out: au_start_endpoint_out, + request_endpoints: au_request_endpoints, + set_endpoints: au_set_endpoints, + cancel_in_irq: au_cancel_in_irq, + cancel_out_irq: au_cancel_out_irq, + serial_init: au_serial_init, + halt_endpoint: au_pcd_halt_endpoint, + endpoint_halted: au_pcd_endpoint_halted, +}; + +struct pcd_ops pcd_ops = { + pcd_en_func: pcd_en_func, + mod_init: au_mod_init, + mod_exit: au_mod_exit, + framenum: au_framenum, +}; + +struct ocd_ops ocd_ops = { + #if defined(CONFIG_OTG_TR_AUTO) + capabilities: OCD_CAPABILITIES_TR | OCD_CAPABILITIES_AUTO, + #else + capabilities: OCD_CAPABILITIES_TR, + #endif + ticks: au_ticks, + elapsed: au_elapsed, +}; + +struct tcd_ops tcd_ops = { + dp_pullup_func: au_loc_conn, +}; + diff -uNr linux/drivers/no-otg/ocd/au1x00/au1x00.c linux/drivers/otg/ocd/au1x00/au1x00.c --- linux/drivers/no-otg/ocd/au1x00/au1x00.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/au1x00/au1x00.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,1293 @@ +/* + * otg/ocd/au1x00/au1x00.c -- USB Device Controller driver. + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + */ +/*! + * @file otg/ocd/au1x00/au1x00.c + * @brief AU1X00 Header + * + * This file contains the private defines and structures for the AU1X00 + * Drivers. + * + * @ingroup AU1X00 + */ + + +#include +#include "au1x00.h" + +#include +#include +#include + +#if defined(CONFIG_SOC_AU1550) +#define DBDMA +#endif +#ifdef DBDMA +#warning "Using AU1XXX_DBMDA" +#include +#else /* DBDMA */ +#warning "Using AU1X00_MDA" +#endif /* DBDMA */ + +#include + + +#ifdef CONFIG_MIPS_FREEHAND +#include +#include /* for reading serial number */ +#endif + + +#undef CHECK_LATENCY +#undef RECORD_LATENCY +#undef MAX_INTR_LOOP_STATS //10 +#if defined(MAX_INTR_LOOP_STATS) +static u32 interrupt_loop_stats[MAX_INTR_LOOP_STATS+1]; +#endif +#ifdef RECORD_LATENCY +#define CP0_COUNTS 50 +u32 cp0_counts[CP0_COUNTS]; +u32 cp0_record; +#endif +u32 cp0_count; +unsigned int udc_saw_bus_activity; +int au_halt_dma_expired; +u32 au1x00_new_silicon; +u32 au1x00_inten; + +u8 au1x00_config_bulk[25] = { + 0x04, (USB_ENDPOINT_CONTROL << 4) | (EP0_PACKETSIZE & 0x380) >> 7, // 1 + (EP0_PACKETSIZE & 0x7F) << 1, 0x00, 0x00, + 0x24, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 6 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x02, + 0x34, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 11 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x03, + 0x44, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 16 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x04, + 0x54, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 21 + (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x05, +}; + +/* ********************************************************************************************* */ +/* Note - The endpoints names are confusing. To simplify mapping of the interrupt request lines + * we use a numbering of the physical endpoints of 0-5, which also matches the fifo numbering + * (see the config block). + * + * Physical FIFO Name Direction Logical + * 0 0 EP0 OUT 0 + * 1 1 EP0 IN 0 + * 2 2 EP1 IN 81 + * 3 3 EP2 IN 82 + * 4 4 EP3 OUT 3 + * 5 5 EP4 OUT 4 + * + * The ep_regs array maps a physical endpoint number to the registers required to access the udc + * for that endpoint. Note that ep0 is always accessed via 0. The epl2p array maps the logical + * addresses back to the physical number. The epp2l array maps the physical number to the + * logical endpoint address + */ +#ifdef DBDMA +struct ep_regs ep_regs[6] = { + + { rd: USBD_EP0RD, rds: USBD_EP0RDSTAT, /*rx_id: DMA_ID_USBDEV_EP0_RX,*/ cs: USBD_EP0CS, rx_str: "EP0 OUT RD", + wr: USBD_EP0WR, wrs: USBD_EP0WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX0, tx_str: "EP0 IN WR",}, + + { rds: 0, wrs: 0, indma: -1, outdma: -1, }, + { wr: NUSBD_EP1WR, wrs: NUSBD_EP1WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX1, cs: NUSBD_EP1CS, tx_str: "EP1 IN WR",}, + { wr: NUSBD_EP2WR, wrs: NUSBD_EP2WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX2, cs: NUSBD_EP2CS, tx_str: "EP2 IN WR",}, + + { rd: NUSBD_EP3RD, rds: NUSBD_EP3RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX3, cs: NUSBD_EP3CS, rx_str: "EP3 OUT RD",}, + { rd: NUSBD_EP4RD, rds: NUSBD_EP4RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX4, cs: NUSBD_EP4CS, rx_str: "EP4 OUT RD",}, +}; +#else /*DBMA */ +struct ep_regs ep_regs[6] = { + { rd: USBD_EP0RD, rds: USBD_EP0RDSTAT, /*rx_id: DMA_ID_USBDEV_EP0_RX,*/ cs: USBD_EP0CS, rx_str: "EP0 OUT RD", + wr: USBD_EP0WR, wrs: USBD_EP0WRSTAT, tx_id: DMA_ID_USBDEV_EP0_TX, tx_str: "EP0 IN WR",}, + { rds: 0, wrs: 0, indma: -1, outdma: -1, }, + { wr: NUSBD_EP1WR, wrs: NUSBD_EP1WRSTAT, tx_id: NDMA_ID_USBDEV_EP1_TX, cs: NUSBD_EP1CS, tx_str: "EP1 IN WR",}, + { wr: NUSBD_EP2WR, wrs: NUSBD_EP2WRSTAT, tx_id: NDMA_ID_USBDEV_EP2_TX, cs: NUSBD_EP2CS, tx_str: "EP2 IN WR",}, + + { rd: NUSBD_EP3RD, rds: NUSBD_EP3RDSTAT, rx_id: NDMA_ID_USBDEV_EP3_RX, cs: NUSBD_EP3CS, rx_str: "EP3 OUT RD",}, + { rd: NUSBD_EP4RD, rds: NUSBD_EP4RDSTAT, rx_id: NDMA_ID_USBDEV_EP4_RX, cs: NUSBD_EP4CS, rx_str: "EP4 OUT RD",}, +}; +#endif /*DBMA */ +u8 epl2p[6] = { 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, }; // map logical to physical +u8 epp2l[6] = { 0x00, 0x00, 0x81, 0x82, 0x03, 0x04, }; // map physical to logical + +static __inline__ void au_fifo_read(struct ep_regs *ep, unsigned char * cp, int bytes) +{ + u32 rd = ep->rd; + for (; bytes--; *cp++ = au_readl(rd)); +} + +static __inline__ void au_fifo_write(int ep, unsigned char * cp, int bytes) +{ + u32 wr = ep_regs[ep].wr; + for (; bytes--; au_writel(*cp++, wr)); + ep_regs[ep].last = bytes; +} + +void __inline__ au_inten(u32 inten) +{ + au1x00_inten = inten; + au_writel(au1x00_inten, USBD_INTEN); +} + +void __inline__ au_epn_interrupt_enable(int epn) +{ + au_inten(au1x00_inten | (1 << epn)); +} + +void __inline__ au_epn_interrupt_disable(int epn) +{ + au_inten(au1x00_inten & ~(1 << epn)); + au_writel((1 << epn) , USBD_INTSTAT); +} + +static void __inline__ send_zlp(unsigned char epn) +{ + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep_regs[epn].wrs); + au_writel(0 << 1, ep_regs[epn].cs); + au_writel(0, ep_regs[epn].wr); +} +/* ********************************************************************************************* */ +#define AU_DMA_HALT_POLL 0x1000 +#ifdef DBDMA +static void __inline__ au_halt_dma(int chan) +{ + au1xxx_dbdma_stop(chan); + au_halt_dma_expired++; +} + +static int __inline__ au_get_dma_residue(int chan) +{ + return au1xxx_get_dma_residue(chan); +} + +static void __inline__ au_start_dma_in(int indma, __u8 *bp, int len) +{ + //printk("\n----------------------STARTING DMA IN dma:%X buffer:%p len:%d---------------\n", indma, bp, len); + // au1xxx_dbdma_stop(indma); + au1xxx_dbdma_put_source(indma, bp, len); + au1xxx_dbdma_start(indma); +} + +static void __inline__ au_start_dma_out(unsigned int outdma, __u8 *bp, int packetSize) +{ + //printk("\n----------------------STARTING DMA OUT dma:%X buffer:%p len:%d---------------\n", outdma, bp, packetSize); + au1xxx_dbdma_put_dest(outdma, bp, packetSize); + au1xxx_dbdma_start(outdma); +} + +static u32 __inline__ au_get_dma_chan(u32 dma) +{ + return dma; +} +#else /* DBDMA */ +static void __inline__ au_halt_dma(struct dma_chan *chan) +{ + int i; + au_writel(DMA_GO, chan->io + DMA_MODE_CLEAR); + for (i = 0; i < AU_DMA_HALT_POLL; i++) + RETURN_IF (au_readl(chan->io + DMA_MODE_READ) & DMA_HALT); + au_halt_dma_expired++; +} + +static int __inline__ au_get_dma_residue(struct dma_chan *chan) +{ + int curBufCntReg = (au_readl(chan->io + DMA_MODE_READ) & DMA_AB) ? DMA_BUFFER1_COUNT : DMA_BUFFER0_COUNT; + return au_readl(chan->io + curBufCntReg) & DMA_COUNT_MASK; +} + +static void __inline__ au_start_dma(struct dma_chan *chan, u8 *bp, int len) +{ + if (au_readl(chan->io + DMA_MODE_READ) & DMA_AB) { + au_writel(DMA_D1, chan->io + DMA_MODE_CLEAR); + au_writel(len & DMA_COUNT_MASK, chan->io + DMA_BUFFER1_COUNT); + au_writel(0, chan->io + DMA_BUFFER0_COUNT); + au_writel(virt_to_phys(bp), chan->io + DMA_BUFFER1_START); + au_writel(DMA_BE1, chan->io + DMA_MODE_SET); + } + else { + au_writel(DMA_D0, chan->io + DMA_MODE_CLEAR); + au_writel(len & DMA_COUNT_MASK, chan->io + DMA_BUFFER0_COUNT); + au_writel(0, chan->io + DMA_BUFFER1_COUNT); + au_writel(virt_to_phys(bp), chan->io + DMA_BUFFER0_START); + au_writel(DMA_BE0, chan->io + DMA_MODE_SET); + } + au_writel(DMA_GO, chan->io + DMA_MODE_SET); +} + +static void __inline__ au_start_dma_in(int indma, u8 *bp, int len) +{ + au_start_dma(get_dma_chan(indma), bp, len); +} + +static void __inline__ au_start_dma_out(int outdma, u8 *bp, int wMaxPacketSize) +{ + dma_cache_inv((unsigned long) bp, wMaxPacketSize); + au_start_dma(get_dma_chan(outdma), bp, wMaxPacketSize); +} + +static __inline__ struct dma_chan * au_get_dma_chan(u32 dma) +{ + return get_dma_chan(dma); +} +#endif /* DBDMA */ +static struct usbd_urb *au_rcv_complete_irq(struct usbd_endpoint_instance *endpoint, int len, int urb_bad) +{ + return pcd_rcv_complete_irq(endpoint, len, urb_bad); +} +/* ********************************************************************************************* */ +/* au_start_in_ep0 - start transmit + */ +static void au_start_in_ep0 (struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_urb *urb = endpoint->tx_urb; + int last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); + TRACE_MSG2(PCD, "START IN EP0 SENT: %d SENDING: %d", endpoint->sent, last); + RETURN_IF ((urb->actual_length - endpoint->sent) <= 0); + au_writel(last << 1, ep->cs); // XXX + au_fifo_write(0, urb->buffer + endpoint->sent, last); + endpoint->last = last; +} + +/* au_in_ep0 - called to service an endpoint zero IN interrupt, data sent + */ +static void au_in_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + u32 cs; + struct usbd_urb *tx_urb; + int last; + TRACE_MSG1(PCD, "EP0 IN: tx_urb: %p", (int)endpoint->tx_urb); + if ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) { // clear stall if present + TRACE_MSG1(PCD, "CLEAR STALL %d", 0); + cs &= ~USBDEV_CS_STALL; + au_writel(cs, ep->cs); + return; + } + if (!(tx_urb = pcd_tx_complete_irq(endpoint, 0))) { // wait for setup if no more data + endpoint->state = WAIT_FOR_SETUP; + return; + } + TRACE_MSG4(PCD, "EP0 IN actual: %d last: %d sent: %d flags: %x", endpoint->tx_urb->actual_length, + endpoint->last, endpoint->sent, endpoint->tx_urb->flags); + if (pcd_tx_sendzlp(endpoint)) { // check if tx_urb we have is finished + TRACE_MSG0(PCD, "EP0 IN BULK - sending ZLP"); + tx_urb->flags &= ~USBD_URB_SENDZLP; + send_zlp(0); + pcd_tx_complete_irq(endpoint, 0); + return; + } + if (tx_urb->actual_length > endpoint->sent) { + if ((tx_urb->actual_length - endpoint->sent) < endpoint->wMaxPacketSize) + TRACE_MSG1(PCD, "EP0 IN starting short packet %d", tx_urb->actual_length - endpoint->sent); + else + TRACE_MSG1(PCD, "EP0 IN LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); + au_start_in_ep0(endpoint, ep); + } +} + +/* au_out_ep0 - called to service an endpoint zero OUT interrupt, data received + */ +static void au_out_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_device_request request; + int i; + u32 cs; + u32 bytes; + TRACE_MSG0(PCD, "EP0 OUT"); + cs = au_readl(ep->cs); // check if host aborted transfer and flush the write fifo + bytes = au_readl(ep->rds) & USBDEV_FSTAT_FCNT_MASK; + if (endpoint->state == DATA_STATE_RECV) { + struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); + TRACE_MSG1(PCD, "EP0 OUT: RECV: rcv_urb: %x", (int) rcv_urb); + if (rcv_urb) { + au_fifo_read(ep, rcv_urb->buffer + rcv_urb->actual_length, bytes); + if (au_rcv_complete_irq(endpoint, bytes, 0)) + return; + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + endpoint->state = WAIT_FOR_SETUP; + send_zlp(0); + return; + } + endpoint->state = WAIT_FOR_SETUP; + } + pcd_tx_cancelled_irq(endpoint); + pcd_rcv_cancelled_irq(endpoint); + au_fifo_read(ep, (u8 *)&request, bytes); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); + if (bytes != 8) { + TRACE_MSG1(PCD, "ERROR SETUP SET not eight bytes: %d", bytes); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); + return; + } + TRACE_MSG4(PCD, "SETUP bmRequestType: %02x bRequest %02x state: %d status: %d", request.bmRequestType, + request.bRequest, pcd_instance->bus->device_state, pcd_instance->bus->status); + switch (request.bRequest) { // we need to simply ignore any of these + case USB_REQ_SET_ADDRESS: // Fake a bus reset IFF not state default and then process normally + BREAK_IF (pcd_instance->bus->device_state == STATE_DEFAULT); + udc_saw_bus_activity = 0; + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); + break; + case USB_REQ_GET_DESCRIPTOR: // Fake a bus reset IFF suspended and then process normally + BREAK_IF (STATE_SUSPENDED != pcd_instance->bus->device_state); + udc_saw_bus_activity = 0; + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); + break; + } + if (pcd_recv_setup_irq(pcd_instance, &request)) { + TRACE_MSG1(PCD, "ep0 STALL %d", cs); + au_writel(USBDEV_CS_STALL, USBD_EP0CS); + return; + } + if (((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) && le16_to_cpu (request.wLength)) { + TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); + endpoint->state = DATA_STATE_RECV; + return; + } + if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); + if ((request.bmRequestType & ~USB_REQ_DIRECTION_MASK)) { + TRACE_MSG1(PCD, "ep0 Class or Vendor, send ZLP %d", cs); + send_zlp(0); + return; + } + } + TRACE_MSG1(PCD, "ep0 Class D2H request %04x", le16_to_cpu(request.wLength)); +} +/* ********************************************************************************************* */ +/* au_start_in_bulk - start transmit + * The au1x00 will start to send when the first byte is loaded into the FIFO, either by + * DMA or PIO. The packetsize must be set first. + */ +static void au_start_in_bulk (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_urb *urb = endpoint->tx_urb; + unsigned char *bp = urb->buffer + endpoint->sent; + int last; + TRACE_MSG1(PCD, "START IN BULK %d", epn); + RETURN_IF (!urb || (( (urb->actual_length - endpoint->sent) == 0) && !(urb->flags & USBD_URB_SENDZLP))); + last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); + TRACE_MSG2(PCD, "START IN BULK sent: %d last:%d", endpoint->sent, last); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX + if (!last) { + if (endpoint->tx_urb->flags & USBD_URB_SENDZLP) { + TRACE_MSG0(PCD, "START IN BULK - zending ZLP"); + send_zlp(epn); + endpoint->tx_urb->flags &= ~USBD_URB_SENDZLP; + } + return; + } + else if (au1x00_new_silicon && (8 >= last)) { + au_writel(last << 1, ep->cs); + au_fifo_write(epn, bp, last); + return; + } + if (!au1x00_new_silicon) + au_epn_interrupt_disable(epn); + dma_cache_wback_inv((unsigned long) bp, last); + au_writel(last << 1, ep->cs); + au_start_dma_in(ep->indma, bp, last); +} + +static void au_in_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb; + int rc = 0; + u32 cs = au_readl(ep->cs); + u32 wrs = au_readl(ep->wrs); + TRACE_MSG2(PCD, "BULK IN EPN - cs: %x wrs: %x", cs, wrs); + if (!au1x00_new_silicon) + if (epn && (ep->last > 8)) { + TRACE_MSG2(PCD, "BULK IN EPN - DMA ACTIVE epn %d last %d", epn, ep->last); + au_epn_interrupt_disable(epn); + return; + } + if (wrs) { // check for underflow or overflow + rc = 1; + if (wrs & USBDEV_FSTAT_UF) { + TRACE_MSG2(PCD, "BULK IN EPN - UF epn %d wrs: %x", epn, wrs); + rc = 1; // set rc to indicate an error + } + if (wrs & USBDEV_FSTAT_OF) + TRACE_MSG2(PCD, "BULK IN EPN - OF epn %d wrs: %x", epn, wrs); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // flush the fifo + } + if (cs & USBDEV_CS_NAK) { + RETURN_IF (ep->last && ((wrs&0x1f) == ep->last)); + rc = 1; + } + if (cs & USBDEV_CS_STALL) { // clear stall if present + TRACE_MSG1(PCD, "BULK IN EPN - CLEAR STALL %d", epn); + cs &= ~USBDEV_CS_STALL; + au_writel(cs, ep->cs); + return; + } + TRACE_MSG4(PCD, "BULK IN EPN epn: %d rc: %d last: %d sent: %d", epn, rc, endpoint->last, endpoint->sent); + if ((tx_urb = pcd_tx_complete_irq(endpoint, rc))) { + if ((tx_urb->actual_length > endpoint->sent) || (endpoint->tx_urb->flags & USBD_URB_SENDZLP)) { + au_start_in_bulk(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); + /* XXX magic delay - without this large packets will eventually stall the transmit + * XXX and all traffic in both directions will stop. + */ + if (!au1x00_new_silicon) + udelay(8); + TRACE_MSG1(PCD, "BULK IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); + return; + } + } + au_epn_interrupt_disable(epn); // disable interrupts +} + +/* au_start_in_iso - start transmit + * The au1x00 will start to send when the first byte is loaded into the FIFO, either by DMA or + * PIO. The packetsize must be set first. + */ +static void au_start_in_iso (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + struct usbd_urb *urb = endpoint->tx_urb; + unsigned char *bp = urb->buffer + endpoint->sent; + int last; + TRACE_MSG2(PCD, "START IN ISO actual: %d sent: %d", urb->actual_length, endpoint->sent); + RETURN_IF ((urb->actual_length - endpoint->sent) == 0); + last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); + TRACE_MSG2(PCD, "START IN ISO last: %d packetSize: %d", last, endpoint->wMaxPacketSize); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX + au1x00_new_silicon ? au_epn_interrupt_enable : au_epn_interrupt_disable (epn); + dma_cache_wback_inv((unsigned long) bp, last); + au_writel(last << 1, ep->cs); + au_start_dma_in(ep->indma, bp, last); +} + +static void au_in_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb = pcd_tx_complete_irq(endpoint, 0); + u32 cs = au_readl(ep->cs); + u32 wrs = au_readl(ep->wrs); + TRACE_MSG2(PCD, "ISO IN EPN - cs: %x wrs: %x", cs, wrs); + if (!au1x00_new_silicon) + if (epn && (ep->last > 8)) { + au_epn_interrupt_disable(epn); + return; + } + TRACE_MSG4(PCD, "ISO IN EPN epn: %d last: %d sent: %d", epn, endpoint->last, endpoint->sent, 0); + ep->last = 0; + if ((tx_urb = endpoint->tx_urb) && (tx_urb->actual_length > endpoint->sent)) { + au_start_in_iso(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); + /* XXX magic delay - without this large packets will eventually stall the transmit + * XXX and all traffic in both directions will stop. + */ + if (!au1x00_new_silicon) + udelay(8); + TRACE_MSG1(PCD, "ISO IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); + return; + } + else + TRACE_MSG0(PCD, "ISO IN EPN - nothing to send"); + au_epn_interrupt_disable(epn); // disable interrupts +} +/* ********************************************************************************************* */ + +static void au_start_out_bulk(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + TRACE_MSG2(PCD, "START OUT BULK %d %d", epn, endpoint->wMaxPacketSize); + if (!endpoint->rcv_urb) { + TRACE_MSG0(PCD, "START OUT BULK DISABLE"); + au_epn_interrupt_disable(epn); + return; + } + if (endpoint->rcv_error) { + TRACE_MSG0(PCD, "START OUT BULK reseting rcv_error"); + endpoint->rcv_error = 0; + } + au_start_dma_out(ep->outdma, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); +} + +static void au_out_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + int bytes = 0; + struct usbd_urb *urb; + struct usbd_urb *completed_urb = NULL; + u32 cs; + u32 rds; + u32 nrds; + au_halt_dma(au_get_dma_chan(ep->outdma)); + cs = au_readl(ep->cs); + rds = au_readl(ep->rds); + TRACE_MSG2(PCD, "BULK OUT CS: %04x RD: %04x", cs, rds); + bytes = endpoint->wMaxPacketSize - au_get_dma_residue(au_get_dma_chan(ep->outdma)); + if (!(urb = pcd_rcv_next_irq(endpoint))) { + TRACE_MSG0(PCD, "BULK OUT EPN - no rcv_urb"); + au_epn_interrupt_disable(epn); + return; + } + /* The original AU1X00 UDC design will continue to receive data as long as there is room + * in the FIFO. We cannot tell when we are at the end of a packet and/or have the start + * of a new one. + * + * There are only two scenarios that are guaranteed (almost) to be correct: + * + * 64 bytes of data from DMA, empty fifo, continue Bulk OUT < 60 bytes of data and + * < 4 bytes in fifo, end Bulk OUT. + * + * There may be a third scenario that is ok: + * + * 0 bytes dma, 0 bytes in fifo, NAK + * + * Everything else is an error. In all cases we assume that it is safer to drop data + * than to accept it in error. This allows CRC or size protected encapsulations to + * notice bulk transfers received with errors. + * + * In general none of the policies or strategies are able to cope with all errors + * without missing errors and dropping good data. The intent is to minimize the amount + * of potentially bad data getting to the function driver while minimizing the amount of + * good data that is dropped. + * + * The new silicon mitigates this problem for non control endpoints because it will NAK + * additional data until the interrupt service flag is reset. + * + * Start with generic error tests, OF, UF or NAK indicate an error we cannot recover + * from, start flushing until end of current bulk transfer (wait for a short packet) + * + */ + if (rds & (USBDEV_FSTAT_OF | USBDEV_FSTAT_UF)) { + TRACE_MSG2(PCD, "BULK OUT FLUSHING %d length: %d", bytes, urb->actual_length); + THROW(start_flushing); + } + rds = rds & USBDEV_FSTAT_FCNT_MASK; + nrds = au_readl(ep->rds); + if (64 == bytes) { + TRACE_MSG2(PCD, "BULK OUT 64 BYTES %d length: %d", bytes, urb->actual_length); + /* full size packet received, check that we are not flushing and that the FIFO + * does not have any data. If there is data in the FIFO we may not be able to + * restart DMA in time, so start flushing + */ + if (endpoint->rcv_error) { + TRACE_MSG4(PCD, "FULL PACKET bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + if ((nrds > 6) && (nrds < 8) ) { + TRACE_MSG4(PCD, "FIFO not empty bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING nrds > 6 < 8", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + if (!urb->actual_length) + TRACE_MSG4(PCD, "PACKET ok bytes: %d rds: %d nrds: %d cp0: %d ACCEPTING 64 bytes", + bytes, rds, nrds, cp0_count); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + au_rcv_complete_irq(endpoint, 64, 0); + if (cs & USBDEV_CS_NAK) + if (nrds && (nrds < 8) ) { + TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING CS_NAK", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + } + else if ((cs & USBDEV_CS_NAK) && (!nrds || (nrds == 8)) ) { + TRACE_MSG2(PCD, "BULK OUT NAK BYTES %d length: %d", bytes, urb->actual_length); + /* a nak'd packet may be ok to ignore IFF the FIFO is empty(?) or completely full. + */ + if (endpoint->rcv_error) { + TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + } + else { + TRACE_MSG2(PCD, "BULK OUT < 64 BYTES %d length: %d", bytes, urb->actual_length); + /* short packet by DMA, additional data for this packet in FIFO, more than 3 + * bytes is probably an error. + */ + if ((cs & USBDEV_CS_NAK) && nrds && (nrds < 8) ) { + TRACE_MSG4(PCD, "BULK OUT NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + if (nrds > 4) { + TRACE_MSG4(PCD, "BULK OUT SHORT PACKET by DMA full FIFO bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", + bytes, rds, nrds, cp0_count); + THROW(start_flushing); + } + TRACE_MSG4(PCD, "BULK OUT SHORT bytes: %d rds: %d nrds: %d cp0: %d reading fifo", bytes, rds, nrds, cp0_count); + au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, nrds); + bytes += nrds; + TRACE_MSG1(PCD, "BULK OUT < 64 BYTES %d", bytes); + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + if (!endpoint->rcv_error) { + TRACE_MSG0(PCD, "BULK OUT COMPLETED URB"); + au_rcv_complete_irq(endpoint, bytes, 0); + } + else { + TRACE_MSG0(PCD, "BULK OUT - FLUSHING URB - reseting rcv_error"); + endpoint->rcv_error = 0; + } + } + CATCH(start_flushing) { + TRACE_MSG0(PCD, "BULK OUT - START FLUSHING URB"); + endpoint->rcv_error = 1; + endpoint->rcv_urb->actual_length = 0; + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + bytes = 0; + } + TRACE_MSG0(PCD, "BULK OUT - RESTARTING"); + au_start_out_bulk(epn, endpoint, ep); +} + +static void au_start_out_iso(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) +{ + int outdma = ep->outdma; + RETURN_IF(!endpoint->rcv_urb); + au_start_dma_out(outdma, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); +} + +static void au_out_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) +{ + int bytes = 0; + struct usbd_urb *urb; + u32 cs; + u32 rds; + au_halt_dma(au_get_dma_chan(ep->outdma)); + cs = au_readl(ep->cs); + rds = au_readl(ep->rds); + if (!endpoint) { + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + return; + } + if (!(urb = endpoint->rcv_urb)) { + TRACE_MSG2(PCD, "ISO OUT EPN - rcv_urb was NULL bytes: %d rds: %d", bytes, rds); + au_rcv_complete_irq(endpoint, bytes, 1); + TRACE_MSG0(PCD, "ISO OUT setting rcv_error"); + } + bytes = endpoint->wMaxPacketSize - au_get_dma_residue(au_get_dma_chan(ep->outdma)); + rds = rds & USBDEV_FSTAT_FCNT_MASK; + au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, rds); + bytes += rds; + au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); + au_rcv_complete_irq(endpoint, bytes, 0); + au_start_out_iso(epn, endpoint, ep); +} +/* ********************************************************************************************* */ +/* au_tx_dma_done - TX DMA interrupt handler + */ +static void au_tx_dma_done(int irq, void *dev_id, struct pt_regs *regs) +{ + int epn = (int) dev_id; + struct usbd_endpoint_instance *endpoint = pcd_instance->bus->endpoint_array + epn; + struct ep_regs *ep = &ep_regs[epn]; + int residue; +#if 0 + int indma = irq - 6; + struct dma_chan *chan = get_dma_chan(indma); +#endif +#ifdef DBDMA +#else /* DBDMA */ + struct dma_chan *chan = get_dma_chan(ep->indma); + u32 mode = au_readl(chan->io + DMA_MODE_READ); + TRACE_MSG1(PCD, "TX DMA done: mode: %x", mode); + if (mode & DMA_D0) + au_writel(DMA_D0, chan->io + DMA_MODE_CLEAR); + if (mode & DMA_D1) + au_writel(DMA_D1, chan->io + DMA_MODE_CLEAR); +#endif /* DBDMA */ + ocd_ops.interrupts++; + RETURN_IF (!epn); + au_halt_dma(au_get_dma_chan(ep->indma)); + residue = au_get_dma_residue(au_get_dma_chan(ep->indma)); + TRACE_MSG2(PCD, "TX DMA IRQ - epn %d residue %d", epn, residue); + if (!au1x00_new_silicon) { + /* XXX magic delay - without this large packets will eventually stall the transmit + * XXX and all traffic in both directions will stop. + */ + udelay(8); + pcd_tx_complete_irq (endpoint, residue?1:0); + au_epn_interrupt_enable(epl2p[endpoint->bEndpointAddress&0xf]); + } + ep->last = 0; +} + +/* au_int_req - usb interrupt handler + */ +static void au_int_req (int irq, void *dev_id, struct pt_regs *regs) +{ + u32 intstat; + struct ep_regs *ep; +#if defined(MAX_INTR_LOOP_STATS) + u32 loopcount = 0; +#endif +#ifdef RECORD_LATENCY + cp0_count = (read_c0_count(CP0_COUNT) - cp0_record) >> 9; + if (cp0_count < CP0_COUNTS) + cp0_counts[cp0_count]++; +#endif +#ifdef CHECK_LATENCY + u32 cp0_count = read_c0_count(CP0_COUNT); +#endif + ocd_ops.interrupts++; +#if 0 + if (ocd_ops.interrupts > 2000) { + TRACE_MSG0(PCD, "UDC_INT call udc_disable_interrupts"); + au_inten(0); + return; + } +#endif + while (( intstat = au_readl(USBD_INTSTAT) & au1x00_inten)) { // read and reset interrupt status register + + int epn; +#if 1 + for (epn = 2; epn < 6; epn++) { + CONTINUE_IF (!(intstat & (1 << epn))); +#else + // NOT TESTED + u32 local_intstat = intstat; + TRACE_MSG2(PCD, "INTSTAT: %04x %04x", intstat, au_readl(USBD_INTEN)); + while (local_intstat & 0x3f) { + int epn = 31 - au_clz(local_intstat); + local_intstat &= ~(1<bus, DEVICE_BUS_ACTIVITY, 0); + au_inten(0x0033|USBDEV_INT_SOF); + } + ep = &ep_regs[epn]; + switch(ep->eptype) { + case USB_DIR_IN | USB_ENDPOINT_BULK: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: + //TRACE_MSG2(PCD, "BULK IN %d %02x", epn, ep->eptype); + au_in_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: + //TRACE_MSG2(PCD, "ISO IN %d %02x", epn, ep->eptype); + au_in_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + case USB_DIR_OUT | USB_ENDPOINT_BULK: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: + //TRACE_MSG2(PCD, "BULK OUT %d %02x", epn, ep->eptype); + au_out_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: + //TRACE_MSG2(PCD, "ISO OUT %d %04d", epn, au_readl(NUSBD_FRAMENUM)); + au_out_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); + break; + } + } + au_writel(intstat, USBD_INTSTAT); // Only clear the interrupt(s) AFTER servicing OUT + /* even though we disable the bulk-in interrupt (endpoint 2) prior to enabling + * DMA we always see one additional interrupt that is a NAK on that endpoint. + */ + CONTINUE_IF(!(intstat & au1x00_inten)); + /* handle control endpoint and suspend interrupt + */ + if (intstat & ( ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 5) | USBDEV_INT_SOF))) { + if (intstat & (1 << 0)) + au_out_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); + if (intstat & (1 << 1)) + au_in_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); + if (intstat & USBDEV_INT_SOF) + if (USBD_SUSPENDED == pcd_instance->bus->status) { + TRACE_MSG0(PCD, "SUS - ACTIVITY"); + udc_saw_bus_activity++; + } + } +#if defined(MAX_INTR_LOOP_STATS) + loopcount += 1; // Gather stats on how many times this loop is performed. +#endif + } +#if defined(MAX_INTR_LOOP_STATS) + interrupt_loop_stats[MIN(loopcount, MAX_INTR_LOOP_STATS)]++; +#endif +#if defined(CHECK_LATENCY) + TRACE_MSG1(PCD, "USB IRQ - %d", read_c0_count(CP0_COUNT) - cp0_count); +#endif +#if defined(RECORD_LATENCY) + cp0_record = read_c0_count(CP0_COUNT); +#endif +} + +/* au_int_sus -suspend interrupt handler + */ +static void au_int_sus (int irq, void *dev_id, struct pt_regs *regs) +{ + ocd_ops.interrupts++; + TRACE_MSG2(PCD, "SUS - INACTIVE device: %d status: %d", pcd_instance->bus->device_state, pcd_instance->bus->status); + switch(pcd_instance->bus->status) { + case USBD_OPENING: + case USBD_OK: + au_inten(0x0033); + usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_BUS_INACTIVE, 0); + break; + default: + break; + } +} +/* ********************************************************************************************* */ +/* + * au_connect - enable pullup resistor + * Turn on the USB connection by enabling the pullup resistor. + * + * au_disconnect - disable pullup resistor + * Turn off the USB connection by disabling the pullup resistor. + */ + +#if defined(CONFIG_MIPS_PICOENGINE_MVCI) +void au_connect (struct pcd_instance *pcd) +{ + extern void pico_mvci_set_usb_pullup(int); + pico_mvci_set_usb_pullup(1); +} + +void au_disconnect (struct pcd_instance *pcd) +{ + extern void pico_mvci_set_usb_pullup(int); + pico_mvci_set_usb_pullup(0); +} + +#elif defined(CONFIG_MIPS_FREEHAND_NATIVE) +void au_connect (struct pcd_instance *pcd) +{ + au1000gpio_set(GPIO01); +} + +void au_disconnect (struct pcd_instance *pcd) +{ + au1000gpio_clear(GPIO01); +} +#elif defined(CONFIG_AMX_MORDOR) +void au_connect (struct pcd_instance *pcd) +{ + au_writel(0x00000008, SYS_OUTPUTSET); +} + +void au_disconnect (struct pcd_instance *pcd) +{ + au_writel(0x00000008, SYS_OUTPUTCLR); +} +#else +void au_connect (struct pcd_instance *pcd) +{ +} +void au_disconnect (struct pcd_instance *pcd) +{ +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) +#undef read_c0_prid +#define read_c0_prid() read_32bit_cp0_register(CP0_PRID) +#endif +#undef read_c0_COUNT +#ifndef read_c0_count +#define read_c0_count() read_32bit_cp0_register(CP0_COUNT) +#endif +/* au_loc_conn - used to enable or disable peripheral connecting to bus + * + */ +void au_loc_conn(struct otg_instance *otg, u8 flag) +{ + struct pcd_instance *pcd = (struct pcd_instance *)otg->pcd; + TRACE_MSG0(PCD, "--"); + TRACE_MSG1(PCD, "PCD: %x", (int)pcd); + switch (flag) { + case SET: + TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_SET"); + TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET - Enable DP PULLUP"); + au_connect(pcd); + break; + + case RESET: + TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET"); + TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET - Disable DP PULLUP"); + au_disconnect(pcd); + break; + + case PULSE: + TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_PULSE"); + break; + } +} + +/* ********************************************************************************************* */ +/* au_ticks - get current ticks +* */ +u64 au_ticks (void) +{ + return read_c0_count(); +} + +/* au_elapsed - return micro-seconds between two tick values + */ +u64 au_elapsed(u64 *t1, u64 *t2) +{ + u64 ticks = (*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1); + ticks = (u32)((u32) ticks / (u32) CONFIG_OTG_AU1X00_SCLOCK); + return ticks; +} + +/* au_framenum - get current framenum + */ +u16 au_framenum (void) +{ + return au_readl(NUSBD_FRAMENUM); +} + + +/* ********************************************************************************************* */ +/* au_start_endpoint_in - start transmit + */ +void au_start_endpoint_in(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + int epn = epl2p[endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { + case USB_ENDPOINT_CONTROL: + au_in_ep0(endpoint, ep); + break; + case USB_ENDPOINT_BULK: + case USB_ENDPOINT_INTERRUPT: + au_start_in_bulk(epn, endpoint, ep); + break; + case USB_ENDPOINT_ISOCHRONOUS: + au_start_in_iso(epn, endpoint, ep); + break; + } + if (au1x00_new_silicon) + au_epn_interrupt_enable(epn); +} + +/* au_start_endpoint_out - start receive + */ +void au_start_endpoint_out(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + int epn = epl2p[endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { + case USB_ENDPOINT_CONTROL: + break; + case USB_ENDPOINT_BULK: + case USB_ENDPOINT_INTERRUPT: + au_start_out_bulk(epn, endpoint, ep); + au_epn_interrupt_enable(epn); + break; + case USB_ENDPOINT_ISOCHRONOUS: + au_start_out_iso(epn, endpoint, ep); + break; + } +} + +void au_cancel_in_irq(struct pcd_instance *pcd, struct usbd_urb *urb) +{ + int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + au_in_bulk(epn, ep, urb->endpoint); +} + +void au_cancel_out_irq(struct pcd_instance *pcd, struct usbd_urb *urb) +{ + int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; + struct ep_regs *ep = &ep_regs[epn]; + au_out_bulk(epn, ep, urb->endpoint); +} + +/* au_serial_init - set a serial number if available + */ +int au_serial_init (struct pcd_instance *pcd) +{ +#if defined(CONFIG_MIPS_FREEHAND) + int length; + long data[16]; /* yeah a hack, but we KNOW it's 16 */ + struct i2c_client *client; + char chData[16]; + int i; + + if (!(client = getFreeHandEepromClient())) { + printk(KERN_INFO"eeprom not ready when au_serial_init called\n"); + return -EINVAL; + } + eeprom_contents(client, SENSORS_PROC_REAL_READ, EEPROM_SYSCTL1, &length, (long *)data); + + /* serial number is first 9 longs. But each long is just an ASCII char + * convert this to a string and then extract a 4 byte value from it + */ + for (i = 0; i < 9; chData[i] = (char)data[i], i++); /* trunc, is ok */ + chData[9] = 0; /* terminate string */ + printk(KERN_INFO"%s: %s\n", __FUNCTION__, chData); + pcd_instance->bus->serial_number_str = lstrdup(chData); + return 0; +#else + return -EINVAL; +#endif +} + + +#ifdef DBDMA +static int request_dma(int ep, int src, int dest, char *str, void dma_done(int , void *, struct pt_regs *)) +{ + unsigned int dma; + + if ((dma = au1xxx_dbdma_chan_alloc(src, dest, dma_done, (void *)ep)) < 0) { + printk(KERN_INFO"request_io[%d] dma: %X src: %x dest: %x %s FAILED\n", ep, dma, src, dest, str); + return -1; + } + + au1xxx_dbdma_set_devwidth(dma, 16); + + if(au1xxx_dbdma_ring_alloc(dma, 4) == 0) { + printk(KERN_INFO"Ring Buffer Allocation for dma: %X src: %x dest: %x %s FAILED\n", ep, dma, src, dest, str); + return -1; + } + printk(KERN_INFO"request_io[%d] dma: %X src: %x dest: %x %s SUCCEDED\n", ep, dma, src, dest, str); + return dma; +} +#else /* DBDMA */ +static int request_dma(int ep, int id, char *str, void dma_done(int , void *, struct pt_regs *)) +{ + int dma = request_au1000_dma(id, str, dma_done, SA_INTERRUPT, (void *)ep); + if (dma >= 0) return dma; + printk(KERN_INFO"request_io[%d] dma: %d id: %x %s FAILED\n", ep, dma, id, str); + return -1; +} +#endif /* DBDMA */ + +/* au_start - start session + */ +void au_start (struct pcd_instance *pcd) +{ + au_inten(0x0033|USBDEV_INT_SOF); // Only enable receive interrupts. +} + +/* au_stop - stop session + */ +void au_stop (struct pcd_instance *pcd) +{ + au_inten(0); +} + +int au_assign_endpoint( u8 physicalEndpoint, int used[6], struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, + u16 wMaxPacketSize, u16 transferSize) +{ + struct ep_regs *ep = &ep_regs[physicalEndpoint]; + RETURN_EINVAL_IF(used[physicalEndpoint]); + endpoint_map->bEndpointAddress[0] = epp2l[physicalEndpoint]; + endpoint_map->physicalEndpoint[0] = physicalEndpoint; + endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; + endpoint_map->transferSize[0] = transferSize; + endpoint_map->bmAttributes[0] = bmAttributes; + used[physicalEndpoint]++; + ep->eptype = bmAttributes & 0x83; + return 0; +} + +int au_request_endpoints(struct pcd_instance *pcd, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, + struct usbd_endpoint_request *requestedEndpoints) +{ + struct usbd_device_description *device_description; + int i, j; + int used[UDC_MAX_ENDPOINTS]; + memset(used, 0, sizeof(used)); + for (j = 0, i = 0; i < endpointsRequested; i++, j++) { + struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; + u8 bmAttributes = requestedEndpoints[i].bmAttributes; + u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; + RETURN_EINVAL_UNLESS(j < UDC_MAX_ENDPOINTS); + switch(bmAttributes) { + case USB_DIR_OUT | USB_ENDPOINT_BULK: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, 0x40, transferSize)); + CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, 0x40, transferSize)); + break; + case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: + CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, transferSize, transferSize)); + CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, transferSize, transferSize)); + break; + case USB_DIR_IN | USB_ENDPOINT_BULK: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, 0x40, transferSize)); + CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, 0x40, transferSize)); + break; + case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: + CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, transferSize, transferSize)); + CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, transferSize, transferSize)); + break; + } + CONTINUE_IF(bmAttributes & USB_ENDPOINT_OPT); + return -EINVAL; + } + return 0; +} + +int au_set_endpoints(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) +{ + int i, j; + u8 config[25]; + u8 *cp; + memcpy(config, au1x00_config_bulk, sizeof(config)); + for (i = 0; i < endpointsRequested; i++) { + struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; + int epreq = endpoint_map->bmAttributes[0]; + int eptype = epreq & USB_ENDPOINT_MASK; + int epdir = epreq & USB_ENDPOINT_DIR_MASK ? 0x8 : 0; + int epsize = endpoint_map->wMaxPacketSize[0]; + int epaddr = epp2l[endpoint_map->physicalEndpoint[0]]; + CONTINUE_IF(!endpoint_map->physicalEndpoint[0] || (endpoint_map->physicalEndpoint[0] > 5)); + cp = config + ((endpoint_map->physicalEndpoint[0] - 1) * 5); + cp[0] = (epaddr & 0xf) << 4 | 0x4; + cp[1] = (eptype << 4) | epdir | (epsize & 0x380) >> 7; + cp[2] = (epsize & 0x7F) << 1; + } + au_inten(0); // disable interrupts + au_writel(0x0002, USBD_ENABLE); // reset controller + udelay(100); + au_writel(0x0003, USBD_ENABLE); // enable controller + udelay(100); + for (cp = config, i = 0; i < 25; i++, au_writel(*cp++, USBD_CONFIG)); // feed the config into the UDC + return 0; +} +/* ********************************************************************************************* */ +/* au_mod_init + */ +int au_mod_init (void) +{ + int dev = request_irq (AU1000_USB_DEV_REQ_INT, au_int_req, SA_INTERRUPT, UDC_NAME "UDC Req", NULL); + int sus = request_irq (AU1000_USB_DEV_SUS_INT, au_int_sus, SA_INTERRUPT, UDC_NAME "UDC Sus", NULL); + u32 cp0_prid = read_c0_prid(); + int i; + + + if (dev || sus) { // check if either request irq failed + if (!dev) free_irq (AU1000_USB_DEV_REQ_INT, NULL); // free irqs that might have + if (!sus) free_irq (AU1000_USB_DEV_SUS_INT, NULL); // been successfully allocated + return -EINVAL; + } +#ifdef DBDMA + for (i = 0; i < 6; i++) { + ep_regs[i].indma = ep_regs[i].tx_id ? + request_dma(i, DBDMA_MEM_CHAN, ep_regs[i].tx_id, ep_regs[i].tx_str, au_tx_dma_done) : -1; + ep_regs[i].outdma = ep_regs[i].rx_id ? + request_dma(i, ep_regs[i].rx_id, DBDMA_MEM_CHAN, ep_regs[i].rx_str, NULL) : -1; + } +#else /*DBMA */ + for (i = 0; i < 6; i++) { + ep_regs[i].indma = ep_regs[i].tx_id ? request_dma(i, ep_regs[i].tx_id, ep_regs[i].tx_str, au_tx_dma_done) : -1; + ep_regs[i].outdma = ep_regs[i].rx_id ? request_dma(i, ep_regs[i].rx_id, ep_regs[i].rx_str, NULL) : -1; + } +#endif /*DBMA */ + switch (cp0_prid & CP0_PRID_SOC_MASK) { + case CP0_PRID_AU1000: + case CP0_PRID_AU1100: + au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 4; + printk(KERN_INFO"%s: AU1100 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon); + break; + case CP0_PRID_AU1500: + au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 2; + printk(KERN_INFO"%s: AU1500 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon); + break; + case CP0_PRID_AU1550: + au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 0; + printk(KERN_INFO"%s: AU1550 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon); + break; + default: + printk(KERN_INFO"%s: UNKNOWN CPU cp0_prid: %08x UNKNOWN UDC\n", __FUNCTION__, cp0_prid); + break; + } + return 0; +} + +/* au_mod_exit + */ +void au_mod_exit (void) +{ + int j; + au_writel(0x0000, USBD_ENABLE); + for (j = 0; j < 6; j++) { + struct ep_regs *ep = &ep_regs[j]; +#ifdef DBDMA + if (ep->indma != -1) { + au1xxx_dbdma_chan_free(ep->indma); + ep->indma = -1; + } + if (ep->outdma != -1) { + au1xxx_dbdma_chan_free(ep->outdma); + ep->outdma = -1; + } +#else /*DBMA */ + if (ep->indma != -1) { + free_au1000_dma(ep->indma); + ep->indma = -1; + } + if (ep->outdma != -1) { + free_au1000_dma(ep->outdma); + ep->outdma = -1; + } +#endif /*DBMA */ + } + free_irq (AU1000_USB_DEV_REQ_INT, NULL); + free_irq (AU1000_USB_DEV_SUS_INT, NULL); +#if defined(MAX_INTR_LOOP_STATS) + { + u32 lc; + for (lc = 0; lc <= MAX_INTR_LOOP_STATS; lc++) + if (interrupt_loop_stats[lc]) + printk(KERN_ERR "%s: interrupt loopcount[%02u] %9u\n", __FUNCTION__, lc,interrupt_loop_stats[lc]); + printk(KERN_INFO"%s: halt_dma_expired: %d\n", __FUNCTION__, au_halt_dma_expired); + } +#endif +#ifdef RECORD_LATENCY + { + int i; + for (i = 0; i < CP0_COUNTS; i++) + if (cp0_counts[i]) + printk(KERN_INFO"%s: cp0_counts[%d] %d\n", __FUNCTION__, i, cp0_counts[i]); + } +#endif +} + +/* ********************************************************************************************* */ +struct usbd_pcd_ops usbd_pcd_ops = { + max_endpoints: UDC_MAX_ENDPOINTS, + ep0_packetsize: EP0_PACKETSIZE, + name: UDC_NAME, + start: au_start, + stop: au_stop, + start_endpoint_in: au_start_endpoint_in, + start_endpoint_out: au_start_endpoint_out, + request_endpoints: au_request_endpoints, + set_endpoints: au_set_endpoints, + cancel_in_irq: au_cancel_in_irq, + cancel_out_irq: au_cancel_out_irq, + serial_init: au_serial_init, +}; + +struct pcd_ops pcd_ops = { + pcd_en_func: pcd_en_func, + mod_init: au_mod_init, + mod_exit: au_mod_exit, +}; + +struct ocd_ops ocd_ops = { + #if defined(CONFIG_OTG_TR_AUTO) + capabilities: OCD_CAPABILITIES_TR | OCD_CAPABILITIES_AUTO, + #else + capabilities: OCD_CAPABILITIES_TR, + #endif + ticks: au_ticks, + elapsed: au_elapsed, +}; + +struct tcd_ops tcd_ops = { + dp_pullup_func: au_loc_conn, +}; + diff -uNr linux/drivers/no-otg/ocd/au1x00/au1x00.h linux/drivers/otg/ocd/au1x00/au1x00.h --- linux/drivers/no-otg/ocd/au1x00/au1x00.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/au1x00/au1x00.h 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,112 @@ +/* + * otg/ocd/au1x00/au1100.h + * + * Copyright (c) 2004 Belcarra + * + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + */ + /*! + * @defgroup AU1X00 AMD AU1X00 + * @ingroup pcdgroup + */ + +/*! + * @file otg/ocd/au1x00/au1x00.h + * @brief AU1X00 Header + * + * This file contains the private defines and structures for the AU1X00 + * Drivers. + * + * @ingroup AU1X00 + */ + + + +/* + * + * au1100 + * + * The au1100 does not seem to work properly with an 8 byte + * packetsize. So 16, 32 or 64 are the only valid values. + * + * Unfortunately this means that a DMA channel is required for + * EP0 transmit. + */ + +#define EP0_PACKETSIZE 0x8 + +#define UDC_MAX_ENDPOINTS 6 + +#define UDC_NAME "AU1100" + +#define NUSBD_EP0RD 0xB0200000 +#define NUSBD_EP0WR 0xB0200004 +#define NUSBD_EP1WR 0xB0200008 +#define NUSBD_EP2WR 0xB020000C +#define NUSBD_EP3RD 0xB0200010 +#define NUSBD_EP4RD 0xB0200014 + +#define NUSBD_EP0CS 0xB0200024 +#define NUSBD_EP1CS 0xB0200028 +#define NUSBD_EP2CS 0xB020002C +#define NUSBD_EP3CS 0xB0200030 +#define NUSBD_EP4CS 0xB0200034 + +#define NUSBD_EP0RDSTAT 0xB0200040 +#define NUSBD_EP0WRSTAT 0xB0200044 +#define NUSBD_EP1WRSTAT 0xB0200048 +#define NUSBD_EP2WRSTAT 0xB020004C +#define NUSBD_EP3RDSTAT 0xB0200050 +#define NUSBD_EP4RDSTAT 0xB0200054 + +#define NUSBD_FRAMENUM 0xB0200038 + +enum { + NDMA_ID_UART0_TX = 0, + NDMA_ID_UART0_RX, + NDMA_ID_GP04, + NDMA_ID_GP05, + NDMA_ID_AC97C_TX, + NDMA_ID_AC97C_RX, + NDMA_ID_UART3_TX, + NDMA_ID_UART3_RX, + NDMA_ID_USBDEV_EP0_RX, + NDMA_ID_USBDEV_EP0_TX, + NDMA_ID_USBDEV_EP1_TX, + NDMA_ID_USBDEV_EP2_TX, + NDMA_ID_USBDEV_EP3_RX, + NDMA_ID_USBDEV_EP4_RX, + NDMA_ID_I2S_TX, + NDMA_ID_I2S_RX, + NDMA_NUM_DEV +}; + +typedef struct ep_regs { + int rd; + int wr; + int cs; + int rds; + int wrs; + int rx_id; + int tx_id; + char * rx_str; + char * tx_str; + u32 indma; + u32 outdma; + int last; + int eptype; +} ep_regs_t; + +#define MAX_EPN_PACKET_SIZE 64 +#define CP0_PRID_SOC_MASK 0xff000000 +#define CP0_PRID_AU1000 0x00000000 +#define CP0_PRID_AU1500 0x01000000 +#define CP0_PRID_AU1100 0x02000000 +#define CP0_PRID_AU1550 0x03000000 /* AMX */ + +#define CP0_PRID_REV_MASK 0x000000ff + diff -uNr linux/drivers/no-otg/ocd/fsotg/fs-hcd.c linux/drivers/otg/ocd/fsotg/fs-hcd.c --- linux/drivers/no-otg/ocd/fsotg/fs-hcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/fsotg/fs-hcd.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,1527 @@ +/* + * otg/hcd/fsotg/fs-hcd.c - Freescale USBOTG aware Host Controller Driver (HCD) + * + * Copyright (c) 2004 Belcarra Technologies + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + */ +/*! + * @file otg/ocd/fsotg/fs-hcd.c + * @brief Freescale USB Host Controller Driver + * + * This is the hardware specific component of the HCD. It knows nothing + * about URBs, and deals with "transfers". This is not because it shouldn't + * know about them, but because URBs are generic, and so it seems reasonable + * to keep as much generic code in the hardware independent portion as + * possible. As a result, the parameter list to hcd_hw_start_transfer() + * is really fat. + * + * This is not compiled as a self contained module, but is linked with + * the generic component hc_xfer.o. + * + * There is some processor specific code here to support the alternate processors + * that implement this type of USB support. + * + * There is no board or platform level code here. + * + * @ingroup FSOTG + * @{ + */ + +#include +#include +#include +#include // For DMA directions +//#include +#include + +#include +#include +#include + +#include + +#include "../core/hcd.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifndef CONFIG_OTG_ROOTHUB_VENDORID +#define CONFIG_OTG_ROOTHUB_VENDORID 0x15ec +#endif +#ifndef CONFIG_OTG_ROOTHUB_PRODUCTID +#define CONFIG_OTG_ROOTHUB_PRODUCTID 0xf010 +#endif +#ifndef CONFIG_OTG_ROOTHUB_BCDDEVICE +#define CONFIG_OTG_ROOTHUB_BCDDEVICE 0x0100 +#endif + + +#define STATIC static +extern void hcd_trace_mem(otg_tag_t tag, char *fn, char *msg, int len, volatile void *data); + +// Start of hardware specific components.... + +/* ********************************************************************************************* */ + +// Globals for debug +static u32 monitoring_etd_mask = 0; // 0 == no monitoring +static u32 monitoring_etd_num; +#define MAX_ETD_FRAMES 3 +static u32 current_etd_frame; +static u32 done_flags[1+MAX_ETD_FRAMES]; +static u32 done_status[1+MAX_ETD_FRAMES]; +static endpoint_transfer_descriptor mon_etd[1+MAX_ETD_FRAMES]; + + + +/*! + * @struct cc_name + * @brief Map CC values to names + */ +static char *cc_name[] = { + "no error", "CRC error", "bitstuff error", "data toggle error", + "stall", "device not responding", "PID failure" "reserved", + "data overrun", "data underrun", "ACK", "NAK", "buffer overrun", + "buffer underrun", "schedule overrun" "not accessed" +}; + + +/*! + * comp_code_to_status() - map condition cod3 to errno value + * @param cc condition code + * @return errno value + */ +STATIC int comp_code_to_status(int cc) +{ + // + if (cc) + TRACE_MSG2(HCD,"NON-ZERO CC=%d %s", cc,cc_name[cc]); + + switch (cc) { + case ETD_CC_NOERROR: return 0; + case ETD_CC_CRCERR: return -EILSEQ; + case ETD_CC_BITSTUFFERR: return -EPROTO; + case ETD_CC_DATATOGERR: return -EPROTO; // I guess, nothing else looks better. + case ETD_CC_STALL: return -EPIPE; + case ETD_CC_DEVNOTRESPONDING: return -ETIMEDOUT; + case ETD_CC_PIDFAILURE: return -EPROTO; + case ETD_CC_DATAOVERRUN: return -EOVERFLOW; + case ETD_CC_DATAUNDERRUN: return -EREMOTEIO; + case ETD_CC_ACK: return -EPROTO; // For lack of anything better + case ETD_CC_NAK: return -EPROTO; // For lack of anything better + case ETD_CC_BUFFEROVERRUN: return -ECOMM; + case ETD_CC_BUFFERUNDERRUN: return -ENOSR; + case ETD_CC_SCHEDOVERRUN: return -ENOSPC; + case ETD_CC_NOTACCESSED: return -EPROTO; // For lack of anything better + default: return -EINVAL; + } +} + +/*! + * rel_etd() - release etd + * {get,rel}_etd maintain a free list of ETDs by saving the index of the next free ETD in the + * etd->xbufsrtad field. The list is terminated by 0x0000ffff. List manipulation needs to be + * protected by irq_lock, since ETDs are released in the interrupt handler, but allocated at + * regular priority. + * @param fs_hcpriv + * @param etdn + */ +STATIC void rel_etd(fs_hcpriv *fs_hcpriv, int etdn) +{ + fs_wl(etd_word(etdn, 0), 0); + fs_wl(etd_word(etdn, 2), 0); + fs_wl(etd_word(etdn, 3), 0); + fs_wl(etd_word(etdn, 1), fs_hcpriv->free_etds); + fs_hcpriv->free_etds = etdn; +} + +/*! + * get_etd() - get etd + * {get,rel}_etd maintain a free list of ETDs by saving the index of the next free ETD in the + * etd->xbufsrtad field. The list is terminated by 0x0000ffff. List manipulation needs to be + * protected by irq_lock, since ETDs are released in the interrupt handler, but allocated at + * regular priority. + * @param fs_hcpriv + * @return int + */ +STATIC int get_etd(fs_hcpriv *fs_hcpriv) +{ + // Return the index of an available ETD, or -1 if none are available. + unsigned long flags; + int etdn = -1; + local_irq_save(flags); + // Is list is empty? + if (0x0000ffff != (etdn = fs_hcpriv->free_etds)) + fs_hcpriv->free_etds = fs_rl(etd_word(etdn, 1)); + local_irq_restore(flags); + return etdn; +} + +/* ********************************************************************************************* */ +/* + * For the generic transfer aware framework. + */ + +// For debugging... +static int num_urbs_started = 0; +#define MAX_URBS_STARTED 0 +static char *dirpid_name[4] = {"SETUP","OUT","IN","WRONG"}; +//static char *direct_name[4] = {"TD00","OUT","IN","TD11"}; +static char *etd_urb_state_name[] = {"COMPLETED","SETUP_STATUS","SETUP_DATA","SETUP_PKT","BULK","INTERRUPT","ISOC","WRONG"}; +static char *format_name[4] = { "CONTROL", "ISO", "BULK", "INT", }; + + +/*! + * start_etd() - start an etd + * @param etdn + * @param len + * @param data + * @param out + */ +STATIC void start_etd(int etdn, int len, void *data, int out) +{ + if (len) { + consistent_sync(data,len, (out ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE)); + fs_wl(OTG_DMA_ETD_MSA(etdn), virt_to_bus(data)); + } + if (len || out) + fs_wl(OTG_DMA_ETD_EN, ETD_MASK(etdn)); + + fs_orl(OTG_HOST_IINT, ETD_MASK(etdn)); // Don't wait until SOF, interrupt ASAP. + fs_orl(OTG_HOST_ETD_DONE, ETD_MASK(etdn)); // Interrupt on ETD done (forget DMA interrupt). + fs_wl(OTG_HOST_ETD_EN, ETD_MASK(etdn)); +} + +/*! + * hcd_hw_start_transfer() - start a transfer + * Return 0..MAX_ACTIVE_XFERS-1 if the transfer is successfully started, + * -1 if there is something wrong with the requested transfer, + * -2 if there are no resources available at this time, but resubmission is OK. + * + * @param bus_hcpriv + * @param len length of data region + * @param data virtual address of data region + * @param toggle toggle value to start the xfer with + * @param maxps max packet size of endpoint + * @param slow 1 ==> slow speed, 0 ==> full speed QQSV verify: (((urb->pipe) >> 26) & 1) + * @param endpoint endpoint number + * @param address USB device address + * @param pid USB_PID_{OUT,IN,SETUP} + * @param format PIPE_{CONTROL,BULK,INTERRUPT,ISOCHRONOUS} + * @param other value depending on format + * + * PIPE_CONTROL: other == address of setup packet. + * PIPE_BULK: other == ZLP at end (T/F), + * PIPE_INTERRUPT: other == poll interval + */ +int hcd_hw_start_transfer(struct bus_hcpriv *bus_hcpriv, int len, void *data, int toggle, int maxps, + int slow, int endpoint, int address, int pid, int format, u32 other) +{ + fs_hcpriv *fs_hcpriv = (struct fs_hcpriv *) (bus_hcpriv->hw_hci); + fs_data_buff *x = NULL; + fs_data_buff *y = NULL; + int fmt = 0; + int out; + int etdn; + //int rc = 0; + int dir = 0; + endpoint_transfer_descriptor *sdp = NULL; + transfer_descriptor td; + int sdp_out = FALSE; + + //TRACE_MSG8(HCD, "len: %d toggle: %d endpoint: %02x pid: %d format: %d address: %d %s %s", + // len, toggle, endpoint, pid, format, address, dirpid_name[pid], format_name[format]); + + // data address needs to be on a 32-bit boundary. + if (0x3 & (u32)data) { + printk(KERN_ERR "%s: invalid data alignment for urb, urb rejected.\n",__FUNCTION__); + TRACE_MSG1(HCD, "invalid data alignment for urb (%08x), urb rejected.",(u32)data); + return -1; + } + num_urbs_started += 1; + if ((MAX_URBS_STARTED > 0) && (num_urbs_started > MAX_URBS_STARTED)) { + // Can the urb, don't touch the HW + printk(KERN_ERR "%s: ignoring urb %d\n",__FUNCTION__,num_urbs_started); + return -1; + } + + /* Check pid, setup pid and out flags + */ + switch (pid) { + case USB_PID_SETUP: + pid = ETD_DIRPID_SETUP; + out = TRUE; + break; + case USB_PID_OUT: + pid = ETD_DIRPID_OUT; + out = TRUE; + break; + case USB_PID_IN: + pid = ETD_DIRPID_IN; + out = FALSE; + break; + default: + TRACE_MSG1(HCD,"invalid PID %x",pid); + return -1; + } + switch (format) { + case PIPE_CONTROL: + case PIPE_BULK: + case PIPE_INTERRUPT: + td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len)); + break; + default: + return -1; + } + + /* + * 1. Allocate resources: + * a. X,Y buffers + * b. ETD (& matching DMA) + * 2. Set the registers + */ + UNLESS ((x = get_data_buff(fs_hcpriv)) && (y = get_data_buff(fs_hcpriv)) && (0 <= (etdn = get_etd(fs_hcpriv)))) { + // Resource allocation failure, retry later is OK. + TRACE_MSG0(HCD,"resource allocation failure -> resubmit OK"); + y = rel_data_buff(fs_hcpriv,y); + x = rel_data_buff(fs_hcpriv,x); + return -2; + } + + // OK, we've got all we need. + td.w1.bufsrtad.y = data_buff_boff(y); + td.w1.bufsrtad.x = data_buff_boff(x); + + switch (format) { + case PIPE_CONTROL: + fmt = ETD_FORMAT_CONTROL; + dir = ETD_DIRECT_TD00; + + /* build setup data/status phase ETD for use after completion of 8 byte setup packet + */ + fs_hcpriv->sdp_data[etdn] = data; // address of data phase buffer. + data = (void *) other; // address of setup packet. + sdp = &fs_hcpriv->sdp_etd[etdn]; + memset((void*)sdp, 0, sizeof(endpoint_transfer_descriptor)); + + /* toggle is constant 0 for setup packet, and 1 for data and status phases, + * so toggle was overloaded to handle data phase direction. If len == 0, there + * is no data phase, so direction is IN. + */ + sdp_out = (toggle && len > 0); + sdp->td.w1.val = td.w1.val; + sdp->td.w2.cb.flags = ETD_SET_DATATOGL((0x2|1)) | // toggle is always 1, for data or status phases. + ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) + ETD_SET_BUFROUND(1) | // Allow short recv xfers + ETD_SET_DIRPID((sdp_out?ETD_DIRPID_OUT:ETD_DIRPID_IN)); + sdp->td.w2.cb.reserved = 0; + sdp->td.w2.cb.rtrydelay = 1; // Number of frames to wait before retry on failure. + sdp->td.w3.val = ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len); + + /* build SETUP ETD for initial 8 byte setup packet + */ + len = 8; + td.w2.cb.flags = ETD_SET_DATATOGL((0x2|0)) | // starting toggle is here, not TOGCRY, and is always 0. + ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) + ETD_SET_BUFROUND(1) | // Allow short recv xfers + ETD_SET_DIRPID(pid); + td.w2.cb.reserved = 0; + td.w2.cb.rtrydelay = 1; // Number of frames to wait before retry on failure. + td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len)); + + fs_hcpriv->etd_urb_state[etdn] = ETD_URB_SETUP_START; + + //TRACE_MSG3(HCD,"SETUP pkt, len=%d : %08x : %08x",len,*((u32*)(void*)data),*(1+(u32*)(void*)data)); + + /* Complete the setup data phase ETD. + */ + sdp->epd = ETD_SET_MAXPKTSIZ(maxps) | + ETD_SET_TOGCRY(0) | + ETD_SET_FORMAT(ETD_FORMAT_BULK) | + ETD_SET_SPEED((slow?ETD_SPEED_LOW:ETD_SPEED_FULL)) | + ETD_SET_DIRECT(ETD_DIRECT_TD00) | + ETD_SET_ENDPNT(endpoint) | + ETD_SET_ADDRESS(address); + + break; + + case PIPE_BULK: + fmt = ETD_FORMAT_BULK; + dir = ETD_DIRECT_TD00; + + //TRACE_MSG1(HCD,"BULK, starting toggle %x",toggle); + td.w2.cb.flags = ETD_SET_DATATOGL((0x2|toggle)) | // starting toggle is here, not TOGCRY + ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) + ETD_SET_BUFROUND(1) | // Allow short recv xfers + ETD_SET_DIRPID(pid); + td.w2.cb.reserved = 0; + td.w2.cb.rtrydelay = 0; // Number of frames to wait before retry on failure. + + /* Set the state to indicate if a trailing ZLP is required. + */ + fs_hcpriv->etd_urb_state[etdn] = ((other && out && 0 == + (len % maxps)) ? ETD_URB_BULKWZLP_START : ETD_URB_BULK_START); + break; + + case PIPE_INTERRUPT: + fmt = ETD_FORMAT_INTERRUPT; + dir = ETD_DIRECT_TD00; + + //TRACE_MSG1(HCD,"INTERRUPT, starting toggle %x",toggle); + td.w2.intr.flags = ETD_SET_DATATOGL((0x2|toggle)) | // starting toggle is here, not TOGCRY + ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) + ETD_SET_BUFROUND(1) | // Allow short recv xfers + ETD_SET_DIRPID(pid); + + td.w2.intr.relpolpos = (fs_rl(OTG_HOST_FRM_NUM) + 1) & 0xff; // Start next frame + + td.w2.intr.polinterv = other & 0xff; + fs_hcpriv->etd_urb_state[etdn] = ETD_URB_INTERRUPT_START; + break; + + case PIPE_ISOCHRONOUS: + fmt = ETD_FORMAT_ISOC; + dir = out ? ETD_DIRECT_OUT : ETD_DIRECT_IN; + td.w2.isoc.startfrm = (fs_rl(OTG_HOST_FRM_NUM) + 1) & 0xffff; // next frame + if (len > 1023) { + // Two frames needed, send 1023 in the first, remainder in the second. + td.w2.isoc.flags = ETD_SET_DELAYINT(0) | ETD_SET_AUTOISO(0) | ETD_SET_FRAMECNT(1); + td.w3.isoc.pkt0 = ETD_SET_PKTLEN(1023); + td.w3.isoc.pkt1 = ETD_SET_PKTLEN(len-1023); + } + else { + // Only one frame needed, send it all at once. + td.w2.isoc.flags = ETD_SET_DELAYINT(0) | ETD_SET_AUTOISO(0) | ETD_SET_FRAMECNT(0); + td.w3.isoc.pkt0 = ETD_SET_PKTLEN(len); + td.w3.isoc.pkt1 = 0; + } + fs_hcpriv->etd_urb_state[etdn] = ETD_URB_ISOC_START; + break; + } + + fs_wl(etd_word(etdn, 0), ETD_SET_MAXPKTSIZ(maxps) | ETD_SET_TOGCRY(0) | ETD_SET_FORMAT(fmt) | + ETD_SET_SPEED((slow ? ETD_SPEED_LOW : ETD_SPEED_FULL)) | + ETD_SET_DIRECT(dir) | ETD_SET_ENDPNT(endpoint) | ETD_SET_ADDRESS(address)); + + fs_wl(etd_word(etdn, 1), td.w1.val); + fs_wl(etd_word(etdn, 2), td.w2.val); + fs_wl(etd_word(etdn, 3), td.w3.val); + + //TRACE_MSG5(HCD, "ETD[%02d] %08x %08x %08x %08x", etdn, + // fs_rl(etd_word(etdn, 0)), fs_rl(etd_word(etdn, 1)), + // fs_rl(etd_word(etdn, 2)), fs_rl(etd_word(etdn, 3))); + + start_etd(etdn, len, data, out); + return etdn; +} + +/*! + * finish_urb_irq() - called to complete transfer + * @param fs_hcpriv + * @param etdn + * @param killed + */ +STATIC void finish_urb_irq(fs_hcpriv *fs_hcpriv, int etdn, int killed) +{ + int cc = 0; + int next_toggle = 0; + int format = 0; + u32 remaining = 0; + fs_data_buff *x,*y; + endpoint_transfer_descriptor res; + + TRACE_MSG5(HCD, "etdn=%d state=%d %s nus=%u %s", etdn, fs_hcpriv->etd_urb_state[etdn], + etd_urb_state_name[fs_hcpriv->etd_urb_state[etdn]], num_urbs_started, (killed?"KILLED":"")); + + if (monitoring_etd_mask == ETD_MASK(etdn)) + monitoring_etd_mask = 0; + + + /* Disable/Abort DMA. R1: sec 23.13.16 pg 23-103 + * (supposed to auto-clear on DMA completion, but this shouldn't hurt). + */ + fs_wl(OTG_DMA_ETD_CH_CLR, ETD_MASK(etdn)); + + if (fs_rl(OTG_HOST_ETD_EN) & ETD_MASK(etdn)) + fs_andl(OTG_HOST_ETD_EN, ~ETD_MASK(etdn)); // This register IS write 0 to disable. Really. + + if ( fs_rl(OTG_HOST_ETD_DONE) & ETD_MASK(etdn)) + fs_andl(OTG_HOST_ETD_DONE, ~ETD_MASK(etdn)); // This register IS write 0 to disable. Really. + + // Hardware should have disabled ETD, but it doesn't hurt to check. + if (fs_rl(OTG_HOST_ETD_EN) & ETD_MASK(etdn)) + fs_wl(OTG_HOST_ETD_EN, ETD_MASK(etdn)); // This register IS write 1 to clear. Really. + + if (fs_rl(OTG_HOST_XINT_STAT) & ETD_MASK(etdn)) + fs_wl(OTG_HOST_XINT_STAT, ETD_MASK(etdn)); // This register IS write 1 to clear. Really. + + if (fs_rl(OTG_HOST_YINT_STAT) & ETD_MASK(etdn)) + fs_wl(OTG_HOST_YINT_STAT, ETD_MASK(etdn)); // This register should be write 1 to clear. + + + if (fs_rl(OTG_HOST_XYINT_STEN) & ETD_MASK(etdn)) + fs_andl(OTG_HOST_XYINT_STEN, ~ETD_MASK(etdn)); // This register should be write 0 to disable. + + if (fs_rl(OTG_HOST_XFILL_STAT) & ETD_MASK(etdn)) + fs_wl(OTG_HOST_XFILL_STAT, ETD_MASK(etdn)); // This register IS write 1 to clear. Really. + + if (fs_rl(OTG_HOST_YFILL_STAT) & ETD_MASK(etdn)) + fs_wl(OTG_HOST_YFILL_STAT, ETD_MASK(etdn)); // This register should be write 1 to clear. + + + // Extract results + res.epd = fs_rl(etd_word(etdn, 0)); + res.td.w1.val = fs_rl(etd_word(etdn, 1)); + res.td.w2.val = fs_rl(etd_word(etdn, 2)); + res.td.w3.val = fs_rl(etd_word(etdn, 3)); + + fs_wl(OTG_HOST_EP_DSTAT, ETD_MASK(etdn)); + + //TRACE_MSG1(HCD,"OTG_HOST_ETD_DONE: %08x", fs_rl(OTG_HOST_ETD_DONE)); + + if ((ETD_URB_BULK_START == fs_hcpriv->etd_urb_state[etdn] || + (ETD_URB_BULKWZLP_START == fs_hcpriv->etd_urb_state[etdn])) && + (ETD_GET_DIRPID(res.td.w2.cb.flags) != ETD_DIRPID_IN) + ) + TRACE_MSG4(HCD,"RES: epd: %08x w1: %08x w2: %08x w3: %08x", res.epd,res.td.w1.val,res.td.w2.val,res.td.w3.val); + + if (ETD_GET_HALTED(res.epd)) + TRACE_MSG0(HCD,"endpoint HALTED"); + + format = ETD_GET_FORMAT(res.epd); + + switch (fs_hcpriv->etd_urb_state[etdn]) { + case ETD_URB_SETUP_START: + /* Just finished the setup packet. + * Go to data or status phase of setup packet while we still have the ETD and data buffers allocated. + */ + if (!killed && ETD_CC_NOERROR == (cc = ETD_GET_COMPCODE(res.td.w2.cb.flags))) { + + endpoint_transfer_descriptor *sdp = &fs_hcpriv->sdp_etd[etdn]; + int len = ETD_GET_TOTBYECNT(sdp->td.w3.val); + void *data = fs_hcpriv->sdp_data[etdn]; + fs_hcpriv->etd_urb_state[etdn] = ((len > 0) ? ETD_URB_SETUP_DATA : ETD_URB_SETUP_STATUS); + + // Copy into the active ETD and start it. + fs_wl(etd_word(etdn, 0), sdp->epd); + fs_wl(etd_word(etdn, 1), sdp->td.w1.val); + fs_wl(etd_word(etdn, 2), sdp->td.w2.val); + fs_wl(etd_word(etdn, 3), sdp->td.w3.val); + // watch out for ETD_DIRPID_SETUP + start_etd(etdn, len,data,(ETD_GET_DIRPID(sdp->td.w2.cb.flags)!=ETD_DIRPID_IN)); + TRACE_MSG1(HCD,"SETUP %s Phase started",((len > 0) ? "DATA" : "STATUS")); + return; + } + /* Something is wrong, finish the URB as is. + */ + remaining = ETD_GET_TOTBYECNT(fs_rl(etd_word(etdn, 3))); + format = PIPE_CONTROL; + break; + + case ETD_URB_SETUP_DATA: + /* Go to status phase of setup packet while we still have the ETD and data buffers allocated. + * Save the data phase length for after status. + */ + if (!killed && ETD_CC_NOERROR == (cc = ETD_GET_COMPCODE(res.td.w2.cb.flags))) { + endpoint_transfer_descriptor *sdp = &fs_hcpriv->sdp_etd[etdn]; + fs_hcpriv->sdp_data[etdn] = (void *) ETD_GET_TOTBYECNT(res.td.w3.val); + fs_hcpriv->etd_urb_state[etdn] = ETD_URB_SETUP_STATUS; + + /* Rebuild data phase sdp for status phase with opposite direction, and 0 length. + */ + + sdp->td.w2.cb.flags = ETD_FLIP_DIRPID(sdp->td.w2.cb.flags); + sdp->td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(0)); + + /* Copy into the active ETD and start it. + */ + fs_wl(etd_word(etdn, 0), sdp->epd); + fs_wl(etd_word(etdn, 1), sdp->td.w1.val); + fs_wl(etd_word(etdn, 2), sdp->td.w2.val); + fs_wl(etd_word(etdn, 3), sdp->td.w3.val); + start_etd(etdn, 0,NULL,(ETD_GET_DIRPID(sdp->td.w2.cb.flags)!=ETD_DIRPID_IN)); + TRACE_MSG0(HCD,"SETUP STATUS Phase started"); + return; + } + // Something is wrong, finish the URB as is. + remaining = ETD_GET_TOTBYECNT(res.td.w3.val); + format = PIPE_CONTROL; + break; + + case ETD_URB_SETUP_STATUS: + cc = ETD_GET_COMPCODE(res.td.w2.cb.flags); + // Fetch saved length. + remaining = (u32) fs_hcpriv->sdp_data[etdn]; + format = PIPE_CONTROL; + break; + + case ETD_URB_BULKWZLP_START: + // Need a trailing ZLP. + next_toggle = ETD_GET_TOGCRY(res.epd); + if (!killed && ETD_CC_NOERROR == ETD_GET_COMPCODE(res.td.w2.cb.flags)) { + // Since TOTBYECNT should be 0, re-enabling the same ETD ought to give a ZLP. + fs_hcpriv->etd_urb_state[etdn] = ETD_URB_BULKWZLP; + // Save the data phase length for after ZLP. + // fs_hcpriv->sdp_data[etdn] = (void *) ETD_GET_TOTBYECNT(res.td.w3.val); + // FIXME - verify toggle OK + + start_etd(etdn, 0,NULL,TRUE/*always OUT*/); + + TRACE_MSG0(HCD,"ZLP started"); + return; + } + // Something is wrong, finish the URB as is. + remaining = ETD_GET_TOTBYECNT(res.td.w3.val); + format = PIPE_BULK; + break; + + case ETD_URB_BULKWZLP: + // Trailing ZLP now finished. + cc = ETD_GET_COMPCODE(res.td.w2.cb.flags); + next_toggle = ETD_GET_TOGCRY(res.epd); + // Fetch saved length (it was zero, or we wouldn't be here). + remaining = 0; + format = PIPE_BULK; + break; + + case ETD_URB_BULK_START: + cc = ETD_GET_COMPCODE(res.td.w2.cb.flags); + next_toggle = ETD_GET_TOGCRY(res.epd); + remaining = ETD_GET_TOTBYECNT(res.td.w3.val); + format = PIPE_BULK; + break; + + case ETD_URB_INTERRUPT_START: + cc = ETD_GET_COMPCODE(res.td.w2.intr.flags); + remaining = ETD_GET_TOTBYECNT(res.td.w3.val); + format = PIPE_INTERRUPT; + break; + + case ETD_URB_ISOC_START: + // This probably needs more work, best guess only here. + remaining = ETD_GET_PKTLEN(res.td.w3.isoc.pkt0); + cc = ETD_GET_COMPCODE(res.td.w3.isoc.pkt0); + if (ETD_GET_FRAMECNT(res.td.w2.isoc.flags)) { + // There were two packets in this transfer + remaining += ETD_GET_PKTLEN(res.td.w3.isoc.pkt1); + } + format = PIPE_ISOCHRONOUS; + } + + fs_hcpriv->etd_urb_state[etdn] = ETD_URB_COMPLETED; + + /* Convert completion code to HW independent value. + */ + if (killed) { + cc = -ENOENT; + remaining = 0; + } + else + cc = comp_code_to_status(cc); + + /* Return ETD and X,Y buffers to free list. + */ + y = data_buff_addr(res.td.w1.bufsrtad.y); + x = data_buff_addr(res.td.w1.bufsrtad.x); + y = rel_data_buff(fs_hcpriv,y); + x = rel_data_buff(fs_hcpriv,x); + rel_etd(fs_hcpriv, etdn); + + /* Forward results to the layers above. + */ + //TRACE_MSG4(HCD,"completing urb on ETD %d cc=%d remaining=%d next_toggle=%d", etdn, cc, remaining,next_toggle); + hcd_transfer_complete(fs_hcpriv->bus_hcpriv, etdn, format, cc, remaining, next_toggle); +} + +/*! + * hcd_hw_unlink_urb() - unlink urb + * Called in irq_lock + * @param bus_hcpriv + * @param u + */ +int hcd_hw_unlink_urb(struct bus_hcpriv *bus_hcpriv, int u) +{ + unsigned long flags; + struct fs_hcpriv *fs_hcpriv; + RETURN_EINVAL_UNLESS ((fs_hcpriv = (struct fs_hcpriv *) bus_hcpriv->hw_hci)); + local_irq_save(flags); + finish_urb_irq(fs_hcpriv, u, TRUE); + local_irq_restore(flags); + return 0; +} + +/*! + * hcd_hw_frame_number() - return current frame number + * @param bus_hcpriv + * @return u32 ??? + */ +u32 hcd_hw_frame_number(struct bus_hcpriv *bus_hcpriv) +{ + return(fs_rl(OTG_HOST_FRM_NUM)); +} + +/* ********************************************************************************************* */ + +/*! + * MX21_PORT_CHANGED() - send otg event information to state machine + * @param name + * @param cs + * @param changed + * @param status + * @param set + * @param reset + */ +void MX21_PORT_CHANGED(char *name, u32 cs, u32 changed, u32 status, u64 set, u64 reset) +{ + RETURN_UNLESS(cs & changed); + TRACE_MSG2(HCD, "%s%s", name, (cs & (1 << status)) ? "" : "/"); + if (cs & (1 << status) && set) + otg_event(hcd_instance->otg, set, HCD, name); + if (!(cs & (1 << status)) && reset) + otg_event(hcd_instance->otg, reset, HCD, name); +} + +/*! + * update_shadow_rh() - Update shadow _status_ info (change info already updated). + * @param bus_hcpriv + * @param port_num - 1-N based port number + * @param cs + * + */ +STATIC void update_shadow_rh(struct bus_hcpriv *bus_hcpriv, int port_num, u32 cs) +{ + u32 msk_and = 0; // bits to turn off + u32 msk_or = 0; // bits to turn on + + TRACE_MSG2(HCD,"port: %d cs: %08x", port_num, cs); + + /* PORT_STATUS_PRTRSTSC (20) - PORT_RESET (4) changed + * PORT_RESET (4) status change, end of reset, PORT_ENABLE may (should) be on. + */ + MX21_PORT_CHANGED("PORT_RESET", cs, PORT_STATUS_PRTRSTSC, PORT_RESET, + BUS_RESET, BUS_RESET_); + + if (cs & (1 << C_PORT_RESET)) { + TRACE_MSG1(HCD,"port: %d PORT_RESET complete (s.b. enabled)", port_num); + if (cs & (1 << PORT_ENABLE)) { + TRACE_MSG1(HCD,"port: %d enabled", port_num); + //otg_write_info_message("port update C_PORT_RESET"); + msk_or |= (1 << PORT_ENABLE); + } + else { + TRACE_MSG1(HCD,"port: %d NOT enabled!!!", port_num); + //otg_write_info_message("update C_PORT_RESET/"); + } + + /* Clear the PORT_RESET bit too, since it's over. + */ + msk_and |= (1 << PORT_RESET); + bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); + } + + /* PORT_OVER_CURRENT (3) bit has changed (either direction). + * PORT_STATUS_OVRCURIC (19) - PORT_OVER_CURRENT (3) changed + */ + MX21_PORT_CHANGED("PORT_OVER_CURRENT", cs, PORT_STATUS_OVRCURIC, PORT_OVER_CURRENT, (u64) 0, (u64) 0); + if (cs & (1 << C_PORT_OVER_CURRENT)) { + /* OVER_CURRENT has come on. + */ + if (cs & (1 << PORT_OVER_CURRENT)) { + TRACE_MSG1(HCD,"port: %d over current ON", port_num); + msk_or |= (1 << PORT_OVER_CURRENT); + } + /* OVER_CURRENT has gone away. + */ + else { + TRACE_MSG1(HCD,"port: %d over current OFF", port_num); + msk_and |= (1 << PORT_OVER_CURRENT); + } + bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); + } + + /* PORT_SUSPEND (2) Resume sequence has completed.... + * PORT_STATUS_PRTSTATSC (18) - PORT_SUSPEND (2) changed + */ + MX21_PORT_CHANGED("PORT_SUSPEND", cs, PORT_STATUS_PRTSTATSC, PORT_SUSPEND, + BUS_SUSPENDED, BUS_SUSPENDED_); + + if (cs & (1 << C_PORT_SUSPEND)) { + // FIXME - figure this out when we get to suspend/resume.... + + } + + + /* PORT_ENABLE (1) has been __cleared__ by some HW event + * PORT_STATUS_PRTENBLSC (17) - PORT_ENABLE (1) changed + */ + MX21_PORT_CHANGED("PORT_ENABLE", cs, PORT_STATUS_PRTENBLSC, PORT_ENABLE, (u64) 0, (u64) 0); + if (cs & (1 << C_PORT_ENABLE)) { + //otg_write_info_message("update C_PORT_ENABLE"); + TRACE_MSG1(HCD,"port: %d disabled (by HW)", port_num); + if (!(cs & (1 << PORT_ENABLE))) + msk_and |= ( 1 << PORT_ENABLE); + bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); + } + + /* PORT_CONNECTION (0) bit has changed (either direction). + * PORT_STATUS_CONNECTSC (16) - PORT_CONNECTION (0) changed + */ + MX21_PORT_CHANGED("PORT_CONNECTION", cs, PORT_STATUS_CONNECTSC, PORT_CONNECTION, + HUB_PORT_CONNECT, HUB_PORT_CONNECT_ ); + + if (cs & (1 << C_PORT_CONNECTION)) { + /* CONNECT has come on. + */ + if (cs & (1 << PORT_CONNECTION)) { + TRACE_MSG1(HCD,"port: %d new connection", port_num); + msk_or |= (1 << PORT_CONNECTION); + if (cs & (1 << PORT_LOW_SPEED)) { + TRACE_MSG1(HCD,"port: %d low speed device", port_num); + msk_or |= (1 << PORT_LOW_SPEED); + } + //otg_write_info_message("update PORT_CONNECTION"); + //printk(KERN_INFO"%s: AAA\n", __FUNCTION__); + //otg_event(hcd_instance->otg, HUB_PORT_CONNECT, HCD, "HUB_PORT_CONNECT (mx21 hw)"); + //printk(KERN_INFO"%s: BBB\n", __FUNCTION__); + } + /* CONNECT has gone away. + */ + else { + TRACE_MSG1(HCD,"port: %d disconnection", port_num); + //otg_write_info_message("update PORT_CONNECTION/"); + msk_and |= (1 << PORT_CONNECTION) | (1 << PORT_LOW_SPEED); + //otg_event(hcd_instance->otg, HUB_PORT_CONNECT_, HCD, "HUB_PORT_CONNECT/ (mx21 hw)"); + } + bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); + //printk(KERN_INFO"%s: CCC\n", __FUNCTION__); + } + + TRACE_MSG5(HCD, "port: %d shadow port_change_status: ((%08x & ~%04x) | %04x) hpcs: %04x", + port_num, bus_hcpriv->rh_hcpriv->port_change_status[port_num - 1], + msk_and, msk_or, bus_hcpriv->rh_hcpriv->hub_port_change_status); +} + +/* ********************************************************************************************* */ +static int num_host_interrupts = 0; +#define MAX_HOST_INTERRUPTS 0 +static int num_psc_interrupts = 0; +#define MAX_PSC_INTERRUPTS 0 + +extern struct bus_hcpriv fs_bus_hcpriv; + +/*! + * hcd_hw_int_hndlr() - interrupt handler for hcd controller + * @param irq + * @param dev_id + * @param regs + */ +irqreturn_t hcd_hw_int_hndlr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) &fs_bus_hcpriv; + struct fs_hcpriv *fs_hcpriv = (struct fs_hcpriv *) bus_hcpriv->hw_hci; + u32 host_sint; // C.f. 23.11.11 Host Interrupt Register + + if (OTG_USBDMA == irq) { + // HOST DMA error interrupt + u32 err_stat = fs_rl(OTG_DMA_ETD_ERR); + TRACE_MSG1(HCD,"host DMA interrupt, error status %08x",err_stat); + if (0 != err_stat) { + // Disable the matching DMA channels and clear the interrupt + fs_wl(OTG_DMA_ETD_CH_CLR, err_stat); + fs_wl(OTG_DMA_ETD_ERR, err_stat); + } + // local_irq_restore(flags); + return IRQ_HANDLED; + } + + /* FIXME - should check fs_hcpriv->mm->otg.OTG_Module_Interrupt_Status & (0x1 << 3) | 0x1; + * for Host (async + regular) Interrupt - enable clock on async. + */ + + num_host_interrupts += 1; + if (MAX_HOST_INTERRUPTS > 0 && num_host_interrupts > MAX_HOST_INTERRUPTS) { + // Host (async + regular) Interrupt Disable + + fs_wl(OTG_HOST_SINT_STEN, 0); + fs_andl(OTG_CORE_CINT_STEN, ~(/*(0x1 << 3) |*/ 0x1)); + } + + while ((host_sint = fs_rl(OTG_HOST_SINT_STAT))) { + + while (HOST_DONEINT & host_sint) { + u32 etds_done; + + TRACE_MSG1(HCD,"HOST_DONEINT: %08x", fs_rl(OTG_HOST_EP_DSTAT)); + fs_wl(OTG_HOST_SINT_STAT, HOST_DONEINT); + host_sint = fs_rl(OTG_HOST_SINT_STAT); + while ((etds_done = fs_rl(OTG_HOST_EP_DSTAT))) + finish_urb_irq(fs_hcpriv, fls(etds_done) - 1, FALSE); + } + + host_sint = fs_rl(OTG_HOST_SINT_STAT); + + /* Start Of Frame. + * Note: this interrupt seems to happen even if disabled when there is a frame number overflow. + */ + if (host_sint & HOST_SOFINT) { + if (0 == (fs_hcpriv->sof_count & (4096 - 1))) { + TRACE_MSG1(HCD,"SOF %08lx",fs_hcpriv->sof_count); + } + fs_hcpriv->sof_count += 1; + if (monitoring_etd_mask != 0) { + // This ETD needs to be watched.... + current_etd_frame += 1; + + done_flags[current_etd_frame] = fs_rl(OTG_HOST_ETD_EN); + done_status[current_etd_frame] = fs_rl(OTG_HOST_EP_DSTAT); + + mon_etd[current_etd_frame].epd = fs_rl(etd_word(monitoring_etd_num, 0)); + mon_etd[current_etd_frame].td.w1.val = fs_rl(etd_word(monitoring_etd_num, 1)); + mon_etd[current_etd_frame].td.w2.val = fs_rl(etd_word(monitoring_etd_num, 2)); + mon_etd[current_etd_frame].td.w3.val = fs_rl(etd_word(monitoring_etd_num, 3)); + + if (current_etd_frame >= MAX_ETD_FRAMES) { + // This has taken too long! + int i,u; + u = ((mon_etd[0].td.w3.val == mon_etd[1].td.w3.val) && + (mon_etd[1].td.w3.val == mon_etd[2].td.w3.val) && + (mon_etd[2].td.w3.val == mon_etd[3].td.w3.val)); + + TRACE_MSG5(HCD,"SOF: OUT urb TIMEOUT at %d/%d frames, mask: %08x num=%u u=%d", + current_etd_frame,MAX_ETD_FRAMES,monitoring_etd_mask,monitoring_etd_num,u); + + for (i = 0; i <= MAX_ETD_FRAMES; i++) + TRACE_MSG7(HCD,"SOF: %d epd: %08x w1: %08x w2: %08x w3: %08x ef: %08x st: %08x", + i, mon_etd[i]. epd, mon_etd[i]. td.w1.val, mon_etd[i].td.w2.val, + mon_etd[i].td.w3.val, done_flags[i], done_status[i]); + // Disable monitoring by killing the urb. FUTURE, look into restarting it. + finish_urb_irq(fs_hcpriv, monitoring_etd_num,TRUE); + } + } + } + + /* Frame Number Overflow. + */ + if (host_sint & HOST_FMOFINT) { + //TRACE_MSG0(HCD,"Frame Number Overflow"); + //printk(KERN_ERR "%s: Frame Number Overflow\n",__FUNCTION__); + } + + /* Port Status Change. + */ + if (host_sint & HOST_PSCINT) { + int port_num; + num_psc_interrupts += 1; + + /* Clear the interrupt by clearing the port(s) in question. + * Reflect any changes in the shadow values. + */ + for (port_num = 1; port_num <= bus_hcpriv->num_ports; port_num++) { + + u32 cs = fs_rl(fs_host_port_stat(port_num)); + u32 cm = cs & 0xFFFF0000; + + CONTINUE_UNLESS (cm); + fs_wl(fs_host_port_stat(port_num), cm); + bus_hcpriv->rh_hcpriv->port_change_status[port_num - 1] |= cm; + update_shadow_rh(bus_hcpriv, port_num, cs); + } + + /* Tell the RH to scan for changes in shadow data. (schedule bottom half handler) + */ + hcd_rh_int_hndlr(irq,bus_hcpriv,NULL); + } + + /* Overrun + */ + if (host_sint & (HOST_SORINT | HOST_HERRINT)) { + if (host_sint & HOST_SORINT) { + TRACE_MSG0(HCD,"Scheduling Overrun"); + printk(KERN_ERR "%s: Scheduling Overrun\n",__FUNCTION__); + } + if (host_sint & HOST_HERRINT) { + TRACE_MSG0(HCD,"Host Scheduling Error"); + printk(KERN_ERR "%s: Host Scheduling Error\n",__FUNCTION__); + } + } + /* Resume Detected. + */ + if (host_sint & HOST_RESDETINT) { + TRACE_MSG0(HCD,"Resume Detected"); + } + /* Clear by writing back the ones we've checked for since the last read. + */ + fs_wl(OTG_HOST_SINT_STAT, host_sint & (HOST_RESDETINT | HOST_FMOFINT | HOST_SORINT | + HOST_HERRINT | HOST_PSCINT | HOST_SOFINT)); + } + return IRQ_HANDLED; +} + +/* ********************************************************************************************* */ + + +/*! + * hcd_hw_rh_hub_attributes() - get root hub attributes + * These provide access to the real root hub. + * @param bus_hcpriv + */ +u16 hcd_hw_rh_hub_attributes(struct bus_hcpriv *bus_hcpriv) +{ + // R1: sec 23.11.27 + return 0xFFFF & (fs_rl(OTG_HOST_ROOTHUB_DESCA) >> 8); +} + +/*! + * hcd_hw_rh_power_delay() - get power delay attributes + * These provide access to the real root hub. + * @param bus_hcpriv + */ +u8 hcd_hw_rh_power_delay(struct bus_hcpriv *bus_hcpriv) +{ + // R1: sec 23.11.27 + return fs_rl(OTG_HOST_ROOTHUB_DESCA) >> 24; +} + +/*! + * hcd_hw_rh_contr_current() - get contr current attributes + * These provide access to the real root hub. + * @param bus_hcpriv + */ +u8 hcd_hw_rh_hub_contr_current(struct bus_hcpriv *bus_hcpriv) +{ + return 0x0; +} + +/*! + * hcd_hw_rh_DeviceRemovalbe() - get DeviceRemovalbe attributes + * These provide access to the real root hub. + * @param bus_hcpriv + */ +u8 hcd_hw_rh_DeviceRemovable(struct bus_hcpriv *bus_hcpriv) +{ + // USB2.0 pg 418 ==> bit 0 reserved, 1..7 for ports + // R1: sec 23.11.28 + return 0xFF & fs_rl(OTG_HOST_ROOTHUB_DESCB); +} + +/*! + * hcd_hw_rh_PortPwrCtrlMask() - get DeviceRemovalbe attributes + * These provide access to the real root hub. + * @param bus_hcpriv + */ +u8 hcd_hw_rh_PortPwrCtrlMask(struct bus_hcpriv *bus_hcpriv) +{ + // USB2.0 pg 418 ==> all 1s??? QQSV + // R1: sec 23.11.28 + return 0xFF & fs_rl(OTG_HOST_ROOTHUB_DESCB); +} + + +/*! + * hcd_hw_rh_get_hub_change_status() - get change status + * These provide access to the real root hub. + * @param bus_hcpriv + */ +u32 hcd_hw_rh_get_hub_change_status(struct bus_hcpriv *bus_hcpriv) +{ + // R1: sec 23.11.29 pg 23-62, USB2.0 11.24.2.6 pg 425 + return fs_rl(OTG_HOST_ROOTHUB_STATUS & (ROOTHUB_STATUS_LOCPWRSC | ROOTHUB_STATUS_DEVCONWUE | + ROOTHUB_STATUS_OVRCURI | ROOTHUB_STATUS_LOCPWRS)); +} + +/*! + * hcd_hw_rh_hub_feature() - get hub feature + * These provide access to the real root hub. + * @param bus_hcpriv + * @param feat_selector + * @param set_flag + */ +void hcd_hw_rh_hub_feature(struct bus_hcpriv *bus_hcpriv, int feat_selector, int set_flag) +{ + if (set_flag) { + // SET feature USB2.0 11.24.2.12 pg 434 and USB2.0 11.24.2 tbl 11-17 pg 421 + // R1: sec 23.11.29 pg 23-62 + switch (feat_selector) { + // case C_HUB_OVER_CURRENT: MX21 doesn't support SW setting the over current feature + // case C_HUB_LOCAL_POWER: MX21 doesn't support local power + default: + // Error, but ignore. + TRACE_MSG1(HCD,"SET invalid feature %d",feat_selector); + break; + } + } + else { + // CLEAR feature USB2.0 11.24.2.1 pg 422 and USB2.0 11.24.2 tbl 11-17 pg 421 + // R1: sec 23.11.29 pg 23-62 + switch (feat_selector) { + case C_HUB_OVER_CURRENT: + fs_wl(OTG_HOST_ROOTHUB_STATUS, ROOTHUB_STATUS_OVRCURCHG); + break; + + // case C_HUB_LOCAL_POWER: MX21 doesn't support local power + default: + // Error, but ignore. + TRACE_MSG1(HCD,"CLEAR invalid feature %d",feat_selector); + break; + } + } +} + +/*! + * hcd_hw_rh_get_port_change_status() - get port change status + * R1: High 16 bits == change, low 16 == status + * Change & Status bits in register match USB2.0 spec on (read). + * @param bus_hcpriv + * @param wIndex is 1-origin + */ +u32 hcd_hw_rh_get_port_change_status(struct bus_hcpriv *bus_hcpriv, int wIndex) +{ + u32 port_status = fs_rl(fs_host_port_stat(wIndex)); + + TRACE_MSG8(HCD,"port %d cs: %08lx %s %s %s %s %s %s", + wIndex, port_status, + port_status & PORT_STATUS_CONNECTSC ? "CHANGE" : "", + port_status & PORT_STATUS_PRTPWRST ? "POWER" : "", + port_status & PORT_STATUS_PRTRSTST ? "RESET" : "", + port_status & PORT_STATUS_PRTSUSPST ? "SUSPEND" : "", + port_status & PORT_STATUS_PRTENABST ? "ENABLE" : "", + port_status & PORT_STATUS_CURCONST ? "CONNECT" : "" + ); + + return fs_rl(fs_host_port_stat(wIndex)); +} + +extern char *port_feature_name[]; + +/*! + * hcd_hw_rh_port_feature() - get rh port feature + * @param bus_hcpriv + * @param wValue is feature selector + * @param wIndex is port number (1-N) + * @param set_flag + */ +void hcd_hw_rh_port_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, u16 wIndex, int set_flag) +{ + char buf[64]; + sprintf(buf, "port feature %s %s", port_feature_name[wValue], set_flag ? "SET" : "RESET"); + //otg_write_info_message(buf); + + TRACE_MSG4(HCD, "mx21 %s (%d) %s port_stat: %8x", port_feature_name[wValue], wValue, set_flag ? "SET" : "RESET", + fs_rl(fs_host_port_stat(wIndex))); + if (set_flag) { + // SET feature + switch (wValue) { + case PORT_SUSPEND: + otg_event(hcd_instance->otg, BUS_SUSPENDED, HCD, "HUB_PORT_SUSPEND (mx21 hw)"); + fs_wl(fs_host_port_stat(wIndex), 1 << wValue); + break; + case PORT_RESET: + case PORT_POWER: + fs_wl(fs_host_port_stat(wIndex), 1 << wValue); + break; + default: + // Error, but ignore. + TRACE_MSG2(HCD,"SET port %d invalid feature %d", wIndex, wValue); + break; + } + } + else { + // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423). + switch (wValue) { + case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. + wValue++; // yes, really. this clears PORT_SUSPEND + fs_wl(fs_host_port_stat(wIndex), 1 << wValue); + otg_event(hcd_instance->otg, BUS_SUSPENDED_, HCD, "HUB_PORT_SUSPEND/ (mx21 hw)"); + break; + + case PORT_POWER: // Put port in powered-off state. + wValue++; // yes, really. this clears PORT_POWER + fs_wl(fs_host_port_stat(wIndex), 1 << wValue); + break; + + case PORT_ENABLE: // Disable port. + //wValue--; + break; + default: + break; + } + switch (wValue) { + case PORT_POWER: // Put port in powered-off state. + case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. + case PORT_ENABLE: // Disable port. + case PORT_INDICATOR: // ind_selector gives which indicator to clear + case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit + case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit + case C_PORT_RESET: // clear the PORT_RESET change bit + case C_PORT_ENABLE: // clear the PORT_ENABLE change bit + case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit + fs_wl(fs_host_port_stat(wIndex), 1 << wValue); + break; + default: + // Error, but ignore. + TRACE_MSG2(HCD,"CLEAR port %d invalid feature %d", wIndex, wValue); + break; + } + } + + TRACE_MSG3(HCD, "mx21 %s %s port_stat: %8x", port_feature_name[wValue], set_flag ? "SET" : "RESET", + fs_rl(fs_host_port_stat(wIndex))); +} + +/* ********************************************************************************************* */ +/* Hardware Initialization - this is used by the OTG state machine to + * enable and disable the host controller hardware + */ +void rh_hcd_en_func(struct otg_instance *otg, u8 flag); + +extern void fs_host_clock_on(void); +extern void fs_host_clock_off(void); + +/*! + * fs_hcd_en_func() - otg hcd enable output function + * @param otg + * @param flag + */ +void fs_hcd_en_func(struct otg_instance *otg, u8 flag) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); + fs_hcpriv *fs_hcpriv = bus_hcpriv->hw_hci; + int i; + u32 hwmode = fs_rl(OTG_CORE_HWMODE); + unsigned long flags; + + /* puts OTG capable port into a state where host is enabled */ + + fs_andl(OTG_CORE_HWMODE, 0xffffff0f); // clear + + switch (flag) { + case SET: + local_irq_save(flags); + printk(KERN_INFO"%s: SET\n", __FUNCTION__); + if (hwmode & MODULE_CRECFG_HOST) { + printk(KERN_INFO"%s: warning FUNC STILL SET\n", __FUNCTION__); + } + //printk(KERN_INFO"%s: SET\n", __FUNCTION__); + fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_HOST); // set to software hnp + local_irq_restore(flags); + break; + case RESET: + local_irq_save(flags); + printk(KERN_INFO"%s: RESET\n", __FUNCTION__); + fs_andl(OTG_CORE_HWMODE, ~MODULE_CRECFG_HOST); // set to software hnp + local_irq_restore(flags); + break; + } + + TRACE_MSG1(HCD, "HWMODE: %08x", fs_rl(OTG_CORE_HWMODE)); +} + + +void rh_loc_sof_func(struct otg_instance *otg, u8 flag); +void rh_loc_suspend_func(struct otg_instance *otg, u8 flag); + +/*! + * fs_loc_suspend() - otg loc suspend output function + * @param otg + * @param flag + */ +void fs_loc_suspend_func(struct otg_instance *otg, u8 flag) +{ + unsigned long flags; + switch (flag) { + case SET: + + fs_orl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_SUSPEND); + break; + + case RESET: + break; + } + + rh_loc_suspend_func(otg, flag); + + switch (flag) { + case SET: + break; + + case RESET: + local_irq_save(flags); + fs_andl(OTG_HOST_CONTROL, ~HOST_CONTROL_HCUSBSTE_SUSPEND); + fs_orl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_OPERATIONAL); + local_irq_restore(flags); + break; + } +} + +/*! + * fs_loc_sof_func() - otg loc sof output function + * @param otg + * @param flag + */ +void fs_loc_sof_func(struct otg_instance *otg, u8 flag) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); + fs_hcpriv *fs_hcpriv = bus_hcpriv->hw_hci; + int i; + u32 mask; + unsigned long flags; + + switch (flag) { + case SET: + //printk(KERN_INFO"%s: SET\n", __FUNCTION__); + //TRACE_MSG0(HCD, "-----------------------------------------"); + + local_irq_save(flags); + + /* reset the host core + */ + fs_wl_clr(HCD, OTG_CORE_RST_CTRL, MODULE_RSTRH | MODULE_RSTHSIE | MODULE_RSTHC); + while (fs_rl(OTG_CORE_RST_CTRL)); + + fs_host_clock_on(); + + for (i = 0; i < NUM_DATA_BUFFS; i++) + (void) rel_data_buff(fs_hcpriv, ((fs_data_buff *)OTG_DATA_BASE)+i); + + for (i = 0; i < NUM_ETDS; i++) + rel_etd(fs_hcpriv,i); + + fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCRESET | HOST_CONTROL_RMTWUEN | + HOST_CONTROL_HCUSBSTE_RESET | HOST_CONTROL_CTLBLKSR_11); + + + //TRACE_MSG0(HCD, "HW HCD_ENABLE_SET"); + fs_andl(OTG_CORE_HNP_CSTAT, ~(MODULE_MASTER | MODULE_SLAVE | MODULE_CMPEN | + MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ)); + fs_rl(OTG_CORE_HNP_CSTAT); + + fs_orl(OTG_CORE_HNP_CSTAT, MODULE_MASTER | MODULE_CMPEN | MODULE_BGEN | MODULE_ABBUSREQ); + fs_orl(OTG_CORE_HNP_CSTAT, MODULE_ARMTHNPE | MODULE_BHNPEN); // XXX + + fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_OPERATIONAL); + + //hcd_hw_enable_interrupts(bus_hcpriv); + + mask = (HOST_PSCINT_EN | HOST_FMOFINT_EN | HOST_HERRINT_EN | + HOST_RESDETINT_EN | /* HOST_SOFINT_EN | */ HOST_DONEINT_EN | HOST_SORINT_EN); + + // R1: sec 23.11.15 pg 23-54 + fs_rl(OTG_CORE_HNP_CSTAT); + fs_rl(OTG_HOST_CONTROL); + fs_rl(OTG_HOST_ROOTHUB_STATUS); + fs_rl(OTG_HOST_PORT_STATUS_1); + fs_rl(OTG_HOST_PORT_STATUS_2); + fs_rl(OTG_HOST_PORT_STATUS_3); + + fs_wl(OTG_HOST_SINT_STEN, mask); + fs_rl(OTG_HOST_SINT_STEN); + local_irq_restore(flags); + } + + rh_loc_sof_func(otg, flag); + + switch (flag) { + case RESET: + local_irq_save(flags); + + //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); + //TRACE_MSG0(HCD, "HW HCD_ENABLE_RESET"); + + //hcd_hw_disable_interrupts(bus_hcpriv); + // R1: sec 23.11.15 pg 23-54 + fs_wl(OTG_HOST_SINT_STAT, 0); + + fs_andl(OTG_HOST_CONTROL, ~HOST_CONTROL_HCUSBSTE_OPERATIONAL); + + // Shut down hardware if not already shut down.... + //hcd_hw_disable_interrupts(bus_hcpriv); + + fs_host_clock_off(); + local_irq_restore(flags); + break; + } +} +/* ********************************************************************************************* */ +/* Module init + * + * Define the global data structures and call the initialization functions required + * to start the generic hcd layer and register with the otg core. + * + */ + +void rh_suspend_func(struct otg_instance *otg, u8 on); +void hcd_init_func (struct otg_instance *otg, u8 flag); +int hcd_probe(struct usb_interface *intf, const struct usb_device_id *id); +void hcd_disconnect(struct usb_interface *intf); + +int fs_mod_init(void); +#ifdef MODULE +void fs_mod_exit(void); +#endif + +#if !defined(OTG_C99) + +static struct usb_driver mx2_usb_driver; +fs_hcpriv priv_mx21; +struct bus_hcpriv fs_bus_hcpriv; +struct hcd_ops hcd_ops; + + +/*! + * fs_hcd_global_init() - initialize global vars for non C99 systems + */ +void fs_hcd_global_init(void) +{ + ZERO(mx2_usb_driver); + mx2_usb_driver.owner = THIS_MODULE; + mx2_usb_driver.name = "MX21_HCD"; + mx2_usb_driver.probe = hcd_probe; + mx2_usb_driver.disconnect = hcd_disconnect; + + ZERO(priv_mx21); + priv_mx21.free_buffs = 0xffff; + priv_mx21.free_etds = 0x0000ffff; + priv_mx21.bus_hcpriv = &fs_bus_hcpriv; + + ZERO(fs_bus_hcpriv); + fs_bus_hcpriv.hw_hci = &priv_mx21; + fs_bus_hcpriv.usb_driver = &mx2_usb_driver; + fs_bus_hcpriv.bus_device.driver = &mx2_usb_driver.driver; + fs_bus_hcpriv.bus_device.parent = NULL; + fs_bus_hcpriv.bus_device.bus = &usb_bus_type; // struct bus_type * +#if 0 + strncpy(fs_bus_hcpriv.bus_device.name, "MX21 HOST", sizeof(fs_bus_hcpriv.bus_device.name)); +#endif + fs_bus_hcpriv.num_ports = 1; + fs_bus_hcpriv.otg_port = 1; + fs_bus_hcpriv.otg_capable_mask = (1 << 1); + fs_bus_hcpriv.rh_serial = "000001"; + fs_bus_hcpriv.rh_product = "Virtual Root Hub"; + fs_bus_hcpriv.rh_manufacturer = "Belcarra Technologies"; + fs_bus_hcpriv.rh_vendorid = CONFIG_OTG_ROOTHUB_VENDORID; + fs_bus_hcpriv.rh_productid = CONFIG_OTG_ROOTHUB_PRODUCTID; + fs_bus_hcpriv.rh_bcddevice = CONFIG_OTG_ROOTHUB_BCDDEVICE; + fs_bus_hcpriv.rh_bmAttributes = 0x0; + fs_bus_hcpriv.rh_bMaxPower = 0; + + fs_bus_hcpriv.max_active_transfers = NUM_ETDS; + fs_bus_hcpriv.max_active_urbs = NUM_ETDS; + + ZERO(hcd_ops); + hcd_ops.name = "MX21 HCD"; + hcd_ops.max_ports = 1; + hcd_ops.capabilities = 0; + // module + hcd_ops.mod_init = fs_mod_init; // called for module init +#ifdef MODULE + hcd_ops.mod_exit = fs_mod_exit; // called for module exit +#endif + // otg state machine + hcd_ops.hcd_init_func = hcd_init_func; // initialize when otg enabled + hcd_ops.hcd_en_func = fs_hcd_en_func; // setup hardware as host + hcd_ops.loc_suspend_func = rh_loc_suspend_func; // enable port on hub + hcd_ops.loc_sof_func = fs_loc_sof_func; // enable port on hub +}; + +#else /* !defined(OTG_C99) */ + +static struct usb_driver mx2_usb_driver = { + .owner = THIS_MODULE, + .name = "MX21_HCD", + .probe = hcd_probe, + .disconnect = hcd_disconnect, +}; + +extern struct bus_hcpriv fs_bus_hcpriv; +fs_hcpriv priv_mx21 = { + .free_buffs = 0xffff, + .free_etds = 0x0000ffff, + .bus_hcpriv = &fs_bus_hcpriv, +}; + +struct bus_hcpriv fs_bus_hcpriv = { + + .hw_hci = &priv_mx21, + .usb_driver = &mx2_usb_driver, + .bus_device.driver = &mx2_usb_driver.driver, + .bus_device.parent = NULL, + .bus_device.bus = &usb_bus_type, // struct bus_type * + .bus_device.name = "MX21 HOST", + + .num_ports = 1, + .otg_port = 1, + .otg_capable_mask = (1 << 1), + .rh_serial = "000001", + .rh_product = "Virtual Root Hub", + .rh_manufacturer = "Belcarra Technologies", + .rh_vendorid = CONFIG_OTG_ROOTHUB_VENDORID, + .rh_productid = CONFIG_OTG_ROOTHUB_PRODUCTID, + .rh_bcddevice = CONFIG_OTG_ROOTHUB_BCDDEVICE, + .rh_bmAttributes = 0x0, + .rh_bMaxPower = 0, + + .max_active_transfers = NUM_ETDS, + .max_active_urbs = NUM_ETDS, +}; + +struct hcd_ops hcd_ops = { + + .name = "MX21 HCD", + .max_ports = 1, + .capabilities = 0, + // module + .mod_init = fs_mod_init, // called for module init +#ifdef MODULE + .mod_exit = fs_mod_exit, // called for module exit +#endif + .hcd_init_func = hcd_init_func, // initialize when otg enabled + .hcd_en_func = fs_hcd_en_func, // setup hardware as host + .loc_suspend_func = rh_loc_suspend_func, // enable port on hub + .loc_sof_func = fs_loc_sof_func, // enable port on hub +}; +#endif /* !defined(OTG_C99) */ + + +extern int hcd_init(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver); +extern void hcd_exit(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver); + +/*! + * fs_mod_init() - initialize mx2 hcd + * + * This calls hcd_init() with the mx2 data structures. + */ +int fs_mod_init(void) +{ + RETURN_ENOMEM_IF(hcd_init(&fs_bus_hcpriv, &mx2_usb_driver)); + + hcd_instance->privdata = &fs_bus_hcpriv; + return 0; +} + +#ifdef MODULE +/*! + * fs_mod_exit() - de-initialize mx2 hcd + * + * This calls hcd_exit() with the mx2 data structures. + */ +void fs_mod_exit(void) +{ + hcd_exit(&fs_bus_hcpriv, &mx2_usb_driver); +} +#endif + +/* @} */ + diff -uNr linux/drivers/no-otg/ocd/fsotg/fs-ocd.c linux/drivers/otg/ocd/fsotg/fs-ocd.c --- linux/drivers/no-otg/ocd/fsotg/fs-ocd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/fsotg/fs-ocd.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,263 @@ +/* + * otg/ocd/fsotg/fs-ocd.c -- USB Device Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/fsotg/fs-ocd.c + * @brief Freescale USB OTG Controller Driver + * This is the OTG Controller Driver. + * + * This implements overall configuration and control of the Freescale OTG hardware + * and implements the main interrupt handler. + * + * There is some processor specific code here to support the alternate processors + * that implement this type of USB support. + * + * There is no board or platform level code here. + * + * @ingroup FSOTG + * + * @{ + */ + +#include +#include +#include +#undef GPT_BASE_ADDR +#include +#include + +#include + + +/* ********************************************************************************************* */ +#define TIMEOUT_VALUE 1000 +/*! + * fs_func_clock_on() - enable function controller clock + */ +void fs_func_clock_on(void) +{ + u32 timeout = TIMEOUT_VALUE; + //TRACE_MSG0(OCD, "FUNC CLOCK ON:"); + fs_orl(OTG_CORE_CLK_CTRL, MODULE_FUNC_CLK); + while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_FUNC_CLK)) { timeout--; if (!timeout) break; } + + //fs_orl(OTG_CORE_CINT_STEN, MODULE_ASFCINT_EN |MODULE_FCINT_EN); +} + +/*! + * fs_host_clock_on() - enable host controller clock + */ +void fs_host_clock_on(void) +{ + u32 timeout = TIMEOUT_VALUE; + //TRACE_MSG0(OCD, "HOST CLOCK ON"); + fs_orl(OTG_CORE_CLK_CTRL, MODULE_HOST_CLK); + while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_HOST_CLK)) { timeout--; if (!timeout) break; } +} + +/*! + * fs_func_clock_off() - disable function controller clock + */ +void fs_func_clock_off(void) +{ + u32 timeout = TIMEOUT_VALUE; + //TRACE_MSG0(OCD, "FUNC CLOCK OFF"); + fs_andl(OTG_CORE_CLK_CTRL, 0x0); + while((fs_rl(OTG_CORE_CLK_CTRL) & (MODULE_FUNC_CLK | MODULE_MAIN_CLK))) { timeout--; if (!timeout) break; } +} + +/*! + * fs_host_clock_off() - disable host controller clock + */ +void fs_host_clock_off(void) +{ + u32 timeout = TIMEOUT_VALUE; + //TRACE_MSG0(OCD, "HOST CLOCK OFF"); + fs_andl(OTG_CORE_CLK_CTRL, 0x0); + while((fs_rl(OTG_CORE_CLK_CTRL) & (MODULE_HOST_CLK | MODULE_MAIN_CLK))) { timeout--; if (!timeout) break; } +} + +/*! + * fs_main_clock_on() - enable main clock + */ +void fs_main_clock_on(void) +{ + u32 timeout = TIMEOUT_VALUE; + //TRACE_MSG0(OCD, "MAINFUNC CLOCK ON"); + fs_orl(OTG_CORE_CLK_CTRL, MODULE_MAIN_CLK); + while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_MAIN_CLK)) { timeout--; if (!timeout) break; } +} + +/*! + * fs_main_clock_off() - disable main clock + */ +void fs_main_clock_off(void) +{ + u32 timeout = TIMEOUT_VALUE; + //TRACE_MSG0(OCD, "MAIN CLOCK OFF"); + fs_wl_set(OCD, OTG_CORE_CLK_CTRL, 0); + while((fs_rl(OTG_CORE_CLK_CTRL) & MODULE_MAIN_CLK)) { timeout--; if (!timeout) break; } +} + +/* ********************************************************************************************* */ + +/*! + * fs_disable_interrupts() - disable interrupts + */ +void fs_disable_interrupts (void) +{ + fs_wl(OTG_CORE_CINT_STEN, 0 ); + fs_wl(OTG_CORE_HINT_STEN, 0 ); + fs_wl(OTG_HOST_SINT_STEN, 0 ); + fs_wl(OTG_HOST_XYINT_STEN, 0 ); + fs_wl(OTG_HOST_ETD_EN, 0); + fs_wl(OTG_HOST_ETD_DONE, 0 ); + fs_wl(OTG_FUNC_SINT_STEN, 0 ); + fs_wl(OTG_FUNC_XYINT_STEN, 0 ); + fs_wl(OTG_FUNC_EP_EN, 0 ); + fs_wl(OTG_FUNC_EP_DEN, 0 ); +} + +/* ********************************************************************************************* */ + +/* ********************************************************************************************* */ +/*! + * fs_init() - initial tcd setup + * Allocate interrupts and setup hardware. + */ +void fs_init (void) +{ + int timeout; + unsigned long flags; + + TRACE_MSG0(OCD, "MX2_MOD_OCD_INIT"); + local_irq_save (flags); + + fs_wl(OTG_SYS_CTRL, 0x0); + + /* 2. Ensure hardware is reset and cleared + */ + fs_clear_words((volatile u32 *)MX2_IO_ADDRESS(OTG_DMA_BASE), (32*16/4)); + //fs_clear((void *)MX2_IO_ADDRESS(OTG_FUNC_BASE), 0x200); + fs_main_clock_off(); + + fs_wl_clr(OCD, OTG_CORE_RST_CTRL, MODULE_RSTI2C | 0x3f); + while (fs_rl(OTG_CORE_RST_CTRL)); + + /* 3. OTG Hardware Mode and clocks + * set to diff, diff and Configure the OTG to behave as function + */ + TRACE_MSG0(OCD, "3. OTG Software Mode and clock"); + fs_andl(OTG_CORE_HWMODE, 0xffffff0f); // clear + fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_SHNP); // set to software hnp + fs_rl(OTG_CORE_HWMODE); + + fs_andl(OTG_CORE_HNP_CSTAT, ~0x00000800); + fs_rl(OTG_CORE_HNP_CSTAT); + + fs_wl_set(OCD, OTG_CORE_HNP_T3PCR, 0x00000000); + fs_rl(OTG_CORE_HNP_T3PCR); + + TRACE_MSG0(OCD, "6. Enable "); + TRACE_MSG0(OCD, "enable core interrupts"); + + fs_wl(OTG_CORE_CINT_STEN, 0); + + fs_wl(OTG_CORE_CINT_STEN, MODULE_ASHNPINT_EN | + MODULE_ASHCINT_EN | MODULE_HNPINT_EN | MODULE_HCINT); + + + TRACE_MSG0(OCD, "enable host interrupts"); + fs_wl(OTG_CORE_HINT_STEN, HNP_I2COTGINT_EN | HNP_AWAITBTO_EN | + HNP_AIDLEBDTO_EN | HNP_SRPSUCFAIL_EN | HNP_SRPINT_EN | HNP_VBUSERROR_EN | + HNP_ABSEVAILD_EN | HNP_ABUSVALID_EN | HNP_MASSLVCHG_EN | HNP_IDCHANGE_EN); + + TRACE_MSG0(OCD, "disable various host interrupts"); + fs_wl(OTG_HOST_XYINT_STEN, 0); + fs_wl(OTG_HOST_ETD_EN, 0); + fs_wl(OTG_HOST_ETD_DONE, 0); + fs_wl(OTG_HOST_SINT_STEN, 0); + + TRACE_MSG0(OCD, "disable various function interrupts"); + fs_wl(OTG_FUNC_XYINT_STEN, 0); + fs_wl(OTG_FUNC_EP_EN, 0); + fs_wl(OTG_FUNC_EP_DEN, 0); + + fs_wl(OTG_DMA_DINT_STEN, 0x3); + //fs_wb(I2C_MASTER_INT_REG_ADD, 0xf0); + //fs_wb(I2C_MASTER_INT_REG_ADD, 0x00); + + // XXX note that newer designs than the mx21 will also need to check + // and/or set OTG_CORE_INTERRUPT_STEN + + + TRACE_MSG0(OCD, "--"); + TRACE_MSG0(OCD, "8. Ready "); + TRACE_MSG0(OCD, "--"); + + local_irq_restore (flags); +} + +extern void fs_stop_ep(int epn, int dir); + +/*! + * fs_exit() - de-initialize + */ +void fs_exit(void) +{ + int i; + unsigned long flags; + TRACE_MSG0(OCD, "MX2_MOD_OCD_EXIT"); + + fs_disable_interrupts(); + //_reg_CRM_PCCR1 &= ~(1<<27); // disable GPT3 + + local_irq_save (flags); + for (i = 0; i < 16; i++) { + fs_stop_ep(i, USB_DIR_IN); + fs_stop_ep(i, USB_DIR_OUT); + } + fs_clear_words((volatile u32 *)MX2_IO_ADDRESS(OTG_EP_BASE), (32*16/4)); + //fs_clear((void *)MX2_IO_ADDRESS(OTG_EP_BASE), 0x200); + //fs_clear((volatile u32 *)MX2_IO_ADDRESS(OTG_DMA_BASE), 32*16); + //fs_clear((void *)MX2_IO_ADDRESS(OTG_FUNC_BASE), 0x200); + fs_wl( OTG_CORE_HWMODE, 0xa3); + fs_main_clock_off(); + local_irq_restore (flags); +} + + +/*! + * fs_ocd_init() - used to initialize/enable or disable the tcd driver + * @param otg + * @param flag + */ +void fs_ocd_init(struct otg_instance *otg, u8 flag) +{ + TRACE_MSG0(OCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(OCD, "MX2_OCD_EN SET"); + fs_init(); + otg_event(otg, TCD_OK | ID_FLOAT, OCD, "OCD_OK"); + break; + case RESET: + fs_exit(); + TRACE_MSG0(OCD, "MX2_OCD_EN RESET"); + otg_event(otg, TCD_OK | ID_FLOAT, OCD, "OCD_OK"); + break; + } +} + +/* ********************************************************************************************* */ + +/* @} */ + diff -uNr linux/drivers/no-otg/ocd/fsotg/fs-pcd.c linux/drivers/otg/ocd/fsotg/fs-pcd.c --- linux/drivers/no-otg/ocd/fsotg/fs-pcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/fsotg/fs-pcd.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,793 @@ +/* + * otg/ocd/fsotg/fs-pcd.c -- Freescale USBOTG Peripheral Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/fsotg/fs-pcd.c + * @brief Freescale USB Peripheral Controller Driver + * This implements the Freescale USBOTG Peripheral Controller Driver. + * + * There is some processor specific code here to support the alternate processors + * that implement this type of USB support. + * + * There is no board or platform level code here. + * + * @ingroup FSOTG + * + * @{ + */ + +#include +#include +#include + +#include + +void fs_func_clock_on(void); /* defined in mx2-ocd.c */ +void fs_func_clock_off(void); /* defined in mx2-ocd.c */ + +u8 fs_new_address, fs_usb_address; /* used to save and set USB address */ + +/* ********************************************************************************************* */ +/*! + * fs_ep_config() - configure and enable an endpoint to send or receive data + * First setup the endpoint descriptor words, and then enable the endpoint and done interrupt + * @param epn - endpoint number + * @param dir - direction IN or OUT + * @param ep_num - + * @param wMaxPacketSize + * @param format + * @param ttlbtecnt + * @param stall + */ +static void inline +fs_ep_config(int epn, int dir, u32 ep_num, int wMaxPacketSize, int format, int ttlbtecnt, int stall) +{ + TRACE_MSG6(PCD, "epn: %02x dir: %02x sz: %d fmt: %x cnt: %d stall: %d", + epn, dir, wMaxPacketSize, format, ttlbtecnt, stall); + + fs_wl(ep_word(epn, dir, 0), ((stall ? 1 : 0) << 31) | (wMaxPacketSize << 16) | ((format & 3) << 14)); + fs_wl(ep_word(epn, dir, 1), ttlbtecnt ? (data_y_buf(epn, dir) << 16) | data_x_buf(epn, dir): 0); + fs_wl(ep_word(epn, dir, 3), ((wMaxPacketSize-1) << 21) | ttlbtecnt); + fs_orl(OTG_FUNC_IINT, ep_num); // issue done status interrupts immediately + fs_orl(OTG_FUNC_EP_DEN, ep_num); // enable endpoint done interrupt + fs_orl(OTG_FUNC_EP_EN, ep_num); // enable the interrupt +} + +/*! + * fs_ep_config_dma() - configure and enable an endpoint to send or receive data + * Check the RDY bit and clear if necessary, setup dma, do common setup and then set RDY. + * @param epn - endpoint number + * @param dir - direction IN or OUT + * @param wMaxPacketSize + * @param format + * @param ttlbtecnt + * @param bp + */ +static void inline +fs_ep_config_dma(int epn, int dir, int wMaxPacketSize, int format, int ttlbtecnt, u8 *bp) +{ + u32 ep_num = ep_num_dir(epn, dir); + u32 dma_num = dma_num_dir(epn, dir); + TRACE_MSG2(PCD, "EP CONFIG DMA: epn: %02x num: %02x", epn, ep_num); + if (fs_rl(OTG_FUNC_EP_RDY) & ep_num) fs_wl_set(PCD, OTG_FUNC_EP_RDY, ep_num); + fs_wl(OTG_DMA_EPN_MSA(dma_num), (int)bp); + fs_wl_set(PCD, OTG_DMA_EP_EN, ep_num); + fs_ep_config(epn, dir, ep_num, wMaxPacketSize, format, ttlbtecnt, 0); + fs_wl_set(PCD, OTG_FUNC_EP_RDY, ep_num); // toggle ready bit +} + +/*! + * fs_ep_config_nodma() - configure and enable an endpoint to send or receive data + * Non-DMA setup, optional stall, ready etc. + * @param epn - endpoint number + * @param dir + * @param stall - stall endpoint + * @param wMaxPacketSize + * @param format + * @param ttlbtecnt + * @param rdy1 + * @param rdy2 + */ +static void +fs_ep_config_nodma(int epn, int dir, int stall, int wMaxPacketSize, int format, int ttlbtecnt, int rdy1, int rdy2) +{ + u32 ep_num = ep_num_dir(epn, dir); + TRACE_MSG5(PCD, "EP CONFIG NODMA: epn: %02x num: %02x stall: %d r1: %d r2: %d", epn, ep_num, stall, rdy1, rdy2); + #if 1 + // XXX This should work, but enumeratation fails after disable/enable + if (rdy1) { + TRACE_MSG3(PCD, "EP CONFIG NODMA: epn: %02x num: %02x checking RDY: %02x", + epn, ep_num, rdy1 && fs_rl(OTG_FUNC_EP_RDY) & ep_num); + RETURN_IF (rdy1 && (fs_rl(OTG_FUNC_EP_RDY) & ep_num)); + } + #endif + UNLESS(epn) + fs_orl(OTG_FUNC_XYINT_STEN, ep_num); // enable x or y buffer interrupt + + fs_ep_config(epn, dir, ep_num, wMaxPacketSize, format, ttlbtecnt, stall); + RETURN_UNLESS(rdy2); + UNLESS (fs_rl(OTG_FUNC_EP_RDY) & ep_num) fs_wl_set(PCD, OTG_FUNC_EP_RDY, ep_num); // toggle ready bit +} + +/*! + * fs_sendzlp() - start sending a buffer on a specified endpoint + * Toggle XFILL_STAT or YFILL_STAT bit, then call fs_ep_config_nodma(). + * @param epn - endpoint number + * @param endpoint - endpoint instance + * @param wMaxPacketSize + */ +static void inline +fs_sendzlp (int epn, struct usbd_endpoint_instance *endpoint, int wMaxPacketSize) +{ + u32 ep_num_in = ep_num_in(epn); + TRACE_MSG0(PCD, "ZLP"); + if (!(fs_rl(OTG_FUNC_XFILL_STAT) & ep_num_in)) { + //TRACE_MSG0(PCD, "EMPTY Y BUFFER: ZLP"); + fs_wl_set(PCD, OTG_FUNC_XFILL_STAT, ep_num_in); + } + else if (!(fs_rl(OTG_FUNC_YFILL_STAT) & ep_num_in)) { + //TRACE_MSG0(PCD, "EMPTY Y BUFFER: ZLP"); + fs_wl_set(PCD, OTG_FUNC_YFILL_STAT, ep_num_in); + } + fs_ep_config_nodma(epn, USB_DIR_IN, 0, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, 0, 1, 1); +} + +/*! + * fs_stop_ep() - stop endpoint + * @param epn - endpoint number + * @param dir - direction IN or OUT + */ +static void fs_stop_ep(int epn, int dir) +{ + int ep_num = ep_num_dir(epn, dir); + + TRACE_MSG3(PCD, "STOP EP: %08x dir: %02x [%08x]", ep_num, dir, fs_rl(ep_word(epn, dir, 0))); + + //fs_wl(ep_word(epn, dir, 0), ((stall ? 1 : 0) << 31) | (wMaxPacketSize << 16) | ((format & 3) << 14)); + + if (fs_rl(OTG_FUNC_EP_RDY) & ep_num) + fs_wl_clr(PCD, OTG_FUNC_EP_RDY, ep_num); + fs_andl(OTG_FUNC_XYINT_STEN, ~ep_num); + fs_andl(OTG_FUNC_IINT, ~ep_num); + if (fs_rl(OTG_FUNC_XINT_STAT) & ep_num) { + fs_wl_clr(PCD, OTG_FUNC_XINT_STAT, ep_num); + fs_wl_clr(PCD, OTG_FUNC_XFILL_STAT, ep_num); + } + else if (fs_rl(OTG_FUNC_YINT_STAT) & ep_num) { + fs_wl_clr(PCD, OTG_FUNC_YINT_STAT, ep_num); + fs_wl_clr(PCD, OTG_FUNC_YFILL_STAT, ep_num); + } +} + +/* ********************************************************************************************* */ +/*! + * fs_pcd_setup_ep() - setup endpoint + * @param pcd - pcd instance + * @param epn - endpoint number + * @param endpoint - endpoint instance + */ +static void inline +fs_pcd_setup_ep (struct pcd_instance *pcd, unsigned int epn, struct usbd_endpoint_instance *endpoint) +{ + TRACE_MSG2(PCD, "START EPN: config %d %02x", epn, endpoint->bEndpointAddress); + if (epn && endpoint->bEndpointAddress) + fs_ep_config_nodma(endpoint->bEndpointAddress & 0x7f, endpoint->bEndpointAddress & USB_DIR_IN, 0, + endpoint->wMaxPacketSize, endpoint->bmAttributes&0x3, 0, 0, 0); +} + +/*! + * fs_pcd_start_ep0_setup() - enable ep0 for receiving SETUP request + * Unless already ready, configure ep0 to receive. + * @param endpoint - endpoint instance + */ +static void inline +fs_pcd_start_ep0_setup (struct usbd_endpoint_instance *endpoint) +{ + TRACE_MSG0(PCD, "START EP0: config"); + fs_ep_config_nodma(0, USB_DIR_OUT, 0, endpoint->wMaxPacketSize, endpoint->bmAttributes&0x3, + endpoint->wMaxPacketSize, 0, 1); // XXX rdy1 must be zero +} +/* ********************************************************************************************* */ +/*! + * fs_pcd_start_endpoint_out() - start receive + * @param pcd - pcd instance + * @param endpoint - endpoint number + */ +static void +fs_pcd_start_endpoint_out(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); + int epn = endpoint->bEndpointAddress & 0x7f; + UNLESS (rcv_urb) { + UNLESS (!epn) fs_pcd_start_ep0_setup(endpoint); // if EP0 then restart for receive setup + return; + } + rcv_urb->pcimap = (void *)pci_map_single (NULL, (void *) rcv_urb->buffer, rcv_urb->buffer_length, PCI_DMA_FROMDEVICE); + fs_ep_config_dma(epn, USB_DIR_OUT, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, + rcv_urb->buffer_length, rcv_urb->pcimap); + TRACE_MSG3(PCD, "START OUT: %02x rcv_urb: %x length: %d", endpoint->bEndpointAddress, rcv_urb, rcv_urb->actual_length); +} + +/*! + * fs_pcd_stop_out() - process interrupt for received buffer + * @param pcd - pcd instance + * @param epn - endpoint number + * @param endpoint - endpoint instance + */ +static void +fs_pcd_stop_out (struct pcd_instance *pcd, int epn, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); + u32 ep3 = fs_rl(ep_word(epn, USB_DIR_OUT, 3)); + u32 ep_num_out = ep_num_out(epn); + fs_stop_ep(epn, USB_DIR_OUT); + RETURN_UNLESS (rcv_urb); + pcd_rcv_finished_irq (endpoint,rcv_urb->buffer_length - (ep3 & 0xffff), 0); // XXX fix mask + if (!epn) + fs_sendzlp(epn, endpoint, endpoint->wMaxPacketSize); // ZLP + else + fs_pcd_start_endpoint_out(pcd, endpoint); // restart +} +/* fs_pcd_cancel_out_irq - cancel OUT urb + */ +static void +fs_pcd_cancel_out_irq (struct pcd_instance *pcd,struct usbd_urb *urb) +{ + struct usbd_endpoint_instance *endpoint = urb->endpoint; + int epn = endpoint->bEndpointAddress & 0x7f; + TRACE_MSG3(PCD, "urb: %x endpoint: %x epn: %d", urb, endpoint, epn); + fs_pcd_stop_out (pcd, epn, endpoint); +} +/* ********************************************************************************************* */ +/*! + * fs_pcd_start_endpoint_in() - start transmit + * Get next tx_urb (completing old one if there is one) and start sending it. + * @param pcd - pcd instance + * @param endpoint - endpoint instance + */ +static void +fs_pcd_start_endpoint_in(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb = pcd_tx_complete_irq(endpoint, 0); + int epn = endpoint->bEndpointAddress & 0x7f; + + UNLESS (tx_urb) { + TRACE_MSG1(PCD, "START IN: %02x finished", endpoint->bEndpointAddress); + UNLESS (epn) { // if not EP0 we are done + if (fs_new_address != fs_usb_address) { + fs_usb_address = fs_new_address; + //TRACE_MSG1(PCD, "START IN: SETTING ADDRESS %x", fs_usb_address); + fs_wl(OTG_FUNC_DEV_ADDR, fs_usb_address); + } + fs_pcd_start_ep0_setup(endpoint); // if EP0 then restart for receive setup + } + return; + } + + if (pcd_tx_sendzlp(endpoint)) { // check if we need to send ZLP + TRACE_MSG1(PCD, "START IN: ZLP tx_urb: %x", tx_urb); + fs_sendzlp(epn, endpoint, endpoint->wMaxPacketSize); // ZLP + return; + } + endpoint->last = tx_urb->actual_length; + consistent_sync (tx_urb->buffer, tx_urb->actual_length, PCI_DMA_BIDIRECTIONAL); + fs_ep_config_dma(epn, USB_DIR_IN, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, + tx_urb->actual_length, tx_urb->buffer); + + TRACE_MSG4(PCD, "START IN: %02x tx_urb: %x length: %d %s", + endpoint->bEndpointAddress, tx_urb, tx_urb->actual_length, + (tx_urb->flags & USBD_URB_SENDZLP) ? "ZLP": ""); +} + +/*! + * fs_pcd_stop_in() - process interrupt for transmitted buffer + * @param pcd - pcd instance + * @param epn -endpoint number + * @param endpoint - endpoint instance + */ +static void inline +fs_pcd_stop_in (struct pcd_instance *pcd, int epn, struct usbd_endpoint_instance *endpoint) +{ + fs_stop_ep(epn, USB_DIR_IN); + fs_pcd_start_endpoint_in(pcd, endpoint); +} +/*! + * fs_pcd_cancel_in_irq() - cancel IN urb + */ +static void inline +fs_pcd_cancel_in_irq (struct pcd_instance *pcd,struct usbd_urb *urb) +{ + struct usbd_endpoint_instance *endpoint = urb->endpoint; + int epn = endpoint->bEndpointAddress & 0x7f; + TRACE_MSG3(PCD, "urb: %x endpoint: %x epn: %d", urb, endpoint, epn); + fs_pcd_stop_in (pcd, epn, endpoint); +} + +/* ********************************************************************************************* */ +/*! + * fs_pcd_stop_ep0_setup() - setup endpoint zero + * @param pcd - pcd instance + * @param endpoint - endpoint instance + */ +static void +fs_pcd_stop_ep0_setup (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + static struct usbd_device_request request; + u8 *cp = (u8 *) &request; + u32 ep0 = fs_rl(ep_word(0, USB_DIR_OUT, 0)); + u32 ep3 = fs_rl(ep_word(0, USB_DIR_OUT, 3)); + + fs_andl(OTG_FUNC_XYINT_STEN, ~ep_num_both(0)); + fs_andl(OTG_FUNC_IINT, ~ep_num_both(0)); + UNLESS (ep0 & EP0_SETUP) { // check if SETUP flag set + fs_pcd_stop_out(pcd, 0, endpoint); + return; + } + TRACE_MSG1(PCD, "EP0 - SETUP: size: %x", ep3 & 0xffff); + pcd_tx_cancelled_irq(endpoint); + pcd_rcv_cancelled_irq(endpoint); + fs_orl(OTG_FUNC_EP_TOGGLE, 0x2); // XXX is this required? + + /* get request from x or y buffer + */ + if (fs_rl(OTG_FUNC_XINT_STAT) & ep_num_out(0)) { + TRACE_MSG0(PCD, "READ X SETUP"); + fs_memcpy((u8 *)&request, (u8 *)data_x_address(0, USB_DIR_OUT), 8); + fs_wl_clr(PCD, OTG_FUNC_XINT_STAT, ep_num_out(0)); + fs_wl_clr(PCD, OTG_FUNC_XFILL_STAT, ep_num_out(0)); + fs_rl(OTG_FUNC_XFILL_STAT); + } + else if (fs_rl(OTG_FUNC_YINT_STAT) & ep_num_out(0)) { + TRACE_MSG0(PCD, "READ Y SETUP"); + fs_memcpy((u8 *)&request, (u8 *)data_y_address(0, USB_DIR_OUT), 8); + fs_wl_clr(PCD, OTG_FUNC_YINT_STAT, ep_num_out(0)); + fs_wl_clr(PCD, OTG_FUNC_YFILL_STAT, ep_num_out(0)); + fs_rl(OTG_FUNC_YFILL_STAT); + } + else + TRACE_MSG0(PCD, "READ NO SETUP"); // XXX what should we do here? + if (pcd_recv_setup_irq(pcd, &request)) { // process setup packet + TRACE_MSG0(PCD, "pcd_ep0: STALLING"); + fs_rl(OTG_FUNC_XINT_STAT); + fs_ep_config_nodma(0, USB_DIR_IN, 1, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, 0, 1, 1); + return; + } + RETURN_IF (request.wLength); // fs_pcd_start_endpoint_in() will start xfer + if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + //RETURN_IF (request.wLength); // fs_pcd_start_endpoint_in() will start xfer + fs_sendzlp(0, endpoint, endpoint->wMaxPacketSize); // wLength zero, so send ZLP + return; + } + fs_pcd_start_ep0_setup(endpoint); // restart endpoint +} +/* ********************************************************************************************* */ +static int pcd_resetdet_count; +/*! + * fs_pcd_int_hndlr() - interrupt handler + * @param irq + * @param dev_id + * @param regs + * @return interrupt handled status + */ +static irqreturn_t +fs_pcd_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + struct pcd_instance *pcd = pcd_instance; + u32 cmd_stat, sint_stat, ep_dstat, ep_stats, mask; + + /* Reset and process any System interrupts + */ + if ((sint_stat = fs_rl(OTG_FUNC_SINT_STAT))) + fs_wl_clr(PCD, OTG_FUNC_SINT_STAT, sint_stat); + + cmd_stat = fs_rl(OTG_FUNC_CMD_STAT); + + TRACE_MSG2(PCD, "FUNC INT sint: %08x cmd: %08x", sint_stat, cmd_stat); + + if ((cmd_stat & COMMAND_RESETDET) || (sint_stat & SYSTEM_RESETINT)) { + TRACE_MSG1(PCD, "RESETDET: %08x", sint_stat); + usbd_bus_event_handler_irq (pcd->bus, DEVICE_RESET, 0); + fs_wl(OTG_FUNC_DEV_ADDR, 0); + fs_new_address = fs_usb_address = 0; + fs_wl(OTG_FUNC_XYINT_STEN, 0); + fs_wl(OTG_FUNC_IINT, 0); + //fs_wl(OTG_FUNC_EP_DEN, 0); // XXX this fails, + fs_wl(OTG_FUNC_EP_TOGGLE, 0); + fs_wl_clr(PCD, OTG_FUNC_EP_RDY, fs_rl(OTG_FUNC_EP_RDY)); + fs_wl_clr(PCD, OTG_FUNC_EP_DSTAT, fs_rl(OTG_FUNC_EP_DSTAT)); + fs_wl_clr(PCD, OTG_FUNC_XINT_STAT, fs_rl(OTG_FUNC_XINT_STAT)); + fs_wl_clr(PCD, OTG_FUNC_YINT_STAT, fs_rl(OTG_FUNC_YINT_STAT)); + if (pcd_resetdet_count++ > 10) { + + //printk(KERN_INFO"%s: SINT: %08x %08x CINT: %08x\n", __FUNCTION__, + // sint_stat, fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); + fs_func_clock_on(); + fs_wl(OTG_FUNC_SINT_STEN, 0); + fs_wl(OTG_CORE_CINT_STEN, 0); + fs_wl(OTG_FUNC_XYINT_STEN, 0); + fs_wl(OTG_FUNC_EP_EN, 0); + fs_wl(OTG_FUNC_EP_DEN, 0); + fs_func_clock_off(); + //printk(KERN_INFO"%s: SINT: %08x %08x CINT: %08x RESET\n", __FUNCTION__, + // sint_stat, fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); + } + } + if ((cmd_stat & COMMAND_RSMINPROG) || (sint_stat & SYSTEM_RSMFININT)) { + TRACE_MSG1(PCD, "RSMINPRO: %08x", sint_stat); + fs_wl_set(PCD, OTG_FUNC_SINT_STAT,SYSTEM_RSMFININT); + fs_func_clock_on(); + usbd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_ACTIVITY, 0); + } + if ((cmd_stat & COMMAND_SUPDET) || (sint_stat & SYSTEM_SUSPDETINT)) { + int timeout = 0; + TRACE_MSG1(PCD, "SUSPENDDET: %08x", sint_stat); + fs_wl_set(PCD, OTG_FUNC_SINT_STAT,SYSTEM_SUSPDETINT); + fs_func_clock_off(); + usbd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_INACTIVE, 0); + } + ep_dstat = fs_rl(OTG_FUNC_EP_DSTAT); + ep_stats = ep_dstat | fs_rl(OTG_FUNC_XINT_STAT) | fs_rl(OTG_FUNC_YINT_STAT); + if (ep_stats) { + int i; + + pcd_resetdet_count = 0; + + /* clear the done status flags + */ + TRACE_MSG1(PCD, "EP STATS: %08x", ep_stats); + fs_wl_clr(PCD, OTG_FUNC_EP_DSTAT, ep_dstat); + + /* check ep0 first - special handling for control endpoint, need to check dstat, y/x stats + */ + if (ep_stats & EP_OUT) fs_pcd_stop_ep0_setup(pcd, pcd->bus->endpoint_array); + if (ep_stats & EP_IN) fs_pcd_stop_in(pcd, 0, pcd->bus->endpoint_array); + + /* now check all of the data endpoints, only if in dstat + */ + for (i = 1, ep_dstat >>= 2; ep_dstat && (i < 16); i++, ep_dstat >>= 2) { + if (ep_dstat & EP_OUT) fs_pcd_stop_out(pcd, i, pcd->bus->endpoint_array + (2 * i)); + if (ep_dstat & EP_IN) fs_pcd_stop_in(pcd, i, pcd->bus->endpoint_array + (2 * i) + 1); + } + } + return IRQ_HANDLED; +} + +/* ********************************************************************************************* */ +/*! + * fs_pcd_set_address() - set the USB address for this device + * @param pcd + * @param address + */ +static void inline +fs_pcd_set_address (struct pcd_instance *pcd, u8 address) +{ + fs_usb_address = 0; + fs_new_address = address; /* this will be used in the interrupt handler */ +} + +/* ********************************************************************************************* */ +/*! + * fs_pcd_en_func() - enable + * This is called to enable / disable the PCD and USBD stack. + * @param otg - otg instance + * @param flag - SET or RESET + */ +static void +fs_pcd_en_func (struct otg_instance *otg, u8 flag) +{ + unsigned long flags; + struct pcd_instance *pcd = otg->pcd; + struct usbd_bus_instance *bus = pcd->bus; + u32 hwmode = fs_rl(OTG_CORE_HWMODE); + + + switch (flag) { + case SET: + TRACE_MSG0(PCD, "PCD_EN: SET"); + local_irq_save (flags); + fs_andl(OTG_CORE_HWMODE, 0xffffff0f); // clear + + //printk(KERN_INFO"%s: SET\n", __FUNCTION__); + if (hwmode & MODULE_CRECFG_HOST) { + printk(KERN_INFO"%s: warning HOST STILL SET\n", __FUNCTION__); + } + fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_FUNC); // set to software hnp + + /* reset the function core + */ + //fs_wl_clr(PCD, OTG_CORE_RST_CTRL, MODULE_RSTFSIE | MODULE_RSTFC); + //while (fs_rl(OTG_CORE_RST_CTRL)); + + fs_func_clock_on(); + + fs_pcd_start_ep0_setup(pcd->bus->endpoint_array); + + /* C.f. L3 12.1 - enable Slave, Band Gap enable, and Comparator enable. + * C.f. SCM-11 version of the USBOTG specification, ABBusReq enables the charge pump + */ + fs_andl(OTG_CORE_HNP_CSTAT, ~(MODULE_MASTER | MODULE_SLAVE | + MODULE_CMPEN | MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ)); + + fs_orl(OTG_CORE_HNP_CSTAT, MODULE_SLAVE | MODULE_CMPEN | MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ); + fs_rl(OTG_CORE_HNP_CSTAT); + + fs_orl(OTG_FUNC_SINT_STEN, ( /*SYSTEM_DONEREGINTDS_EN | *//*SYSTEM_SOFDETINT_EN |*/ SYSTEM_DONEREGINT_EN | + SYSTEM_SUSPDETINT_EN | SYSTEM_RSMFININT_EN | SYSTEM_RESETINT_EN)); + + fs_orl(OTG_CORE_CINT_STEN, MODULE_FCINT_EN | MODULE_ASFCINT_EN); + //printk(KERN_INFO"%s: SINT: %08x CINT: %08x SET\n", __FUNCTION__, + // fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); + local_irq_restore (flags); + + break; + + case RESET: + TRACE_MSG0(PCD, "PCD_EN: RESET"); + //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); + + //fs_andl(OTG_FUNC_SINT_STEN, ~( /*SYSTEM_DONEREGINTDS_EN | *//*SYSTEM_SOFDETINT_EN |*/ SYSTEM_DONEREGINT_EN | + // SYSTEM_SUSPDETINT_EN | SYSTEM_RSMFININT_EN | SYSTEM_RESETINT_EN)); + + local_irq_save (flags); + fs_func_clock_on(); + fs_andl(OTG_FUNC_SINT_STEN, 0); + fs_andl(OTG_CORE_CINT_STEN, ~(MODULE_FCINT_EN | MODULE_ASFCINT_EN)); + + fs_func_clock_off(); + //printk(KERN_INFO"%s: SINT: %08x CINT: %08x RESET\n", __FUNCTION__, + // fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); + + /* reset the function core + */ + fs_wl_clr(PCD, OTG_CORE_RST_CTRL, MODULE_RSTFSIE | MODULE_RSTFC); + while (fs_rl(OTG_CORE_RST_CTRL)); + fs_andl(OTG_CORE_HWMODE, ~MODULE_CRECFG_FUNC); // set to software hnp + local_irq_restore (flags); + + usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); + usbd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0); + + break; + } + TRACE_MSG1(PCD, "HWMODE: %08x", fs_rl(OTG_CORE_HWMODE)); +} + +/*! + * fs_remote_wakeup() - perform remote wakeup. + * Initiate a remote wakeup to the host. + * @param otg - otg instance + * @param flag - SET or RESET + */ +static void +fs_remote_wakeup(struct otg_instance *otg, u8 flag) +{ + unsigned long flags; + TRACE_MSG0(PCD, "MX2_REMOTE_WAKEUP: "); + local_irq_save (flags); + fs_wl_set(PCD, OTG_FUNC_CMD_STAT, COMMAND_RSMINPROG); + fs_wl(OTG_FUNC_CMD_STAT, COMMAND_RSMINPROG); + local_irq_restore (flags); +} + +/* ********************************************************************************************* */ +/*! + * fs_pcd_start() - start the UDC + * @param pcd + */ +static void +fs_pcd_start (struct pcd_instance *pcd) +{ + //TRACE_MSG0(PCD, "UDC START"); + fs_rl(OTG_FUNC_SINT_STEN); + //fs_pcd_start_ep0_setup(pcd_instance->bus->endpoint_array); +} + +/*! + * fs_pcd_stop() - stop the UDC + * @param pcd + */ +static void +fs_pcd_stop (struct pcd_instance *pcd) +{ + TRACE_MSG0(PCD, "UDC STOP"); +} + +/*! + * pcd_assign_endpoint() + */ +static int +pcd_assign_endpoint(u8 physicalEndpoint, int used[UDC_MAX_ENDPOINTS], + struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, u16 wMaxPacketSize, u16 transferSize) +{ + RETURN_EINVAL_IF( used[physicalEndpoint] ); + endpoint_map->bEndpointAddress[0] = physicalEndpoint | (USB_DIR_IN & bmAttributes); + endpoint_map->physicalEndpoint[0] = (physicalEndpoint * 2) + ((USB_DIR_IN & bmAttributes) ? 1 : 0); + endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; + endpoint_map->transferSize[0] = transferSize; + endpoint_map->bmAttributes[0] = bmAttributes; + used[physicalEndpoint]++; + TRACE_MSG4(PCD, "ASSIGN: index: %02x phys: %02x addr: %02x", physicalEndpoint, + endpoint_map->physicalEndpoint[0], endpoint_map->bEndpointAddress[0], 0); + return 0; +} + +/*! + * fs_pcd_request_endpoints() + * @param pcd + * @param endpoint_map_array + * @param endpointsRequested + * @param requestedEndpoints + */ +static int +fs_pcd_request_endpoints(struct pcd_instance *pcd, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, + struct usbd_endpoint_request *requestedEndpoints) +{ + struct usbd_device_description *device_description; + int i, in, out; + int in_used[UDC_MAX_ENDPOINTS]; + int out_used[UDC_MAX_ENDPOINTS]; + memset(in_used, 0, sizeof(in_used)); + memset(out_used, 0, sizeof(out_used)); + TRACE_MSG1(PCD, "REQUEST ENDPOINTS: %d", endpointsRequested); + for (in = out = 1, i = 0; i < endpointsRequested; i++) { + struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; + u8 bmAttributes = requestedEndpoints[i].bmAttributes; + u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; + switch(bmAttributes) { + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + case USB_DIR_IN | USB_ENDPOINT_BULK: + CONTINUE_IF(!pcd_assign_endpoint(in++, in_used, endpoint_map, bmAttributes, 0x40, transferSize)); + THROW(error); + + case USB_DIR_OUT | USB_ENDPOINT_BULK: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + CONTINUE_IF(!pcd_assign_endpoint(out++, out_used, endpoint_map, bmAttributes, 0x40, transferSize)); + //CONTINUE_IF(!pcd_assign_endpoint(in++, out_used, endpoint_map, bmAttributes, 0x40, transferSize)); + THROW(error); + + case USB_ENDPOINT_CONTROL: + continue; + case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: + case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: + THROW(error); + } + THROW(error); + CATCH(error) { + printk(KERN_ERR"%s: FAILED num: %d bmAttributes: %02x transferSize: %02x\n", + __FUNCTION__, i, bmAttributes, transferSize); + return -EINVAL; + } + } + return 0; +} + +/*! + * fs_pcd_set_endpoints() - setup the physical endpoints for the endpoint map + * @param pcd + * @param endpointsRequested + * @param endpoint_map_array + */ +static int +fs_pcd_set_endpoints (struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) +{ + fs_clear_words((volatile u32 *)MX2_IO_ADDRESS(OTG_EP_BASE), (32*16/4)); + //fs_clear((void *)MX2_IO_ADDRESS(OTG_EP_BASE), 0x200); + TRACE_MSG0(PCD, "Enable system control interrupts"); + fs_wl(OTG_SYS_CTRL, SYS_CTRL_I2C_WU_INT_EN | SYS_CTRL_OTG_WU_INT_EN | SYS_CTRL_HOST_WU_INT_EN | SYS_CTRL_FNT_WU_INT_EN); + return 0; +} + +/*! + * fs_pcd_framenum() - get current framenum + */ +static u16 +fs_pcd_framenum (void) +{ + return fs_rl(OTG_FUNC_FRM_NUM); +} + +/* + * fs_endpoint_halted() - is endpoint halted + */ +static int +fs_endpoint_halted (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + int epn = endpoint->bEndpointAddress & 0x7f; + int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; + + TRACE_MSG2(PCD, "epn: %02x dir: %x", epn, dir); + return fs_rl(ep_word(epn, dir, 0)) & (1 << 31) ? TRUE : FALSE; +} + +/* fs_halt_endpoint - halt endpoint + */ +int fs_halt_endpoint(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int flag) +{ + int epn = endpoint->bEndpointAddress & 0x7f; + int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; + TRACE_MSG3(PCD, "epn: %02x dir: %x flag: %d", epn, dir, flag); + fs_ep_config_nodma(epn, USB_DIR_IN, flag, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, 0, 0, 1); + return 0; +} + +/* ********************************************************************************************* */ + +#if !defined(OTG_C99) +struct usbd_pcd_ops usbd_pcd_ops; +struct pcd_ops pcd_ops; + +/*! + * fs_pcd_global_init() - initialize globals + */ +static void +fs_pcd_global_init(void) +{ + ZERO(usbd_pcd_ops); + usbd_pcd_ops.bmAttributes = USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED; + usbd_pcd_ops.max_endpoints = UDC_MAX_ENDPOINTS; + usbd_pcd_ops.ep0_packetsize = EP0_PACKETSIZE; + usbd_pcd_ops.capabilities = REMOTE_WAKEUP_SUPPORTED; + usbd_pcd_ops.name = UDC_NAME; + usbd_pcd_ops.start = fs_pcd_start; + usbd_pcd_ops.stop = fs_pcd_stop; + usbd_pcd_ops.start_endpoint_in = fs_pcd_start_endpoint_in; + usbd_pcd_ops.start_endpoint_out = fs_pcd_start_endpoint_out; + usbd_pcd_ops.request_endpoints = fs_pcd_request_endpoints; + usbd_pcd_ops.set_endpoints = fs_pcd_set_endpoints; + usbd_pcd_ops.set_address = fs_pcd_set_address; + usbd_pcd_ops.setup_ep = fs_pcd_setup_ep; + usbd_pcd_ops.halt_endpoint = fs_halt_endpoint; + usbd_pcd_ops.endpoint_halted = fs_endpoint_halted; + + usbd_pcd_ops.cancel_in_irq = fs_pcd_cancel_in_irq; + usbd_pcd_ops.cancel_out_irq = fs_pcd_cancel_out_irq; + + ZERO(pcd_ops); + pcd_ops.pcd_en_func = fs_pcd_en_func; + pcd_ops.pcd_init_func = pcd_init_func; + pcd_ops.remote_wakeup_func = fs_remote_wakeup; + pcd_ops.framenum = fs_pcd_framenum; +} +#else /* !defined(OTG_C99) */ +static struct +usbd_pcd_ops usbd_pcd_ops = { + .bmAttributes = USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED, + .max_endpoints = UDC_MAX_ENDPOINTS, + .ep0_packetsize = EP0_PACKETSIZE, + .capabilities = REMOTE_WAKEUP_SUPPORTED, + .name = UDC_NAME, + .start = fs_pcd_start, + .stop = fs_pcd_stop, + .start_endpoint_in = fs_pcd_start_endpoint_in, + .start_endpoint_out = fs_pcd_start_endpoint_out, + .request_endpoints = fs_pcd_request_endpoints, + .set_endpoints = fs_pcd_set_endpoints, + .set_address = fs_pcd_set_address, + .setup_ep = fs_pcd_setup_ep, + .halt_endpoint = fs_halt_endpoint, + .endpoint_halted = fs_endpoint_halted, + + .cancel_in_irq = fs_pcd_cancel_in_irq, + .cancel_out_irq = fs_pcd_cancel_out_irq, +}; + +struct pcd_ops pcd_ops = { + .pcd_en_func = fs_pcd_en_func, + .pcd_init_func = pcd_init_func, + .remote_wakeup_func = fs_remote_wakeup, + .framenum = fs_pcd_framenum, +}; +#endif /* !defined(OTG_C99) */ + +/* @} */ + diff -uNr linux/drivers/no-otg/ocd/gen/gen.c linux/drivers/otg/ocd/gen/gen.c --- linux/drivers/no-otg/ocd/gen/gen.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/gen/gen.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,268 @@ +/* + * otg/ocd/gen/gen.c -- USB Device Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/gen/gen.c + * @brief Sample driver + * + * + * @ingroup USBP + */ + +#include +#include +#include "gen.h" + + +static unsigned char usb_address; + +/* ********************************************************************************************* */ +/* udc_write_buffer - write a buffer to the udc fifo + */ +static void udc_write_buffer (unsigned char ep, unsigned char *b, unsigned char size) +{ +} + +/* udc_read_buffer - fill a buffer from the udc fifo + */ +static void udc_read_buffer (unsigned char ep, unsigned char *b, unsigned char size) +{ +} + +/* udc_out - process an OUT interrupt + */ +static void udc_out (struct usbd_endpoint_instance *endpoint) +{ +} + +/* udc_in - process an IN interrupt + */ +static void udc_in (struct usbd_endpoint_instance *endpoint) +{ +} + +/* ********************************************************************************************* */ +/* udc_in_ep0 - start transmit of ep0 Control Read data + */ +static void udc_in_ep0 (struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb = pcd_tx_complete_irq (endpoint, 0); + if (!tx_urb || pcd_tx_sendzlp (endpoint)) { + udc_write_buffer (0, NULL, 0); + return; + } + TRACE_MSG16("EP0 IN: actual: %d sent: %d", endpoint->tx_urb->actual_length, endpoint->sent); + endpoint->last = MIN (tx_urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); + udc_write_buffer (0, tx_urb->buffer + endpoint->sent, endpoint->last); +} + +/* udc_out_ep0 - start receive of ep0 Control Write data + */ +static void udc_out_ep0 (struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *rcv_urb = pcd_rcv_next_irq (endpoint); + int size = 0; + RETURN_IF(!rcv_urb); + TRACE_MSG8("OUT EP0: actual: %d buffer_length: %d size: %d", rcv_urb->actual_length, rcv_urb->buffer_length, size, 0); + udc_read_buffer (0, rcv_urb->buffer + rcv_urb->actual_length, size); + if (!pcd_rcv_complete_irq (endpoint, size, 0)) + endpoint->state = WAIT_FOR_SETUP; +} + +/* udc_ep0_setup - process a Control Setup command + */ +static void udc_ep0_setup (struct usbd_endpoint_instance *endpoint) +{ + static struct usbd_device_request request; + u8 *cp = (u8 *) &request; + int i; + TRACE_MSG32("EP0 SETUP: tx_urb: %p", (int)endpoint->tx_urb); + pcd_tx_cancelled_irq (endpoint); + pcd_rcv_cancelled_irq (endpoint); + if (pcd_recv_setup_irq (&request)) { // process setup packet + TRACE_MSG("udc_ep0: setup failed"); + //udc_stall_ep (0); + endpoint->state = WAIT_FOR_SETUP; + return; + } + if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + TRACE_MSG32("udc_ep0: send zlp %d", le16_to_cpu (request.wLength)); // XXX + //ctrl_outb (EP0s_RDFN, USBTRG); + RETURN_IF(pcd_rcv_next_irq (endpoint)); + //and_b (~EP0o_TS, USBIER0); + return; + } +} +/* ********************************************************************************************* */ +/* udc_int_hndlr - interrupt handler + */ +static void udc_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + trace_ops.interrupts++; +#if 0 + if (trace_ops.interrupts > 1000) { udc_disable_interrupts (); udc_disable (); TRACE_MSG("UDC DISABLE"); return; } +#endif +} +/* ********************************************************************************************* */ +/* udc_start_endpoint_in - start transmit + */ +void udc_start_endpoint_in (struct usbd_endpoint_instance *endpoint) +{ +} + +/* udc_start_endpoint_out - start receive + */ +void udc_start_endpoint_out (struct usbd_endpoint_instance *endpoint) +{ +} +/* ********************************************************************************************* */ +/* udc_attached - is the USB cable attached + * Return non-zero if cable is attached. + */ +int udc_attached (void) +{ + return 0; +} + +static int udc_connect_status; + +/* udc_connected - is the USB pullup enabled + * Return non-zero if pullup is enabled. + */ +int udc_connected (void) +{ + return udc_connect_status; +} + +/* udc_connect - enable pullup resistor + */ +void udc_connect (void) +{ + udc_connect_status = 1; +} + +/* udc_disconnect - disable pullup resistor + */ +void udc_disconnect (void) +{ + udc_connect_status = 0; +} + +/* udc_start - start session + */ +void udc_start (void) +{ + // udc_enable_interrupts (); +} + +/* udc_stop - stop session + */ +void udc_stop (void) +{ + // udc_disable_interrupts (); +} + +/* udc_enable - enable the UDC + */ +void udc_enable (void) +{ +} + +/* udc_disable - disable the UDC + */ +void udc_disable (void) +{ +} + +/* udc_init - initialize + * Return non-zero if we cannot see device. + */ +int udc_init (void) +{ + return 0; +} + +/* udc_exit - disable the UDC + */ +void udc_exit (void) +{ +} + +/* ********************************************************************************************* */ +/* udc_assign_endpoint + */ +int udc_assign_endpoint (u8 physicalEndpoint, int used[6], struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, + u16 wMaxPacketSize, u16 transferSize) +{ + RETURN_EINVAL_IF(used[physicalEndpoint]); + endpoint_map->bEndpointAddress[0] = physicalEndpoint | (bmAttributes & USB_IN_DIR); + endpoint_map->physicalEndpoint[0] = physicalEndpoint; + endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; + endpoint_map->transferSize[0] = transferSize; + endpoint_map->bmAttributes[0] = bmAttributes; + used[physicalEndpoint]++; + return 0; +} + +/* udc_request_endpoints - process endpoint request list + */ +int udc_request_endpoints (struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, + struct usbd_endpoint_request *requestedEndpoints) +{ + struct usbd_device_description *device_description; + int i, j; + int used[UDC_MAX_ENDPOINTS]; + memset (used, 0, sizeof (used)); + for (j = 0, i = 0; i < endpointsRequested; i++) { + struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; + u8 bmAttributes = requestedEndpoints[i].bmAttributes; + u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; + switch (bmAttributes) { + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: + case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + case USB_DIR_IN | USB_ENDPOINT_BULK: + case USB_DIR_OUT | USB_ENDPOINT_BULK: + BREAK_IF(!udc_assign_endpoint (j++, used, endpoint_map, bmAttributes, 0x40, transferSize)); + return -EINVAL; + case USB_ENDPOINT_CONTROL: + continue; + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: + case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: + case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: + case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: + return -EINVAL; + } + return -EINVAL; + } + RETURN_EINVAL_UNLESS (j < UDC_MAX_ENDPOINTS); + } + return 0; +} +/* ********************************************************************************************* */ +struct udc_ops udc_ops = { + max_endpoints: UDC_MAX_ENDPOINTS, + ep0_packetsize: EP0_PACKETSIZE, + name: UDC_NAME, + init: udc_init, + exit: udc_exit, + enable: udc_enable, + disable: udc_disable, + start: udc_start, + stop: udc_stop, + start_endpoint_in: udc_start_endpoint_in, + start_endpoint_out: udc_start_endpoint_out, + request_endpoints: udc_request_endpoints, + attached: udc_attached, + connected: udc_connected, + connect: udc_connect, + disconnect: udc_disconnect, +}; + diff -uNr linux/drivers/no-otg/ocd/gen/gen.h linux/drivers/otg/ocd/gen/gen.h --- linux/drivers/no-otg/ocd/gen/gen.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/gen/gen.h 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,29 @@ +/* + * otg/ocd/gen/gen.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @file otg/ocd/gen/gen.h + * @brief Private structures and defines for Intel PXA-270 + * + * Notes + * + * 1. This is a minmal outline for a simple UDC driver. + * + * + * @ingroup BVD + */ + + +#define UDC_NAME "GEN" +#define UDC_MAX_ENDPOINTS 3 +#define EP0_PACKETSIZE 8 + diff -uNr linux/drivers/no-otg/ocd/isp1301/Config.in linux/drivers/otg/ocd/isp1301/Config.in --- linux/drivers/no-otg/ocd/isp1301/Config.in 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/Config.in 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,17 @@ + +# +# Copyright (c) 2004 Belcarra +# +# ISP 1301 TCD + +if [ "$CONFIG_OTG_ISP1301" = "y" ]; then + mainmenu_option next_comment + + comment 'ISP 1301 (i2c)' + + dep_bool 'Proc FS debug' CONFIG_OTG_ISP1301_PROCFS $CONFIG_OTG_ISP1301 + + endmenu +else + define_bool CONFIG_OTG_ISP1301_PROCFS n +fi diff -uNr linux/drivers/no-otg/ocd/isp1301/Kconfig linux/drivers/otg/ocd/isp1301/Kconfig --- linux/drivers/no-otg/ocd/isp1301/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/Kconfig 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,15 @@ + +# +# Copyright (c) 2004 Belcarra +# + +# OMAP 1610 H2 development board +# +config OTG_ISP1301_OMAP_H2 + tristate "OMAP 1610 H2 Development board - ISP 1301 (i2c)" + depends on I2C && OTG && ARCH_OMAP && OMAP_H2 + +config OTG_ISP1301_MX2ADS + tristate "MX2ADS Development board - ISP 1301 (i2c)" + depends on I2C && OTG && ARCH_MX2ADS + diff -uNr linux/drivers/no-otg/ocd/isp1301/Makefile linux/drivers/otg/ocd/isp1301/Makefile --- linux/drivers/no-otg/ocd/isp1301/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/Makefile 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,96 @@ +# +# Makefile for the kernel USBD (device not host) drivers. +# +# Copyright (c) 2004 Belcarra + +# Subdirs. +# This is a bit complex, because some subdirs are for +# proprietary code, and are simply not present in a +# general distribution. + +# The all-CAPS *_DIRS get nuked in the new versions +# of Rules.make, so use only the subdir-* methods. +subdir-y := +subdir-m := +subdir-n := +subdir- := + +# The target object and module list name. + +O_TARGET := isp1301_target.o +list-multi := isp1301_tcd.o + +# Objects that export symbols. + +# Multipart objects. + +omap_h2_tcd-objs := tcd-init-l24.o tcd-omap-h2.o isp1301.o i2c-l26.o tcd.o isp1301-procfs.o +mainstone_tcd-objs := tcd-init-l24.o tcd-mainstone.o tcd.o i2c-l26.o isp1301.o isp1301-procfs.o thread-l24.o + +# Optional parts of multipart objects. + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_OTG_ISP1301_OMAP_H2) += omap_h2_tcd.o +obj-$(CONFIG_OTG_ISP1301_MAINSTONE) += mainstone_tcd.o + +# Object files in subdirectories + + +# 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. + +DOT_DIR=$(TCD_DIR)/isp1301 + +include $(TOPDIR)/Rules.make +OTG=$(TOPDIR)/drivers/otg +OCD_DIR=$(OTG)/ocd +INCLUDE_DIRS = -I$(OTG) +EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} +EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} + +PCD=$(OTG)/ocd/otg-pcd +vpath %.c $(USBDCORE_DIR) $(OCD_DIR) $(PCD) + + +# Link rules for multi-part drivers. + +omap_h2_tcd.o: $(omap_h2_tcd-objs) + $(LD) -r -o $@ $(iomap_h2_tcd-objs) + +mainstone_tcd.o: $(mainstone_tcd-objs) + $(LD) -r -o $@ $(mainstone_tcd-objs) + +# dependencies: + +# local + + diff -uNr linux/drivers/no-otg/ocd/isp1301/Makefile-l26 linux/drivers/otg/ocd/isp1301/Makefile-l26 --- linux/drivers/no-otg/ocd/isp1301/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/Makefile-l26 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,26 @@ +# +# Makefile for the kernel USBD (device not host) drivers. +# +# Copyright (c) 2004 Belcarra Technologies Corp + +DOT_DIR=$(TCD_DIR)/isp1301 + +OTG=$(TOPDIR)/drivers/otg +HCD_DIR=$(OTG)/hcd +TCD_DIR=$(OTG)/tcd +USBDCORE_DIR=$(OTG)/usbdcore +OTGCORE_DIR=$(OTG)/otgcore +INCLUDE_DIRS = -I$(OTG) +EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} +EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} + +vpath %.c $(USBDCORE_DIR) $(OCD_DIR) + + +# Link rules for multi-part drivers. + +omap_h2_tcd-objs := ../tcd-init-l24.o tcd-omap-h2.o isp1301.o i2c-l26.o ../tcd.o +obj-$(CONFIG_OTG_ISP1301_OMAP_H2) += omap_h2_tcd.o + + + diff -uNr linux/drivers/no-otg/ocd/isp1301/isp1301-procfs.c linux/drivers/otg/ocd/isp1301/isp1301-procfs.c --- linux/drivers/no-otg/ocd/isp1301/isp1301-procfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/isp1301-procfs.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,385 @@ +/* + * otg/ocd/isp1301/isp1301-procfs.c - USB Device Core Layer + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * + */ +/*! + * @file otg/ocd/isp1301/isp1301-procfs.c + * @brief Implement /proc/isp1301 to dump ISP1301 registers. + * + * + * @ingroup ISP1301TCD + */ + + +#include +#include "otghw/isp1301-hardware.h" +#include + +#ifdef CONFIG_ARCH_MX2ADS +#include +#define MX2_OTG_XCVR_DEVAD 0x18 +#define MX2_SEQ_OP_REG 0x19 +#define MX2_SEQ_RD_STARTAD 0x1a +#define MX2_I2C_OP_CTRL_REG 0x1b +#define MX2_SCLK_TO_SCL_HPER 0x1e +#define MX2_I2C_INTERRUPT_AND_CTRL 0x1f + +#define OTG_BASE_ADDR 0x10024000 +//#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) + +#endif /* CONFIG_ARCH_MX2ADS */ + +#ifdef CONFIG_OTG_ISP1301_PROCFS +/* Proc Filesystem *************************************************************************** */ + +extern struct isp1301_private isp1301_private; + +#define MAX_HISTORY 6 +struct reg_list { + u8 reg; + u8 size; + char *name; + u32 values[MAX_HISTORY]; +}; + +#define REG(r, s) {r, s, #r, } + +struct reg_list isp1301_prod_list[] = { + REG(ISP1301_VENDOR_ID, 2), + REG(ISP1301_PRODUCT_ID, 2), + REG(ISP1301_VERSION_ID, 2), + { 0, 1, NULL,}, +}; +struct reg_list isp1301_reg_list[] = { + REG(ISP1301_OTG_CONTROL_SET, 1), + REG(ISP1301_INTERRUPT_SOURCE, 1), + REG(ISP1301_INTERRUPT_LATCH_SET, 1), + REG(ISP1301_INTERRUPT_ENABLE_LOW_SET, 1), + REG(ISP1301_INTERRUPT_ENABLE_HIGH_SET, 1), + REG(ISP1301_MODE_CONTROL_1_SET, 1), + { 0, 1, NULL,}, +}; +struct reg_list isp1301_spec_list[] = { + REG(ISP1301_MODE_CONTROL_2_SET, 1), + REG(ISP1301_OTG_STATUS, 1), + { 0, 1, NULL,}, +}; +struct reg_list max3301e_spec_list[] = { + REG(MAX3301E_SPECIAL_FUNCTION_1_SET, 1), + REG(MAX3301E_SPECIAL_FUNCTION_2_SET, 1), + { 0, 1, NULL,}, +}; +#ifdef CONFIG_ARCH_MX2ADS +struct reg_list mx21_spec_list[] = { + REG(MX2_OTG_XCVR_DEVAD, 1), + REG(MX2_SEQ_OP_REG, 1), + REG(MX2_SEQ_RD_STARTAD, 1), + REG(MX2_I2C_OP_CTRL_REG, 1), + REG(MX2_SCLK_TO_SCL_HPER, 1), + REG(MX2_I2C_INTERRUPT_AND_CTRL, 1), + { 0, 1, NULL,}, +}; +#endif /* CONFIG_ARCH_MX2ADS */ + +void isp1301_update(struct reg_list *list) +{ + for (; list && list->name; list++) { + TRACE_MSG1(TCD, "list: %s", list->name); + memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); + switch(list->size) { + case 1: + list->values[0] = i2c_readb(list->reg); + break; + case 2: + list->values[0] = i2c_readw(list->reg); + break; + case 4: + list->values[0] = i2c_readl(list->reg); + break; + } + } +} + +#ifdef CONFIG_ARCH_MX2ADS +static u8 __inline__ mx2_rb(u32 port) +{ + return *(volatile u8 *) (MX2_IO_ADDRESS(port + OTG_I2C_BASE)); +} + + +void mx21_update(struct reg_list *list) +{ + for (; list && list->name; list++) { + memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); + list->values[0] = mx2_rb(list->reg); + } +} + +#endif /* CONFIG_ARCH_MX2ADS */ + +void isp1301_update_all(void) +{ + isp1301_update(isp1301_reg_list); + switch (isp1301_private.transceiver_map->transceiver_type) { + case isp1301: + isp1301_update(isp1301_spec_list); + break; + case max3301e: + isp1301_update(max3301e_spec_list); + break; + default: + break; + } +#ifdef CONFIG_ARCH_MX2ADS + mx21_update(mx21_spec_list); +#endif /* CONFIG_ARCH_MX2ADS */ + +} + + + +/* + * dohex + * + */ +static void dohexdigit (char *cp, unsigned char val) +{ + if (val < 0xa) + *cp = val + '0'; + else if ((val >= 0x0a) && (val <= 0x0f)) + *cp = val - 0x0a + 'a'; +} + +/* + * dohex + * + */ +static void dohexval (char *cp, unsigned char val) +{ + dohexdigit (cp++, val >> 4); + dohexdigit (cp++, val & 0xf); +} + +int isp1301_dump(char *buf, char *name, char *fmt, struct reg_list *reg) +{ + int len = 0, i; + len += sprintf (buf + len, "%-20s %-34s [%03x]: ", name, reg->name, reg->reg); + len += sprintf (buf + len, fmt, reg->values[0]); + for (i = 1; i < MAX_HISTORY; i++) + if (reg->values[i - 1] == reg->values[i]) + len += sprintf (buf + len, " "); + else + len += sprintf (buf + len, fmt, reg->values[i]); + len += sprintf (buf + len, "\n"); + return len; +} + +int isp1301_dump_list(char * buf, char *name, struct reg_list *list) +{ + int len = 0; + for (; list && list->name; list++) + switch(list->size) { + case 1: len += isp1301_dump(buf + len, name, " %02x", list); break; + case 2: len += isp1301_dump(buf + len, name, " %04x", list); break; + case 4: len += isp1301_dump(buf + len, name, " %08x", list); break; + } + return len; +} + +char *isp1301_otg_control[8] = { + "DP_PULLUP", + "DM_PULLUP", + "DP_PULLDOWN", + "DM_PULLDOWN", + "ID_PULLDOWN", + "VBUS_DRV", + "VBUS_DISCHRG", + "VBUS_CHRG", +}; +char *isp1301_interrupt_source[8] = { + "VBUS_VLD", + "SESS_VLD", + "DP_HI", + "ID_GND", + "DM_HI", + "ID_FLOAT", + "BDIS_ACON", + "CR_INT", +}; + +char *isp1301_mode_control_1[8] = { + "SPEED_REG", + "SUSPEND_REG", + "DAT_SE0", + "TRANSP_EN", + "BDIS_ACON_EN", + "OE_INT_EN", + "UART_EN", + NULL, +}; + +char *isp1301_otg_status[8] = { + NULL, NULL, NULL, NULL, NULL, NULL, + "B_SESS_END", + "B_SESS_VLD", +}; + +int isp1301_detailed(char *buf, char *name, u8 reg, char **detail) +{ + u8 val; + int i; + int len = 0; + + val = i2c_readb(reg); + for (i = 7; i >= 0; i--) { + + if (3 == (i % 4)) + len += sprintf (buf + len, "\n%-20s [%02d] ", name, reg); + + if (detail[i]) + len += sprintf (buf + len, "%14s%s ", detail[i], (val & (1 << i)) ? " " : "/"); + else + len += sprintf (buf + len, "%14s%s ", "", " "); + } + len += sprintf (buf + len, "\n", name); + + return len; +} + +int isp1301_dump_all(char *buf) +{ + int len = 0; + len += isp1301_dump_list(buf + len, "ISP1301 Standard", isp1301_reg_list); + switch (isp1301_private.transceiver_map->transceiver_type) { + case isp1301: + len += isp1301_dump_list(buf + len, "ISP1301 Extra", isp1301_spec_list); + break; + case max3301e: + len += isp1301_dump_list(buf + len, "MAX3301E Extra", max3301e_spec_list); + break; + default: + break; + } +#ifdef CONFIG_ARCH_MX2ADS + len += isp1301_dump_list(buf + len, "MX21 ADS Extra", mx21_spec_list); +#endif /* CONFIG_ARCH_MX2ADS */ + return len; +} + +int isp1301_dump_detail(char *buf) +{ + int len = 0; + + len += isp1301_detailed(buf + len, "MODE CONTROL 1", ISP1301_MODE_CONTROL_1, isp1301_mode_control_1); + len += isp1301_detailed(buf + len, "INTERRUPT ENABLE", ISP1301_INTERRUPT_ENABLE_HIGH, isp1301_interrupt_source); + + len += isp1301_detailed(buf + len, "OTG CONTROL", ISP1301_OTG_CONTROL_SET, isp1301_otg_control); + len += isp1301_detailed(buf + len, "INTERRUPT SOURCE", ISP1301_INTERRUPT_SOURCE, isp1301_interrupt_source); + len += isp1301_detailed(buf + len, "OTG STATUS", ISP1301_OTG_STATUS, isp1301_otg_status); + return len; +} + + +/*! + * isp1301_device_proc_read - implement proc file system read. + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return + * statistics for. + */ +static ssize_t isp1301_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + int i; + u32 r; + + int config_size; + + // get a page, max 4095 bytes of data... + RETURN_EINVAL_UNLESS ((page = get_free_page (GFP_KERNEL))); + + len = 0; + index = (*pos)++; + + //printk(KERN_INFO"%s: index: %d\n", __FUNCTION__, index); + switch(index) { + case 0: + len += sprintf ((char *) page + len, "ISP1301 Transceiver Registers\n"); + + TRACE_MSG0(TCD, "UPDATING"); + isp1301_update_all(); + TRACE_MSG0(TCD, "UPDATE FINISHED"); + len += sprintf ((char *) page + len , "Vendor: %04x Product: %04x Revision: %04x %s\n", + isp1301_private.vendor, isp1301_private.product, + isp1301_private.revision, isp1301_private.transceiver_map->name + ); + + len += isp1301_dump_all((char *) page + len); + len += sprintf ((char *) page + len, "\n"); + + break; + + case 1: + len += sprintf ((char *) page + len, "\n--\n"); + len += isp1301_dump_detail((char *) page + len); + len += sprintf ((char *) page + len, "\n"); + break; + + default: + break; + + } + + //printk(KERN_INFO"%s: len: %d\n", __FUNCTION__, len); + + if (len > count) + len = -EINVAL; + + else if ((len > 0) && copy_to_user (buf, (char *) page, len)) + len = -EFAULT; + + //printk(KERN_INFO"%s: len: %d\n", __FUNCTION__, len); + free_page (page); + return len; +} + +static struct file_operations isp1301_device_proc_operations_functions = { + read:isp1301_device_proc_read_functions, +}; + +/* Module init ******************************************************************************* */ + +static int isp1301_procfs_init (void) +{ + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("isp1301", 0, 0)) == NULL) + return -ENOMEM; + + p->proc_fops = &isp1301_device_proc_operations_functions; + + isp1301_update_all(); + + return 0; +} +static void isp1301_procfs_exit (void) +{ + remove_proc_entry ("isp1301", NULL); +} +#else +static int isp1301_procfs_init (void) { return 0; +} +static void isp1301_procfs_exit (void) { } +#endif diff -uNr linux/drivers/no-otg/ocd/isp1301/isp1301.c linux/drivers/otg/ocd/isp1301/isp1301.c --- linux/drivers/no-otg/ocd/isp1301/isp1301.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/isp1301.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,923 @@ +/* + * otg/ocd/isp1301/isp1301.c -- USB Transceiver Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/isp1301/isp1301.c + * @brief ISP1301 OTG Transceiver Driver. + * + * This is the generic ISP1301 TCD core support. + * + * Notes: + * + * 1. The ISP1301 can control the speed and suspend directly, would it be + * appropriate to allow state machine to control this. + * + * 2. The ISP1301 has auto connect feature, can this be used without change + * to state machine. + * + * 3. The ISP1301 can control the ADR/PSW pin to enable / disable external + * charge pump. + * + * @ingroup ISP1301TCD + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_OMAP_H2 +#include +#include +#endif /* CONFIG_OMAP_H2 */ + +struct otg_transceiver_map isp1301_transceiver_map[] = { + { isp1301, 0x4cc, 0x1301, 0x00, "Phillips ISP1301", }, + { max3301e, 0x6a0b, 0x0133, 0x00, "Maxim MAX3301E", }, + { 0, 0, 0, 0, "Unknown Transceiver", }, +}; + +struct isp1301_private isp1301_private; + + + +/* ********************************************************************************************* */ +#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) +#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) +#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) +#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) + + + +/*! isp1301_int_src - update interrupt source test mask + * + * This sets the current mask and updates the interrupt registers to match. + */ +void isp1301_int_src(u8 int_src) +{ + TRACE_MSG1(TCD, "setting int_src %02x", int_src); + isp1301_private.int_src = int_src; + i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, ~int_src); + i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, ~int_src); + i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_SET, int_src); + i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_SET, int_src); + i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_src); +} + +/*! isp1301_int_src_set - add to the interrupt source mask + * + */ +void isp1301_int_src_set(u8 int_src) +{ + isp1301_int_src(int_src |= isp1301_private.int_src); +} + +/*! isp1301_int_src_clr - remove from the interrup source mask + */ +void isp1301_int_src_clr(u8 int_src) +{ + isp1301_int_src(int_src = isp1301_private.int_src & ~int_src); +} + +/* ********************************************************************************************* */ + +typedef struct isp1301_test { + u32 input; + char *name; + u8 mask; +} isp1301_test_t; + +#define OV(i, m) {i, #i, m} + +struct isp1301_test isp1301_int_src_tests[9] = { + + OV(VBUS_VLD, ISP1301_VBUS_VLD), + OV(A_SESS_VLD, ISP1301_SESS_VLD), + OV(DP_HIGH, ISP1301_DP_HI), + OV(ID_GND, ISP1301_ID_GND), + OV(DM_HIGH, ISP1301_DM_HI), + OV(ID_FLOAT, ISP1301_ID_FLOAT), + OV(BDIS_ACON, ISP1301_BDIS_ACON), + OV(CR_INT_DET, ISP1301_CR_INT), + {0, NULL, 0}, +}; + +struct isp1301_test isp1301_otg_status_tests[3] = { + + OV(B_SESS_END, ISP1301_B_SESS_END), + OV(B_SESS_VLD, ISP1301_B_SESS_VLD), + {0, NULL, 0}, +}; + +/*! isp1301_event() - set input bit based on current settings and interrupt mask + */ +u64 isp1301_event(struct otg_instance *otg, struct isp1301_test *tests, int val, int testmask, char *msg) +{ + int i; + u64 inputs = 0; + TRACE_MSG3(TCD, "val: %02x mask: %02x %s", val, testmask, msg); + for (i = 0; tests[i].mask; i++) { + u64 input = tests[i].input; + u8 mask = tests[i].mask; + CONTINUE_UNLESS(mask & testmask); + inputs |= (val & mask) ? input : _NOT(input); + TRACE_MSG6(TCD, "%s%s %02x %08x inputs: %08x %08x", + tests[i].name, (val & mask) ? "" : "/", tests[i].mask, tests[i].input, + (u32) (inputs >> 32), (u32)(inputs & 0xffffffff) + ); + } + return inputs; +} + + + +int isp1301_bh_first; +int isp1301_debounce; +u64 isp1301_ticks; + +void isp1301_info(char *msg, u8 value) +{ + u64 new_ticks = otg_tmr_ticks(); + u64 ticks = otg_tmr_elapsed(&new_ticks, &isp1301_ticks); + + if (ticks < 10000) + TRACE_MSG3(PCD, "ISP1301 %d uS %s: %02x", (u32)(ticks & 0xffffffff), msg, value); + else if (ticks < 10000000) + TRACE_MSG3(PCD, "ISP1301 %d mS %s: %02x", (u32)((ticks >> 10) & 0xffffffff), msg, value); + else + TRACE_MSG3(PCD, "ISP1301 %d S %s: %02x", (u32)((ticks >> 20) & 0xffffffff), msg, value); + + isp1301_ticks = new_ticks; +} + + +void isp1301_trace(u8 int_src, u8 int_changed, char *msg) +{ + + u64 new_ticks = otg_tmr_ticks(); + u64 ticks = otg_tmr_elapsed(&new_ticks, &isp1301_ticks); + + if (ticks < 10000) + TRACE_MSG4(PCD, "ISP1301 %d uS %s: src: %02x chng: %02x", + (u32)(ticks & 0xffffffff), msg, int_src, int_changed); + else if (ticks < 10000000) + TRACE_MSG4(PCD, "ISP1301 %d mS %s src: %02x chng: %02x", + (u32)((ticks >> 10) & 0xffffffff), msg, int_src, int_changed); + else + TRACE_MSG4(PCD, "ISP1301 %d S %s src: %02x chng: %02x ", + (u32)((ticks >> 20) & 0xffffffff), msg, int_src, int_changed); + + isp1301_ticks = new_ticks; +} + +/*! isp1301_bh + */ +void isp1301_bh(void *arg) +{ + u64 inputs = 0; + u8 int_lat, int_src1 = 0, int_src2 = 0, otg_status = 0, special_function_22 = 0; + static u8 int_src_saved, otg_status_saved, special_function_22_saved; + + //TRACE_MSG0(TCD, "--"); + /* read the interrupt latch and and clear latch and update otg + */ + if ((int_lat = i2c_readb(ISP1301_INTERRUPT_LATCH_SET))) { + //TRACE_MSG1(TCD, "CLEARING INT LAT: %02x", int_lat); + isp1301_info("INT LAT", int_lat); + i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_lat); + } + + isp1301_info("INT_LAT", int_lat); + + /* If int_lat shows a change or we are forcing an update, read interrupt source. + * Note that we mask DP_HI and DM_HI when LOC_CONN is set, we don't want them + * when USB Bus is in use. + * + * Mask with bits we are currently interested in. + * + * Ensure that we have two matching reads to ensure that value is stable. + * Also ensure that interrupt source is non-zero. + */ + + int_src1 = i2c_readb(ISP1301_INTERRUPT_SOURCE); + isp1301_trace(int_src1, int_src_saved ^ int_src1, "INT_SRC_1"); + + do { + int_src2 = i2c_readb(ISP1301_INTERRUPT_SOURCE); // re-read interrupt source + BREAK_IF(int_src1 && (int_src1 & (ISP1301_ID_GND | ISP1301_ID_FLOAT)) && + (int_src1 == int_src2)); // finished if non-zero and same + isp1301_trace(int_src2, int_src_saved ^ int_src2, "INT_SRC_2"); + int_src1 = int_src2; // save most recent read value + } while (1); + + + #if 0 + while ( (int_src1 != (int_src2 = i2c_readb(ISP1301_INTERRUPT_SOURCE) & isp1301_private.int_src))) { + isp1301_trace(int_src2, int_src_saved ^ int_src2, "INT_SRC_2"); + int_src1 = int_src2; + } + #endif + + if (isp1301_bh_first || int_src_saved ^ (int_src1 & isp1301_private.int_src)) { + int_src_saved = int_src1; + inputs |= isp1301_event(tcd_instance->otg, isp1301_int_src_tests, int_src1, + isp1301_private.int_src, "ISP1301 INT SRC"); + if (int_src1 & ISP1301_VBUS_VLD) { + TRACE_MSG0(TCD, "VBUS_VLD, setting B_SESS_VLD"); + inputs |= B_SESS_VLD; + } + else if (!(int_src1 & ISP1301_SESS_VLD)) { + TRACE_MSG0(TCD, "A_SESS_VLD/, setting B_SESS_VLD/"); + inputs |= B_SESS_VLD_; + } + } + + /* further work if B-Device + */ + if ( !(int_src1 & ISP1301_ID_GND) || !(int_src_saved & ISP1301_ID_GND) ) { + /* read the otg_status register and update otg state machine + * + * XXX this needs to be conditional on Transceiver type. The + * MAX3301E for example supports sess_end in a separate register. + */ + switch (isp1301_private.transceiver_map->transceiver_type) { + case isp1301: + otg_status = i2c_readb(ISP1301_OTG_STATUS); + isp1301_trace(otg_status, int_src_saved ^ int_src1, "OTG_STATUS"); + TRACE_MSG3(TCD, "otg_status_saved: %02x otg_status: %02x chng: %02x", + otg_status_saved, otg_status, otg_status_saved ^ otg_status); + + if (isp1301_bh_first || (otg_status_saved ^ otg_status)) { + inputs |= isp1301_event(tcd_instance->otg, isp1301_otg_status_tests, otg_status, 0xff, + "ISP1301 OTG STATUS"); + otg_status_saved = otg_status; + } + + #if 0 + isp1301_info("OTG STATUS", otg_status); + if (isp1301_bh_first) + otg_status_saved = ~otg_status; + + otg_status_saved = isp1301_update_otg_status( tcd_instance->otg, otg_status, + otg_status_saved ^ otg_status); + #endif + break; + + // XXX + case max3301e: + special_function_22 = i2c_readb(MAX3301E_SPECIAL_FUNCTION_2_SET); + break; + case unknown_transceiver: + break; + } + } + if (inputs) otg_event(tcd_instance->otg, inputs, TCD, "ISP1301"); + isp1301_bh_first = 0; +} + + +/*! isp1301_bh_wakeup - wakeup the isp1301 bottom half + */ +void isp1301_bh_wakeup(int first) +{ + //TRACE_MSG0(TCD, "--"); + isp1301_bh_first |= first; + SCHEDULE_WORK(isp1301_private.bh); +} + + +/* ********************************************************************************************** */ +/*! isp1301_vbus - Do we have Vbus (cable attached?) + * Return non-zero if Vbus is detected. + * + */ +int isp1301_vbus (struct otg_instance *otg) +{ + return 0; +} + +/*! isp1301_id - Do we have Vbus (cable attached?) + * Return non-zero if Vbus is detected. + * + */ +int isp1301_idvbus (struct otg_instance *otg) +{ + return 0; +} + + +/* ********************************************************************************************* */ +/*! isp1301_tcd_en() - used to enable + * + */ +void isp1301_tcd_en(struct otg_instance *otg, u8 flag) +{ + switch (flag) { + case SET: + TRACE_MSG0(TCD, "SET"); + break; + case PULSE: + TRACE_MSG0(TCD, "PULSE"); + break; + case RESET: + TRACE_MSG0(TCD, "RESET"); + break; + } + isp1301_bh_wakeup(TRUE); +} + + +/*! isp1301_chrg_vbus - used to enable or disable B-device Vbus charging + */ +void isp1301_chrg_vbus(struct otg_instance *otg, u8 flag) +{ + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "CHRG_VBUS_SET"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_CHRG); + break; + case RESET: + TRACE_MSG0(TCD, "CHRG_VBUS_RESET"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); + break; + case PULSE: + break; + } +} + +/*! isp1301_drv_vbus - used to enable or disable A-device driving Vbus + */ +void isp1301_drv_vbus(struct otg_instance *otg, u8 flag) +{ + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "DRV_VBUS_SET"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DRV); + break; + case RESET: + TRACE_MSG0(TCD, "DRV_VBUS_RESET"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); + break; + } +} + +/*! isp1301_dischrg_vbus - used to enable Vbus discharge + */ +void isp1301_dischrg_vbus(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_SET"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DISCHRG); + break; + case RESET: + TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_RESET"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); + break; + } +} + +/*! isp1301_mx21_vbus_drain - used to enable Vbus discharge + */ +void isp1301_mx21_vbus_drain_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_SET"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DISCHRG); + break; + case RESET: + TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_RESET"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); + break; + } +} + +/*! isp1301_dp_pullup_func - used to enable or disable peripheral connecting to bus + * + * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 + * + * host peripheral + * d+ pull-up clr set + * d+ pull-down set clr + * + * d- pull-up clr clr + * d- pull-down set set + * + */ +void isp1301_dp_pullup_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + isp1301_private.flags |= ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DP PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLUP); + break; + + case RESET: + isp1301_private.flags &= ~ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DP PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLUP); + break; + } +} + +/*! isp1301_dm_pullup_func - used to enable or disable peripheral connecting to bus + * + */ +void isp1301_dm_pullup_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + isp1301_private.flags |= ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DM PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLUP); + break; + + case RESET: + isp1301_private.flags &= ~ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DM PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DM_PULLUP); + break; + } +} + +/*! isp1301_dp_pulldown_func - used to enable or disable peripheral connecting to bus + * + * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 + * + * host peripheral + * d+ pull-up clr set + * d+ pull-down set clr + * + * d- pull-up clr clr + * d- pull-down set set + * + */ +void isp1301_dp_pulldown_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + isp1301_private.flags |= ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DP PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLDOWN); + break; + + case RESET: + isp1301_private.flags &= ~ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DP PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLDOWN); + break; + } +} + +/*! isp1301_dm_pulldown_func - used to enable or disable peripheral connecting to bus + * + */ +void isp1301_dm_pulldown_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + isp1301_private.flags |= ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DM PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN); + break; + + case RESET: + isp1301_private.flags &= ~ISP1301_LOC_CONN; + TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DM PULLUP"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DM_PULLDOWN); + break; + } +} + +/*! isp1301_peripheral_host_func - used to enable or disable peripheral connecting to bus + * + * A-Device D+ pulldown D- pulldown + * idle set set + * host set set + * peripheral reset set + * + * B-Device + * idle set set + * host set set + * peripheral reset set + */ +void isp1301_peripheral_host_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: // peripheral + TRACE_MSG0(TCD, "SET - CLR DP PULLDOWN"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLDOWN); + break; + + case RESET: // host + TRACE_MSG0(TCD, "RESET - SET DM PULLDOWN"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLDOWN); + break; + } +} + +/*! isp1301_dm_det_func - used to enable or disable D- detect + * + */ +void isp1301_dm_det_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting DM_HI detect"); + isp1301_int_src_set(ISP1301_DM_HI); + isp1301_bh_wakeup(TRUE); + isp1301_debounce = 0; + break; + + case RESET: + TRACE_MSG0(TCD, "reseting DM_HI detect"); + isp1301_int_src_clr(ISP1301_DM_HI); + isp1301_bh_wakeup(TRUE); + isp1301_debounce = 1; + break; + } +} + +/*! isp1301_dp_det_func - used to enable or disable D+ detect + * + */ +void isp1301_dp_det_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting DP_HI detect"); + isp1301_int_src_set(ISP1301_DP_HI); + isp1301_bh_wakeup(TRUE); + break; + + case RESET: + TRACE_MSG0(TCD, "reseting DP_HI detect"); + isp1301_int_src_clr(ISP1301_DP_HI); + isp1301_bh_wakeup(TRUE); + break; + } +} + +/*! isp1301_cr_det_func - used to enable or disable D+ detect + * + */ +void isp1301_cr_det_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting CR_INT detect"); + isp1301_int_src_set(ISP1301_CR_INT); + isp1301_bh_wakeup(TRUE); + break; + + case RESET: + TRACE_MSG0(TCD, "reseting CR_INT detect"); + isp1301_int_src_clr(ISP1301_CR_INT); + isp1301_bh_wakeup(TRUE); + break; + } +} + +/*! isp1301_bdis_acon_func - used to enable or disable auto a-connect + * + */ +void isp1301_bdis_acon_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting BDIS ACON"); + isp1301_int_src_set(ISP1301_BDIS_ACON); + i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_BDIS_ACON_EN); + break; + + case RESET: + TRACE_MSG0(TCD, "reseting BDIS ACON"); + i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ISP1301_BDIS_ACON_EN); + isp1301_int_src_clr(ISP1301_BDIS_ACON); + break; + } +} + +/*! isp1301_id_pulldown_func - used to enable or disable ID pulldown + * + */ +void isp1301_id_pulldown_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting ID PULLDOWN"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_ID_PULLDOWN); + break; + + case RESET: + TRACE_MSG0(TCD, "reseting ID PULLDOWN"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_ID_PULLDOWN); + break; + } +} + +/*! isp1301_audio_func - used to enable or disable Carkit Interrupt + * + */ +void isp1301_audio_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "SET AUDIO_EN"); + //isp1301_int_src_set(ISP1301_CR_INT); + i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_AUDIO_EN); + break; + + case RESET: + TRACE_MSG0(TCD, "RESET AUDIO_EN"); + i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_AUDIO_EN); + //isp1301_int_src_clr(ISP1301_CR_INT); + break; + } +} + +/*! isp1301_uart_func - used to enable or disable transparent uart mode + * + */ +void isp1301_uart_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting UART_EN"); + i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_UART_EN); + /* XXX enable uart */ + break; + + case RESET: + TRACE_MSG0(TCD, "reseting UART_EN"); + i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ISP1301_UART_EN); + /* XXX disable uart */ + break; + } +} + +/*! isp1301_mono_func - used to enable or disable mono audio connection + * + */ +void isp1301_mono_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + //TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting MONO"); + /* XXX enable mono output */ + break; + + case RESET: + TRACE_MSG0(TCD, "reseting MONO"); + /* XXX disable mono output */ + break; + } +} + + +/* ********************************************************************************************* */ +extern int isp1301_procfs_init (void); +extern void isp1301_procfs_exit (void); + +extern void mx2_isp1301_bh(void *arg); + +/*! isp1301_mod_init + */ +int isp1301_mod_init(WORK_PROC bh_proc) +//int isp1301_mod_init(void) +{ + /* for interrupt bottom half handler + */ + //PREPARE_WORK_ITEM(isp1301_private.bh, &isp1301_bh, NULL); + + UNLESS(bh_proc) + bh_proc = &isp1301_bh; + + PREPARE_WORK_ITEM(isp1301_private.bh, bh_proc, NULL); + + //else + // PREPARE_WORK_ITEM(isp1301_private.bh, &isp1301_bh, NULL); + + /* setup isp1301, enable interrupts, clear latch + * XXX when and how do we do DM_HI and DP_HI, when ID is present? + */ + + isp1301_int_src(ISP1301_ID_FLOAT | ISP1301_DM_HI | ISP1301_ID_GND | ISP1301_DP_HI | ISP1301_SESS_VLD | ISP1301_VBUS_VLD); + + //isp1301_int_src(ISP1301_INT_SRC); + //isp1301_int_src(ISP1301_NO_SE1_INT); + //isp1301_int_src_clr(ISP1301_CR_INT); + + //i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN | ISP1301_DP_PULLDOWN); + +#ifdef CONFIG_OTG_ISP1301_PROCFS + isp1301_procfs_init (); +#endif /* CONFIG_OTG_ISP1301_PROCFS */ + + return 0; +} + +/*! isp1301_mod_exit + */ +void isp1301_mod_exit(void) +{ + i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, 0xff); + i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, 0xff); + //i2c_close(); +#ifdef CONFIG_OTG_ISP1301_PROCFS + isp1301_procfs_exit (); +#endif /* CONFIG_OTG_ISP1301_PROCFS */ + + while (PENDING_WORK_ITEM(isp1301_private.bh)) { + printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } + +} + +/* ********************************************************************************************* */ +/*! isp1301_configure - configure the ISP1301 for this host + * @param tx_mode - the type of connection between the host USB and the ISP1301 + * @param spd_ctrl - suspend control method + */ +void isp1301_configure(tx_mode_t tx_mode, spd_ctrl_t spd_ctrl) +{ + u16 vendor = 0, product = 0, revision = 0; + u8 mode1, mode2, control; + + struct otg_transceiver_map *map = isp1301_transceiver_map; + + +#if defined(CONFIG_OTG_ISP1301_MX2ADS) || defined(CONFIG_OTG_ISP1301_MX2ADS_MODULE) + u32 vp; + TRACE_MSG0(TCD, "1. Read Transceiver ID's with long read"); + vp = i2c_readl(ISP1301_VENDOR_ID); + isp1301_private.vendor = vp & 0xffff; + isp1301_private.product = vp >> 16; + isp1301_private.revision = i2c_readw(ISP1301_VERSION_ID); + mode1 = i2c_readb(ISP1301_MODE_CONTROL_1_SET); + mode2 = i2c_readb(ISP1301_MODE_CONTROL_2_SET); + + TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", + vendor, product, revision, mode1, mode2); + +#else /* defined(CONFIG_OTG_ISP1301_MX2ADS) */ + + isp1301_private.vendor = i2c_readw(ISP1301_VENDOR_ID); + isp1301_private.product = i2c_readw(ISP1301_PRODUCT_ID); + isp1301_private.revision = i2c_readw(ISP1301_VERSION_ID); + mode1 = i2c_readb(ISP1301_MODE_CONTROL_1_SET); + mode2 = i2c_readb(ISP1301_MODE_CONTROL_2_SET); + + TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", + isp1301_private.vendor, isp1301_private.product, isp1301_private.revision, mode1, mode2); + +#endif /* defined(CONFIG_OTG_ISP1301_MX2ADS) */ + + for ( ; map && map->transceiver_type != unknown_transceiver; map++) { + CONTINUE_UNLESS ((isp1301_private.vendor == map->vendor) && (isp1301_private.product == map->product)); + TRACE_MSG1(TCD, "Found Transceiver: %s", map->name); + isp1301_private.transceiver_map = map; + break; + } + UNLESS (isp1301_private.transceiver_map) + isp1301_private.transceiver_map = isp1301_transceiver_map; + + TRACE_MSG0(TCD, "3. Disable All Transceiver Control Register 1 "); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, 0xff); // clear + + TRACE_MSG0(TCD, "4. Enable D-/D+ Pulldowns in Transceiver Control Register 1 "); + + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN | ISP1301_DP_PULLDOWN); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, (u8) ISP1301_DM_PULLUP | ISP1301_DP_PULLUP); + + + TRACE_MSG0(TCD, "5. Clear latch and enable interrupts"); + i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, 0xff); + //i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, 0xeb); + //i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, 0xeb); + isp1301_int_src_set(0xeb); + + /* The PSW_OE enables the ADR/PSW pin for output driving either high + * or low depending on the address. + * + * ADR ADR_REG Address PSW_OE=0 PSW_OE=1 + * + * low 0 0x2c LOW HIGH + * + * high 1 0x2d HIGH LOW + * + * + * The i2c address by tying the ADR/PSW pin either high or low. + * Setting the PSW_OE drives the ADR/PSW into the opposite of the + * default wiring. + * + * On the MX21ADS the ADR/PSW pin is wired high which enables the + * charge pump. So enabling the PSW_OE is required to disable Vbus + * generation on the Charge Pump. + * + * The MAX3355E will only enable Vbus when this signal is high AND + * the ID signal is low. So it may be safe to leave enabled when + * operating as a B-device (ID floating.) + */ + + //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_PSW_OE); // PSW_OE - OFFVBUS low + + + TRACE_MSG1(TCD, "6. tx_mode: %02x", tx_mode); + switch (tx_mode) { + case vp_vm_bidirectional: + break; + case dat_se0_unidirectional: + i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_BI_DI); + case dat_se0_bidirectional: + i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_DAT_SE0); + break; + } + TRACE_MSG0(TCD, "6. tx_mode done"); + + TRACE_MSG1(TCD, "7. spd_ctrl: %02x", spd_ctrl); + switch (spd_ctrl) { + case spd_susp_pins: + i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_SPD_SUSP_CTRL); + break; + case spd_susp_reg: + i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_SPD_SUSP_CTRL); + break; + } + + //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_PSW_OE); // PSW_OE - OFFVBUS low + + TRACE_MSG0(TCD, "7. spd_ctrl done"); + + TRACE_MSG1(TCD, "8. transceiver_type: %02x", isp1301_private.transceiver_map); + + TRACE_MSG1(TCD, "8. transceiver_type: %02x", isp1301_private.transceiver_map->transceiver_type); + switch (isp1301_private.transceiver_map->transceiver_type) { + case isp1301: + break; + case max3301e: + i2c_writeb(MAX3301E_SPECIAL_FUNCTION_1_SET, MAX3301E_SESS_END); + break; + case unknown_transceiver: + break; + } + TRACE_MSG0(TCD, "8. transceiver_type done"); +} + diff -uNr linux/drivers/no-otg/ocd/isp1301/tcd-mainstone.c linux/drivers/otg/ocd/isp1301/tcd-mainstone.c --- linux/drivers/no-otg/ocd/isp1301/tcd-mainstone.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/tcd-mainstone.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,177 @@ +/* + * otg/ocd/mx2/tcd-mainstone.c -- Mainstone ISP1301 Transceiver Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/isp1301/tcd-mainstone.c + * @brief Mainstone ISP1301 driver. + * + * This implements the ISP1301 Transceiver Controller Driver for the Mainstone + * using the USBOTG I2C controller for access. + * + * Notes + * + * 1. The interrupt source is handled by the OCD driver so we just publish + * an interrupt handler for it to call. + * + * + * @ingroup ISP1301TCD + */ + +#include +#include +#include +#include +#include +#include "mx2.h" +#include "otghw/bvd-hardware.h" +#include + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ + + +/* ********************************************************************************************* */ + + +/*! isp1301_int_hndlr + */ +static irqreturn_t isp1301_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + TRACE_MSG0(TCD, "--"); + + SCHEDULE_WORK(isp1301_private.bh); + + return IRQ_HANDLED; +} + + +/* ********************************************************************************************* */ + +/*! mainstone_tcd_init - used to initialize/enable or disable the tcd driver + */ +void mainstone_tcd_init(struct otg_instance *otg, u8 flag) +{ + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "MAINSTONE_TCD_EN SET"); + otg_event(otg, TCD_OK, TCD, "TCD_OK"); + isp1301_thread_wakeup(1, 1); + break; + case RESET: + TRACE_MSG0(TCD, "MAINSTONE_TCD_EN RESET"); + isp1301_thread_wakeup(0, 0); + otg_event(otg, TCD_OK, TCD, "TCD_OK"); + break; + } +} + + +struct tcd_ops tcd_ops; +struct tcd_instance *mainstone_tcd_instance; +//wait_queue_head_t mx2_i2c_wait; // wait structure for i2c i/o interrupt + +#define ADAPTOR_NAME "PXA-I2C-Adapter" + +/*! mainstone_mod_init + */ +int mainstone_mod_init(void) +{ + struct otg_instance *otg; + int i2c = i2c_configure(ADAPTOR_NAME, ISP1301_I2C_ADDR_HIGH); + int irq = request_irq (GPIOX_TO_IRQ(35), isp1301_int_hndlr, SA_INTERRUPT, "ISP1301", NULL); + + TRACE_MSG0(TCD, "--"); + + THROW_IF(i2c, error); + + TRACE_MSG0(TCD, "1. Enable Mainstone external OTG Transceiver!"); + + TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); + MST_MSCWR2 |= MSCWR2_USB_OTG_SEL | MSCWR2_USB_OTG_RST; + TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); + + + TRACE_MSG0(TCD, "2. configure i2c driver!"); + isp1301_configure(dat_se0_bidirectional, spd_susp_reg); + + TRACE_MSG0(TCD, "3. configure isp1301!"); + isp1301_mod_init(); + + TRACE_MSG0(TCD, "4. set ops"); + THROW_UNLESS(ocd_instance && (otg = ocd_instance->otg), error); + THROW_UNLESS(mainstone_tcd_instance = otg_set_tcd_ops(&tcd_ops), error); + + /* Success! + */ + TRACE_MSG0(TCD, "5. Success!"); + + otg_init(otg); + //otg_start(otg, ocd_ops.capabilities & OCD_CAPABILITIES_AUTO); + + // XXX TESTING + //isp1301_dp_pullup_func(otg, 1); + + /* XXX start thread to monitor for changes - not needed once gpio interrupt implmented + */ + isp1301_thread_init(isp1301_bh); + + /* error handling + */ + CATCH(error) { + //if (irq) free_irq(GPIO_2_80_TO_IRQ(35), NULL); + if (irq) free_irq(GPIOX_TO_IRQ(35), NULL); + if (i2c) i2c_close(); + } + return 0; +} + +/*! mainstone_mod_exit + */ +void mainstone_mod_exit(void) +{ + //free_irq(GPIO_2_80_TO_IRQ(35), NULL); + free_irq(GPIOX_TO_IRQ(35), NULL); + + /* XXX stop thread - not needed once gpio interrupt implmented + */ + isp1301_thread_exit(NULL); + + isp1301_mod_exit(); + TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); + MST_MSCWR2 &= ~(MSCWR2_USB_OTG_SEL | MSCWR2_USB_OTG_RST); + TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); + //isp1301_thread_exit(); + i2c_close(); + +} + +/* ********************************************************************************************* */ + +struct tcd_ops tcd_ops = { + + vbus: isp1301_vbus, + + mod_init: mainstone_mod_init, + mod_exit: mainstone_mod_exit, + + tcd_init_func: mainstone_tcd_init, + + dischrg_vbus_func: isp1301_dischrg_vbus, + chrg_vbus_func: isp1301_chrg_vbus, + drv_vbus_func: isp1301_drv_vbus, + dp_pullup_func: isp1301_dp_pullup_func, + dm_pullup_func: isp1301_dm_pullup_func, + dm_det_func: isp1301_dm_det_func, + dp_det_func: isp1301_dp_det_func, + +}; + diff -uNr linux/drivers/no-otg/ocd/isp1301/tcd-omap-h2.c linux/drivers/otg/ocd/isp1301/tcd-omap-h2.c --- linux/drivers/no-otg/ocd/isp1301/tcd-omap-h2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/tcd-omap-h2.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,180 @@ +/* + * otg/ocd/isp1301/tcd-omap-h2.c -- USB Transceiver Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/isp1301/tcd-omap-h2.c + * @brief OMAP H2 ISP1301 driver. + * + * + * @ingroup ISP1301TCD + */ + +#include +#include +#include +#include +#include +#include "otghw/bvd-hardware.h" +#include + +#include +#include +#include +#include + +#include +#include +//struct isp1301_private isp1301_private; + +/* ********************************************************************************************* */ + + +static irqreturn_t isp1301_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + TRACE_MSG0(TCD, "--"); + + SCHEDULE_WORK(isp1301_private.bh); + + return IRQ_HANDLED; +} + + +/* ********************************************************************************************* */ + +struct tcd_ops tcd_ops; + +#define ADAPTOR_NAME "OMAP I2C" + +/*! omap_mod_init + */ +int omap_mod_init(void) +{ + int i2c = i2c_configure(ADAPTOR_NAME, ISP1301_I2C_ADDR_HIGH); + + int gpio = omap_request_gpio (ISP1301_GPIO); + int irq = 0; + + TRACE_MSG0(TCD, "--"); + + /* did we get gpio and i2c? + */ + THROW_IF(gpio || i2c, error); + + /* for interrupt bottom half handler + */ + // moved isp1301.c - PREPARE_WORK_ITEM(isp1301_private.bh, &isp1301_bh, NULL); + + /* + * See H2 C.f. Figure 17 USBOTG Interface Connection + * GPIO2 M14 + * setup gpio 2 and request interrupt for it + */ + omap_set_gpio_direction(ISP1301_GPIO, 1); + //omap_cfg_reg(M14_1510_GPIO2); + omap_set_gpio_edge_ctrl(ISP1301_GPIO, OMAP_GPIO_FALLING_EDGE); + irq = request_irq (OMAP_GPIO_IRQ(ISP1301_GPIO), isp1301_int_hndlr, SA_INTERRUPT, "ISP1301", NULL); + THROW_IF(irq, error); + + #ifdef CONFIG_OTG_OMAP_H2_3_WIRE + + /* OTG on Pin Group 1 Using 3-Wire OTG Transceiver C.f. Figure 15-59 + * See also H2 C.f. Figure 17 USBOTG Interface Connection + * DAT_SE0 mode + * Set DAT_SE0, SUSPEND_REG and BI_DI + */ + //i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_DAT_SE0 | ISP1301_SUSPEND_REG); + //i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ~(ISP1301_DAT_SE0 | ISP1301_SUSPEND_REG)); + //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_BI_DI); + //i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ~ISP1301_BI_DI); + + isp1301_configure(dat_se0_bidirectional, spd_susp_pins); + + #elif CONFIG_OTG_OMAP_H2_4_WIRE + + /* OTG on Pin Group 1 Using 4-Wire OTG Transceiver C.f. Figure 15-60 + * See also H2 C.f. Figure 17 USBOTG Interface Connection + * VP_VM mode + * Reset DAT_SE0 + * Set SUSPEND_REG and BI_DI + */ + //i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_SUSPEND_REG); + //i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ~ISP1301_SUSPEND_REG); + //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_BI_DI); + //i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ~ISP1301_BI_DI); + + isp1301_configure(vp_vm_bidirectional); + + #else + #error "ISP1301 Connection style not selected!" + #endif /* CONFIG_OTG_OMAP_H2_4_WIRE */ + + isp1301_mod_init(); + + i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN | ISP1301_DP_PULLDOWN); + + /* Success! + */ + + TRACE_MSG0(TCD, "5. Success!"); + + otg_init(otg); + + /* error handling + */ + CATCH(error) { + if (irq) free_irq(OMAP_GPIO_IRQ(ISP1301_GPIO), NULL); + if (gpio) omap_free_gpio(ISP1301_GPIO); + if (i2c) i2c_close(); + } + return 0; +} + +/*! omap_mod_exit + */ +void omap_mod_exit(void) +{ + #if 0 + // moved to isp1301.c + while (PENDING_WORK_ITEM(isp1301_private.bh)) { + printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } + #endif + + isp1301_mod_exit(); + + i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, 0xff); + i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, 0xff); + i2c_close(); + free_irq(OMAP_GPIO_IRQ(ISP1301_GPIO), NULL); + omap_free_gpio(ISP1301_GPIO); +} + +/* ********************************************************************************************* */ + +struct tcd_ops tcd_ops = { + + //initial_state: tr_init, + vbus: isp1301_vbus, + + mod_init: omap_mod_init, + mod_exit: omap_mod_exit, + + dischrg_vbus_func: isp1301_dischrg_vbus, + chrg_vbus_func: isp1301_chrg_vbus, + drv_vbus_func: isp1301_drv_vbus, + dp_pullup_func: isp1301_dp_pullup_func, + dm_pullup_func: isp1301_dm_pullup_func, + dm_det_func: isp1301_dm_det_func, + dp_det_func: isp1301_dp_det_func, + +}; + diff -uNr linux/drivers/no-otg/ocd/isp1301/thread-l24.c linux/drivers/otg/ocd/isp1301/thread-l24.c --- linux/drivers/no-otg/ocd/isp1301/thread-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/isp1301/thread-l24.c 2006-09-01 21:41:31.000000000 +0200 @@ -0,0 +1,144 @@ +/* + * otg/ocd/isp1301/thread-l24.c -- MX21ADS ISP1301 Transceiver Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/isp1301/thread-l24.c + * @brief Implement a kernel thread for checking ISP1301. + * + * + * @ingroup ISP1301TCD + */ + +#include +#include +#include +//#include +#include +//#include "mx2.h" +//#include "otghw/mx2-hardware.h" + + +/* isp1301_bh + */ +extern void isp1301_bh(void *arg); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ + +/* I2C Thread + * The mx21 USBOTG module does not seem to be able to keep uptodate with the OTG Transceiver + * registers. This kernel thread will iterated endlessly reading the appropriate registers. + * + * If this is successful we can assume that the USBOTG module will correctly monitor the appropriate + * registers and allow us to follow them via the normal USBOTG interrupts and registers. + * + * Otherwise we can monitor here and issue the otg events directly. + */ +//#define ISP1301_THREAD_TIMEOUT_EN (10) +#define ISP1301_THREAD_TIMEOUT_EN (1*HZ) +#define ISP1301_THREAD_TIMEOUT_DIS (1*HZ) + +int isp1301_terminate; +int mx2_pid; +wait_queue_head_t isp1301_wait; +static DECLARE_COMPLETION(mx2_exited); +int isp1301_en; + +void (*isp1301_bh_proc)(void *); + +/*! isp1301_thread - implement a kernel task to monitor isp1301 for changes + */ +int isp1301_thread(void *data) +{ + u8 int_src_saved = 0, otg_status_saved = 0, int_src, otg_status; + TRACE_MSG0(TCD, "--"); + daemonize(); + reparent_to_init(); + sprintf(current->comm, "isp1301_thread"); + + /* loop until told to terminate + */ + while (!isp1301_terminate) { + + #if 0 + // XXX TESTING + static int count = 0; + count++; + if (!(count % 5)) + isp1301_dp_pullup_func(tcd_instance->otg, !(count % 10)? 1 : 2); + #endif + + /* sleep for a while if not enabled + */ + if (!isp1301_en) { + TRACE_MSG1(TCD, "WAITING: %d", ISP1301_THREAD_TIMEOUT_DIS); + interruptible_sleep_on_timeout(&isp1301_wait, ISP1301_THREAD_TIMEOUT_DIS); + continue; + } + + /* update 1301 + */ + if (isp1301_bh_proc) + isp1301_bh_proc(NULL); + + //TRACE_MSG1(TCD, "SLEEPING: %d", ISP1301_THREAD_TIMEOUT_EN); + interruptible_sleep_on_timeout(&isp1301_wait, ISP1301_THREAD_TIMEOUT_EN); + //TRACE_MSG0(TCD, "RUNNING"); + } + TRACE_MSG0(TCD, "Exiting"); + complete_and_exit(&mx2_exited, 0); +} + +extern int isp1301_bh_first; +/*! isp1301_thread_wakeup - wakeup the isp1301 task + */ +void isp1301_thread_wakeup(int enabled, int first) +{ + TRACE_MSG0(TCD, "--"); + isp1301_bh_first |= first; + isp1301_en = enabled; + wake_up(&isp1301_wait); +} + +/* ********************************************************************************************* */ + +/*! isp1301_thread_init - initial tcd setup + * Allocate interrupts and setup hardware. + */ +int isp1301_thread_init (void (bh_proc)(void *)) +{ + isp1301_terminate = 0; + TRACE_MSG0(TCD, "--"); + isp1301_bh_proc = bh_proc; + init_waitqueue_head(&isp1301_wait); + THROW_IF((mx2_pid = kernel_thread(&isp1301_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) < 0, error); + isp1301_thread_wakeup(0, 0); + + CATCH(error) { + return -EINVAL; + } + return 0; +} + +/*! isp1301_thread_exit - de-initialize + */ +void isp1301_thread_exit (wait_queue_head_t *wait_queue) +{ + TRACE_MSG0(TCD, "MX2_MOD_TCD_EXIT - terminating"); + isp1301_bh_proc = NULL; + isp1301_terminate = 1; + if (wait_queue) + wake_up_interruptible(wait_queue); // wakeup waiting bottom half handler + isp1301_thread_wakeup(0, 0); + wait_for_completion(&mx2_exited); + TRACE_MSG0(TCD, "MX2_MOD_TCD_EXIT - terminated"); +} + diff -uNr linux/drivers/no-otg/ocd/max3353e/Config.in linux/drivers/otg/ocd/max3353e/Config.in --- linux/drivers/no-otg/ocd/max3353e/Config.in 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/Config.in 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,19 @@ + +# +# @(#) balden@belcarra.com|otg/ocd/max3353e/Config.in|20060831021117|26677 +# Copyright (c) 2004 Belcarra +# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp +# +# MAX 3353E TCD + +if [ "$CONFIG_OTG_MAX3353E" = "y" ]; then + mainmenu_option next_comment + + comment 'MAX 3353E (i2c)' + + dep_bool 'Proc FS debug' CONFIG_OTG_MAX3353E_PROCFS $CONFIG_OTG_MAX3353E + + endmenu +else + define_bool CONFIG_OTG_MAX3353E_PROCFS n +fi diff -uNr linux/drivers/no-otg/ocd/max3353e/Makefile linux/drivers/otg/ocd/max3353e/Makefile --- linux/drivers/no-otg/ocd/max3353e/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/Makefile 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,100 @@ +# +# Makefile for the kernel USBD (device not host) drivers. +# @(#) balden@belcarra.com|otg/ocd/max3353e/Makefile-l24|20060831021117|12633 +# +# Copyright (c) 2004 Belcarra +# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + +# Subdirs. +# This is a bit complex, because some subdirs are for +# proprietary code, and are simply not present in a +# general distribution. + +# The all-CAPS *_DIRS get nuked in the new versions +# of Rules.make, so use only the subdir-* methods. +subdir-y := +subdir-m := +subdir-n := +subdir- := + +# The target object and module list name. + +O_TARGET := max3353e_target.o +list-multi := max3353e_tcd.o + +# Objects that export symbols. + +export-objs := i2c-max3353e.o max3353e.o + +# Multipart objects. + +#db1550_tcd-objs := tcd-init-l24.o tcd-omap-h2.o max3353e.o i2c-l26.o tcd.o max3353e-procfs.o +#db1550_tcd-objs := tcd-init-l24.o tcd-db1550.o tcd-mx2ads.o max3353e.o i2c-l26.o tcd.o max3353e-procfs.o +db1550_tcd-objs := tcd-init-l24.o max3353e.o tcd-db1550.o i2c-max3353e.o +# Optional parts of multipart objects. + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_OTG_MAX3353E_DB1550) += db1550_tcd.o + + +# Object files in subdirectories + + +# 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. + +DOT_DIR=$(TCD_DIR)/max3353e + +include $(TOPDIR)/Rules.make +OTG=$(TOPDIR)/drivers/otg +OCD_DIR=$(OTG)ocd/ +OTGLIB=$(OTG)/ocd/otglib +INCLUDE_DIRS = -I$(OTG) +EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} +EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} + +I2C=$(OTG)/ocd/otg-i2c +PCD=$(OTG)/ocd/otg-pcd +TCD=$(OTG)/ocd/otg-tcd +vpath %.c $(USBDCORE_DIR) $(OCD_DIR) $(PCD) $(TCD) $(OTGLIB) + + +# Link rules for multi-part drivers. + +db1550_tcd.o: $(db1550_tcd-objs) + $(LD) -r -o $@ $(db1550_tcd-objs) + +# dependencies: + +# local + + diff -uNr linux/drivers/no-otg/ocd/max3353e/Makefile-l26 linux/drivers/otg/ocd/max3353e/Makefile-l26 --- linux/drivers/no-otg/ocd/max3353e/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/Makefile-l26 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,28 @@ +# +# Makefile for the kernel USBD (device not host) drivers. +# @(#) balden@belcarra.com|otg/ocd/max3353e/Makefile-l26|20060831021117|56045 +# +# Copyright (c) 2004 Belcarra Technologies Corp +# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + +DOT_DIR=$(TCD_DIR)/max3353e + +OTG=$(TOPDIR)/drivers/otg +HCD_DIR=$(OTG)/hcd +TCD_DIR=$(OTG)/tcd +USBDCORE_DIR=$(OTG)/usbdcore +OTGCORE_DIR=$(OTG)/otgcore +INCLUDE_DIRS = -I$(OTG) +EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} +EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} + +vpath %.c $(USBDCORE_DIR) $(OCD_DIR) + + +# Link rules for multi-part drivers. + +db1550_tcd-objs := ../tcd-init-l24.o max3353e.o i2c-l26.o ../tcd.o +obj-$(CONFIG_OTG_MAX3353E_DB1550) += db1550_tcd.o + + + diff -uNr linux/drivers/no-otg/ocd/max3353e/README linux/drivers/otg/ocd/max3353e/README --- linux/drivers/no-otg/ocd/max3353e/README 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/README 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,9 @@ +Some notes for using max3353 tranciever in DB1550 development board +1- Patch kernel with AMD DB1550 i2c driver. +2- Patch arch/mips/au1000/db1x00/irqmap.c +#if defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_MTX2) + { AU1000_GPIO_3, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 0 IRQ# + { AU1000_GPIO_5, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 1 IRQ# + { AU1500_GPIO_207, INTC_INT_FALL_EDGE, 0 }, // OTG tranciever +#else + diff -uNr linux/drivers/no-otg/ocd/max3353e/i2c-max3353e.c linux/drivers/otg/ocd/max3353e/i2c-max3353e.c --- linux/drivers/no-otg/ocd/max3353e/i2c-max3353e.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/i2c-max3353e.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,178 @@ +/* @(#) balden@belcarra.com|otg/ocd/max3353e/i2c-max3353e.c|20060831021117|42179 + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + */ +#include + +#include +#include +#include +#include + +#include +#include +#include +//#include "isp1301.h" + +/* + * N.B. i2c functions must not be called from interrupt handlers + */ + +static struct file *i2c_file; +static struct i2c_client *db1550_i2c_client; +static int initstate_i2c; +static int initstate_region; +#define MAX_I2C 16 + +#ifdef CONFIG_OMAP_H2 +#define ADAPTOR_NAME "OMAP I2C" +#endif /* CONFIG_OMAP_H2 */ +#ifdef CONFIG_ARCH_MAINSTONE +#define ADAPTOR_NAME "PXA-I2C-Adapter" +#endif /* CONFIG_ARCH_MAINSTONE */ +#ifdef CONFIG_OTG_DB1550_J15 +#define ADAPTOR_NAME "pb1550 adapter" +#endif /* CONFIG_OTG_DB1550_J15 */ + +void i2c_writeb(u8 subaddr, u8 buf) +{ + char tmpbuf[2]; + int ret; + + tmpbuf[0] = subaddr; /*register number */ + tmpbuf[1] = buf; /*register data */ + + ret = i2c_master_send(db1550_i2c_client, &tmpbuf[0], 2); + while (ret != 2){ + ret = i2c_master_send(db1550_i2c_client, &tmpbuf[0], 2); + } + TRACE_MSG1(TCD, "I2C write %d\n", ret); + +} + +void i2c_close() +{ + if (initstate_i2c) + filp_close(i2c_file, NULL); + initstate_i2c = 0; + +} + +int i2c_configure (char *name, int addr) +{ + char filename[20]; + int tmp; + + RETURN_ZERO_IF(initstate_i2c); + + /*find the I2C driver we need + */ + for (tmp = 0; tmp < MAX_I2C; tmp++) { + + sprintf(filename, "/dev/i2c-%d", tmp); + + printk(KERN_INFO"%s: %s\n", __FUNCTION__, filename); + + UNLESS (IS_ERR(i2c_file = filp_open(filename, O_RDWR, 0))) { + + //printk(KERN_INFO"%s: %s found\n", __FUNCTION__, filename); + + /*found some driver */ + db1550_i2c_client = (struct i2c_client *)i2c_file->private_data; + + printk(KERN_INFO"%s: found %s\n", __FUNCTION__, db1550_i2c_client->adapter->name); + if (strlen(db1550_i2c_client->adapter->name) >= 8) { + if (!strncmp(db1550_i2c_client->adapter->name, name, strlen(name))) + break; /*we found our driver! */ + } + db1550_i2c_client = NULL; + filp_close(i2c_file, NULL); + } + printk(KERN_INFO"%s: %s %d\n", __FUNCTION__, filename, i2c_file); + } + if (tmp == MAX_I2C) { // Nothing found + printk(KERN_ERR"%s: cannot find I2C driver", __FUNCTION__); + return -ENODEV; + } + db1550_i2c_client->addr = addr; + initstate_i2c = 1; + return 0; +} + +/*! i2c_readb + * Read byte from i2c device + * @param subaddr + */ +u8 i2c_readb(u8 subaddr) +{ +int ret; + + u8 buf = 0; + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + while (ret != 1){ +// printk(KERN_INFO"send return %d\n", ret); + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + } + // printk(KERN_INFO"send return %d\n", ret); + ret = i2c_master_recv(db1550_i2c_client, &buf, 1); + while (ret != 1){ + // printk(KERN_INFO"rec return %d with %02x\n", ret, buf); + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + while (ret != 1){ + // printk(KERN_INFO"send return %d\n", ret); + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + } + ret = i2c_master_recv(db1550_i2c_client, &buf, 1); + } + // printk(KERN_INFO"rec return %d with %02x\n", ret, buf); + TRACE_MSG2(TCD, "addr: %02x buf: %02x", subaddr, buf); + return buf; +} + + +/*! i2c_readl + * Read long from i2c device + * @param subaddr + */ +u32 i2c_readl(u8 subaddr) +{ + int ret; + // u32 buf = 0; + u8 buf[5]; + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + while (ret != 1){ + printk(KERN_INFO"send return %d\n", ret); + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + } + printk(KERN_INFO"send return %d\n", ret); + ret = i2c_master_recv(db1550_i2c_client, buf, 4); + while (ret != 4){ + printk(KERN_INFO"rec return %d with %02x\n", ret, buf); + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + while (ret != 1){ + printk(KERN_INFO"send return %d\n", ret); + ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); + } + ret = i2c_master_recv(db1550_i2c_client, buf, 4); + } + printk(KERN_INFO"rec return %d with %02x %02x %02x %02x\n", ret, buf[0], buf[1], buf[2], buf[3]); + TRACE_MSG2(TCD, "addr: %02x buf: %08x", subaddr, buf); + return buf; +} + +int i2c_readw (int addr) +{ + int data; + + data = 0; + + return data; +} + + +EXPORT_SYMBOL(i2c_writeb); +EXPORT_SYMBOL(i2c_readw); +EXPORT_SYMBOL(i2c_readb); +EXPORT_SYMBOL(i2c_readl); +EXPORT_SYMBOL(i2c_configure); +EXPORT_SYMBOL(i2c_close); + diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353-procfs.c linux/drivers/otg/ocd/max3353e/max3353-procfs.c --- linux/drivers/no-otg/ocd/max3353e/max3353-procfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/max3353-procfs.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,303 @@ +/* + * otg/ocd/max3353e/max3353-procfs.c - USB Device Core Layer + * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353-procfs.c|20060831021117|57336 + * + * Copyright (c) 2004-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Shahrad Payandeh , + * Bruce Balden + * + * + */ +/*! + * @file otg/ocd/max3353e/max3353-procfs.c + * @brief Implement /proc/max3353 to dump MAX3353 registers. + * + * + * @ingroup MAX3353TCD + */ + + +#include +#include "otghw/max3353-hardware.h" +#include "max3353.h" + +#ifdef CONFIG_ARCH_MX2ADS +#include +#define MX2_OTG_XCVR_DEVAD 0x18 +#define MX2_SEQ_OP_REG 0x19 +#define MX2_SEQ_RD_STARTAD 0x1a +#define MX2_I2C_OP_CTRL_REG 0x1b +#define MX2_SCLK_TO_SCL_HPER 0x1e +#define MX2_I2C_INTERRUPT_AND_CTRL 0x1f + +#define OTG_BASE_ADDR 0x10024000 +//#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) + +#endif /* CONFIG_ARCH_MX2ADS */ + +#ifdef CONFIG_OTG_MAX3353_PROCFS +/* Proc Filesystem *************************************************************************** */ + +extern struct max3353_private max3353_private; + +#define MAX_HISTORY 6 +struct reg_list { + u8 reg; + u8 size; + char *name; + u32 values[MAX_HISTORY]; +}; + +#define REG(r, s) {r, s, #r, } + +struct reg_list max3353_prod_list[] = { + REG(MAX3353_VENDOR_ID, 2), + REG(MAX3353_PRODUCT_ID, 2), + REG(MAX3353_VERSION_ID, 2), + { 0, 1, NULL,}, +}; +struct reg_list max3353_reg_list[] = { + REG(MAX3353_OTG_CONTROL_SET, 1), + REG(MAX3353_INTERRUPT_SOURCE, 1), + REG(MAX3353_INTERRUPT_LATCH_SET, 1), + REG(MAX3353_INTERRUPT_ENABLE_LOW_SET, 1), + REG(MAX3353_INTERRUPT_ENABLE_HIGH_SET, 1), + REG(MAX3353_MODE_CONTROL_1_SET, 1), + { 0, 1, NULL,}, +}; +struct reg_list max3353_spec_list[] = { + REG(MAX3353_MODE_CONTROL_2_SET, 1), + REG(MAX3353_OTG_STATUS, 1), + { 0, 1, NULL,}, +}; +struct reg_list max3301e_spec_list[] = { + REG(MAX3301E_SPECIAL_FUNCTION_1_SET, 1), + REG(MAX3301E_SPECIAL_FUNCTION_2_SET, 1), + { 0, 1, NULL,}, +}; +#ifdef CONFIG_ARCH_MX2ADS +struct reg_list mx21_spec_list[] = { + REG(MX2_OTG_XCVR_DEVAD, 1), + REG(MX2_SEQ_OP_REG, 1), + REG(MX2_SEQ_RD_STARTAD, 1), + REG(MX2_I2C_OP_CTRL_REG, 1), + REG(MX2_SCLK_TO_SCL_HPER, 1), + REG(MX2_I2C_INTERRUPT_AND_CTRL, 1), + { 0, 1, NULL,}, +}; +#endif /* CONFIG_ARCH_MX2ADS */ + +void max3353_update(struct reg_list *list) +{ + for (; list && list->name; list++) { + memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); + switch(list->size) { + case 1: + list->values[0] = i2c_readb(list->reg); + break; + case 2: + list->values[0] = i2c_readw(list->reg); + break; + case 4: + list->values[0] = i2c_readl(list->reg); + break; + } + } +} + +#ifdef CONFIG_ARCH_MX2ADS +static u8 __inline__ mx2_rb(u32 port) +{ + return *(volatile u8 *) (MX2_IO_ADDRESS(port + OTG_I2C_BASE)); +} + + +void mx21_update(struct reg_list *list) +{ + for (; list && list->name; list++) { + memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); + list->values[0] = mx2_rb(list->reg); + } +} + +#endif /* CONFIG_ARCH_MX2ADS */ + +void max3353_update_all(void) +{ + max3353_update(max3353_reg_list); + switch (max3353_private.transceiver_map->transceiver_type) { + case max3353: + max3353_update(max3353_spec_list); + break; + case max3301e: + max3353_update(max3301e_spec_list); + break; + default: + break; + } +#ifdef CONFIG_ARCH_MX2ADS + mx21_update(mx21_spec_list); +#endif /* CONFIG_ARCH_MX2ADS */ +} + + + +/* + * dohex + * + */ +static void dohexdigit (char *cp, unsigned char val) +{ + if (val < 0xa) + *cp = val + '0'; + else if ((val >= 0x0a) && (val <= 0x0f)) + *cp = val - 0x0a + 'a'; +} + +/* + * dohex + * + */ +static void dohexval (char *cp, unsigned char val) +{ + dohexdigit (cp++, val >> 4); + dohexdigit (cp++, val & 0xf); +} + +int max3353_dump(char *buf, char *name, char *fmt, struct reg_list *reg) +{ + int len = 0, i; + len += sprintf (buf + len, "%-20s %-34s [%03x]: ", name, reg->name, reg->reg); + len += sprintf (buf + len, fmt, reg->values[0]); + for (i = 1; i < MAX_HISTORY; i++) + if (reg->values[i - 1] == reg->values[i]) + len += sprintf (buf + len, " "); + else + len += sprintf (buf + len, fmt, reg->values[i]); + len += sprintf (buf + len, "\n"); + return len; +} + +int max3353_dump_list(char * buf, char *name, struct reg_list *list) +{ + int len = 0; + for (; list && list->name; list++) + switch(list->size) { + case 1: len += max3353_dump(buf + len, name, " %02x", list); break; + case 2: len += max3353_dump(buf + len, name, " %04x", list); break; + case 4: len += max3353_dump(buf + len, name, " %08x", list); break; + } + return len; +} + +int max3353_dump_all(char *buf) +{ + int len = 0; + len += max3353_dump_list(buf + len, "MAX3353 Standard", max3353_reg_list); + switch (max3353_private.transceiver_map->transceiver_type) { + case max3353: + len += max3353_dump_list(buf + len, "MAX3353 Extra", max3353_spec_list); + break; + case max3301e: + len += max3353_dump_list(buf + len, "MAX3301E Extra", max3301e_spec_list); + break; + default: + break; + } +#ifdef CONFIG_ARCH_MX2ADS + len += max3353_dump_list(buf + len, "MX21 ADS Extra", mx21_spec_list); +#endif /* CONFIG_ARCH_MX2ADS */ + return len; +} + + + +/*! + * max3353_device_proc_read - implement proc file system read. + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return + * statistics for. + */ +static ssize_t max3353_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + int i; + u32 r; + + int config_size; + + // get a page, max 4095 bytes of data... + RETURN_EINVAL_UNLESS ((page = get_free_page (GFP_KERNEL))); + + len = 0; + index = (*pos)++; + + switch(index) { + case 0: + len += sprintf ((char *) page + len, "MAX3353 Transceiver Registers\n"); + + max3353_update_all(); + len += sprintf ((char *) page + len , "Vendor: %04x Product: %04x Revision: %04x %s\n", + max3353_private.vendor, max3353_private.product, + max3353_private.revision, max3353_private.transceiver_map->name + ); + + len += max3353_dump_all((char *) page + len); + len += sprintf ((char *) page + len, "\n"); + + break; + + case 1: + break; + + } + + if (len > count) { + len = -EINVAL; + } + else if ((len > 0) && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + else { + } + free_page (page); + return len; +} + +static struct file_operations max3353_device_proc_operations_functions = { + read:max3353_device_proc_read_functions, +}; + +/* Module init ******************************************************************************* */ + +static int max3353_procfs_init (void) +{ + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("max3353", 0, 0)) == NULL) + return -ENOMEM; + + p->proc_fops = &max3353_device_proc_operations_functions; + + max3353_update_all(); + + return 0; +} +static void max3353_procfs_exit (void) +{ + remove_proc_entry ("max3353", NULL); +} +#else +static int max3353_procfs_init (void) { return 0; +} +static void max3353_procfs_exit (void) { } +#endif diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353.c linux/drivers/otg/ocd/max3353e/max3353.c --- linux/drivers/no-otg/ocd/max3353e/max3353.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/max3353.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,706 @@ +/* + * otg/ocd/max3353e/max3353.c -- USB Transceiver Controller driver + * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353.c|20060831021117|27226 + * + * Copyright (c) 2003-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Shahrad Payandeh , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/max3353e/max3353.c + * @brief MAX3353 OTG Transceiver Driver. + * + * This is the generic MAX3353 TCD core support. + * + * Notes: + * + * 1. The MAX3353 can control the speed and suspend directly, would it be + * appropriate to allow state machine to control this. + * + * 2. The MAX3353 has auto connect feature, can this be used without change + * to state machine. + * + * 3. The MAX3353 can control the ADR/PSW pin to enable / disable external + * charge pump. + * + * @ingroup MAX3353TCD + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "max3353.h" + + +struct otg_transceiver_map max3353_transceiver_map[] = { + { max3353, 0x4cc, 0x1301, 0x00, "Phillips MAX3353", }, + { max3301e, 0x6a0b, 0x0133, 0x00, "Maxim MAX3301E", }, + { 0, 0, 0, 0, "Unknown Transceiver", }, +}; + +struct max3353_private max3353_private; + + + +/* ********************************************************************************************* */ +#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) +#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) +#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) +#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) + + + +/*! max3353_int_src - update interrupt source test mask + * + * This sets the current mask and updates the interrupt registers to match. + */ +void max3353_int_src(u8 int_src) +{ + TRACE_MSG1(TCD, "setting int_src %02x", int_src); + max3353_private.int_src = int_src; + i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_CLR, ~int_src); + i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_CLR, ~int_src); + i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_SET, int_src); + i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_SET, int_src); + i2c_writeb(MAX3353_INTERRUPT_LATCH_CLR, int_src); +} + +/*! max3353_int_src_set - add to the interrupt source mask + * + */ +void max3353_int_src_set(u8 int_src) +{ + max3353_int_src(int_src |= max3353_private.int_src); +} + +/*! max3353_int_src_clr - remove from the interrup source mask + */ +void max3353_int_src_clr(u8 int_src) +{ + max3353_int_src(int_src = max3353_private.int_src & ~int_src); +} + +/* ********************************************************************************************* */ + + +/*! max3353_event_set_irq + */ +void max3353_event_set_irq(struct otg_instance *otg, int changed, int flag, u32 input, otg_tag_t tag, char *mset, char *mreset) +{ + TRACE_MSG5(TCD, "changed: %x flag: %x input: %08x %s %s", changed, flag, input, + flag ? mset: mreset, + changed ? "": "(ignored)"); + + otg_event_set_irq(otg, changed, flag, input, tag, flag ? mset : mreset); +} + +/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 Interrupt Source Register + */ +u8 max3353_update_int_src(struct otg_instance *otg, u8 int_src, u8 changed) +{ + UNLESS (changed) return int_src; + TRACE_MSG2(TCD, "CHANGED INTERRUPT_SOURCE: %02x (%02x) ", int_src, changed); + + /* Core events + */ + max3353_event_set_irq(otg, MAX3353_ID_GND & changed, MAX3353_ID_GND & int_src, + ID_GND, TCD, "ID_GND", "ID_GND/"); + max3353_event_set_irq(otg, MAX3353_ID_FLOAT & changed, MAX3353_ID_FLOAT & int_src, + ID_FLOAT, TCD, "ID_FLOAT", "ID_FLOAT/"); + max3353_event_set_irq(otg, MAX3353_VBUS_VLD & changed, MAX3353_VBUS_VLD & int_src, + VBUS_VLD, TCD, "VBUS_VLD", "VBUS_VLD/"); + max3353_event_set_irq(otg, MAX3353_SESS_VLD & changed, MAX3353_SESS_VLD & int_src, + A_SESS_VLD, TCD, "A_SESS_VLD", "A_SESS_VLD/"); + + max3353_event_set_irq(otg, MAX3353_DM_HI & changed, MAX3353_DM_HI & int_src, + DM_HIGH, TCD, "DM_HIGH", "DM_HIGH/"); + + max3353_event_set_irq(otg, MAX3353_DP_HI & changed, MAX3353_DP_HI & int_src, + DP_HIGH, TCD, "DP_HIGH", "DP_HIGH/"); + + //max3353_event_set_irq(otg, MAX3353_SE1 & changed, ((MAX3353_SE1 & int_src) == 0), SE0_DET, TCD, "SE0", "SE0/"); + + /* carkit + */ + max3353_event_set_irq(otg, MAX3353_CR_INT & changed, !(MAX3353_CR_INT & int_src), + CR_INT_DET, TCD, "CR_INT", "CR_INT/"); + + //TRACE_MSG3(TCD, "MAX3353_SE1: %02x & int_src: %02x = %02x", MAX3353_SE1, int_src, MAX3353_SE1 & int_src); + //max3353_event_set_irq(otg, MAX3353_SE1 & changed, ((MAX3353_SE1 & int_src) == MAX3353_SE1), + // SE1_DET, TCD, "SE1", "SE1/"); + + + /* A-Device + */ + max3353_event_set_irq(otg, MAX3353_BDIS_ACON & changed, !(MAX3353_BDIS_ACON & int_src), + BDIS_ACON, TCD, "BDIS_ACON", "BDIS_ACON/" ); + + return int_src; +} + +/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 OTG Status Register + */ +u8 max3353_update_otg_status(struct otg_instance *otg, u8 otg_status, u8 changed) +{ + UNLESS (changed) return otg_status; + TRACE_MSG2(TCD, "CHANGED OTG_STATUS_REG: %02x (%02x) ", otg_status, changed); + + max3353_event_set_irq(otg, MAX3353_B_SESS_VLD & changed, MAX3353_B_SESS_VLD & otg_status, + B_SESS_VLD, TCD, "B_SESS_VLD", "B_SESS_VLD/"); + max3353_event_set_irq(otg, MAX3353_B_SESS_END & changed, MAX3353_B_SESS_END & otg_status, + B_SESS_END, TCD, "B_SESS_END", "B_SESS_END/"); + + //otg_b_sess_vld(otg, first || (MAX3353_B_SESS_VLD & changed), MAX3353_B_SESS_VLD & otg_status, MAX3353_NAME ); + return otg_status; +} + +int max3353_bh_first; + + +/*! max3353_bh + */ +void max3353_bh(void *arg) +{ + u8 int_lat, int_src = 0, otg_status = 0, special_function_22 = 0; + static u8 int_src_saved, otg_status_saved, special_function_22_saved; + + //TRACE_MSG0(TCD, "--"); + /* read the interrupt latch and and clear latch and update otg + */ + if ((int_lat = i2c_readb(MAX3353_INTERRUPT_LATCH_SET))) { + //TRACE_MSG1(TCD, "CLEARING INT LAT: %02x", int_lat); + i2c_writeb(MAX3353_INTERRUPT_LATCH_CLR, int_lat); + } + + /* If int_lat shows a change or we are forcing an update, read interrupt source. + * Note that we mask DP_HI and DM_HI when LOC_CONN is set, we don't want them + * when USB Bus is in use. + * + * Mask with bits we are currently interested in. + */ + int_src = i2c_readb(MAX3353_INTERRUPT_SOURCE) & max3353_private.int_src; + + UNLESS ( max3353_private.flags & MAX3353_LOC_CONN) { + } + + + /* further work if B-Device and VBUS_VLD + */ + if ( (!(int_src & MAX3353_ID_GND) && (int_src & MAX3353_VBUS_VLD)) || + (!(int_src_saved & MAX3353_ID_GND) && (int_src_saved & MAX3353_VBUS_VLD)) ) + { + /* read the otg_status register and update otg state machine + * + * XXX this needs to be conditional on Transceiver type. The + * MAX3301E for example supports sess_end in a separate register. + */ + switch (max3353_private.transceiver_map->transceiver_type) { + case max3353: + otg_status = i2c_readb(MAX3353_OTG_STATUS); + if (max3353_bh_first) + otg_status_saved = ~otg_status; + otg_status_saved = max3353_update_otg_status(tcd_instance->otg, otg_status, + otg_status_saved ^ otg_status); + break; + case max3301e: + // XXX + special_function_22 = i2c_readb(MAX3301E_SPECIAL_FUNCTION_2_SET); + break; + case unknown_transceiver: + break; + } + } + if (max3353_bh_first) + int_src_saved = ~int_src; + + int_src_saved = max3353_update_int_src(tcd_instance->otg, int_src, int_src_saved ^ int_src); + + max3353_bh_first = 0; +} + +/* ********************************************************************************************** */ +/*! max3353_vbus - Do we have Vbus (cable attached?) + * Return non-zero if Vbus is detected. + * + */ +int max3353_vbus (struct otg_instance *otg) +{ + return 0; +} + +/*! max3353_id - Do we have Vbus (cable attached?) + * Return non-zero if Vbus is detected. + * + */ +int max3353_idvbus (struct otg_instance *otg) +{ + return 0; +} + + +/* ********************************************************************************************* */ + +/*! max3353_chrg_vbus - used to enable or disable B-device Vbus charging + */ +void max3353_chrg_vbus(struct otg_instance *otg, u8 flag) +{ + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "CHRG_VBUS_SET"); + i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_VBUS_CHRG); + break; + case RESET: + TRACE_MSG0(TCD, "CHRG_VBUS_RESET"); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_VBUS_CHRG); + break; + case PULSE: + break; + } +} + +/*! max3353_drv_vbus - used to enable or disable A-device driving Vbus + */ +void max3353_drv_vbus(struct otg_instance *otg, u8 flag) +{ + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "DRV_VBUS_SET"); + i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_VBUS_DRV); + break; + case RESET: + TRACE_MSG0(TCD, "DRV_VBUS_RESET"); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_VBUS_DRV); + break; + } +} + +/*! max3353_dischrg_vbus - used to enable Vbus discharge + */ +void max3353_dischrg_vbus(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_SET"); + i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_VBUS_DISCHRG); + break; + case RESET: + TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_RESET"); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, MAX3353_VBUS_DISCHRG); + break; + } +} + +/*! max3353_dp_pullup_func - used to enable or disable peripheral connecting to bus + * + * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 + * + * host peripheral + * d+ pull-up clr set + * d+ pull-down set clr + * + * d- pull-up clr clr + * d- pull-down set set + * + */ +void max3353_dp_pullup_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + printk(KERN_INFO"%s: --\n", __FUNCTION__); + max3353_private.flags |= MAX3353_LOC_CONN; + TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Clr DP PULLDOWN"); + //i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_DP_PULLDOWN); + //i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) (MAX3353_DP_PULLDOWN | MAX3353_DM_PULLDOWN) ); + TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DP PULLUP"); + i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_DP_PULLUP); + break; + + case RESET: + max3353_private.flags &= ~MAX3353_LOC_CONN; + TRACE_MSG0(TCD, "MAX3353_LOC_CONN RESET - Clr DP PULLUP"); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8)MAX3353_DP_PULLUP); + TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DP PULLDOWN"); + //i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8)MAX3353_DP_PULLDOWN); + //i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) (MAX3353_DP_PULLDOWN | MAX3353_DM_PULLDOWN) ); + break; + + case PULSE: + TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); + break; + } +} + +/*! max3353_dm_pullup_func - used to enable or disable peripheral connecting to bus + * + */ +void max3353_dm_pullup_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + max3353_private.flags |= MAX3353_LOC_CONN; + TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Clr DM PULLDOWN"); + //i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_DM_PULLDOWN); + TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DM PULLUP"); + i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_DM_PULLUP); + break; + + case RESET: + max3353_private.flags &= ~MAX3353_LOC_CONN; + TRACE_MSG0(TCD, "MAX3353_LOC_CONN RESET - Clr DM PULLUP"); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8)MAX3353_DM_PULLUP); + TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DM PULLDOWN"); + //i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8)MAX3353_DM_PULLDOWN); + break; + + case PULSE: + TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); + break; + } +} + +/*! max3353_dm_det_func - used to enable or disable D- detect + * + */ +void max3353_dm_det_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting DM_HI detect"); + max3353_int_src_set(MAX3353_DM_HI); + max3353_bh_first = 1; + break; + + case RESET: + TRACE_MSG0(TCD, "reseting DM_HI detect"); + max3353_int_src_clr(MAX3353_DM_HI); + break; + } +} + +/*! max3353_dp_det_func - used to enable or disable D+ detect + * + */ +void max3353_dp_det_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting DP_HI detect"); + max3353_int_src_set(MAX3353_DP_HI); + max3353_bh_first = 1; + break; + + case RESET: + TRACE_MSG0(TCD, "reseting DP_HI detect"); + max3353_int_src_clr(MAX3353_DP_HI); + break; + } +} + +/*! max3353_bdis_acon_func - used to enable or disable auto a-connect + * + */ +void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting BDIS ACON"); + i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_ID_PULLDOWN); + break; + + case RESET: + TRACE_MSG0(TCD, "reseting BDIS ACON"); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, MAX3353_ID_PULLDOWN); + break; + } +} + +/*! max3353_id_pulldown_func - used to enable or disable ID pulldown + * + */ +void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting ID PULLDOWN"); + i2c_writeb(MAX3353_MODE_CONTROL_1_SET, MAX3353_BDIS_ACON_EN); + break; + + case RESET: + TRACE_MSG0(TCD, "reseting ID PULLDOWN"); + i2c_writeb(MAX3353_MODE_CONTROL_1_CLR, MAX3353_BDIS_ACON_EN); + break; + } +} + +/*! max3353_audio_func - used to enable or disable Carkit Interrupt + * + */ +void max3353_audio_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting CARKIT INT"); + i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_AUDIO_EN); + break; + + case RESET: + TRACE_MSG0(TCD, "reseting CARKIT INT"); + i2c_writeb(MAX3353_MODE_CONTROL_2_CLR, MAX3353_AUDIO_EN); + break; + } +} + +/*! max3353_uart_func - used to enable or disable transparent uart mode + * + */ +void max3353_uart_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting UART"); + i2c_writeb(MAX3353_MODE_CONTROL_1_SET, MAX3353_UART_EN); + /* XXX enable uart */ + break; + + case RESET: + MAX3353SG0(TCD, "reseting UART"); + i2c_writeb(MAX3353_MODE_CONTROL_1_CLR, MAX3353_UART_EN); + /* XXX disable uart */ + break; + } +} + +/*! max3353_mono_func - used to enable or disable mono audio connection + * + */ +void max3353_mono_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "setting MONO"); + /* XXX enable mono output */ + break; + + case RESET: + TRACE_MSG0(TCD, "reseting MONO"); + /* XXX disable mono output */ + break; + } +} + + +/* ********************************************************************************************* */ +extern int max3353_procfs_init (void); +extern void max3353_procfs_exit (void); + +/*! max3353_mod_init + */ +int max3353_mod_init(void) +{ + /* for interrupt bottom half handler + */ + PREPARE_WORK_ITEM(max3353_private.bh, &max3353_bh, NULL); + + /* setup max3353, enable interrupts, clear latch + * XXX when and how do we do DM_HI and DP_HI, when ID is present? + */ + max3353_int_src(MAX3353_INT_SRC); + //max3353_int_src(MAX3353_NO_SE1_INT); + + i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_DM_PULLDOWN | MAX3353_DP_PULLDOWN); + +#ifdef CONFIG_OTG_MAX3353_PROCFS + max3353_procfs_init (); +#endif /* CONFIG_OTG_MAX3353_PROCFS */ + + return 0; +} + +/*! max3353_mod_exit + */ +void max3353_mod_exit(void) +{ + i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_CLR, 0xff); + i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_CLR, 0xff); + //i2c_close(); +#ifdef CONFIG_OTG_MAX3353_PROCFS + max3353_procfs_exit (); +#endif /* CONFIG_OTG_MAX3353_PROCFS */ + + while (PENDING_WORK_ITEM(max3353_private.bh)) { + printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } + +} + +/* ********************************************************************************************* */ +/*! max3353_configure - configure the MAX3353 for this host + * @param tx_mode - the type of connection between the host USB and the MAX3353 + * @param spd_ctrl - suspend control method + */ +void max3353_configure(tx_mode_t tx_mode, spd_ctrl_t spd_ctrl) +{ + u16 vendor = 0, product = 0, revision = 0; + u8 mode1, mode2, control; + + struct otg_transceiver_map *map = max3353_transceiver_map; + + +#if defined(CONFIG_OTG_MAX3353_MX2ADS) || defined(CONFIG_OTG_MAX3353_MX2ADS_MODULE) + u32 vp; + MAX3353SG0(TCD, "1. Read Transceiver ID's with long read"); + vp = i2c_readl(MAX3353_VENDOR_ID); + max3353_private.vendor = vp & 0xffff; + max3353_private.product = vp >> 16; + max3353_private.revision = i2c_readw(MAX3353_VERSION_ID); + mode1 = i2c_readb(MAX3353_MODE_CONTROL_1_SET); + mode2 = i2c_readb(MAX3353_MODE_CONTROL_2_SET); + + TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", + vendor, product, revision, mode1, mode2); + +#else /* defined(CONFIG_OTG_MAX3353_MX2ADS) */ + + max3353_private.vendor = i2c_readw(MAX3353_VENDOR_ID); + max3353_private.product = i2c_readw(MAX3353_PRODUCT_ID); + max3353_private.revision = i2c_readw(MAX3353_VERSION_ID); + mode1 = i2c_readb(MAX3353_MODE_CONTROL_1_SET); + mode2 = i2c_readb(MAX3353_MODE_CONTROL_2_SET); + + TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", + max3353_private.vendor, max3353_private.product, max3353_private.revision, mode1, mode2); + +#endif /* defined(CONFIG_OTG_MAX3353_MX2ADS) */ + + for ( ; map && map->transceiver_type != unknown_transceiver; map++) { + CONTINUE_UNLESS ((max3353_private.vendor == map->vendor) && (max3353_private.product == map->product)); + TRACE_MSG1(TCD, "Found Transceiver: %s", map->name); + max3353_private.transceiver_map = map; + break; + } + UNLESS (max3353_private.transceiver_map) + max3353_private.transceiver_map = max3353_transceiver_map; + + TRACE_MSG0(TCD, "3. Disable All Transceiver Control Register 1 "); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, 0xff); // clear + + TRACE_MSG0(TCD, "4. Enable D-/D+ Pulldowns in Transceiver Control Register 1 "); + + i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_DM_PULLDOWN | MAX3353_DP_PULLDOWN); + i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_DM_PULLUP | MAX3353_DP_PULLUP); + + + TRACE_MSG0(TCD, "5. Clear latch and enable interrupts"); + i2c_writeb(MAX3353_INTERRUPT_LATCH_CLR, 0xff); + i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_CLR, 0xeb); + i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_CLR, 0xeb); + + /* The PSW_OE enables the ADR/PSW pin for output driving either high + * or low depending on the address. + * + * ADR ADR_REG Address PSW_OE=0 PSW_OE=1 + * + * low 0 0x2c LOW HIGH + * + * high 1 0x2d HIGH LOW + * + * + * The i2c address by tying the ADR/PSW pin either high or low. + * Setting the PSW_OE drives the ADR/PSW into the opposite of the + * default wiring. + * + * On the MX21ADS the ADR/PSW pin is wired high which enables the + * charge pump. So enabling the PSW_OE is required to disable Vbus + * generation on the Charge Pump. + * + * The MAX3355E will only enable Vbus when this signal is high AND + * the ID signal is low. So it may be safe to leave enabled when + * operating as a B-device (ID floating.) + */ + + //i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_PSW_OE); // PSW_OE - OFFVBUS low + + + TRACE_MSG1(TCD, "6. tx_mode: %02x", tx_mode); + switch (tx_mode) { + case vp_vm_bidirectional: + break; + case dat_se0_unidirectional: + i2c_writeb(MAX3353_MODE_CONTROL_2_CLR, MAX3353_BI_DI); + MAX3353t_se0_bidirectional: + i2c_writeb(MAX3353_MODE_CONTROL_1_SET, MAX3353_DAT_SE0); + break; + } + TRACE_MSG0(TCD, "6. tx_mode done"); + + TRACE_MSG1(TCD, "7. spd_ctrl: %02x", spd_ctrl); + switch (spd_ctrl) { + case spd_susp_pins: + i2c_writeb(MAX3353_MODE_CONTROL_2_CLR, MAX3353_SPD_SUSP_CTRL); + break; + case spd_susp_reg: + i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_SPD_SUSP_CTRL); + break; + } + + //i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_PSW_OE); // PSW_OE - OFFVBUS low + + TRACE_MSG0(TCD, "7. spd_ctrl done"); + + TRACE_MSG1(TCD, "8. transceiver_type: %02x", max3353_private.transceiver_map); + + TRACE_MSG1(TCD, "8. transceiver_type: %02x", max3353_private.transceiver_map->transceiver_type); + switch (max3353_private.transceiver_map->transceiver_type) { + case max3353: + break; + case max3301e: + i2c_writeb(MAX3301E_SPECIAL_FUNCTION_1_SET, MAX3301E_SESS_END); + break; + case unknown_transceiver: + break; + } + TRACE_MSG0(TCD, "8. transceiver_type done"); +} + diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353.h linux/drivers/otg/ocd/max3353e/max3353.h --- linux/drivers/no-otg/ocd/max3353e/max3353.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/max3353.h 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,132 @@ +/* + * otg/ocd/max3353/max3353.h -- USB Transceiver Controller driver + * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353.h|20060831021117|56821 + * + * Copyright (c) 2004-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Bruce Balden + * + */ + + +/*! + * @file otg/ocd/max3353e/max3353.h + * @brief Private structures and defines for MAX3353 Transciever Controller Driver. + * + * This file contains the private defines and structures for the MAX3353 Transceiver + * Driver. + * + * @ingroup MAX3353TCD + */ + + +#ifdef CONFIG_XXXXX +#define MAX3353_GPIO (2) +#define TCD_MAX3353_I2C_ADDR MAX3353_I2C_ADDR_HIGH; +#endif + +#define MAX3353_NAME "max3353" + +#define MAX3353_LOC_CONN 0x01 + +#define MAX3353_INT_SRC 0xeb + +//#define MAX3353_ALL_INT_SRC 0xff +//#define MAX3353_NO_SE1_INT 0xeb +//#define MAX3353_NO_SE1_INT 0xfb /* do not pay attention to DP_HI */ + +typedef enum otg_transceiver { + unknown_transceiver, + max3353, + max3301e +} otg_transceiver_t; + +struct otg_transceiver_map { + otg_transceiver_t transceiver_type; + u16 vendor; + u16 product; + u16 revision; + char *name; +}; + +struct max3353_private { + WORK_STRUCT bh; + u16 vendor; + u16 product; + u16 revision; + u32 flags; + u8 int_src; + struct otg_transceiver_map *transceiver_map; +}; + +/*! tx_mode + * This defines the various ways that the MAX3353 can be + * wired into the host USB. + */ +typedef enum tx_mode { + dat_se0_bidirectional, /*!< 3-Wire */ + vp_vm_bidirectional, /*!< 4-Wire */ + dat_se0_unidirectional /*!< 6-Wire */ +} tx_mode_t; + +/*! spd_ctrl + * This defines how the speed and suspend pins are controlled + * for this host. + */ +typedef enum spd_ctrl { + spd_susp_pins, /*!< controlled by SPEED and SUSPEND pins */ + spd_susp_reg, /*!< controled by SPEED_REG and SUSPEND_REG in Mode Control 1 register */ +} spd_ctrl_t; + + +extern struct tcd_ops tcd_ops; +extern struct max3353_private max3353_private; +extern struct tcd_instance *tcd_instance; + +/* max3353.c */ +extern int max3353_mod_init(void); +extern void max3353_mod_exit(void); +extern void max3353_chrg_vbus(struct otg_instance *, u8 ); +extern void max3353_drv_vbus(struct otg_instance *, u8 ); +extern void max3353_dischrg_vbus(struct otg_instance *, u8 ); +extern void max3353_dp_pullup_func(struct otg_instance *, u8 ); +extern void max3353_dm_pullup_func(struct otg_instance *, u8 ); +extern void max3353_dm_det_func(struct otg_instance *, u8 ); +extern void max3353_dp_det_func(struct otg_instance *, u8 ); +extern void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag); +extern void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag); +extern void max3353_audio_func(struct otg_instance *otg, u8 flag); +extern void max3353_uart_func(struct otg_instance *otg, u8 flag); +extern void max3353_mono_func(struct otg_instance *otg, u8 flag); + +extern void max3353_otg_timer(struct otg_instance *, u8 ); +extern void max3353_bh(void *); +extern int max3353_id (struct otg_instance *); +extern int max3353_vbus (struct otg_instance *); +extern void max3353_configure(tx_mode_t, spd_ctrl_t ); + +/* thread */ +extern int max3353_thread_init (void (bh_proc)(void *)); +extern void max3353_thread_exit (wait_queue_head_t *); +extern void max3353_thread_wakeup(int enabled, int first); + + + +/* i2c-xxx.c */ +int i2c_configure(char *name, int addr); +extern void i2c_close(void); +extern u8 i2c_readb(u8 ); +extern u16 i2c_readw(u8 ); +extern u32 i2c_readl(u8 ); +extern int i2c_writeb(u8 , u8 ); + + +/* ********************************************************************************************* */ +#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) +#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) +#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) +#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) + diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353e-procfs.c linux/drivers/otg/ocd/max3353e/max3353e-procfs.c --- linux/drivers/no-otg/ocd/max3353e/max3353e-procfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/max3353e-procfs.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,303 @@ +/* + * otg/ocd/max3353/max3353-procfs.c - USB Device Core Layer + * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353e-procfs.c|20060831021117|57336 + * + * Copyright (c) 2004-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Shahrad Payandeh , + * Bruce Balden + * + * + */ +/*! + * @file otg/ocd/max3353e/max3353e-procfs.c + * @brief Implement /proc/max3353 to dump MAX3353 registers. + * + * + * @ingroup MAX3353TCD + */ + + +#include +#include "otghw/max3353-hardware.h" +#include "max3353.h" + +#ifdef CONFIG_ARCH_MX2ADS +#include +#define MX2_OTG_XCVR_DEVAD 0x18 +#define MX2_SEQ_OP_REG 0x19 +#define MX2_SEQ_RD_STARTAD 0x1a +#define MX2_I2C_OP_CTRL_REG 0x1b +#define MX2_SCLK_TO_SCL_HPER 0x1e +#define MX2_I2C_INTERRUPT_AND_CTRL 0x1f + +#define OTG_BASE_ADDR 0x10024000 +//#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) + +#endif /* CONFIG_ARCH_MX2ADS */ + +#ifdef CONFIG_OTG_MAX3353_PROCFS +/* Proc Filesystem *************************************************************************** */ + +extern struct max3353_private max3353_private; + +#define MAX_HISTORY 6 +struct reg_list { + u8 reg; + u8 size; + char *name; + u32 values[MAX_HISTORY]; +}; + +#define REG(r, s) {r, s, #r, } + +struct reg_list max3353_prod_list[] = { + REG(MAX3353_VENDOR_ID, 2), + REG(MAX3353_PRODUCT_ID, 2), + REG(MAX3353_VERSION_ID, 2), + { 0, 1, NULL,}, +}; +struct reg_list max3353_reg_list[] = { + REG(MAX3353_OTG_CONTROL_SET, 1), + REG(MAX3353_INTERRUPT_SOURCE, 1), + REG(MAX3353_INTERRUPT_LATCH_SET, 1), + REG(MAX3353_INTERRUPT_ENABLE_LOW_SET, 1), + REG(MAX3353_INTERRUPT_ENABLE_HIGH_SET, 1), + REG(MAX3353_MODE_CONTROL_1_SET, 1), + { 0, 1, NULL,}, +}; +struct reg_list max3353_spec_list[] = { + REG(MAX3353_MODE_CONTROL_2_SET, 1), + REG(MAX3353_OTG_STATUS, 1), + { 0, 1, NULL,}, +}; +struct reg_list max3301e_spec_list[] = { + REG(MAX3301E_SPECIAL_FUNCTION_1_SET, 1), + REG(MAX3301E_SPECIAL_FUNCTION_2_SET, 1), + { 0, 1, NULL,}, +}; +#ifdef CONFIG_ARCH_MX2ADS +struct reg_list mx21_spec_list[] = { + REG(MX2_OTG_XCVR_DEVAD, 1), + REG(MX2_SEQ_OP_REG, 1), + REG(MX2_SEQ_RD_STARTAD, 1), + REG(MX2_I2C_OP_CTRL_REG, 1), + REG(MX2_SCLK_TO_SCL_HPER, 1), + REG(MX2_I2C_INTERRUPT_AND_CTRL, 1), + { 0, 1, NULL,}, +}; +#endif /* CONFIG_ARCH_MX2ADS */ + +void max3353_update(struct reg_list *list) +{ + for (; list && list->name; list++) { + memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); + switch(list->size) { + case 1: + list->values[0] = i2c_readb(list->reg); + break; + case 2: + list->values[0] = i2c_readw(list->reg); + break; + case 4: + list->values[0] = i2c_readl(list->reg); + break; + } + } +} + +#ifdef CONFIG_ARCH_MX2ADS +static u8 __inline__ mx2_rb(u32 port) +{ + return *(volatile u8 *) (MX2_IO_ADDRESS(port + OTG_I2C_BASE)); +} + + +void mx21_update(struct reg_list *list) +{ + for (; list && list->name; list++) { + memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); + list->values[0] = mx2_rb(list->reg); + } +} + +#endif /* CONFIG_ARCH_MX2ADS */ + +void max3353_update_all(void) +{ + max3353_update(max3353_reg_list); + switch (max3353_private.transceiver_map->transceiver_type) { + case max3353: + max3353_update(max3353_spec_list); + break; + case max3301e: + max3353_update(max3301e_spec_list); + break; + default: + break; + } +#ifdef CONFIG_ARCH_MX2ADS + mx21_update(mx21_spec_list); +#endif /* CONFIG_ARCH_MX2ADS */ +} + + + +/* + * dohex + * + */ +static void dohexdigit (char *cp, unsigned char val) +{ + if (val < 0xa) + *cp = val + '0'; + else if ((val >= 0x0a) && (val <= 0x0f)) + *cp = val - 0x0a + 'a'; +} + +/* + * dohex + * + */ +static void dohexval (char *cp, unsigned char val) +{ + dohexdigit (cp++, val >> 4); + dohexdigit (cp++, val & 0xf); +} + +int max3353_dump(char *buf, char *name, char *fmt, struct reg_list *reg) +{ + int len = 0, i; + len += sprintf (buf + len, "%-20s %-34s [%03x]: ", name, reg->name, reg->reg); + len += sprintf (buf + len, fmt, reg->values[0]); + for (i = 1; i < MAX_HISTORY; i++) + if (reg->values[i - 1] == reg->values[i]) + len += sprintf (buf + len, " "); + else + len += sprintf (buf + len, fmt, reg->values[i]); + len += sprintf (buf + len, "\n"); + return len; +} + +int max3353_dump_list(char * buf, char *name, struct reg_list *list) +{ + int len = 0; + for (; list && list->name; list++) + switch(list->size) { + case 1: len += max3353_dump(buf + len, name, " %02x", list); break; + case 2: len += max3353_dump(buf + len, name, " %04x", list); break; + case 4: len += max3353_dump(buf + len, name, " %08x", list); break; + } + return len; +} + +int max3353_dump_all(char *buf) +{ + int len = 0; + len += max3353_dump_list(buf + len, "MAX3353 Standard", max3353_reg_list); + switch (max3353_private.transceiver_map->transceiver_type) { + case max3353: + len += max3353_dump_list(buf + len, "MAX3353 Extra", max3353_spec_list); + break; + case max3301e: + len += max3353_dump_list(buf + len, "MAX3301E Extra", max3301e_spec_list); + break; + default: + break; + } +#ifdef CONFIG_ARCH_MX2ADS + len += max3353_dump_list(buf + len, "MX21 ADS Extra", mx21_spec_list); +#endif /* CONFIG_ARCH_MX2ADS */ + return len; +} + + + +/*! + * max3353_device_proc_read - implement proc file system read. + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return + * statistics for. + */ +static ssize_t max3353_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + int i; + u32 r; + + int config_size; + + // get a page, max 4095 bytes of data... + RETURN_EINVAL_UNLESS ((page = get_free_page (GFP_KERNEL))); + + len = 0; + index = (*pos)++; + + switch(index) { + case 0: + len += sprintf ((char *) page + len, "MAX3353 Transceiver Registers\n"); + + max3353_update_all(); + len += sprintf ((char *) page + len , "Vendor: %04x Product: %04x Revision: %04x %s\n", + max3353_private.vendor, max3353_private.product, + max3353_private.revision, max3353_private.transceiver_map->name + ); + + len += max3353_dump_all((char *) page + len); + len += sprintf ((char *) page + len, "\n"); + + break; + + case 1: + break; + + } + + if (len > count) { + len = -EINVAL; + } + else if ((len > 0) && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + else { + } + free_page (page); + return len; +} + +static struct file_operations max3353_device_proc_operations_functions = { + read:max3353_device_proc_read_functions, +}; + +/* Module init ******************************************************************************* */ + +static int max3353_procfs_init (void) +{ + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("max3353", 0, 0)) == NULL) + return -ENOMEM; + + p->proc_fops = &max3353_device_proc_operations_functions; + + max3353_update_all(); + + return 0; +} +static void max3353_procfs_exit (void) +{ + remove_proc_entry ("max3353", NULL); +} +#else +static int max3353_procfs_init (void) { return 0; +} +static void max3353_procfs_exit (void) { } +#endif diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353e.c linux/drivers/otg/ocd/max3353e/max3353e.c --- linux/drivers/no-otg/ocd/max3353e/max3353e.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/max3353e.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,657 @@ +/* + * otg/ocd/max3353/max3353.c -- USB Transceiver Controller driver + * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353e.c|20060831021117|08740 + * + * Copyright (c) 2004-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Shahrad Payandeh , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/max3353e/max3353e.c + * @brief MAX3353 OTG Transceiver Driver. + * + * This is the generic MAX3353 TCD core support. + * + * Notes: + * + * 1. The MAX3353 can control the speed and suspend directly, would it be + * appropriate to allow state machine to control this. + * + * 2. The MAX3353 has auto connect feature, can this be used without change + * to state machine. + * + * 3. The MAX3353 can control the ADR/PSW pin to enable / disable external + * charge pump. + * + * @ingroup MAX3353TCD + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "max3353e.h" + + +struct otg_transceiver_map max3353_transceiver_map[] = { + { max3353, 0x4cc, 0x1301, 0x00, "Phillips MAX3353", }, + { max3301e, 0x6a0b, 0x0133, 0x00, "Maxim MAX3301E", }, + { 0, 0, 0, 0, "Unknown Transceiver", }, +}; + +struct max3353_private max3353_private; + + + +/* ********************************************************************************************* */ +#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) +#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) +#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) +#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) + + + +/*! max3353_int_src - update interrupt source test mask + * + * This sets the current mask and updates the interrupt registers to match. + */ +void max3353_int_src(u8 int_src) +{ + TRACE_MSG1(TCD, "setting int_src %02x", int_src); + max3353_private.int_src = int_src; + i2c_writeb(MAX3353E_INTERRUPT_MASK, int_src); + i2c_writeb(MAX3353E_INTERRUPT_EDGE, 3); //detect on positive edge + i2c_writeb(MAX3353E_INTERRUPT_LATCH, 0); +} + +/*! max3353_int_src_set - add to the interrupt source mask + * + */ +void max3353_int_src_set(u8 int_src) +{ + max3353_int_src(int_src |= max3353_private.int_src); +} + +/*! max3353_int_src_clr - remove from the interrup source mask + */ +void max3353_int_src_clr(u8 int_src) +{ + max3353_int_src(int_src = max3353_private.int_src & ~int_src); +} + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ + + + +typedef struct max3353_test { + u32 input; + char *name; + u8 mask; +} max3353_test_t; + +#define OV(i, m) {i, #i, m} +#define MAX3353E_B_SESS_END 1<<4 +#define MAX3353E_B_SESS_VLD 1<<6 +#define MAX3353E_A_SESS_VLD 1<<5 + + +struct max3353_test max3353_int_src_tests[5] = { + OV(VBUS_VLD, MAX3353E_VBUS_VALID), + OV(ID_GND, MAX3353E_ID_GND), + OV(ID_FLOAT, MAX3353E_ID_FLOAT), + OV(BDIS_ACON, MAX3353E_A_HNP), + {0, NULL, 0}, +}; + +struct max3353_test max3353_otg_status_tests[4] = { + OV(B_SESS_END, MAX3353E_B_SESS_END), + OV(B_SESS_VLD, MAX3353E_B_SESS_VLD), + OV(A_SESS_VLD, MAX3353E_A_SESS_VLD), + {0, NULL, 0}, +}; + +u64 max3353_event(struct otg_instance *otg, struct max3353_test *tests, int val, char *msg) +{ + int i; + u64 inputs = 0; + TRACE_MSG2(TCD, "%02x %s", val, msg); + for (i = 0; tests[i].mask; i++) { + u64 input = tests[i].input; + u8 mask = tests[i].mask; + inputs |= (val & mask) ? input : _NOT(input); + TRACE_MSG5(TCD, "%d - %s%s %02x %08x", i, tests[i].name, (val & mask) ? "" : "/", tests[i].mask, tests[i].input); + } + return inputs; +} + +/*! max3353_event_set_irq + */ +void max3353_event_set_irq(struct otg_instance *otg, int changed, int flag, u32 input, otg_tag_t tag, char *mset, char *mreset) +{ + TRACE_MSG5(TCD, "changed: %x flag: %x input: %08x %s %s", changed, flag, input, + flag ? mset: mreset, + changed ? "": "(ignored)"); + + otg_event_set_irq(otg, changed, flag, input, tag, flag ? mset : mreset); +} + +/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 Interrupt Source Register + */ +u8 max3353_update_int_src(struct otg_instance *otg, u8 int_src, u8 changed) +{ + UNLESS (changed) return int_src; + TRACE_MSG2(TCD, "CHANGED INTERRUPT_SOURCE: %02x (%02x) ", int_src, changed); + + /* Core events + */ + max3353_event_set_irq(otg, MAX3353E_ID_GND & changed, MAX3353E_ID_GND & int_src, + ID_GND, TCD, "ID_GND", "ID_GND/"); + max3353_event_set_irq(otg, MAX3353E_ID_FLOAT & changed, MAX3353E_ID_FLOAT & int_src, + ID_FLOAT, TCD, "ID_FLOAT", "ID_FLOAT/"); + max3353_event_set_irq(otg, MAX3353E_VBUS_VALID & changed, MAX3353E_VBUS_VALID & int_src, + VBUS_VLD, TCD, "VBUS_VLD", "VBUS_VLD/"); + max3353_event_set_irq(otg, MAX3353E_SESSION_END & changed, MAX3353E_SESSION_END & int_src, + A_SESS_VLD, TCD, "A_SESS_VLD", "A_SESS_VLD/"); + + + return int_src; +} + +/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 OTG Status Register + */ +u8 max3353_update_otg_status(struct otg_instance *otg, u8 otg_status, u8 changed) +{ + UNLESS (changed) return otg_status; + TRACE_MSG2(TCD, "CHANGED OTG_STATUS_REG: %02x (%02x) ", otg_status, changed); + + max3353_event_set_irq(otg, MAX3353E_SESSION_VALID_EN & changed, MAX3353E_SESSION_VALID_EN & otg_status, + B_SESS_VLD, TCD, "B_SESS_VLD", "B_SESS_VLD/"); + max3353_event_set_irq(otg, MAX3353E_SESSION_END & changed, MAX3353E_SESSION_END & otg_status, + B_SESS_END, TCD, "B_SESS_END", "B_SESS_END/"); + + return otg_status; +} + + +int max3353_bh_first; + +/*! max3353_bh + */ +void max3353_bh(void *arg) +{ + + u64 inputs = 0; + u8 int_lat,status_reg,otg_reg; + u8 reg1,reg2; + + au_writel(0x40000000, IC1_MASKCLR); //disable interrupt + au_writel(0x40000000, IC1_FALLINGCLR); + au_writel(0x40000000, IC1_RISINGCLR); + TRACE_MSG0(TCD, "--MAX3353 ISR start"); +// printk(KERN_INFO"ISR Start .......\n"); + + /* read the interrupt latch and and clear latch and update otg + */ + if ((int_lat = i2c_readb(MAX3353E_INTERRUPT_LATCH))) { + TRACE_MSG1(TCD, "INT LAT: %02x", int_lat); + i2c_writeb(MAX3353E_INTERRUPT_LATCH, 0); + } + + status_reg = i2c_readb(MAX3353E_STATUS_REGISTER); + inputs |= max3353_event(tcd_instance->otg, max3353_int_src_tests, status_reg, "MAX3353 STAT SRC"); + TRACE_MSG1(TCD, "inputs after applying status register %02x\n", inputs); + + + otg_reg = 0; + + if (status_reg & MAX3353E_SESSION_VALID_EN) { //1.4,,4.5 + TRACE_MSG0(TCD, "Setting B_SESS_VLD"); + otg_reg |= MAX3353E_B_SESS_VLD; + } + if (status_reg & MAX3353E_SESSION_END) { + TRACE_MSG0(TCD, "Setting B_SESS_END"); + otg_reg |= MAX3353E_B_SESS_END; + } + if (!(status_reg & MAX3353E_SESSION_END)) { //0.8<<1.4 + if (!(status_reg & MAX3353E_SESSION_VALID_EN)){ + TRACE_MSG0(TCD, "Setting A_SESS_VLD"); + otg_reg |= MAX3353E_A_SESS_VLD; + } + } + + + + inputs |= max3353_event(tcd_instance->otg, max3353_otg_status_tests, otg_reg, "MAX3353 OTG SRC"); + TRACE_MSG1(TCD, "inputs after applying otg register %02x\n", inputs); + + + + TRACE_MSG0(TCD, "--MAX3353 ISR end"); + if (inputs) otg_event(tcd_instance->otg, inputs, TCD, "MAX3353"); + au_writel(0x40000000, IC1_MASKSET); //enable interrupt +} + + + +/* ********************************************************************************************** */ +/*! max3353_vbus - Do we have Vbus (cable attached?) + * Return non-zero if Vbus is detected. + * + */ +int max3353_vbus (struct otg_instance *otg) +{ + return 0; +} + +/*! max3353_id - Do we have Vbus (cable attached?) + * Return non-zero if Vbus is detected. + * + */ +int max3353_idvbus (struct otg_instance *otg) +{ + return 0; +} + + +/* ********************************************************************************************* */ + +/*! max3353_chrg_vbus - used to enable or disable B-device Vbus charging + */ +void max3353_chrg_vbus(struct otg_instance *otg, u8 flag) +{ + u8 reg2; + + TRACE_MSG0(TCD, "--"); + reg2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); + TRACE_MSG1(TCD, "Control 2 - Begin = %02x", reg2); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "CHRG_VBUS_SET"); + reg2 |= MAX3353E_VBUS_CHG2; + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); + break; + case RESET: + TRACE_MSG0(TCD, "CHRG_VBUS_RESET"); + reg2 &= (~MAX3353E_VBUS_CHG2); + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); + break; + case PULSE: + break; + } + TRACE_MSG1(TCD, "Control 2 - End = %02x", reg2); +} + +/*! max3353_drv_vbus - used to enable or disable A-device driving Vbus + */ +void max3353_drv_vbus(struct otg_instance *otg, u8 flag) +{ +u8 reg2; + + TRACE_MSG0(TCD, "--"); + reg2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); + TRACE_MSG1(TCD, "Control 2 - Begin = %02x", reg2); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "DRV_VBUS_SET"); + reg2 |= MAX3353E_VBUS_DRV; + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); + break; + case RESET: + TRACE_MSG0(TCD, "DRV_VBUS_RESET"); + reg2 &= (~MAX3353E_VBUS_DRV); + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); + break; + case PULSE: + break; + } + TRACE_MSG1(TCD, "Control 2 - End = %02x", reg2); +} + +/*! max3353_dischrg_vbus - used to enable Vbus discharge + */ +void max3353_dischrg_vbus(struct otg_instance *otg, u8 flag) +{ + u8 reg2; + + TRACE_MSG0(TCD, "--"); + reg2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); + TRACE_MSG1(TCD, "Control 2 - Begin = %02x", reg2); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "DISCHG_VBUS_SET"); + reg2 |= MAX3353E_VBUS_DISCHG; + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); + break; + case RESET: + TRACE_MSG0(TCD, "DISCHG_VBUS_RESET"); + reg2 &= (~MAX3353E_VBUS_DISCHG); + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); + break; + case PULSE: + break; + } + TRACE_MSG1(TCD, "Control 2 - End = %02x", reg2); +} + +/*! max3353_dp_pullup_func - used to enable or disable peripheral connecting to bus + * + * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 + * + * host peripheral + * d+ pull-up clr set + * d+ pull-down set clr + * + * d- pull-up clr clr + * d- pull-down set set + * + */ +void max3353_dp_pullup_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "-- "); + switch (flag) { + case SET: + printk(KERN_INFO"%s: --\n", __FUNCTION__); + max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + TRACE_MSG0(TCD, "Clr DP PULLDOWN"); + max3353_private.flags &= (~MAX3353E_DP_PULLDOWN); + TRACE_MSG0(TCD, "Set DP PULLUP"); + max3353_private.flags |= (MAX3353E_DP_PULLUP); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); + break; + + case RESET: + max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + TRACE_MSG0(TCD, "Clr DP PULLUP"); + max3353_private.flags &= (~MAX3353E_DP_PULLUP); + TRACE_MSG0(TCD, "Set DP PULLDOWN"); + max3353_private.flags |= (MAX3353E_DP_PULLDOWN); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); + break; + + case PULSE: + TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); + break; + } + int temp = 0; + temp = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + TRACE_MSG1(TCD, "Control Register 1: %02x", temp); + +} + +/*! max3353_dm_pullup_func - used to enable or disable peripheral connecting to bus + * + */ +void max3353_dm_pullup_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + TRACE_MSG0(TCD, "Clr DM PULLDOWN"); + max3353_private.flags &= (~MAX3353E_DM_PULLDOWN); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); + TRACE_MSG0(TCD, "Set DM PULLUP"); + max3353_private.flags |= (MAX3353E_DM_PULLUP); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); + break; + + case RESET: + max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + TRACE_MSG0(TCD, "Clr DM PULLUP"); + max3353_private.flags &= (~MAX3353E_DM_PULLUP); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); + TRACE_MSG0(TCD, "Set DM PULLDOWN"); + max3353_private.flags |= (MAX3353E_DM_PULLDOWN); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); + break; + + case PULSE: + TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); + break; + } + int temp = 0; + temp = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + TRACE_MSG1(TCD, "Control Register 1 %02x", temp); +} + +/*! max3353_dm_det_func - used to enable or disable D- detect + * + */ +void max3353_dm_det_func(struct otg_instance *otg, u8 flag) +{ + return; +} + +/*! max3353_dp_det_func - used to enable or disable D+ detect + * + */ +void max3353_dp_det_func(struct otg_instance *otg, u8 flag) +{ + return; +} + +/*! max3353_bdis_acon_func - used to enable or disable auto a-connect + * + */ +void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag) +{ + return; +} + +/*! max3353_id_pulldown_func - used to enable or disable ID pulldown + * + */ +void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag) +{ + return; +} + +/*! max3353_audio_func - used to enable or disable Carkit Interrupt + * + */ +void max3353_audio_func(struct otg_instance *otg, u8 flag) +{ + return; +} + +/*! max3353_uart_func - used to enable or disable transparent uart mode + * + */ +void max3353_uart_func(struct otg_instance *otg, u8 flag) +{ + return; +} + +/*! max3353_mono_func - used to enable or disable mono audio connection + * + */ +void max3353_mono_func(struct otg_instance *otg, u8 flag) +{ + return; +} + + +/* ********************************************************************************************* */ +extern int max3353_procfs_init (void); +extern void max3353_procfs_exit (void); + +/*! max3353_mod_init + */ +int max3353_mod_init(void) +{ + int ret; + + TRACE_MSG0(TCD, "Initilize"); + /* for interrupt bottom half handler + */ + PREPARE_WORK_ITEM(max3353_private.bh, &max3353_bh, NULL); + + /* setup max3353, enable interrupts, clear latch + * XXX when and how do we do DM_HI and DP_HI, when ID is present? + */ + max3353_int_src_set(0x1f); + //max3353_int_src(MAX3353_NO_SE1_INT); + /*Turn on the max3353 and discharge the bus*/ + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x0); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, 0x0); + int status = i2c_readb(MAX3353E_STATUS_REGISTER); + if ((status & 0x1) == 0){ + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x10); + } + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x0); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, MAX3353E_DM_PULLDOWN | MAX3353E_DP_PULLDOWN); + +#ifdef CONFIG_OTG_MAX3353_PROCFS + TRACE_MSG0(TCD, "MAX3353 Procfs"); + max3353_procfs_init (); +#endif /* CONFIG_OTG_MAX3353_PROCFS */ + + au_sync(); + + // au_writel(0x1, IC1_TESTBIT); //disable test bit for low leve interrupt + //All this done when define type of interrupt in irqmap.c + /*au_writel(0x40000000, IC1_CFG0CLR); //low level activation + au_writel(0x40000000, IC1_CFG1SET); + au_writel(0x40000000, IC1_CFG2CLR); + au_writel(0x40000000, IC1_MASKSET);//enable interrupt + */ + //au_writel(0x40000000, IC1_SRCCLR);//set test bit as a source + au_writel(0x40000000, IC1_SRCSET);//set source to original + //au_writel(0x0, IC1_TESTBIT); + //au_writel(0x1, IC1_TESTBIT); + +#if 0 + ret = au_readl(IC0_CFG0RD); + printk (KERN_INFO"IC0 Reg 0 : %08x\n", ret); + ret = au_readl(IC0_CFG1RD); + printk (KERN_INFO"IC0 Reg 1 : %08x\n", ret); + ret = au_readl(IC0_CFG2RD); + printk (KERN_INFO"IC0 Reg 2 : %08x\n", ret); + ret = au_readl(IC0_SRCRD); + printk (KERN_INFO"IC0 Source : %08x\n", ret); + ret = au_readl(IC0_ASSIGNRD); + printk (KERN_INFO"IC0 Assign : %08x\n", ret); + ret = au_readl(IC0_WAKERD); + printk (KERN_INFO"IC0 Wakeup : %08x\n", ret); + ret = au_readl(IC0_MASKRD); + printk (KERN_INFO"IC0 Mask : %08x\n", ret); + + ret = au_readl(IC1_CFG0RD); + printk (KERN_INFO"IC1 Reg 0 : %08x\n", ret); + ret = au_readl(IC1_CFG1RD); + printk (KERN_INFO"IC1 Reg 1 : %08x\n", ret); + ret = au_readl(IC1_CFG2RD); + printk (KERN_INFO"IC1 Reg 2 : %08x\n", ret); + ret = au_readl(IC1_SRCRD); + printk (KERN_INFO"IC1 Source : %08x\n", ret); + ret = au_readl(IC1_ASSIGNRD); + printk (KERN_INFO"IC1 Assign : %08x\n", ret); + ret = au_readl(IC1_WAKERD); + printk (KERN_INFO"IC1 Wakeup : %08x\n", ret); + ret = au_readl(IC1_MASKRD); + printk (KERN_INFO"IC1 Mask : %08x\n", ret); + +#endif + + return 0; +} + +/*! max3353_mod_exit + */ +void max3353_mod_exit(void) +{ + i2c_writeb(MAX3353E_INTERRUPT_MASK, 0x0); + //i2c_close(); +#ifdef CONFIG_OTG_MAX3353_PROCFS + max3353_procfs_exit (); +#endif /* CONFIG_OTG_MAX3353_PROCFS */ + + while (PENDING_WORK_ITEM(max3353_private.bh)) { + printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } + +} + +/* ********************************************************************************************* */ +/*! max3353_configure - configure the MAX3353 for this host + * @param tx_mode - the type of connection between the host USB and the MAX3353 + * @param spd_ctrl - suspend control method + */ +void max3353_configure(tx_mode_t tx_mode, spd_ctrl_t spd_ctrl) +{ + u16 vendor = 0, product = 0, revision = 0; + u8 mode1, mode2, control; + + struct otg_transceiver_map *map = max3353_transceiver_map; + TRACE_MSG0(TCD, "Configure"); + + //#if defined(CONFIG_OTG_MAX3353_MX2ADS) || defined(CONFIG_OTG_MAX3353_MX2ADS_MODULE) + u32 vp; + u8 vb=0; + TRACE_MSG0(TCD, "1. Read Transceiver ID's with long read"); + vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_0); + max3353_private.vendor = vb; + vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_1); + max3353_private.vendor += vb<<8; + vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_2); + max3353_private.vendor += vb<<16; + vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_3); + max3353_private.vendor += vb<<24; + + vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_0); + max3353_private.product = vb; + vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_1); + max3353_private.product += vb<<8; + vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_2); + max3353_private.product += vb<<16; + vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_3); + max3353_private.product += vb<<24; + + mode1 = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + mode2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); + + TRACE_MSG4(TCD, "2. OTG Transceiver: vendor: %08x product: %08x mode1: %02x mode2: %02x", + max3353_private.vendor, max3353_private.product, mode1, mode2); + + TRACE_MSG0(TCD, "3. Disable All Transceiver Control Register 1 "); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, 0x0); // clear + i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x0); // clear + + TRACE_MSG0(TCD, "4. Enable D-/D+ Pulldowns in Transceiver Control Register 1 "); + + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, MAX3353E_DM_PULLDOWN | MAX3353E_DP_PULLDOWN); + max3353_private.flags = MAX3353E_DM_PULLDOWN | MAX3353E_DP_PULLDOWN; + + TRACE_MSG0(TCD, "5. Clear latch and enable interrupts"); + i2c_writeb(MAX3353E_INTERRUPT_LATCH, 0x0); + max3353_int_src_set(0x1f); + + + +} + + diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353e.h linux/drivers/otg/ocd/max3353e/max3353e.h --- linux/drivers/no-otg/ocd/max3353e/max3353e.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/max3353e.h 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,134 @@ +/* + * otg/ocd/max3353/max3353.h -- USB Transceiver Controller driver + * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353e.h|20060831021118|62926 + * + * Copyright (c) 2004-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @defgroup MAX3353TCD TCD - Maxim MAX3353 + * @ingroup platformgroup + */ +/*! + * @file otg/ocd/max3353e/max3353e.h + * @brief Private structures and defines for MAX3353 Transciever Controller Driver. + * + * This file contains the private defines and structures for the MAX3353 Transceiver + * Driver. + * + * @ingroup MAX3353TCD + */ + + +#ifdef CONFIG_XXXXX +#define MAX3353_GPIO (2) +#define TCD_MAX3353_I2C_ADDR MAX3353_I2C_ADDR_HIGH; +#endif + +#define MAX3353_NAME "max3353" + +#define MAX3353_LOC_CONN 0x01 + +#define MAX3353_INT_SRC 0xeb + +//#define MAX3353_ALL_INT_SRC 0xff +//#define MAX3353_NO_SE1_INT 0xeb +//#define MAX3353_NO_SE1_INT 0xfb /* do not pay attention to DP_HI */ + +typedef enum otg_transceiver { + unknown_transceiver, + max3353, + max3301e +} otg_transceiver_t; + +struct otg_transceiver_map { + otg_transceiver_t transceiver_type; + u16 vendor; + u16 product; + u16 revision; + char *name; +}; + +struct max3353_private { + WORK_STRUCT bh; + u16 vendor; + u16 product; + u16 revision; + u32 flags; + u8 int_src; + struct otg_transceiver_map *transceiver_map; +}; + +/*! tx_mode + * This defines the various ways that the MAX3353 can be + * wired into the host USB. + */ +typedef enum tx_mode { + dat_se0_bidirectional, /*!< 3-Wire */ + vp_vm_bidirectional, /*!< 4-Wire */ + dat_se0_unidirectional /*!< 6-Wire */ +} tx_mode_t; + +/*! spd_ctrl + * This defines how the speed and suspend pins are controlled + * for this host. + */ +typedef enum spd_ctrl { + spd_susp_pins, /*!< controlled by SPEED and SUSPEND pins */ + spd_susp_reg, /*!< controled by SPEED_REG and SUSPEND_REG in Mode Control 1 register */ +} spd_ctrl_t; + + +extern struct tcd_ops tcd_ops; +extern struct max3353_private max3353_private; +extern struct tcd_instance *tcd_instance; + +/* max3353.c */ +extern int max3353_mod_init(void); +extern void max3353_mod_exit(void); +extern void max3353_chrg_vbus(struct otg_instance *, u8 ); +extern void max3353_drv_vbus(struct otg_instance *, u8 ); +extern void max3353_dischrg_vbus(struct otg_instance *, u8 ); +extern void max3353_dp_pullup_func(struct otg_instance *, u8 ); +extern void max3353_dm_pullup_func(struct otg_instance *, u8 ); +extern void max3353_dm_det_func(struct otg_instance *, u8 ); +extern void max3353_dp_det_func(struct otg_instance *, u8 ); +extern void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag); +extern void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag); +extern void max3353_audio_func(struct otg_instance *otg, u8 flag); +extern void max3353_uart_func(struct otg_instance *otg, u8 flag); +extern void max3353_mono_func(struct otg_instance *otg, u8 flag); + +extern void max3353_otg_timer(struct otg_instance *, u8 ); +extern void max3353_bh(void *); +extern int max3353_id (struct otg_instance *); +extern int max3353_vbus (struct otg_instance *); +extern void max3353_configure(tx_mode_t, spd_ctrl_t ); + +/* thread */ +extern int max3353_thread_init (void (bh_proc)(void *)); +extern void max3353_thread_exit (wait_queue_head_t *); +extern void max3353_thread_wakeup(int enabled, int first); + + + +/* i2c-xxx.c */ +int i2c_configure(char *name, int addr); +extern void i2c_close(void); +extern u8 i2c_readb(u8 ); +extern u16 i2c_readw(u8 ); +extern u32 i2c_readl(u8 ); +extern int i2c_writeb(u8 , u8 ); + + +/* ********************************************************************************************* */ +#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) +#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) +#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) +#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) + diff -uNr linux/drivers/no-otg/ocd/max3353e/tcd-db1550.c linux/drivers/otg/ocd/max3353e/tcd-db1550.c --- linux/drivers/no-otg/ocd/max3353e/tcd-db1550.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/tcd-db1550.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,227 @@ +/* + * otg/ocd/max3353e/tcd-db1550.c -- USB Transceiver Controller driver + * @(#) balden@belcarra.com|otg/ocd/max3353e/tcd-db1550.c|20060831021118|37213 + * + * Copyright (c) 2004-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Shahrad Payandeh , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/max3353e/tcd-db1550.c + * @brief DB1550 MAX3353 driver. + * + * + * @ingroup MAX3353TCD + */ + +#include +#include +#include +#include "otghw/bvd-hardware.h" +#include "max3353e.h" +#include +#include +#include + +struct ocd_instance *ocd_instance; +otg_tag_t OCD; + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "isp1301.h" + +#endif +/*SP +#include +#include +*/ +//struct isp1301_private isp1301_private; + +#ifdef CONFIG_OMAP_H2 +#define ADAPTOR_NAME "OMAP I2C" +#endif /* CONFIG_OMAP_H2 */ +#ifdef CONFIG_ARCH_MAINSTONE +#define ADAPTOR_NAME "PXA-I2C-Adapter" +#endif /* CONFIG_ARCH_MAINSTONE */ +#ifdef CONFIG_OTG_DB1550_J15 +#define ADAPTOR_NAME "pb1550 adapter" +#endif /* CONFIG_OTG_DB1550_J15 */ + +/* ********************************************************************************************* */ + + +static irqreturn_t max3353_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + TRACE_MSG0(TCD, "--"); + + SCHEDULE_WORK(max3353_private.bh); + + return IRQ_HANDLED; +} + + +/* ********************************************************************************************* */ + +struct ocd_ops ocd_ops; +struct tcd_instance *db1550_tcd_instance; + +//#define ADAPTOR_NAME "pb1550 adapter" + +/*! tcd_mod_init + */ +int tcd_mod_init(void) +{ + + struct otg_instance *otg; + + int i2c = i2c_configure(ADAPTOR_NAME, MAX3353E_I2C_ADDR_HIGH); + int irq = request_irq (AU1500_GPIO_207, max3353_int_hndlr, SA_INTERRUPT, "MAX3353", NULL); + THROW_IF(irq, error); + + TRACE_MSG0(TCD, "--"); + + THROW_IF(i2c, error); + + TRACE_MSG0(TCD, "1. Enable db1550 external OTG Transceiver!"); + + + TRACE_MSG0(TCD, "2. configure i2c driver!"); + max3353_configure(dat_se0_bidirectional, spd_susp_reg); + + TRACE_MSG0(TCD, "3. configure max3353!"); + max3353_mod_init(); + + TRACE_MSG0(TCD, "4. set ops"); + THROW_UNLESS(ocd_instance = otg_set_ocd_ops(&ocd_ops), error); + THROW_UNLESS(ocd_instance && (otg = ocd_instance->otg), error); + THROW_UNLESS(db1550_tcd_instance = otg_set_tcd_ops(&tcd_ops), error); + +#if 0 //test for making pulse on DP + for (;;){ + max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); + TRACE_MSG0(TCD, "Clr DP PULLDOWN"); + max3353_private.flags &= (~MAX3353E_DP_PULLDOWN); + TRACE_MSG0(TCD, "Set DP PULLUP"); + max3353_private.flags |= (MAX3353E_DP_PULLUP); + i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); + } +#endif + + + /* Success! + */ + + TRACE_MSG0(TCD, "5. Success!"); + + otg_init(otg); + + //In device mode only this part makes a virtual interrupt, while the cable is + //and modules are loaded. + au_writel (0x40000000, IC1_SRCCLR); + au_writel (0x0, IC1_TESTBIT); + au_writel (0x1, IC1_TESTBIT); + au_writel (0x0, IC1_TESTBIT); + au_writel (0x40000000, IC1_SRCSET); + + + /* error handling + */ + CATCH(error) { + printk(KERN_INFO"Error in tcd initilization\n"); + if (irq) free_irq(AU1500_GPIO_207, NULL); + if (i2c) i2c_close(); + return -EINVAL; + } + return 0; +} + +/*! tcd_mod_exit + */ +void tcd_mod_exit(void) +{ + struct otg_instance *otg = tcd_instance->otg; + TRACE_MSG0(TCD, "Exit."); + + otg_event(otg, enable_otg_, OCD, "enable_otg_"); + //if (otg) + // otg_exit(otg); + + db1550_tcd_instance = otg_set_tcd_ops(NULL); + free_irq(AU1500_GPIO_207, NULL); + max3353_mod_exit(); + + i2c_writeb(MAX3353E_INTERRUPT_MASK, 0xff); + i2c_close(); + + if (otg) + otg_exit(otg); +} +/* ********************************************************************************************* */ +struct tcd_ops tcd_ops = { + +// vbus: max3353_vbus, + + mod_init: tcd_mod_init, + mod_exit: tcd_mod_exit, + + dischrg_vbus_func: max3353_dischrg_vbus, + chrg_vbus_func: max3353_chrg_vbus, + drv_vbus_func: max3353_drv_vbus, + dp_pullup_func: max3353_dp_pullup_func, + dm_pullup_func: max3353_dm_pullup_func, + +}; + +#if 0 + +struct tcd_ops tcd_ops; + + +void db1550_tcd_global_init(void) +{ + ZERO(tcd_ops); + + tcd_ops.id = max3353_id; + tcd_ops.vbus = max3353_vbus; + tcd_ops.chrg_vbus_func = max3353_chrg_vbus; + tcd_ops.dischrg_vbus_func = max3353_dischrg_vbus; + tcd_ops.dp_pullup_func = max3353_dp_pullup_func; + tcd_ops.dm_pullup_func = max3353_dm_pullup_func; + tcd_ops.dp_pulldown_func = max3353_dp_pulldown_func; + tcd_ops.dm_pulldown_func = max3353_dm_pulldown_func; + tcd_ops.overcurrent_func = NULL; + tcd_ops.dm_det_func = max3353_dm_det_func; + tcd_ops.dp_det_func = max3353_dp_det_func; + tcd_ops.cr_det_func = max3353_cr_det_func; + + tcd_ops.peripheral_host_func = max3353_peripheral_host_func; + + tcd_ops.id_pulldown_func = max3353_id_pulldown_func; + tcd_ops.audio_func = max3353_audio_func; + tcd_ops.uart_func = max3353_uart_func; + tcd_ops.mono_func = max3353_mono_func; + + tcd_ops.mod_init = tcd_mod_init; + tcd_ops.mod_exit = tcd_mod_exit; +} + + +#endif + + + + diff -uNr linux/drivers/no-otg/ocd/max3353e/tcd-mx2ads.c linux/drivers/otg/ocd/max3353e/tcd-mx2ads.c --- linux/drivers/no-otg/ocd/max3353e/tcd-mx2ads.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/max3353e/tcd-mx2ads.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,541 @@ +/* + * otg/ocd/max3353e/tcd-mx2ads.c -- MX21ADS ISP1301 Transceiver Controller driver + * @(#) balden@belcarra.com|otg/ocd/max3353e/tcd-mx2ads.c|20060831021118|46922 + * + * Copyright (c) 2004-2005 Belcarra + * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp + * + * By: + * Stuart Lynne , + * Shahrad Payandeh , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/max3353e/tcd-mx2ads.c + + * @brief MX2ADS ISP1301 driver. + * + * This implements the ISP1301 Transceiver Controller Driver for the mx21 + * using the USBOTG I2C controller for access. + * + * Notes + * + * 1. There are two interrupt sources, the I2C IO completion type generated when either of NOACK + * or RWREADY is set and the XCVR activity type generated when there is a OTG transceiver + * generated interrupt. + * + * It is important to only enable a single source at a time. By default we leave the XCVR + * interrupt enabled for normal operation and only enable the I2C IO type when we are actively + * waiting for an I2C IO operation to complete. + * + * 2. The USBOTG module can also interact with the OTG Transceiver, attempting to do register + * reads for various conditions (such as XCVR interrupts.) These interfere and cause problems so + * we keep the the I2C Controller interface in software mode at all times. + * + * 3. Because we must wait several hundred micro-seconds for I/O operations to complete, the i2c + * implementation is only usable from non-interrupt contexts. This means that all work that + * needs to be done needs to be pushed to a bottom half handler. A generic i2cWrite_irq() is + * provided where simple writes are all that are required. + * + * 4. The handling of the ISC_INTERRUPT_AND_CTRL register is complicated because the top nibble + * is r/w controlling the interrupt source enables while the bottom nibble is write 1 to clear + * to reset two of the three interrupt source bits (NOACK and RWREADY). + * + * + * TODO + * + * 1. The MX2ADS seems to have a hardware problem, when operating as a B-Device, Vbus does not + * drop to zero when disconnected (unplugged.) A 22kohm resister across C133 is suggested. + * Alternately I am just enabling VBUS_DISCHRG in LOC_CONN when we enable DP_PULLUP. Hopefully + * this will not be required in production code. + * + * @ingroup ISP1301TCD + */ + +#include +#include +#include +#include +#include "mx2.h" +#include +#include "isp1301.h" +//#include + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ + +/*! mx2_urb + */ +u8 mx2_urbv(u32 port) +{ + u8 val = mx2_rb(port); + TRACE_MSG2(TCD, "PORT: %08x %02x", port, val); + return val; +} + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/* I2C Bottom Half I/O + * + * These are the I2C I/O routines that can be used from any non-interrupt + * context, typically from a bottom half handler. + * + * There is an interrupt safe, delayed i2cWrite function, it queues the + * writes and schedules work via a bottom half handler that can safely call + * the i2cWrite function. + */ +#define MX2_I2C_TIMEOUT 1 +#define I2C_READ_MASK 0x80 +#define I2C_MAX_WRITE 100 + +DECLARE_MUTEX(i2c_sem); // semaphore to prevent overlapped i2c i/o +wait_queue_head_t mx2_i2c_wait; // wait structure for i2c i/o interrupt +struct WORK_STRUCT mx2_i2c_io_work_struct; // work structure for queued i2c writes + +int i2c_Write_Queued; +u8 i2c_Write_Data[I2C_MAX_WRITE]; +u32 i2c_Write_Addr[I2C_MAX_WRITE]; +char * i2c_Write_Msg[I2C_MAX_WRITE]; + +/*! i2c_read - read from otg transceiver + * This programs the I2C controller to do a read, waits for the completion + * interrupt, cleans up and returns the value for the specified register. + */ +void i2c_read(u32 port, int ops) +{ + u8 i2c_interrupt_and_ctrl; + u8 ctrl_reg; + int count; + RETURN_IF(i2c_Write_Queued < 0); + down(&i2c_sem); // pass through semaphore +#if 1 + if ((ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x4) { + TRACE_MSG1(TCD, "bad ctrl_reg: %02x", ctrl_reg); + } + for (count = 0; (count < 100) && (ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x80; count++) { + TRACE_MSG1(TCD, "busy ctrl_reg: %02x", ctrl_reg); + } +#endif + mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, 0x6); // reset and disable interrupts + mx2_wb(MX2_SEQ_OP_REG, ops); // set number of sequential operations to 1 + mx2_wb(MX2_SEQ_RD_STARTAD, port); // sequential read start address + mx2_wb(MX2_OTG_XCVR_DEVAD, I2C_READ_MASK | MX2_I2C_DEV_ADDR); // receiver address + mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_NOACK_EN | I2C_RWREADY_EN); // enable i2x I/O interrupt + interruptible_sleep_on_timeout(&mx2_i2c_wait, MX2_I2C_TIMEOUT); // wait for signal from i2c io interrupt handler + up(&i2c_sem); +} + +/*! i2c_readb - read a byte from otg transceiver + * This programs the I2C controller to do a single byte read, waits for the + * completion interrupt, cleans up and returns the value for the specified + * register. + */ +u8 i2c_readb(u8 port) +{ + u8 data; + u32 addr = OTG_I2C_BASE + port; + i2c_read(addr, 1); + data = mx2_rb(addr); // read the register + //TRACE_MSG2(TCD, "port: %08x data: %02x", port, data); + return data; +} + +/*! i2c_readw - read two bytes from otg transceiver + * This programs the I2C controller to do a two byte read, waits for the + * completion interrupt, cleans up and returns the value for the specified + * registers. + */ +u16 i2c_readw(u8 port) +{ + u16 data; + u32 addr = OTG_I2C_BASE + port; + i2c_read(addr, 2); + data = mx2_rb(addr) | (mx2_rb(addr + 1) << 8); // read the register + //TRACE_MSG2(TCD, "port: %08x data: %04x", port, data); + return data; +} + +/*! i2c_readl - read two bytes from otg transceiver + * This programs the I2C controller to do a two byte read, waits for the + * completion interrupt, cleans up and returns the value for the specified + * registers. + */ +u32 i2c_readl(u8 port) +{ + u32 data; + u32 addr = OTG_I2C_BASE + port; + i2c_read(addr, 2); + data = mx2_rb(addr) | (mx2_rb(addr + 1) << 8) | (mx2_rb(addr + 2) << 16) | (mx2_rb(addr + 3) << 24); + //TRACE_MSG2(TCD, "port: %08x data: %04x", port, data); + return data; +} + +/*! i2cWrite - write a byte to otg transceiver + * This writes the data to the shadow register, programs the I2C controller + * to do a single byte write, waits for the completion interrupt, cleans up + * and exits. + */ +void i2cWrite(u32 port, u8 data, char *msg) +{ + u8 i2c_interrupt_and_ctrl; + u8 test; + u8 ctrl_reg; + int count; + //TRACE_FMSG3(TCD, msg, "i2cWrite: port: %s (%08x) data: %02x", mx2_ureg_name(port), port, data); + TRACE_FMSG2(TCD, msg, "i2cWrite: (%08x) data: %02x", port, data); + + //test = i2c_readb(port & 0xfffffffe); + //TRACE_MSG2(TCD, "i2cWrite: %02x before %02x", port, test); + + down(&i2c_sem); // pass through semaphore + +#if 1 + if ((ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x4) { + TRACE_MSG1(TCD, "bad ctrl_reg: %02x", ctrl_reg); + } + for (count = 0; (count < 10) && (ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x80; count++) { + TRACE_MSG1(TCD, "busy ctrl_reg: %02x", ctrl_reg); + } +#endif + mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, 0x6); // reset and disable interrupts + mx2_wb(port, data); // write the register + mx2_wb(MX2_SEQ_OP_REG, 1); // set number of sequential operations to 1 + mx2_wb(MX2_SEQ_RD_STARTAD, port); // sequential read start address + mx2_wb(MX2_OTG_XCVR_DEVAD, MX2_I2C_DEV_ADDR); // receiver address + mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_NOACK_EN | I2C_RWREADY_EN); // enable i2x I/O interrupt + interruptible_sleep_on_timeout(&mx2_i2c_wait, MX2_I2C_TIMEOUT); // wait for signal from i2c io interrupt handler + up(&i2c_sem); + + //test = i2c_readb(port & 0xfffffffe); + //TRACE_MSG2(TCD, "i2cWrite: %02x after %02x", port, test); +} + +/*! mx2_i2c_io_bh - bottom half handler to use i2cWrite on queued data + */ +void mx2_i2c_io_bh(void *arg) +{ + while (i2c_Write_Queued > 0) { + u8 write = i2c_Write_Data[0]; + u32 port = i2c_Write_Addr[0]; + char *msg = i2c_Write_Msg[0]; + u8 data; + unsigned long flags; // atomic update of counter and saved values + local_irq_save (flags); + i2c_Write_Queued--; + memcpy(i2c_Write_Data, i2c_Write_Data + 1, sizeof(i2c_Write_Data) - 1); + memcpy(i2c_Write_Addr, i2c_Write_Addr + 1, sizeof(i2c_Write_Addr) - 4); + memcpy(i2c_Write_Msg, i2c_Write_Msg + 1, sizeof(i2c_Write_Msg) - 4); + local_irq_restore (flags); + i2cWrite(port, write, msg); // perform write + } +} + +/*! i2cWrite_irq - queue data for i2cWrite bottom half handler + */ +void i2cWrite_irq(u32 port, u8 data, char *msg) +{ + RETURN_IF(i2c_Write_Queued < 0); + if ((i2c_Write_Queued == I2C_MAX_WRITE)) { + TRACE_MSG3(TCD, "TOO MANY QUEUED CANNOT WRITE port: %08x data: %02x active: %d", port, data, i2c_Write_Queued); + return; + } + i2c_Write_Addr[i2c_Write_Queued] = port; + i2c_Write_Data[i2c_Write_Queued] = data; + i2c_Write_Msg[i2c_Write_Queued] = msg; + i2c_Write_Queued++; + TRACE_FMSG3(TCD, msg, "i2cWrite_irq: port: %08x data: %02x active: %d", port, data, i2c_Write_Queued); + SCHEDULE_WORK(mx2_i2c_io_work_struct); +} + +/*! i2c_writeb + */ +int i2c_writeb(u8 subaddr, u8 buf) +{ + i2cWrite_irq(OTG_I2C_BASE + subaddr, buf, "i2c_writeb"); + return 0; +} + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! mx2ads_bh + */ +void mx2ads_bh(void *arg) +{ + isp1301_bh(NULL); + //mx2_bh(NULL); + + /* re-enable the OTG XCVRINT + */ + mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_OTGXCVRINT_EN); +} + + + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/* I2C I/O and XCVR Interrupt Handler + */ + +/*! mx2_i2c_int_hndlr + * This is called from the main mx21ads interrupt handler to handle the + * i2c i/o interrupt. There are two main interrupt sources: + * + */ +irqreturn_t mx2_i2c_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + //TRACE_MSG0(TCD, "MX2ADS I2C INTERRUPT: WAKEUP"); + wake_up_interruptible(&mx2_i2c_wait); // wakeup waiting bottom half handler + return IRQ_HANDLED; +} + +/*! mx2_tcd_int_hndlr + * This is called from the main mx21ads interrupt handler to handle the + * i2c i/o interrupt. There are two main interrupt sources: + */ +irqreturn_t mx2_tcd_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + //TRACE_MSG0(TCD, "MX2ADS TCD INTERRUPT: SCHEDULE WORK"); + isp1301_thread_wakeup(1, 0); // wakeup kernel thread + return IRQ_HANDLED; +} + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ + +/*! mx2_tcd_init - used to initialize/enable or disable the tcd driver + */ +void mx2_tcd_init(struct otg_instance *otg, u8 flag) +{ + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "MX2_TCD_EN SET"); + otg_event(otg, TCD_OK | ID_FLOAT, TCD, "TCD_OK"); + //mx2_i2c_xcvr_schedule(1); + isp1301_thread_wakeup(1, 1); + break; + case RESET: + TRACE_MSG0(TCD, "MX2_TCD_EN RESET"); + isp1301_thread_wakeup(0, 0); + otg_event(otg, TCD_OK, TCD, "TCD_OK"); + break; + } +} + +/*! mx2_dp_pullup_func - used to enable or disable peripheral connecting to bus + */ +void mx2_dp_pullup_func(struct otg_instance *otg, u8 flag) +{ + struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "MX2_LOC_CONN SET"); + + TRACE_MSG0(TCD, "MX2_LOC_CONN SET - enable function interrupts"); + mx2_wl(OTG_FUNC_SINT_STEN, /*SYSTEM_DONEREGINTDS_EN | *//*SYSTEM_SOFDETINT_EN |*/ SYSTEM_DONEREGINT_EN | + SYSTEM_SUSPDETINT_EN | SYSTEM_RSMFININT_EN | SYSTEM_RESETINT_EN); + + TRACE_MSG0(TCD, "MX2_LOC_CONN SET - Set VBUS_DISCHRG"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, (u8) ISP1301_VBUS_DISCHRG); + break; + } + + /* call generic dp pullup function + */ + isp1301_dp_pullup_func(otg, flag); + + switch (flag) { + case RESET: + TRACE_MSG0(TCD, "MX2_LOC_CONN SET - Clr VBUS_DISCHRG"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, (u8) ISP1301_VBUS_DISCHRG); + break; + } + +} + +/*! mx2_drv_vbus - used to enable or disable A-device driving Vbus + */ +void mx2_drv_vbus(struct otg_instance *otg, u8 flag) +{ + TRACE_MSG0(TCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(TCD, "DRV_VBUS_SET"); + i2c_writeb(ISP1301_OTG_CONTROL_CLR, (u8) ISP1301_VBUS_DRV); + break; + } + + /* call generic drv vbus function + */ + isp1301_drv_vbus(otg, flag); + + switch (flag) { + case RESET: + TRACE_MSG0(TCD, "DRV_VBUS_RESET"); + i2c_writeb(ISP1301_OTG_CONTROL_SET, (u8) ISP1301_VBUS_DRV); + break; + } +} + + + +/*! mx2_id - return true if id is present + */ +int mx2_id(struct otg_instance *otg) +{ + /* this will force the bottom half handler to read the interrupt + * source register and generate an otg event + */ + TRACE_MSG0(TCD, "ID: 0"); + //mx2_i2c_xcvr_schedule(1); + isp1301_thread_wakeup(1, 1); + return 0; +} + +/*! mx2_vbus - return true if Vbus is present + */ +int mx2_vbus(struct otg_instance *otg) +{ + /* this will force the bottom half handler to read the interrupt + * source register and generate an otg event + */ + TRACE_MSG0(TCD, "VBUS: 0"); + //mx2_i2c_xcvr_schedule(1); + isp1301_thread_wakeup(1, 1); + return 0; +} + +/* ********************************************************************************************* */ +struct tcd_ops *otg_tcd_ops; +struct tcd_instance *mx2ads_tcd_instance; + +/*! mx2_tcd_mod_init - initial tcd setup + * Allocate interrupts and setup hardware. + * This is called from mx2-ocd.c + */ +int mx2_tcd_mod_init (void) +{ + struct otg_instance *otg; + + printk(KERN_INFO"%s\n", __FUNCTION__); + + TRACE_MSG0(TCD, "0. Basic Setup"); + + + init_waitqueue_head(&mx2_i2c_wait); + PREPARE_WORK_ITEM(mx2_i2c_io_work_struct, &mx2_i2c_io_bh, NULL); + //PREPARE_WORK_ITEM(mx2_i2c_xcvr_work_struct, &mx2_i2c_xcvr_bh, NULL); + + isp1301_thread_init(mx2ads_bh); + + + TRACE_MSG1(TCD, "i2c_int_hndlr: %x", otg_tcd_ops); + TRACE_MSG2(TCD, "i2c_int_hndlr: %x %x", &tcd_ops, tcd_ops.i2c_int_hndlr); + TRACE_MSG2(TCD, "tcd_int_hndlr: %x %x", &tcd_ops, tcd_ops.tcd_int_hndlr); + + TRACE_MSG0(TCD, "4. Enable Interrupts"); + + mx2_wb(MX2_I2C_OP_CTRL_REG, 0x04); // Software mode, output enabled + + mx2_urbv(MX2_OTG_XCVR_DEVAD); + mx2_urbv(MX2_SEQ_OP_REG); + mx2_urbv(MX2_SEQ_RD_STARTAD); + mx2_urbv(MX2_I2C_OP_CTRL_REG); + mx2_urbv(MX2_SCLK_TO_SCL_HPER); + mx2_urbv(MX2_I2C_INTERRUPT_AND_CTRL); + + mx2_wb(MX2_I2C_OP_CTRL_REG, 0x01); // Software mode, output enabled + + + + mx2_wb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_OTGXCVRINT_EN | I2C_NOACK_EN | I2C_RWREADY_EN); + + isp1301_configure(vp_vm_bidirectional, spd_susp_pins); + isp1301_mod_init(); + + THROW_UNLESS(tcd_instance && (otg = tcd_instance->otg), error); + THROW_UNLESS(mx2ads_tcd_instance = otg_set_tcd_ops(&tcd_ops), error); + + /* Success! + */ + + TRACE_MSG0(TCD, "5. Success!"); + + otg_init(otg); + //otg_start(otg, ocd_ops.capabilities & OCD_CAPABILITIES_AUTO); + + /* + if ((ocd_ops.capabilities & OCD_CAPABILITIES_TR) && (ocd_ops.capabilities & OCD_CAPABILITIES_AUTO)) { + otg_event_irq(otg, enable_otg, TCD, "enable_otg"); + otg_event_irq(otg, bus_req, TCD, "bus_req"); + } + */ + + CATCH(error) { + printk(KERN_INFO"%s: failed\n", __FUNCTION__); + return -EINVAL; + } + + TRACE_MSG0(TCD, "MX2_MOD_TCD_INIT FINISHED"); + return 0; +} + +/*! mx2_tcd_mod_exit - de-initialize + * This is called from mx2-ocd.c + */ +void mx2_tcd_mod_exit (void) +{ + struct otg_instance *otg = tcd_instance->otg; + TRACE_MSG0(TCD, "MX2_MOD_TCD_EXIT"); + + otg_event(otg, enable_otg_, OCD, "enable_otg_"); + //if (otg) + // otg_exit(otg); + + mx2ads_tcd_instance = otg_set_tcd_ops(NULL); + + i2c_Write_Queued = -1; + wake_up_interruptible(&mx2_i2c_wait); // wakeup waiting bottom half handler + + while (PENDING_WORK_ITEM(mx2_i2c_io_work_struct) /*|| PENDING_WORK_ITEM(mx2_i2c_xcvr_work_struct)*/ ) { + printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); + schedule_timeout(10 * HZ); + } + isp1301_thread_exit(&mx2_i2c_wait); + isp1301_mod_exit(); + + if (otg) + otg_exit(otg); +} + +/* ********************************************************************************************* */ +struct tcd_ops tcd_ops = { + id: mx2_id, + vbus: mx2_vbus, + + tcd_init_func: mx2_tcd_init, + tcd_en_func: NULL, + chrg_vbus_func: isp1301_chrg_vbus, + drv_vbus_func: isp1301_drv_vbus, + dischrg_vbus_func: isp1301_dischrg_vbus, + dp_pullup_func: mx2_dp_pullup_func, + dm_pullup_func: isp1301_dm_pullup_func, + + overcurrent_func: NULL, + dm_det_func: isp1301_dm_det_func, + dp_det_func: isp1301_dp_det_func, + charge_pump_func: NULL, + bdis_acon_func: isp1301_bdis_acon_func, + id_pulldown_func: isp1301_id_pulldown_func, + audio_func: isp1301_audio_func, + uart_func: isp1301_uart_func, + mono_func: isp1301_mono_func, + + i2c_int_hndlr: mx2_i2c_int_hndlr, + tcd_int_hndlr: mx2_tcd_int_hndlr, + mod_init: mx2_tcd_mod_init, + mod_exit: mx2_tcd_mod_exit, +}; + diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd-init-l24.c linux/drivers/otg/ocd/otg-hcd/hcd-init-l24.c --- linux/drivers/no-otg/ocd/otg-hcd/hcd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-hcd/hcd-init-l24.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,89 @@ +/* + * otg/ocd/otg-hcd/hcd-l24-init.c - OTG Host Controller Driver Module Initialization + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-hcd/hcd-init-l24.c + * @brief + * + * This file initializes the low level hardware drivers. + * + * This is the linux 2.4 version. + * + * @ingroup OTGHCD + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) +MODULE_LICENSE ("GPL"); +#endif +MODULE_PARM (serial_number_str, "s"); +MODULE_PARM_DESC (serial_number_str, "Serial Number"); +MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MODULE_DESCRIPTION ("USB On-The-Go HCD"); +#endif +char *serial_number_str; + +otg_tag_t HCD; +extern struct hcd_ops hcd_ops; +struct hcd_instance *hcd_instance; + +/* ************************************************************************************* */ + +/* hcd_modexit - This is *only* used for drivers compiled and used as a module. + */ +static void hcd_modexit (void) +{ + printk(KERN_INFO"%s\n", __FUNCTION__); + if (hcd_ops.mod_exit) hcd_ops.mod_exit(); + hcd_instance = otg_set_hcd_ops(NULL); + otg_trace_invalidate_tag(HCD); +} + +/* otg_modinit - linux module initialization + * + * This needs to initialize the hcd, pcd and tcd drivers. This includes tcd and possibly hcd + * for some architectures. + * + */ +static int hcd_modinit (void) +{ + printk(KERN_INFO"%s\n", __FUNCTION__); + HCD = otg_trace_obtain_tag(); + + THROW_UNLESS(hcd_instance = otg_set_hcd_ops(&hcd_ops), error); + THROW_IF((hcd_ops.mod_init) ? hcd_ops.mod_init() : 0, error); + + CATCH(error) { + hcd_modexit(); + return -EINVAL; + } + return 0; +} + +module_init (hcd_modinit); +#ifdef MODULE +module_exit (hcd_modexit); +#endif + + + diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd-l26.c linux/drivers/otg/ocd/otg-hcd/hcd-l26.c --- linux/drivers/no-otg/ocd/otg-hcd/hcd-l26.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-hcd/hcd-l26.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,806 @@ +/* + * otg/ocd/otg-hcd/hcd-l26.c - Generic transfer level USBOTG aware Host Controller Driver (HCD) + * + * Copyright (c) 2004 Belcarra Technologies + * + * By: + * Tom Rushworth , + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-hcd/hcd-l26.c + * @brief Linux Generic Host Controller Driver + * + * @ingroup OTGHCD + */ + +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include "../core/hcd.h" + +#include +#include +#include +#include + +#include +#include +#include + +int hcd_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return 0; +} + +void hcd_disconnect(struct usb_interface *intf) +{ +} + +#define STATIC static + +STATIC __inline__ struct urb *grab_active_urb(struct bus_hcpriv *bus_hcpriv, int u) +{ + unsigned long flags; + struct urb *urb; + // Paranoia. + RETURN_NULL_UNLESS(bus_hcpriv && bus_hcpriv->active_urbs && (u < bus_hcpriv->max_active_urbs)); + local_irq_save(flags); + urb = bus_hcpriv->active_urbs[u]; + bus_hcpriv->active_urbs[u] = NULL; + local_irq_restore(flags); + return urb; +} + +#define OUT(o,e)((o && e) ? 1 : 0) + +STATIC void hcd_queue_urb(struct dev_hcpriv *dev_hcpriv, struct urb *urb, int endpoint, int out) +{ + unsigned long flags; + // Paranoia. + RETURN_UNLESS(dev_hcpriv && urb && (endpoint < 16)); + local_irq_save(flags); + dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] += 1; + list_add_tail(&urb->urb_list, &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]); + TRACE_MSG4(HCD,"urb: %08x ep: %d n: %u %s", (u32)(void*)urb, + endpoint, dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint], "IN"); + local_irq_restore(flags); +} + +STATIC struct urb *hcd_next_urb(struct dev_hcpriv *dev_hcpriv, int endpoint, int out) +{ + // MUST BE CALLED in_irq()!! + struct urb *urb; + struct list_head *epq; + // Paranoia. + RETURN_NULL_UNLESS(dev_hcpriv && (endpoint < 16)); + epq = &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]; + RETURN_NULL_IF(list_empty(epq)); + urb = list_entry(epq->next, struct urb, urb_list); + list_del_init(&urb->urb_list); + dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] -= 1; + return urb; +} + +STATIC void hcd_finish_urb(struct urb *urb, int reason) +{ + /* Call the upper layer completion routine. Decrement the ref count + * (release our interest in this urb). + */ + urb->status = reason; + if (urb->complete) + urb->complete(urb, NULL); + usb_put_urb(urb); +} + +STATIC int hcd_submit_to_hw(struct bus_hcpriv *bus_hcpriv, struct urb *urb, int out) +{ + // Pass the urb down to the hardware. + int rc; + void *data; + int dev_addr; + int endpoint; + int format; + int len; + int pid; + int toggle; + u32 other; + unsigned long flags; + + struct usb_device *dev; + struct usb_bus *bus; + struct dev_hcpriv *dev_hcpriv; + u8 *cp; + + RETURN_EINVAL_UNLESS((dev = urb->dev)); + RETURN_EINVAL_UNLESS((bus = dev->bus)); + RETURN_EINVAL_UNLESS((dev_hcpriv = (struct dev_hcpriv *) (dev->hcpriv))); + + dev_addr = usb_pipedevice(urb->pipe); + endpoint = usb_pipeendpoint(urb->pipe); + + TRACE_MSG4(HCD,"urb: %08lx ep: %d %s len: %d", (u32)(void*)urb, endpoint, (out?"OUT":"IN"), urb->actual_length); + + if (out) { +#if 0 + int i; + u8 *cp = urb->transfer_buffer; + + TRACE_MSG1(HCD, "NEXT TX: length: %d", urb->actual_length); + + for (i = 0; i < urb->actual_length; i+= 8) + + TRACE_MSG8(HCD, "SENT %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] + ); +#endif + } + + if (usb_endpoint_halted(urb->dev, endpoint, out)) { + TRACE_MSG4(HCD,"urb for halted endpoint urb: %08lx dev: %d ep: %d dir: %c", + urb, dev_addr, endpoint, (out?'O':'I')); + urb->actual_length = 0; + hcd_finish_urb(urb,-EPIPE); + return -EINVAL; + } + + data = urb->transfer_buffer; + len = urb->transfer_buffer_length; + format = usb_pipetype(urb->pipe); + other = 0; + + switch (format) { + case PIPE_CONTROL: + pid = USB_PID_SETUP; + // overload toggle with data phase direction (setup is always toggle 0) + // toggle == TRUE --> OUT (HOST2DEVICE --> OUT) + + toggle = ((((struct usbd_device_request *) urb->setup_packet)->bmRequestType & USB_REQ_DIRECTION_MASK) == + USB_REQ_HOST2DEVICE); + + other = (u32) (void *) urb->setup_packet; + + // XXX send set feature otg enable IFF set configuration and + // to first device and previously otg descriptor was seen. + break; + + case PIPE_BULK: + pid = (out ? USB_PID_OUT : USB_PID_IN); + toggle = usb_gettoggle(urb->dev,endpoint,out); + other = (urb->transfer_flags & URB_ZERO_PACKET); + break; + + case PIPE_INTERRUPT: + pid = (out ? USB_PID_OUT : USB_PID_IN); + toggle = usb_gettoggle(urb->dev,endpoint,out); + other = urb->interval; + break; + + case PIPE_ISOCHRONOUS: + pid = (out ? USB_PID_OUT : USB_PID_IN); + toggle = 0; + other = urb->interval; + break; + + default: + pid = 0; + toggle = 0; + break; + } + + /* We need an irq lock here in order to make sure the urb gets saved in + bus_hcpriv->active_urbs[] before any possible completion interrupt. */ + local_irq_save(flags); + rc = hcd_hw_start_transfer(bus_hcpriv, len, data, toggle, usb_maxpacket(urb->dev, urb->pipe, out), + (((urb->pipe) >> 26) & 1), endpoint,dev_addr, pid, format,other); + + if (rc >= 0 && rc < bus_hcpriv->max_active_urbs) { + + if (!bus_hcpriv->active_urbs[rc]) + bus_hcpriv->active_urbs[rc] = urb; + + /* Oops, total screw up, we shouldn't ever see this. + * (The lower layer just told us it started a second urb in + * an already busy slot.) + */ + else + TRACE_MSG3(HCD,"ERROR - act: %08x urb: %08x slot: %d", + (u32)(void*)(bus_hcpriv->active_urbs[rc]), (u32)(void*)urb, rc); + } + local_irq_restore(flags); + if (rc < 0) { + // Resubmit is OK... + if (rc == -2) { + TRACE_MSG1(HCD,"no ETDs for urb: %08lx",urb); + return 1; + } + + // Complete the urb as invalid. + urb->actual_length = 0; + hcd_finish_urb(urb,-EINVAL); + // And signal to try again. + return -1; + } + return 0; +} + +STATIC void hcd_queue_start(struct bus_hcpriv *bus_hcpriv, struct dev_hcpriv *dev_hcpriv, int endpoint, int out) +{ + unsigned long flags; + struct urb *urb = NULL; + int rc; + + TRACE_MSG2(HCD, "endpoint: %2x %s", endpoint, out ? "OUT" : "IN"); + + // Paranoia. + RETURN_UNLESS(dev_hcpriv && endpoint < 16); + do { + local_irq_save(flags); + if (dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] == EPQ_RUNNING) { + // There's already an urb for this device/endpoint in the HW, don't start another. + urb = NULL; + } + else if (!(urb = hcd_next_urb(dev_hcpriv, endpoint, out))) { + dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_EMPTY; + } + else { + // There is an urb to go, set the state to show the HW is (will be) busy. + dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_RUNNING; + } + local_irq_restore(flags); + if (!urb) { + // This endpoint is already active in the HW, or doesn't need to be. + rc = 0; + } + else { + // Try to pass the urb down to the HW. + rc = hcd_submit_to_hw(bus_hcpriv, urb, out); + } + } while (rc < 0); + if (rc > 0) { + // We've run out of HW resources, try again later. + local_irq_save(flags); + list_add(&urb->urb_list, &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]); + dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] += 1; + dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_WAITING; + TRACE_MSG3(HCD,"urb: %08x ep: %d n: %u WAITING IN", + (u32)(void*)urb, endpoint, dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint]); + local_irq_restore(flags); + /* There should be no need to queue a task to try later, as the HW + * should have lots of pending urbs, and urb completion will lead + * to a search for new work. There is a small chance that _all_ the + * pending urbs will complete between the call to hcd_submit_to_hw(), + * but it's unlikely. + */ + } +} + + +/* ********************************************************************************************** */ +/* Functions provided to the HW dependent layer (below this one). + */ +void hcd_transfer_complete(struct bus_hcpriv *bus_hcpriv, int transfer_id, int format, int cc, u32 remaining, int next_toggle) +{ + struct urb *urb = grab_active_urb(bus_hcpriv, transfer_id); + struct usb_device *dev; + struct usb_bus *bus; + struct dev_hcpriv *dev_hcpriv; + int out; + + int endpoint; + u8 *cp; + + UNLESS (urb) { + TRACE_MSG1(HCD,"active_urb[%d] NULL",transfer_id); + return; + } + + RETURN_UNLESS((dev = urb->dev)); + RETURN_UNLESS((bus = dev->bus)); + RETURN_UNLESS((dev_hcpriv = (struct dev_hcpriv *) (dev->hcpriv))); + + out = usb_pipeout(urb->pipe); + + TRACE_MSG5(HCD,"id: %d status: %d tlen: %d rem: %d buffer: %p", + transfer_id, cc, urb->transfer_buffer_length, remaining, urb->transfer_buffer); + //if (out) + if (urb->transfer_buffer) { +#if 0 + int i; + u8 *cp = urb->transfer_buffer; + + TRACE_MSG1(HCD, "NEXT TX: length: %d", urb->actual_length); + + for (i = 0; i < urb->actual_length; i+= 8) + + TRACE_MSG8(HCD, "RECV %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] + ); +#endif + } + + if (-ENOENT == cc) { + // Cancelled. + urb->actual_length = 0; + } + else { + switch (format) { + case PIPE_CONTROL: + urb->actual_length = urb->transfer_buffer_length - remaining; + + if ((cp = urb->setup_packet)) + TRACE_MSG8(HCD, "SETUP %02x %02x %02x %02x %02x %02x %02x %02x", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + + if ((dev == bus_hcpriv->first_dev) && !cc && urb->setup_packet) { + struct usb_ctrlrequest *request = (struct usb_ctrlrequest*) urb->setup_packet; + switch (request->bRequest) { + case USB_REQ_SET_ADDRESS: + otg_event(hcd_instance->otg, ADDRESSED, HCD, "HCD COMPLETE ADDRESSED"); + break; + case USB_REQ_SET_CONFIGURATION: + otg_event(hcd_instance->otg, CONFIGURED, HCD, "HCD COMPLETE CONFIGURED"); + break; + // XXX check for GET_CONFIGURATION and otg descriptor here + } + } + break; + + case PIPE_BULK: + case PIPE_INTERRUPT: + if (0 == cc) { + // QQSV needed for BULK, what about interrupt? + TRACE_MSG4(HCD,"dev: %d ep: %d dir: %d TOGGLE: %d",urb->dev->devnum, + usb_pipeendpoint(urb->pipe),usb_pipeout(urb->pipe),next_toggle); + usb_settoggle(urb->dev,usb_pipeendpoint(urb->pipe),usb_pipeout(urb->pipe),next_toggle); + } + urb->actual_length = urb->transfer_buffer_length - remaining; + //hcd_trace_mem(HCD,__FUNCTION__,"BULK/INTERRUPT urb",urb->actual_length,urb->transfer_buffer); + break; + + case PIPE_ISOCHRONOUS: + urb->actual_length = urb->transfer_buffer_length - remaining; // QQSV + break; + + default: // Paranoia. + urb->actual_length = 0; + break; + } + } + endpoint = usb_pipeendpoint(urb->pipe); + hcd_finish_urb(urb, cc); + + // Start the next urb in the queue for this device/endpoint. + dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_WAITING; + + TRACE_MSG2(HCD,"queue_start ep: %d %s >>>", endpoint, out ? "OUT" : "IN"); + hcd_queue_start(bus_hcpriv, dev_hcpriv, endpoint, out); + + //TRACE_MSG1(HCD,"queue_start ep: %d <<<",endpoint); + // FIXME - in the future, need to kick off a BH to search other EPs/devs for waiting urbs + // if (dev_hcpriv->epq_state[endpoint] == EPQ_EMPTY) { + // /* This queue is empty, so its HW resources are now available, + // kick off BH to search for other EP's and other devs that are waiting. */ + // _________ + // } +} + +/* ********************************************************************************************** */ +/* Functions provided to the USB core layer (above this one). + */ + +/* Allocate space for some USB function plugged into this HC or any hub below it. + * Return 0 on success, error code (e.g. -ENOMEM) on failure. + */ +STATIC int hcd_allocate(struct usb_device *dev) +{ + struct usb_bus *bus; + struct bus_hcpriv *bus_hcpriv; + struct dev_hcpriv *dev_hcpriv; + int endpoint; + + TRACE_MSG1(HCD,"entered devnum: %d", dev->devnum); + + RETURN_ENOMEM_UNLESS((bus = dev->bus)); + RETURN_ENOMEM_UNLESS((bus_hcpriv = (struct bus_hcpriv *) bus->hcpriv)); + + RETURN_ENOMEM_UNLESS(dev_hcpriv = (struct dev_hcpriv *) kmalloc(sizeof(struct dev_hcpriv), GFP_KERNEL)); + + memset(dev_hcpriv, 0, sizeof(struct dev_hcpriv)); + for (endpoint = 0; endpoint < 16; endpoint++) { + INIT_LIST_HEAD(&dev_hcpriv->queued_urbs_both[0][endpoint]); + INIT_LIST_HEAD(&dev_hcpriv->queued_urbs_both[1][endpoint]); + } + + dev->hcpriv = dev_hcpriv; + dev_hcpriv->dev = dev; + + UNLESS (bus_hcpriv->roothub_dev) + bus_hcpriv->roothub_dev = dev; + + else UNLESS (bus_hcpriv->first_dev) + bus_hcpriv->first_dev = dev; + + return 0; +} + + +STATIC int hcd_deallocate(struct usb_device *dev) +{ + struct usb_bus *bus = dev->bus; + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) bus->hcpriv; + struct dev_hcpriv *dev_hcpriv; + + if (dev == bus_hcpriv->first_dev) + bus_hcpriv->first_dev = NULL; + + if (dev == bus_hcpriv->roothub_dev) + bus_hcpriv->roothub_dev = NULL; + + if ((dev_hcpriv = (struct dev_hcpriv *) (dev->hcpriv))) { + dev_hcpriv->dev = NULL; + kfree(dev_hcpriv); + dev->hcpriv = NULL; + } + return 0; +} + + +STATIC void *hcd_buffer_alloc(struct usb_bus *usb_bus, size_t size, int mem_flags, dma_addr_t *dma) +{ + return consistent_alloc(0, size, dma); +} + +STATIC void hcd_buffer_free(struct usb_bus *usb_bus, size_t size, void *addr, dma_addr_t dma) +{ + RETURN_UNLESS(addr); + consistent_free(addr,size,dma); +} + +STATIC int frame_number(struct usb_device *usb_dev) +{ + u32 n = hcd_hw_frame_number((struct bus_hcpriv *)(usb_dev->bus->hcpriv)); + TRACE_MSG1(HCD,"-->: %08lx",n); + return n; +} + +STATIC int submit_urb(struct urb *urb, int mem_flags) +{ + struct bus_hcpriv *bus_hcpriv; + struct dev_hcpriv *dev_hcpriv; + int dev_addr; + int endpoint; + int out; + + RETURN_EINVAL_UNLESS (urb && urb->dev && urb->dev->bus); + RETURN_EINVAL_UNLESS ((bus_hcpriv = (struct bus_hcpriv *) (urb->dev->bus->hcpriv))); + RETURN_EINVAL_UNLESS ((dev_hcpriv = (struct dev_hcpriv *) (urb->dev->hcpriv))); + RETURN_EINVAL_IF (bus_hcpriv->terminating); + + dev_addr = usb_pipedevice(urb->pipe); + endpoint = usb_pipeendpoint(urb->pipe); + out = usb_pipeout(urb->pipe); + + RETURN_EPIPE_IF (usb_endpoint_halted(urb->dev, endpoint, out)); + + /* Divert to root hub if appropriate, (note: NO ref count change). + */ + if (urb->dev == bus_hcpriv->roothub_dev) { + TRACE_MSG4(HCD,"root hub --> urb: %08lx dev: %d ep: %d dir: %s", urb, dev_addr, endpoint, (out ? "OUT" : "IN")); + return hcd_rh_submit_urb(bus_hcpriv, urb, mem_flags); + } + + /* Increase ref count so we own a piece of the URB. Queue the urb, + * then make sure the queue is running. + */ + TRACE_MSG4(HCD,"device --> urb: %08lx dev: %d ep: %d dir: %S", urb, dev_addr, endpoint, (out ? "OUT" : "IN")); + hcd_queue_urb(dev_hcpriv, usb_get_urb(urb), endpoint, out); + hcd_queue_start(bus_hcpriv, dev_hcpriv, endpoint, out); + return 0; +} + +STATIC int unlink_urb(struct urb *urb) +{ + int u; + unsigned long flags; + struct bus_hcpriv *bus_hcpriv = NULL; + struct dev_hcpriv *dev_hcpriv = NULL; + int dev_addr; + int endpoint; + struct list_head *epq; + int out; + + RETURN_EINVAL_UNLESS (urb && urb->dev && urb->dev->bus); + RETURN_EINVAL_UNLESS ((dev_hcpriv = (struct dev_hcpriv *) (urb->dev->hcpriv))); + RETURN_EINVAL_UNLESS ((bus_hcpriv = (struct bus_hcpriv *) (urb->dev->bus->hcpriv))); + + out = usb_pipeout(urb->pipe); + + dev_addr = usb_pipedevice(urb->pipe); + + /* Does it belong to the root hub? + */ + if (urb->dev == bus_hcpriv->roothub_dev) { + TRACE_MSG1(HCD,"root hub --> urb: %08lx",urb); + return hcd_rh_unlink_urb(bus_hcpriv,urb); + } + + /* Look to see if it is in a device/endpoint queue. + */ + endpoint = usb_pipeendpoint(urb->pipe); + + epq = &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]; + + /* Searching a list while in_irq - yuck. For the time being, + * this just assumes that the list is short. In the future, + * it may be possible to keep a small amount of state in the + * urb itself to indicate if it is in this list, so that all + * we need to do in_irq is check the state and delete from the + * list if the state indicates we should. The state info will + * only needed while this HC "owns" the urb. + */ + local_irq_save(flags); + + if (EPQ_EMPTY != dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint]) { + // There are queued urbs, search to see if this is one of them. + struct urb *qe; + list_for_each_entry(qe, epq, urb_list) { + if (qe == urb) { + // Found it. It hasn't been sent to the HW yet, so it can be finished here. + list_del_init(&urb->urb_list); + dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] -= 1; + if (list_empty(epq)) + dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_EMPTY; + + TRACE_MSG3(HCD,"urb: %08x ep: %d n: %u UNLINKED IN", + (u32)(void*)urb, endpoint, + dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint]); + hcd_finish_urb(urb,-ENOENT); + local_irq_restore(flags); + return 0; + } + } + } + + local_irq_restore(flags); + + /* Look to see if this is one of the active urbs. + */ + if (bus_hcpriv->active_urbs) { + + for (u = 0; u < bus_hcpriv->max_active_urbs; u++) { + local_irq_save(flags); + /* This is the one we want, unlink from the HW. + * HW layer does the completion through hcd_transfer_complete() + * in this file, which releases bus_hcpriv->active_urbs[u]. + */ + if (urb == bus_hcpriv->active_urbs[u]) { + int out; + out = usb_pipeout(urb->pipe); + u = hcd_hw_unlink_urb(bus_hcpriv, u); + + TRACE_MSG4(HCD,"urb: %08x ep: %d n: %u UNLINKED from HW %s", (u32)(void*)urb, endpoint, + dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint], out ? "OUT" : "IN"); + + local_irq_restore(flags); + // FUTURE - think about starting a search for new work for the released HW slot. + return u; + } + local_irq_restore(flags); + } + } + // The urb isn't ours, or completed while we were looking for it. + TRACE_MSG1(HCD,"Already completed, or someone else's urb: %08lx", urb); + return -EINVAL; +} + +/* ********************************************************************************************** */ + +// void disable(struct usb_device *dev, int bEndpointAddress); + +static struct usb_operations operations = { + .allocate = hcd_allocate, + .deallocate = hcd_deallocate, + .get_frame_number = frame_number, + .submit_urb = submit_urb, + .unlink_urb = unlink_urb, + .buffer_alloc = hcd_buffer_alloc, /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */ + .buffer_free = hcd_buffer_free, + .disable = NULL +}; + + +/* ********************************************************************************************** */ + +/* Called as a "bottom-half" to do usbcore registration once all the HW is usable. + */ +STATIC void hcd_bh_init(void *arg) +{ + struct hcd_instance *hcd = (struct hcd_instance *) arg; + otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT SET HCD_OK"); + TRACE_MSG0(HCD,"finished"); +} + +STATIC void hcd_bh_exit(void *arg) +{ + struct hcd_instance *hcd = (struct hcd_instance *) arg; + otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT RESET (EXIT) HCD_OK"); +} + + +/*! hcd_init_func - per host controller common initialization + * + * This is called to initialize / de-initialize the HCD, all except the last + * stage of registering the root hub, because that needs to wait until rh_hcd_en_func() + * + * We start work items to do this. + * + */ +void hcd_init_func (struct otg_instance *otg, u8 flag) +{ + struct hcd_instance *hcd = otg->hcd; + + switch (flag) { + case SET: + // Schedule BH for hcd_bh_init... + TRACE_MSG0(HCD, "HCD_INIT: SET"); + PREPARE_WORK_ITEM(hcd->bh, hcd_bh_init, hcd); + SCHEDULE_WORK(hcd->bh); + TRACE_MSG0(HCD, "hcd_bh_init() schedule finished"); + break; + + case RESET: + TRACE_MSG0(HCD, "HCD_INIT: RESET"); + PREPARE_WORK_ITEM(hcd->bh, hcd_bh_exit, hcd); + SCHEDULE_WORK(hcd->bh); + break; + } +} + + +/* ********************************************************************************************** */ + +STATIC void release_hw_rh(struct bus_hcpriv *bus_hcpriv) +{ + RETURN_UNLESS (bus_hcpriv); + + hcd_rh_exit(bus_hcpriv); + + // Complete (cancel) any active urbs. + if (bus_hcpriv->active_urbs) { + int u; + for (u = 0; u < bus_hcpriv->max_active_urbs; u++) { + struct urb *urb; + if ((urb = grab_active_urb(bus_hcpriv,u))) { + urb->actual_length = 0; + hcd_finish_urb(urb,-ENOENT); // USB_ST_URB_KILLED + } + } + } + + // Release any resources from the layer above. + if (bus_hcpriv->usb_bus) { + usb_deregister_bus(bus_hcpriv->usb_bus); + bus_hcpriv->usb_bus = NULL; + } + + // Lastly, release this layer's info. + if (bus_hcpriv->active_urbs) { + kfree(bus_hcpriv->active_urbs); + bus_hcpriv->active_urbs = NULL; + } +} + +/* ********************************************************************************************** */ +/* + * Note that initialization and cleanup happen in two stages: + * 1. Allocation of the basic struct bus_hcpriv, the first level of LDM/USB driver registration, and OTG operations + * 2. Everything else - completion of data allocation, USBcore registration, HC HW init, root hub enumeration - when + * called back by OTG (after any magic required to access the HCD hardware.) + * Only stage 1 is done on module entry. + */ + +int __init hcd_init(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver) +{ + struct usb_bus *usb_bus = NULL; + int usb_registered = 0; + int device_registered = 0; + int bus_registered = 0; + int rc = 0; + struct urb **active_urbs = NULL; + + /* fixup bus_hcpriv structure + */ + bus_hcpriv->usb_driver = usb_driver; + bus_hcpriv->bus_device.driver = &usb_driver->driver; + bus_hcpriv->max_active_urbs = bus_hcpriv->max_active_transfers; + usb_driver->probe = hcd_probe; + + UNLESS (hcd_ops.hcd_init_func) hcd_ops.hcd_init_func = hcd_init_func; + + /* allocate urb array + */ + THROW_UNLESS ((active_urbs = (struct urb **) kmalloc(bus_hcpriv->max_active_urbs*sizeof(struct urb *),GFP_KERNEL)), + error); + + bus_hcpriv->active_urbs = active_urbs; + memset(bus_hcpriv->active_urbs,0,bus_hcpriv->max_active_urbs*sizeof(struct urb *)); + TRACE_MSG1(HCD,"active_urbs ok, max active URBs: %d",bus_hcpriv->max_active_urbs); + + /* register with usb core + */ + THROW_UNLESS(usb_registered = usb_register(usb_driver) ? 0 : 1, error); + TRACE_MSG0(HCD,"usb_register ok"); + + /* We need a bus before we can initialize the root hub. + */ + THROW_UNLESS ((usb_bus = usb_alloc_bus(&operations)), error); + bus_hcpriv->usb_bus = usb_bus; + TRACE_MSG0(HCD,"usb bus allocated"); + + /* register bus + */ + snprintf(bus_hcpriv->bus_device.bus_id, BUS_ID_SIZE, "bus_hcpriv%d", usb_bus->busnum); + snprintf(bus_hcpriv->bus_device.name, DEVICE_NAME_SIZE,"%s%d", bus_hcpriv->usb_driver->name, usb_bus->busnum); + usb_bus->hcpriv = (void *) bus_hcpriv; + usb_bus->controller = &bus_hcpriv->bus_device; + THROW_UNLESS ((bus_registered = usb_register_bus(usb_bus)) ? 0 : 1, error); + TRACE_MSG0(HCD,"usb bus registered"); + + /* All set for the host controller, initialize the virtual root hub. + */ + THROW_IF (hcd_rh_init(bus_hcpriv), error); + TRACE_MSG0(HCD,"root hub initialized"); + + /* register device + */ + //THROW_IF ((device_registered = device_register(&bus_hcpriv->bus_device) ? 0 : 1), error); + if (device_register(&bus_hcpriv->bus_device)) { + printk(KERN_ERR"%s: device_register not ok\n", __FUNCTION__); + } + + /* Everybody's ready, enable the interrupts. + */ + + /* Kick off the root hub. + */ + THROW_IF ((rc = usb_register_root_hub(bus_hcpriv->rh_hcpriv->dev, &(bus_hcpriv->bus_device))), error); + + return 0; + + CATCH(error) { + + if (device_registered) device_unregister(&bus_hcpriv->bus_device); + if (usb_bus) { + if (bus_registered && usb_bus) usb_deregister_bus(usb_bus); + kfree(usb_bus); + } + if (usb_registered) usb_deregister(usb_driver); + if (active_urbs) kfree(active_urbs); + return -EINVAL; + } +} + +#ifdef MODULE +void hcd_exit(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver) +{ + release_hw_rh(bus_hcpriv); + + usb_deregister_bus(bus_hcpriv->usb_bus); + device_unregister(&bus_hcpriv->bus_device); + usb_deregister(usb_driver); +} +#endif + diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd-rh.c linux/drivers/otg/ocd/otg-hcd/hcd-rh.c --- linux/drivers/no-otg/ocd/otg-hcd/hcd-rh.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-hcd/hcd-rh.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,1068 @@ +/* + * otg/ocd/otg-hcd/hcd-rh.c - Generic transfer level USBOTG aware + * virtual root hub _FUNCTION_ driver. + * + * Copyright (c) 2004 Belcarra Technologies + * + * By: + * Tom Rushworth , + * Stuart Lynne , + * Bruce Balden + */ +/*! + * @file otg/ocd/otg-hcd/hcd-rh.c + * @brief Root Hub for Generic Host Controller Driver + * + * + * + * @ingroup OTGHCD + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "../core/hcd.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define STATIC static + +/* NOTE: This code assumes the root hub has < 8 ports. If it has + more some of the descriptors and other structures will have to + be re-sized. */ + +STATIC u8 prepare_str(char *ascii, u8 *str, int str_size) +{ + int c; + u8 *up = str; + u8 *ep = (up + str_size) - 1; + while ((c = *ascii++) && up < ep) { + *up++ = (c & 0x7F); + *up++ = 0; + } + return (u8)(up - str); +} + +STATIC int hcd_rh_string(struct bus_hcpriv *bus_hcpriv, int strno, u8 *buff, int buff_len) +{ + // Provide default strings, but let the HW component override them. + char *str; + switch (strno) { + case 0: // Language ID + *buff++ = 4; + *buff++ = USB_DT_STRING; + *buff++ = 0; + *buff++ = 0; + return 4; + case XHC_RH_STRING_CONFIGURATION: + str = "Root hub"; + break; + case XHC_RH_STRING_INTERFACE: + str = "Root hub - Status"; + break; + case XHC_RH_STRING_SERIAL: + str = bus_hcpriv->rh_serial ? bus_hcpriv->rh_serial : "000001"; + break; + case XHC_RH_STRING_PRODUCT: + str = bus_hcpriv->rh_product ? bus_hcpriv->rh_product : "Virtual Root Hub"; + break; + case XHC_RH_STRING_MANUFACTURER: + str = bus_hcpriv->rh_manufacturer ? bus_hcpriv->rh_manufacturer : "Belcarra Technologies"; + break; + default: + str = "????"; + } + + buff[0] = 2 + prepare_str(str,buff+2,buff_len-2); + buff[1] = USB_DT_STRING; + return buff[0]; +} + + +/* ********************************************************************************************** */ + + +static char *hub_feature_name[] = { + "C_HUB_LOCAL_POWER", + "C_HUB_OVER_POWER", +}; + +STATIC void hcd_rh_hub_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, int set_flag) +{ + unsigned long flags; + TRACE_MSG3(HCD,"%s feature %d %s",(set_flag?"SET":"CLR"), wValue, hub_feature_name[wValue]); + local_irq_save(flags); + if (set_flag) { + // SET feature USB2.0 11.24.2.12 pg 434 and USB2.0 11.24.2 tbl 11-17 pg 421 + // R1: sec 23.11.29 pg 23-62 + switch (wValue) { + // case C_HUB_OVER_CURRENT: ____ FIXME ---- + // case C_HUB_LOCAL_POWER: ____ FIXME ---- + default: + // Error, but ignore. + TRACE_MSG1(HCD,"SET invalid FEATURE %d", wValue); + break; + } + } + else { + // CLEAR feature USB2.0 11.24.2.1 pg 422 and USB2.0 11.24.2 tbl 11-17 pg 421 + // R1: sec 23.11.29 pg 23-62 + switch (wValue) { + case C_HUB_OVER_CURRENT: + bus_hcpriv->rh_hcpriv->hub_change_status &= ~(1 << wValue); + TRACE_MSG1(HCD,"CLR C_HUB_OVER_CURRENT hub_change_status: %08x", bus_hcpriv->rh_hcpriv->hub_change_status); + break; + // case C_HUB_LOCAL_POWER: ____ FIXME ---- + default: + // Error, but ignore. + TRACE_MSG1(HCD,"CLR invalid FEATURE %d", wValue); + break; + } + } + local_irq_restore(flags); + hcd_hw_rh_hub_feature(bus_hcpriv, wValue, set_flag); +} + +/* ********************************************************************************************** */ + +char *port_feature_name[] = { + "PORT_CONNECTION", "PORT_ENABLE", "PORT_SUSPEND", "FEATURE 0x03", + "PORT_RESET", "FEATURE 0x05", "FEATURE 0x06", "FEATURE 0x07", + "PORT_POWER", "PORT_LOW_SPEED", "FEATURE 0x0a", "FEATURE 0x0b", + "FEATURE 0x0c", "FEATURE 0x0d", "FEATURE 0x0e", "FEATURE 0x0f", + "C_PORT_CONNECTION", "C_PORT_ENABLE", "C_PORT_SUSPEND", "C_PORT_OVER_CURRENT", + "C_PORT_RESET", "PORT_TEST", +}; + +void check_port_status_(int feature, u32 change_status, u32 hw_change_status) +{ + int mask = (1 << feature); + if ((change_status & mask) != (hw_change_status & mask)) + TRACE_MSG4(HCD, "%-20s %2d %6s %6s MISMATCH", port_feature_name[feature], feature, + change_status & mask ? "SET" : "RESET", hw_change_status & mask ? "SET" : "RESET" ); +} + +void check_port_status(struct bus_hcpriv *bus_hcpriv, u16 wIndex, char *msg) +{ + struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; + u32 change_status = ((wIndex -1) < bus_hcpriv->num_ports) ? bus_hcpriv->rh_hcpriv->port_change_status[wIndex-1] : 0; + u32 hw_change_status = hcd_hw_rh_get_port_change_status(bus_hcpriv, wIndex); + + TRACE_MSG4(HCD,"PORT_STATUS[%2d] %08x %08x %s", wIndex, + change_status, hcd_hw_rh_get_port_change_status(bus_hcpriv, wIndex), msg); + + UNLESS(rh_hcpriv->suspended) { + // XXX + } + + check_port_status_(PORT_CONNECTION, change_status, hw_change_status); + check_port_status_(PORT_ENABLE, change_status, hw_change_status); + check_port_status_(PORT_RESET, change_status, hw_change_status); + check_port_status_(PORT_POWER, change_status, hw_change_status); + check_port_status_(C_PORT_CONNECTION, change_status, hw_change_status); + check_port_status_(C_PORT_ENABLE, change_status, hw_change_status); + check_port_status_(C_PORT_RESET, change_status, hw_change_status); +} +/* ********************************************************************************************** */ +/* + * wIndex is port number (1-N) + * wValue is feature selector + */ +STATIC void hcd_rh_port_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, u16 wIndex, int set_flag) +{ + TRACE_MSG4(HCD,"%s port %d FEATURE %02x %s", (set_flag?"SET":"CLR"), wIndex, + wValue, port_feature_name[wValue]); + /* Not a valid port number + */ + if (wIndex > bus_hcpriv->num_ports) { + TRACE_MSG1(HCD, "invalid port: %d", wIndex); + return; + } + /* Feature selector is wValue + * wIndex is 1-origin + */ + TRACE_MSG2(HCD, "port_change_status[%d]: -> %08x", wIndex, bus_hcpriv->rh_hcpriv->port_change_status[wIndex -1]); + if (set_flag) { + // SET feature + switch (wValue) { + case PORT_SUSPEND: + case PORT_RESET: + case PORT_POWER: + bus_hcpriv->rh_hcpriv->port_change_status[wIndex - 1] |= (1 << wValue); + break; + } + } + else { + // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423). + switch (wValue) { + case PORT_ENABLE: // Disable port. + case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. + case PORT_POWER: // Put port in powered-off state. + case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit + case C_PORT_RESET: // clear the PORT_RESET change bit + case C_PORT_ENABLE: // clear the PORT_ENABLE change bit + case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit + case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit + bus_hcpriv->rh_hcpriv->port_change_status[wIndex - 1] &= ~(1 << wValue); + break; + } + } + /* update hardware? + */ + + RETURN_UNLESS (wIndex == bus_hcpriv->otg_port); + RETURN_IF (bus_hcpriv->rh_hcpriv->otg_device_mask & (1 << wIndex)); + + hcd_hw_rh_port_feature(bus_hcpriv, wValue, wIndex, set_flag); + + TRACE_MSG4(HCD, "OTG Port wValue: %x wIndex: %d %d set: %d", wValue, wIndex, bus_hcpriv->otg_port, set_flag); + if (set_flag) { + // SET feature + switch (wValue) { + case PORT_RESET: + TRACE_MSG0(HCD, "OTG Port - HUB_PORT_ENABLED - PORT_RESET"); + //otg_event(bus_hcpriv->hcd->otg, HUB_PORT_ENABLED, HCD, "Hub Port Enable (PORT_RESET)"); + otg_event(hcd_instance->otg, BUS_RESET, HCD, "PORT_RESET (SET Feature)"); + break; + case PORT_SUSPEND: + case PORT_POWER: + break; + } + check_port_status(bus_hcpriv, wIndex, "PORT SET"); + } + else { + // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423). + switch (wValue) { + case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit + TRACE_MSG0(HCD, "OTG Port - HUB_PORT_ENABLED/ - C_PORT_CONNECTION"); + break; + case C_PORT_ENABLE: // clear the PORT_ENABLE change bit + TRACE_MSG0(HCD, "OTG Port - HUB_PORT_ENABLED/ - C_PORT_ENABLE"); + break; + case PORT_ENABLE: // Disable port. + case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. + case PORT_POWER: // Put port in powered-off state. + case C_PORT_RESET: // clear the PORT_RESET change bit + case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit + case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit + break; + } + check_port_status(bus_hcpriv, wIndex, "PORT CLR"); + } +} + +/* ********************************************************************************************** */ +/* + * Functions for periodic response to the INT URB. + * Note that the USB2.0 spec says (sec 11.12.2 pg 337) that a hub controller + * will respond to an INT poll as long as the change bits remain set. This + * means we have to record them and continue responding periodically (hence + * the timer) until the class driver clears them. + * + */ + +void hcd_rh_urb_complete(struct bus_hcpriv *bus_hcpriv, struct urb *urb) +{ + // Almost not needed (vrh could do this directly), except for tracing. + TRACE_MSG1(HCD,"urb %p",urb); + + /* Call the upper layer completion routine. + */ + if (urb->complete) + urb->complete(urb,NULL); + + // XXX should we do this here + // usb_put_urb(int_urb); +} +/* ********************************************************************************************** */ +/* + * Some sort of change may have happened on the root hub or one of the ports, + * scan for changes in the shadow data, and notify usbcore if there is an + * INT URB available to do so with. + */ +STATIC void hcd_rh_portstatus_bh(void *data) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) data; + struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; + u8 change_data; + struct urb *int_urb; + + u32 hub_port_change_status; + unsigned long flags; + + TRACE_MSG1(HCD, "suspended: %d", rh_hcpriv->suspended); + RETURN_IF(rh_hcpriv->suspended); + + //printk(KERN_INFO"%s:\n", __FUNCTION__); + + local_irq_save(flags); + hub_port_change_status = bus_hcpriv->rh_hcpriv->hub_port_change_status; + bus_hcpriv->rh_hcpriv->hub_port_change_status = 0; + local_irq_restore(flags); + + + change_data = (u8) hub_port_change_status & 0xff; + //change_data ~= ~bus_hcpriv->rh_hcpriv->otg_device_mask; + + RETURN_UNLESS (change_data && (int_urb = rh_hcpriv->int_urb)); + + TRACE_MSG1(HCD,"non-zero change_data: %02x", change_data); + + change_data |= 1; // set hub change bit XXX check + + // Completing the urb will "use it up", so remove the link to it. + + del_timer(&rh_hcpriv->poll_timer); + rh_hcpriv->int_urb = NULL; + + *((u8 *)(int_urb->transfer_buffer)) = change_data; + int_urb->actual_length = 1; + + /* Completing an INT urb will cause it to be immediately re-submitted, so this will call submit_urb() + */ + hcd_rh_urb_complete(bus_hcpriv, int_urb); + usb_put_urb(int_urb); +} +/* ********************************************************************************************** */ +STATIC void poll_int_urb(unsigned long data) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) (void *) data; + unsigned long flags; + + RETURN_UNLESS (bus_hcpriv && bus_hcpriv->rh_hcpriv); + + local_irq_save(flags); + PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); + SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); + local_irq_restore(flags); +} +/* ********************************************************************************************** */ +STATIC u32 rh_get_port_change_status(struct bus_hcpriv *bus_hcpriv, u16 wIndex) +{ + if ((wIndex != bus_hcpriv->otg_port) || !(bus_hcpriv->rh_hcpriv->otg_device_mask)) + return hcd_hw_rh_get_port_change_status(bus_hcpriv, wIndex); + + TRACE_MSG0(HCD, "USING SHADOW INFORMATION"); + return bus_hcpriv->rh_hcpriv->port_change_status[wIndex-1]; +} + +/* + * hcd_rh_submit_urb() + * Returns: values as for the generic submit_urb() function, see comp_code_to_status(int cc). + */ +int hcd_rh_submit_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb, int mem_flags) +{ + struct rh_hcpriv *rh_hcpriv; + unsigned int pipe = urb->pipe; + struct usbd_device_request *req; + u8 bRequest; + u8 bmRequestType; + u16 wValue; + u16 wIndex; + u16 wLength; + u8 direction; + u8 recipient; + u8 type; + int rc = 0; + int rc_stall = -EPIPE; + void *reply = NULL; + int rlen = 0; + u16 status[2]; + + static rh_urb; + + TRACE_MSG1(HCD,"pipe: %08x", pipe); + + + //printk(KERN_INFO"%s: rh_urb: %d\n", __FUNCTION__); + TRACE_MSG1(HCD, "rh_urb: %d", rh_urb++); + + RETURN_ETIMEDOUT_UNLESS(bus_hcpriv && bus_hcpriv->rh_hcpriv); + + rh_hcpriv = bus_hcpriv->rh_hcpriv; + + if (usb_pipeint(pipe)) { + // This must be our interrupt endpoint poll. + TRACE_MSG1(HCD,"interrupt polling urb: %08lx",urb); + urb->status = 0; + + // Increment the ref count, we're keeping this for a while. + usb_get_urb(urb); + if (rh_hcpriv->int_urb) { + // Hmm, a new one while we still have the old...Shouldn't happen, and doesn't work if it does :(. + //printk(KERN_INFO "%s: releasing old: %08x\n",__FUNCTION__,(u32)(void*)rh_hcpriv->int_urb); + del_timer(&rh_hcpriv->poll_timer); + usb_put_urb(rh_hcpriv->int_urb); + } + + rh_hcpriv->int_urb = urb; + /* Schedule a scan for changes in case anything happened while there was no urb. + * This has to be slightly delayed, in case this urb is a response to a completed + * scan - usbcore submits a new int urb before clearing the hub/port change status, + * relying on the int urb polling delay to give it time to do the clearing. + */ + //printk(KERN_INFO "%s: scheduling PSC poll\n",__FUNCTION__); + //init_poll_timer(bus_hcpriv); + init_timer(&rh_hcpriv->poll_timer); + rh_hcpriv->poll_timer.function = poll_int_urb; + rh_hcpriv->poll_timer.data = (unsigned long) (void *) bus_hcpriv; + rh_hcpriv->poll_timer.expires = jiffies + + ((((rh_hcpriv->int_urb->interval < 10) ? 10 : rh_hcpriv->int_urb->interval) * HZ) / 1000); + add_timer(&rh_hcpriv->poll_timer); + return rc; + } + + TRACE_MSG1(HCD,"req: %08x",(u32)(void*)urb->setup_packet); + RETURN_EINVAL_UNLESS((req = (struct usbd_device_request *) urb->setup_packet)) + + bRequest = req->bRequest; + bmRequestType = req->bmRequestType; + wValue = le16_to_cpu(req->wValue); + wIndex = le16_to_cpu(req->wIndex); + wLength = le16_to_cpu(req->wLength); + direction = bmRequestType & USB_REQ_DIRECTION_MASK; + recipient = bmRequestType & USB_REQ_RECIPIENT_MASK; + type = bmRequestType & USB_REQ_TYPE_MASK; + + TRACE_MSG8(HCD,"urb: %08lx req: %02x reqType: %02x val=%u ndx=%u len=%u dir: %02x recip: %02x", + urb,bRequest,bmRequestType, wValue,wIndex,wLength,direction,recipient); + + if (USB_REQ_HOST2DEVICE == direction) { + /* these do not require a reply */ + int set_flag = (USB_REQ_SET_FEATURE == bRequest); + TRACE_MSG0(HCD,"H->D"); + //printk(KERN_INFO "%s: H->D\n",__FUNCTION__); + urb->actual_length = 0; + switch (recipient) { + case USB_RECIP_HUB: + switch (bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (USB_REQ_TYPE_CLASS == type) { + TRACE_MSG2(HCD,"%s hub feature: %04x",(set_flag?"set":"clear"),wValue); + //printk(KERN_INFO "%s hub feature: %04x\n",(set_flag?"set":"clear"),wValue); + hcd_rh_hub_feature(bus_hcpriv, wValue, set_flag); + } + else { + TRACE_MSG2(HCD,"HUB C/S feature invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_REQ_SET_ADDRESS: + if (USB_REQ_TYPE_STANDARD == type) { + TRACE_MSG1(HCD,"set address %u",wValue); + //printk(KERN_INFO "set address %u\n",wValue); + bus_hcpriv->root_hub_addr = wValue; + } + else { + TRACE_MSG2(HCD,"HUB SETADDRESS invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_REQ_SET_CONFIGURATION: + if (USB_REQ_TYPE_STANDARD == type) { + TRACE_MSG1(HCD,"set configuration: %02x",wValue); + //printk(KERN_INFO "set configuration: %02x\n",wValue); + rh_hcpriv->curr_cfg = (u8) (0xFF & wValue); + } + else { + TRACE_MSG2(HCD,"HUB SETCONFIG invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_REQ_SET_INTERFACE: + // wIndex is interface number + if (USB_REQ_TYPE_STANDARD == type) { + TRACE_MSG2(HCD,"set interface: %02x to : %02x",wIndex,wValue); + //printk(KERN_INFO "set interface: %02x to : %02x\n",wIndex,wValue); + rh_hcpriv->curr_itf = (u8) (0xFF & wValue); + } + else { + TRACE_MSG2(HCD,"HUB SETINTERFACE invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + default: + TRACE_MSG2(HCD,"HUB invalid request r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_RECIP_PORT: + switch (bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + // At this point wIndex == port_num is 1-origin. + if (USB_REQ_TYPE_CLASS == type && wIndex <= bus_hcpriv->num_ports && wIndex > 0) { + TRACE_MSG3(HCD,"%s port %u FEATURE: %04x",(set_flag?"set":"clear"),wIndex,wValue); + hcd_rh_port_feature(bus_hcpriv, wValue, wIndex, set_flag); + } + else { + TRACE_MSG2(HCD,"PORT C/S FEATURE invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + default: + TRACE_MSG2(HCD,"PORT invalid request r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_REQ_RECIPIENT_ENDPOINT: + // Clear stalled endpoint + if (USB_REQ_CLEAR_FEATURE != bRequest || 1 != wValue || + USB_REQ_TYPE_STANDARD != type) { // QQSV verify type correct + TRACE_MSG3(HCD,"ENDPOINT invalid request,val,type r: %02x t: %02x val=%u", + bRequest,bmRequestType,wValue); + rc = rc_stall; + } + // Nothing to really do to "clear" the stalled EP. + TRACE_MSG2(HCD,"clear endpoint ndx: %02x val: %02x",wIndex,wValue); + //printk(KERN_INFO "clear endpoint ndx: %02x val: %02x\n",wIndex,wValue); + break; + case USB_REQ_RECIPIENT_INTERFACE: + default: + TRACE_MSG2(HCD,"H->D invalid recipient r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + } + else { + /* these do require a reply */ + TRACE_MSG0(HCD,"H<-D"); + //printk(KERN_INFO "%s: H<-D\n",__FUNCTION__); + switch (recipient) { + case USB_RECIP_HUB: + switch (bRequest) { + case USB_REQ_GET_DESCRIPTOR: + if (USB_REQ_TYPE_CLASS == type) { + // Return the hub class descriptor + TRACE_MSG0(HCD,"get hub class descriptor"); + //printk(KERN_INFO "get hub class descriptor\n"); + reply = (void *) rh_hcpriv->hd; + rlen = rh_hcpriv->hd->bDescLength; + } + else if (USB_REQ_TYPE_STANDARD != type) { + TRACE_MSG2(HCD,"HUB GETDESCRIPTOR invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + else { + // Return one of the other descriptors + switch (wValue >> 8) { + case USB_DT_DEVICE: + TRACE_MSG0(HCD,"get device descriptor"); + //printk(KERN_INFO "get device descriptor\n"); + reply = (void *) rh_hcpriv->dd; + rlen = rh_hcpriv->dd->bLength; + break; + case USB_DT_CONFIGURATION: + TRACE_MSG0(HCD,"get configuration descriptor"); + reply = (void *) rh_hcpriv->cd; + rlen = USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE+USB_DT_ENDPOINT_SIZE; + break; + case USB_DT_STRING: + TRACE_MSG1(HCD,"get string descriptor %u",wValue); + reply = urb->transfer_buffer; + rlen = hcd_rh_string(bus_hcpriv,(wValue & 0xFF),reply,wLength); + break; + default: + TRACE_MSG1(HCD,"HUB GETDESCRIPTOR invalid wValue: %04x",wValue); + rc = rc_stall; + } + } + break; + case USB_REQ_GET_STATUS: + if (USB_REQ_TYPE_CLASS == type) { + u32 change_status = bus_hcpriv->rh_hcpriv->hub_change_status; + status[0] = cpu_to_le16(change_status & 0xFFFF); + status[1] = cpu_to_le16(change_status >> 16); + reply = (void *) &status[0]; + rlen = 4; + TRACE_MSG1(HCD,"GET HUB_CHANGE_STATUS: %08x", bus_hcpriv->rh_hcpriv->hub_change_status); + } + else { + TRACE_MSG2(HCD,"HUB GETSTATUS invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_REQ_GET_INTERFACE: + // FIXME Verify type and interface number (wIndex) + TRACE_MSG1(HCD,"get interface %u",wIndex); + reply = (void *) &rh_hcpriv->curr_itf; + rlen = 1; + break; + case USB_REQ_GET_CONFIGURATION: + // FIXME Verify type + TRACE_MSG0(HCD,"get configuration"); + // Return a single byte value + reply = (void *) &rh_hcpriv->curr_cfg; + rlen = 1; + break; + default: + TRACE_MSG2(HCD,"H<-D HUB invalid request r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_RECIP_PORT: + switch (bRequest) { + case USB_REQ_GET_STATUS: + if (USB_REQ_TYPE_CLASS == type) { + /* USB2.0 11.24.2.7 pg 426 says first hword of reply are status (Tbl 11-21), + second hword is change (Tbl 11-22). */ + u32 change_status = ((wIndex -1) < bus_hcpriv->num_ports) ? + bus_hcpriv->rh_hcpriv->port_change_status[wIndex-1] : 0; + u32 hw_change_status = ((wIndex -1) < bus_hcpriv->num_ports) ? + rh_get_port_change_status(bus_hcpriv, wIndex) : 0; + + check_port_status(bus_hcpriv, wIndex, "HOST POLL"); + + status[0] = cpu_to_le16(hw_change_status & 0xFFFF); + status[1] = cpu_to_le16(change_status >> 16); + reply = (void *) &status[0]; + rlen = 4; + + TRACE_MSG3(HCD,"GET PORT_CHANGE_STATUS[%2d] %04x %04x", wIndex, status[0], status[1]); + } + else { + TRACE_MSG2(HCD,"PORT GETSTATUS invalid type r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + default: + TRACE_MSG2(HCD,"H<-D PORT invalid request r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + break; + case USB_REQ_RECIPIENT_INTERFACE: + case USB_REQ_RECIPIENT_ENDPOINT: + default: + TRACE_MSG2(HCD,"H<-D invalid recipient r: %02x t: %02x",bRequest,bmRequestType); + rc = rc_stall; + } + if (!rc && reply) { + // There is a reply to copy into place. + if (rlen > wLength) { + // Truncate the reply (used to get part of a descriptor). + rlen = wLength; + } + if (rlen > 0 && reply != urb->transfer_buffer) { + memcpy(urb->transfer_buffer,reply,rlen); + } + urb->actual_length = rlen; + } + } + // All done, "complete" it. + urb->status = rc; + TRACE_MSG3(HCD,"urb: %08lx completing status=%d len=%d",urb,urb->status,urb->actual_length); + hcd_rh_urb_complete(bus_hcpriv,urb); + TRACE_MSG2(HCD,"urb: %08lx completed->%d",urb,rc); + return rc; +} + +/* + * hcd_rh_unlink_urb() + */ +int hcd_rh_unlink_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb) +{ + struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; + RETURN_EINVAL_UNLESS(urb && (urb == rh_hcpriv->int_urb)); + del_timer(&rh_hcpriv->poll_timer); + rh_hcpriv->int_urb = NULL; + urb->status = -ENOENT; // Cancelled + hcd_rh_urb_complete(bus_hcpriv,urb); + usb_put_urb(urb); + return 0; +} +/* ********************************************************************************************** */ +/* + * For the hardware specific root hub component. + */ +irqreturn_t hcd_rh_int_hndlr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) dev_id; + + // Assume port status change. + //printk(KERN_INFO "%s: scheduling hcd_rh_portstatus_bh\n",__FUNCTION__); + + PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); + SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); + return IRQ_HANDLED; +} +/* ********************************************************************************************** */ +#if !defined(OTG_C99) +struct usbd_device_descriptor rh_device_descriptor; +struct usbd_configuration_descriptor rh_configuration_descriptor; +struct usbd_interface_descriptor rh_interface_descriptor; +struct usbd_endpoint_descriptor rh_endpoint_descriptor; +struct hub_descriptor rh_hub_descriptor; + +void rh_global_init(void) +{ + ZERO(rh_device_descriptor); + rh_device_descriptor.bLength = USB_DT_DEVICE_SIZE; + rh_device_descriptor.bDescriptorType = USB_DT_DEVICE; + rh_device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); + rh_device_descriptor.bDeviceClass = USB_CLASS_HUB; + rh_device_descriptor.bDeviceSubClass = 0; + rh_device_descriptor.bDeviceProtocol = 0; + rh_device_descriptor.bMaxPacketSize0 = 8; + rh_device_descriptor.iManufacturer = XHC_RH_STRING_MANUFACTURER; + rh_device_descriptor.iProduct = XHC_RH_STRING_PRODUCT; + rh_device_descriptor.iSerialNumber = XHC_RH_STRING_SERIAL; + rh_device_descriptor.bNumConfigurations = 1; + + ZERO(rh_configuration_descriptor); + rh_configuration_descriptor.bLength = USB_DT_CONFIG_SIZE; + rh_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION; + rh_configuration_descriptor.wTotalLength = __constant_cpu_to_le16(USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE + + USB_DT_ENDPOINT_SIZE); + rh_configuration_descriptor.bNumInterfaces = 1; + rh_configuration_descriptor.bConfigurationValue = 1; + rh_configuration_descriptor.iConfiguration = XHC_RH_STRING_CONFIGURATION; + + ZERO(rh_interface_descriptor); + rh_interface_descriptor.bLength = USB_DT_INTERFACE_SIZE; + rh_interface_descriptor.bDescriptorType = USB_DT_INTERFACE; + rh_interface_descriptor.bInterfaceNumber = 0; + rh_interface_descriptor.bAlternateSetting = 0; + rh_interface_descriptor.bNumEndpoints = 1; + rh_interface_descriptor.bInterfaceClass = USB_CLASS_HUB; + rh_interface_descriptor.bInterfaceSubClass = 0; + rh_interface_descriptor.bInterfaceProtocol = 0; + rh_interface_descriptor.iInterface = XHC_RH_STRING_INTERFACE; + + ZERO(rh_endpoint_descriptor); + rh_endpoint_descriptor.bLength = USB_DT_ENDPOINT_SIZE; + rh_endpoint_descriptor.bDescriptorType = USB_DT_ENDPOINT; + rh_endpoint_descriptor.bEndpointAddress = USB_DIR_IN | 1; // Fixed EP 1 + rh_endpoint_descriptor.bmAttributes = INTERRUPT; + rh_endpoint_descriptor.wMaxPacketSize = __constant_cpu_to_le16(4); + rh_endpoint_descriptor.bInterval = 0xFF; + + ZERO(rh_hub_descriptor); + rh_hub_descriptor.bDescLength = USB_DT_HUB_NONVAR_SIZE+2; + rh_hub_descriptor.bDescriptorType = USB_DT_HUB; +}; + +#else /* !defined(OTG_C99) */ +struct usbd_device_descriptor rh_device_descriptor = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION), + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 8, + .iManufacturer = XHC_RH_STRING_MANUFACTURER, + .iProduct = XHC_RH_STRING_PRODUCT, + .iSerialNumber = XHC_RH_STRING_SERIAL, + .bNumConfigurations = 1, +}; + +struct usbd_configuration_descriptor rh_configuration_descriptor = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = __constant_cpu_to_le16(USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE+USB_DT_ENDPOINT_SIZE), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = XHC_RH_STRING_CONFIGURATION, +}; + +struct usbd_interface_descriptor rh_interface_descriptor = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = XHC_RH_STRING_INTERFACE, +}; + +struct usbd_endpoint_descriptor rh_endpoint_descriptor = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | 1, // Fixed EP 1 + .bmAttributes = INTERRUPT, + .wMaxPacketSize = __constant_cpu_to_le16(4), + .bInterval = 0xFF, +}; + +/* This assumes 1-8 ports. + * If ports > 8, then DeviceRemovable and PortPwrCtrlMask need to be sized accordingly. + */ +struct hub_descriptor rh_hub_descriptor = { + .bDescLength = USB_DT_HUB_NONVAR_SIZE+2, + .bDescriptorType = USB_DT_HUB, +}; +#endif /* !defined(OTG_C99) */ + +/* ********************************************************************************************** */ +/* + * Copy static descriptors to buffer to allow modification for actual + * configuration and provide full configuration descriptor. + */ +STATIC void build_descriptors(struct bus_hcpriv *bus_hcpriv) +{ + struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; + void *bp = rh_hcpriv->descriptors; + + + rh_hcpriv->dd = (struct usbd_device_descriptor *) bp; + memcpy(bp, &rh_device_descriptor, sizeof(rh_device_descriptor)); + rh_hcpriv->cd = (struct usbd_configuration_descriptor *) bp = bp + USB_DT_DEVICE_SIZE; + memcpy(bp, &rh_configuration_descriptor, sizeof(rh_configuration_descriptor)); + rh_hcpriv->id = (struct usbd_interface_descriptor *) bp = bp + USB_DT_CONFIG_SIZE; + memcpy(bp, &rh_interface_descriptor, sizeof(rh_interface_descriptor)); + rh_hcpriv->ed = (struct usbd_endpoint_descriptor *) bp = bp + USB_DT_INTERFACE_SIZE; + memcpy(bp, &rh_endpoint_descriptor, sizeof(rh_endpoint_descriptor)); + rh_hcpriv->hd = (struct hub_descriptor *) bp = bp + USB_DT_ENDPOINT_SIZE; + memcpy(bp, &rh_hub_descriptor, sizeof(rh_hub_descriptor)); + + rh_hcpriv->dd->idVendor = cpu_to_le16(bus_hcpriv->rh_vendorid); + rh_hcpriv->dd->idProduct = cpu_to_le16(bus_hcpriv->rh_productid); + rh_hcpriv->dd->bcdDevice = cpu_to_le16(bus_hcpriv->rh_bcddevice); + rh_hcpriv->cd->bmAttributes = bus_hcpriv->rh_bmAttributes; + rh_hcpriv->cd->bMaxPower = bus_hcpriv->rh_bMaxPower; + rh_hcpriv->hd->bNbrPorts = bus_hcpriv->num_ports, + rh_hcpriv->hd->wHubCharacteristics = cpu_to_le16(hcd_hw_rh_hub_attributes(bus_hcpriv)); + rh_hcpriv->hd->bPwrOn2PwrGood = hcd_hw_rh_power_delay(bus_hcpriv); + rh_hcpriv->hd->bHubContrCurrent = hcd_hw_rh_hub_contr_current(bus_hcpriv); + rh_hcpriv->hd->DeviceRemovable = hcd_hw_rh_DeviceRemovable(bus_hcpriv); + rh_hcpriv->hd->PortPwrCtrlMask = hcd_hw_rh_PortPwrCtrlMask(bus_hcpriv); +} + +/* ********************************************************************************************** */ + +#define PORT_SYNC_MASK ( (1 << PORT_POWER) | (1 << PORT_SUSPEND) | (1 << PORT_RESET) ) + +/* puts OTG capable port into a_host or b_host state - attempt to use port */ +void rh_loc_sof_func(struct otg_instance *otg, u8 on) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); + int i; + unsigned long flags; + + RETURN_UNLESS (bus_hcpriv && bus_hcpriv->rh_hcpriv && bus_hcpriv->rh_hcpriv->port_change_status); + + local_irq_save(flags); + switch (on) { + case SET: + //printk(KERN_INFO"%s: SET\n", __FUNCTION__); + TRACE_MSG0(HCD, "RH LOC_SOF SET"); + + // Stop treating otg capable port as a device + bus_hcpriv->rh_hcpriv->otg_device_mask &= ~(0x1 << (bus_hcpriv->otg_port)); + + /* Sync the state of the HW to the shadow values, because + * the root hub was registered with usbcore a while back, and + * at the very least it probably tried to turn on the power, + * and until now, the otg_device_mask was preventing that. + */ + for (i = 0; i < 16; i++) { + + if (PORT_SYNC_MASK & (0x1 << i) & bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]) { + hcd_hw_rh_port_feature(bus_hcpriv, i, bus_hcpriv->otg_port, TRUE); + check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "OTG EN"); + } + } + + /* "Connect" on OTG port and simulate a port status change interrupt + */ + bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00010001; + TRACE_MSG2(HCD, "OUTPUT: RH LOC_SOF_SET port_change_status[%d]: %08x", + bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); + check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF SET"); + break; + + case RESET: + //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); + TRACE_MSG0(HCD, "RH LOC_SOF RESET"); + + check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF RESET - BEFORE"); + + // "Disconnect" on OTG port and simulate a port status change interrupt + bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] &= ~0x00000001; + bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00010000; + TRACE_MSG2(HCD, "OUTPUT: RH LOC_SOF_RESET port_change_status[%d]: %08x", + bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); + + + // Turn off port power. + hcd_hw_rh_port_feature(bus_hcpriv, PORT_POWER, bus_hcpriv->otg_port, FALSE); + //hcd_hw_rh_port_feature(bus_hcpriv, PORT_ENABLE, bus_hcpriv->otg_port, FALSE); + + check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF RESET - AFTER"); + + // Start treating otg capable port as a device + bus_hcpriv->rh_hcpriv->otg_device_mask |= (0x1 << (bus_hcpriv->otg_port)); + break; + } + + bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << bus_hcpriv->otg_port); + TRACE_MSG0(HCD, "Schedule hcd_rh_portstatus_bh"); + PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); + SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); + local_irq_restore(flags); +} + +/* puts OTG capable port into a_host or b_host state - attempt to use port */ +void rh_loc_suspend_func(struct otg_instance *otg, u8 on) +{ + struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); + struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; + int i; + unsigned long flags; + + //printk(KERN_INFO"%s: \n", __FUNCTION__); + RETURN_UNLESS (bus_hcpriv && bus_hcpriv->rh_hcpriv && bus_hcpriv->rh_hcpriv->port_change_status); + + local_irq_save(flags); + switch (on) { + case SET: + TRACE_MSG0(HCD, "OUTPUT: RH LOC_SUSPEND SET"); + + rh_hcpriv->suspended = 1; + + // suspend + hcd_hw_rh_port_feature(bus_hcpriv, PORT_SUSPEND, bus_hcpriv->otg_port, TRUE); + + // "Connect" on OTG port and simulate a port status change interrupt + bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00040004; + TRACE_MSG2(HCD, "OUTPUT: RH LOC_SUSPNED_SET port_change_status[%d]: %08x", + bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); + check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "LOC_SUSPEND SET"); + break; + + case RESET: + + TRACE_MSG0(HCD, "OUTPUT: RH LOC_SUSPEND RESET"); + check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "LOC_SUSPEND RESET - BEFORE"); + + // "Disconnect" on OTG port and simulate a port status change interrupt + bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] &= ~0x00000004; + bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00040000; + TRACE_MSG2(HCD, "OUTPUT: RH LOC_SUSPEND port_change_status[%d]: %08x", + bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); + + + // suspend + hcd_hw_rh_port_feature(bus_hcpriv, PORT_SUSPEND, bus_hcpriv->otg_port, FALSE); + check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF RESET - AFTER"); + + rh_hcpriv->suspended = 0; + + break; + } + + bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << bus_hcpriv->otg_port); + TRACE_MSG0(HCD, "Schedule hcd_rh_portstatus_bh"); + PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); + SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); + local_irq_restore(flags); +} + +void rh_suspend_func(struct otg_instance *otg, u8 flag) +{ + /* suspends the OTG capable port */ + //TRACE_MSG0(HCD, "--"); + switch (flag) { + case SET: + //printk(KERN_INFO"%s: SET\n", __FUNCTION__); + TRACE_MSG0(HCD, "OUTPUT: RH SUSPEND_SET"); + break; + case RESET: + //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); + TRACE_MSG0(HCD, "OUTPUT: RH SUSPEND_RESET"); + break; + } +} + +/* ********************************************************************************************* */ + +int hcd_rh_init(struct bus_hcpriv *bus_hcpriv) +{ + struct rh_hcpriv *rh_hcpriv; + int wIndex; + int port_mask; + + #if !defined(OTG_C99) + rh_global_init(); + #endif /* !defined(OTG_C99) */ + + //printk(KERN_INFO"%s: \n", __FUNCTION__); + RETURN_ZERO_UNLESS (bus_hcpriv); + RETURN_ENOMEM_UNLESS ((rh_hcpriv = bus_hcpriv->rh_hcpriv = + (struct rh_hcpriv *) kmalloc(sizeof(struct rh_hcpriv),GFP_KERNEL))); + memset(rh_hcpriv, 0, sizeof(struct rh_hcpriv)); + + //printk(KERN_INFO "%s: rh_hcpriv: %08x\n",__FUNCTION__,(u32)(void*)rh_hcpriv); + //init_timer(&rh_hcpriv->poll_timer); + // Get the number of ports, and which port is OTG capable. + + //rh_hcpriv->num_ports = hcd_hw_rh_num_ports(bus_hcpriv); + + // Allocate the shadow port_change_status array. + UNLESS ((rh_hcpriv->port_change_status = (u32 *) kmalloc((bus_hcpriv->num_ports*sizeof(u32)),GFP_KERNEL))) { + //printk(KERN_ERR "%s: allocation of root hub shadow port data fails\n",__FUNCTION__); + kfree(rh_hcpriv); + bus_hcpriv->rh_hcpriv = rh_hcpriv = NULL; + return -ENOMEM; + } + memset(rh_hcpriv->port_change_status,0,(bus_hcpriv->num_ports*sizeof(u32))); + UNLESS ((rh_hcpriv->dev = usb_alloc_dev(NULL, bus_hcpriv->usb_bus, 0))) { + //printk(KERN_ERR "%s: allocation of root hub dev fails\n",__FUNCTION__); + kfree(rh_hcpriv->port_change_status); + rh_hcpriv->port_change_status = NULL; + kfree(rh_hcpriv); + bus_hcpriv->rh_hcpriv = rh_hcpriv = NULL; + return -ENOMEM; + } + rh_hcpriv->dev->speed = USB_SPEED_FULL; // QQSV --- this belongs in the HW dependent portion... + + // Build up USB descriptors + build_descriptors(bus_hcpriv); + + // Leave OTG capable port as disabled as possible until OTG core enables it. + rh_hcpriv->otg_device_mask = bus_hcpriv->otg_capable_mask; + + for (wIndex = 1; wIndex <= bus_hcpriv->num_ports; wIndex++) { + port_mask = (1 << (wIndex - 1)); + if (port_mask & bus_hcpriv->otg_capable_mask) + TRACE_MSG1(HCD,"OTG capable port %d", wIndex); + else + TRACE_MSG1(HCD,"Standard port %d", wIndex); + } + return 0; +} + +void hcd_rh_exit(struct bus_hcpriv *bus_hcpriv) +{ + struct rh_hcpriv *rh_hcpriv; + RETURN_UNLESS(bus_hcpriv && (rh_hcpriv = bus_hcpriv->rh_hcpriv)); + + (void *) hcd_rh_unlink_urb(bus_hcpriv,rh_hcpriv->int_urb); + + if (rh_hcpriv->dev) { + usb_put_dev(rh_hcpriv->dev); + rh_hcpriv->dev = NULL; + } + if (rh_hcpriv->port_change_status) { + kfree(rh_hcpriv->port_change_status); + rh_hcpriv->port_change_status = NULL; + } + kfree(rh_hcpriv); + bus_hcpriv->rh_hcpriv = rh_hcpriv = NULL; +} + diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd.c linux/drivers/otg/ocd/otg-hcd/hcd.c --- linux/drivers/no-otg/ocd/otg-hcd/hcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-hcd/hcd.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,139 @@ +/* + * otg/ocd/otg-hcd/hcd.c - OTG Host Controller Driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * Notes + * + * 1. The usbd-bi layer has be re-implemented to re-factor the UDC layer in a way to + * simplify implementation of UDC drivers. As much of the complexity of dealing with the middle + * layers and buffer (urb) handling is provided for in the common bi layer. + * + * 2. TODO The udc interface will be further modified to allow the UDC to export a block of + * function pointers for the common bi layer to use. This will allow the common layer to + * implement default operations where the UDC does not provide an function. For example many UDC + * drivers do not provide full support for cable detection and usb pullup control. If these + * routines are not provided the common layer will supply defaults. This eliminates a reasonably + * larger amount of effectively unused code from many of the udc drivers. + * + */ +/*! + * @file otg/ocd/otg-hcd/hcd.c + * @brief Root Hub for Generic Host Controller Driver + * + * + * This file initializes the low level hardware drivers. + * + * This is the linux 2.4 version. + * + * @ingroup OTGHCD + */ + +#include + + +#include +#include +#include +#include +#include +#include +#include + +/* ************************************************************************************* */ + +void hcd_en_func (struct otg_instance *otg, u8 flag) +{ + //struct hcd_instance *hcd = otg->hcd; + TRACE_MSG0(HCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(HCD, "OUTPUT: HCD_EN_SET"); + break; + case RESET: + TRACE_MSG0(HCD, "OUTPUT: HCD_EN_RESET"); + break; + } +} + +void hcd_init_func (struct otg_instance *otg, u8 flag) +{ + struct hcd_instance *hcd = otg->hcd; + + TRACE_MSG0(HCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(HCD, "OUTPUT: HCD_INIT_SET"); + otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT SET HCD_OK"); + break; + case RESET: + TRACE_MSG0(HCD, "OUTPUT: HCD_INIT_RESET"); + otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT SET HCD_OK"); + break; + } +} + +/* ************************************************************************************* */ + +void hcd_loc_sof_func (struct otg_instance *otg, u8 flag) +{ + //struct hcd_instance *hcd = otg->hcd; + TRACE_MSG0(HCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(HCD, "OUTPUT: HCD_LOC_SOF_SET"); + break; + case RESET: + TRACE_MSG0(HCD, "OUTPUT: HCD_LOC_SOF_RESET"); + break; + } +} + +void hcd_suspend_func (struct otg_instance *otg, u8 flag) +{ + //struct hcd_instance *hcd = otg->hcd; + TRACE_MSG0(HCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(HCD, "OUTPUT: HCD_SUSPEND_EN_SET"); + break; + case RESET: + TRACE_MSG0(HCD, "OUTPUT: HCD_SUSPEND_EN_RESET"); + break; + } +} + +void hcd_remote_wakeup_en_func (struct otg_instance *otg, u8 flag) +{ + //struct hcd_instance *hcd = otg->hcd; + TRACE_MSG0(HCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(HCD, "OUTPUT: HCD_REMOTE_WAKEUP_EN_SET"); + break; + case RESET: + TRACE_MSG0(HCD, "OUTPUT: HCD_REMOTE_WAKEUP_EN_RESET"); + break; + } +} + +void hcd_hnp_en_func (struct otg_instance *otg, u8 flag) +{ + //struct hcd_instance *hcd = otg->hcd; + TRACE_MSG0(HCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(HCD, "OUTPUT: HCD_HNP_EN_SET"); + break; + case RESET: + TRACE_MSG0(HCD, "OUTPUT: HCD_HNP_EN_RESET"); + break; + } +} + + diff -uNr linux/drivers/no-otg/ocd/otg-i2c/i2c-l26.c linux/drivers/otg/ocd/otg-i2c/i2c-l26.c --- linux/drivers/no-otg/ocd/otg-i2c/i2c-l26.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-i2c/i2c-l26.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,160 @@ +/* + * otg/ocd/otg-i2c/i2c-l26.c -- Linux 2.6 I2C access + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-i2c/i2c-l26.c + * @brief Linux I2C I/O via generic i2c device. + * + * @ingroup ISP1301TCD + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include "isp1301.h" + +/* ********************************************************************************************* */ + +/* + * N.B. i2c functions must not be called from interrupt handlers + */ + +static struct file *i2c_file; +static struct i2c_client *omap_i2c_client; +static int initstate_i2c; +static int initstate_region; +#define MAX_I2C 16 + +#ifdef CONFIG_OMAP_H2 +#define ADAPTOR_NAME "OMAP I2C" +#endif /* CONFIG_OMAP_H2 */ +#ifdef CONFIG_ARCH_MAINSTONE +#define ADAPTOR_NAME "PXA-I2C-Adapter" +#endif /* CONFIG_ARCH_MAINSTONE */ + +/*! i2c_configure + * Attempt to find and open generic i2c device + * @return - non-zero for failure + */ +int i2c_configure(char *name, int addr) +{ + char filename[20]; + int tmp; + + RETURN_ZERO_IF(initstate_i2c); + + /*find the I2C driver we need + */ + for (tmp = 0; tmp < MAX_I2C; tmp++) { + + sprintf(filename, "/dev/i2c/%d", tmp); + + printk(KERN_INFO"%s: %s\n", __FUNCTION__, filename); + + UNLESS (IS_ERR(i2c_file = filp_open(filename, O_RDWR, 0))) { + + //printk(KERN_INFO"%s: %s found\n", __FUNCTION__, filename); + + /*found some driver */ + omap_i2c_client = (struct i2c_client *)i2c_file->private_data; + + printk(KERN_INFO"%s: found %s\n", __FUNCTION__, omap_i2c_client->adapter->name); + if (strlen(omap_i2c_client->adapter->name) >= 8) { + if (!strncmp(omap_i2c_client->adapter->name, name, strlen(name))) + break; /*we found our driver! */ + } + omap_i2c_client = NULL; + filp_close(i2c_file, NULL); + } + printk(KERN_INFO"%s: %s %d\n", __FUNCTION__, filename, i2c_file); + } + if (tmp == MAX_I2C) { // Nothing found + printk(KERN_ERR"%s: cannot find I2C driver", __FUNCTION__); + return -ENODEV; + } + omap_i2c_client->addr = addr; + initstate_i2c = 1; + return 0; +} + +/*! i2c_close + * Close i2c device fd. + */ +void i2c_close(void) +{ + if (initstate_i2c) + filp_close(i2c_file, NULL); + initstate_i2c = 0; +} + +/*! i2c_readb + * Read byte from i2c device + * @param subaddr + */ +u8 i2c_readb(u8 subaddr) +{ + u8 buf = 0; + i2c_master_send(omap_i2c_client, &subaddr, 1); + i2c_master_recv(omap_i2c_client, &buf, 1); + TRACE_MSG2(TCD, "addr: %02x buf: %02x", subaddr, buf); + return buf; +} + +/*! i2c_readw + * Read word from i2c device + * @param subaddr + */ +u16 i2c_readw(u8 subaddr) +{ + u16 buf = 0; + i2c_master_send(omap_i2c_client, &subaddr, 1); + i2c_master_recv(omap_i2c_client, (u8 *)&buf, 2); + TRACE_MSG2(TCD, "addr: %02x buf: %04x", subaddr, buf); + return buf; +} + +/*! i2c_readl + * Read long from i2c device + * @param subaddr + */ +u32 i2c_readl(u8 subaddr) +{ + u32 buf = 0; + i2c_master_send(omap_i2c_client, &subaddr, 1); + i2c_master_recv(omap_i2c_client, (u8 *)&buf, 4); + TRACE_MSG2(TCD, "addr: %02x buf: %08x", subaddr, buf); + return buf; +} + + +/*! i2c_writeb + * Writw byte to i2c device + * @param subaddr + * @param buf + */ +int i2c_writeb(u8 subaddr, u8 buf) +{ + char tmpbuf[2]; + + tmpbuf[0] = subaddr; /*register number */ + tmpbuf[1] = buf; /*register data */ + i2c_master_send(omap_i2c_client, &tmpbuf[0], 2); + + return 0; +} + diff -uNr linux/drivers/no-otg/ocd/otg-pcd/pcd-init-l24.c linux/drivers/otg/ocd/otg-pcd/pcd-init-l24.c --- linux/drivers/no-otg/ocd/otg-pcd/pcd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-pcd/pcd-init-l24.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,119 @@ +/* + * otg/ocd/otg-pcd/pcd-init-l24.c - OTG Peripheral Controller Driver Module Initialization + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-pcd/pcd-init-l24.c + * @brief PCD only driver init. + * + * + * PCD Initialization + * + * This file initializes all of the low level hardware drivers for a PCD. + * + * The Peripheral Controller Driver (pcd) implements the lowest layer of the + * USBD stack to send and receive data from the USB actings as a USB + * peripheral. + * + * Notes + * + * 1. This is the linux 2.4 version. + * + * TODO + * + * 1. hook up serial_number_str to pcd.c for use with bus interface layer. + * + * + * @ingroup OTGPCD + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) +MODULE_LICENSE ("GPL"); +#endif +MODULE_PARM (serial_number_str, "s"); +MODULE_PARM_DESC (serial_number_str, "Serial Number"); +MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MODULE_DESCRIPTION ("USB On-The-Go PCD"); +#endif +char *serial_number_str; + +otg_tag_t PCD; +struct pcd_instance *pcd_instance; + +/* ************************************************************************************* */ + +/* pcd_modexit - module exit or init failure cleanup + * + * Specifically for each driver: + * + * call ops.mod_exit + * reset instance address and ops table address in state machine to NULL + * invalidate tag + */ +static void pcd_modexit (void) +{ + struct otg_instance *otg = ocd_instance->otg; + printk(KERN_INFO"%s\n", __FUNCTION__); + if (otg) + otg_exit(otg); + if (pcd_ops.mod_exit) pcd_ops.mod_exit(); + pcd_instance = otg_set_pcd_ops(NULL); + PCD = otg_trace_invalidate_tag(PCD); +} + +/* pcd_modinit - linux module initialization + * + * This needs to initialize the ocd, pcd and tcd drivers. + * + * Specifically for each driver: + * + * obtain tag + * pass ops table address to state machine and get instance address + * call ops.mod_init + * + * Note that we automatically provide a default tcd_init if + * none is set. + */ +static int pcd_modinit (void) +{ + printk(KERN_INFO"%s\n", __FUNCTION__); + + #if !defined(OTG_C99) + pcd_global_init(); + #endif /* !defined(OTG_C99) */ + + PCD = otg_trace_obtain_tag(); + + UNLESS(pcd_ops.pcd_init_func) pcd_ops.pcd_init_func = pcd_init_func; + THROW_UNLESS(pcd_instance = otg_set_pcd_ops(&pcd_ops), error); + THROW_IF((pcd_ops.mod_init) ? pcd_ops.mod_init() : 0, error); + + CATCH(error) { + pcd_modexit(); + return -EINVAL; + } + return 0; +} +MOD_EXIT(pcd_modexit); +MOD_INIT(pcd_modinit); diff -uNr linux/drivers/no-otg/ocd/otg-pcd/pcd-ocd-init-l24.c linux/drivers/otg/ocd/otg-pcd/pcd-ocd-init-l24.c --- linux/drivers/no-otg/ocd/otg-pcd/pcd-ocd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-pcd/pcd-ocd-init-l24.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,141 @@ +/* + * otg/pcd/pcd-ocd-init-l24.c - OTG Peripheral and OTG Controller Drivers Module Initialization + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-pcd/pcd-ocd-init-l24.c + * @brief PCD only driver init. + * + * + * PCD/OCD Initialization + * + * This file initializes all of the low level hardware drivers for a combined + * PCD/OCD module. It does not initialize the TCD module. + * + * USB Periphirals using this must implement the following: + * + * ocd_ops operations from ocd driver + * pcd_ops operations from pcd driver + * + * The OTG Controller driver (ocd) implements any required OTG timers and if + * necessary mediates access to and initializes the OTG and/or USB hardware. + * + * The Peripheral Controller Driver (pcd) implements the lowest layer of the + * USBD stack to send and receive data from the USB actings as a USB + * peripheral. + * + * Notes + * + * 1. This is the linux 2.4 version. + * + * TODO + * + * 1. hook up serial_number_str to pcd.c for use with bus interface layer. + * + * @ingroup OTGPCD + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) +MODULE_LICENSE ("GPL"); +#endif +MODULE_PARM (serial_number_str, "s"); +MODULE_PARM_DESC (serial_number_str, "Serial Number"); +MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MODULE_DESCRIPTION ("USB On-The-Go PCD"); +#endif +char *serial_number_str; + +otg_tag_t PCD; +struct pcd_instance *pcd_instance; +otg_tag_t OCD; +struct ocd_instance *ocd_instance; + + +/* ************************************************************************************* */ + +/* pcd_ocd_modexit - module exit or init failure cleanup + * + * Specifically for each driver: + * + * call ops.mod_exit + * reset instance address and ops table address in state machine to NULL + * invalidate tag + */ +static void pcd_ocd_modexit (void) +{ + struct otg_instance *otg = ocd_instance->otg; + printk(KERN_INFO"%s\n", __FUNCTION__); + if (otg) + otg_exit(otg); + + if (pcd_ops.mod_exit) pcd_ops.mod_exit(); + pcd_instance = otg_set_pcd_ops(NULL); + PCD = otg_trace_invalidate_tag(PCD); + + if (ocd_ops.mod_exit) ocd_ops.mod_exit(); + ocd_instance = otg_set_ocd_ops(NULL); + OCD = otg_trace_invalidate_tag(OCD); +} + +/* pcd_ocd_modinit - linux module initialization + * + * This needs to initialize the ocd, pcd and tcd drivers. + * + * Specifically for each driver: + * + * obtain tag + * pass ops table address to state machine and get instance address + * call ops.mod_init + * + * Note that we automatically provide a default tcd_init if + * none is set. + */ +static int pcd_ocd_modinit (void) +{ + printk(KERN_INFO"%s\n", __FUNCTION__); + + #if !defined(OTG_C99) + pcd_global_init(); + #endif /* !defined(OTG_C99) */ + + UNLESS(pcd_ops.pcd_init_func) pcd_ops.pcd_init_func = pcd_init_func; + PCD = otg_trace_obtain_tag(); + THROW_UNLESS(pcd_instance = otg_set_pcd_ops(&pcd_ops), error); + THROW_IF((pcd_ops.mod_init) ? pcd_ops.mod_init() : 0, error); + + OCD = otg_trace_obtain_tag(); + THROW_UNLESS(ocd_instance = otg_set_ocd_ops(&ocd_ops), error); + THROW_IF((ocd_ops.mod_init) ? ocd_ops.mod_init() : 0, error); + + CATCH(error) { + pcd_ocd_modexit(); + return -EINVAL; + } + return 0; +} + +MOD_EXIT (pcd_ocd_modexit); +MOD_INIT (pcd_ocd_modinit); + + diff -uNr linux/drivers/no-otg/ocd/otg-pcd/pcd.c linux/drivers/otg/ocd/otg-pcd/pcd.c --- linux/drivers/no-otg/ocd/otg-pcd/pcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-pcd/pcd.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,845 @@ +/* + * otg/ocd/otg-pcd/pcd.c - OTG Peripheral Controller Driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-pcd/pcd.c + * @brief PCD only driver init. + * Notes + * + * 1. The pcd file abstracts much of the UDC complexity as possible and + * provides a common implementation that is shared to various extents by the + * various pcd drivers. + * + * @ingroup OTGPCD + */ + +#include +#include + + +extern char *serial_number_str; + +/* ******************************************************************************************* */ +/*! bus_request_endpoints + */ +int bus_request_endpoints(struct usbd_bus_instance *bus, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, + struct usbd_endpoint_request *requestedEndpoints) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + int i; + TRACE_MSG0(PCD, "BUS_REQUEST ENDPOINTS:"); + if (usbd_pcd_ops.request_endpoints(pcd, endpoint_map_array, endpointsRequested, requestedEndpoints)) { + printk(KERN_INFO"%s: failed\n", __FUNCTION__); + return -EINVAL; + } + for (i = 0; i < endpointsRequested; i++) { + struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; + TRACE_MSG4(PCD, "address: %02x physical: %02x request: %02x size: %04x", + endpoint_map->bEndpointAddress[0], endpoint_map->physicalEndpoint[0], + endpoint_map->bmAttributes[0], endpoint_map->wMaxPacketSize[0]); + } + return 0; +} + +/*! bus_set_endpoints + */ +int bus_set_endpoints(struct usbd_bus_instance *bus, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + TRACE_MSG0(PCD, "SET ENDPOINTS"); + return usbd_pcd_ops.set_endpoints ? usbd_pcd_ops.set_endpoints(pcd, endpointsRequested, endpoint_map_array) : 0; +} + +/*! bus_set_address + */ +int bus_set_address(struct usbd_bus_instance *bus, int address) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + if (usbd_pcd_ops.set_address) usbd_pcd_ops.set_address (pcd, address); + return 0; +} + +/* ******************************************************************************************* */ + +/*! pcd_search_endpoint_index - find endpoint map index given endpoint address + */ +int pcd_search_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress) +{ + int i; + for (i = 0; i < bus->endpoints; i++) + BREAK_IF (bus->endpoint_array[i].bEndpointAddress == bEndpointAddress); + return i; +} + +/*! pcd_search_endpoint - find endpoint given endpoint address + */ +struct usbd_endpoint_instance * pcd_search_endpoint(struct usbd_bus_instance *bus, int bEndpointAddress) +{ + int i = pcd_search_endpoint_index(bus, bEndpointAddress); + TRACE_MSG2(PCD, "BUS_SEARCH ENDPOINT: addr: %02x index: %d", bEndpointAddress, i); + RETURN_NULL_UNLESS(i < bus->endpoints); + return &(bus->endpoint_array[i]); +} + +/*! bus_endpoint_halted - check if endpoint halted + * Used by the USB Device Core to check endpoint halt status. + * + * Return actual halted status if available or'd with endpoint->feature_setting set value. + * Assume not halted if udc driver does not provide an endpoint halted function. + */ +int bus_endpoint_halted (struct usbd_bus_instance *bus, int bEndpointAddress) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + int endpoint_index = pcd_search_endpoint_index(bus, bEndpointAddress); + struct usbd_endpoint_instance *endpoint = pcd_search_endpoint(bus, bEndpointAddress); + int halted; + + TRACE_MSG1(PCD, "BUS_ENDPOINT HALTED: endpoint: %p", (int) endpoint); + RETURN_EINVAL_UNLESS(endpoint); + + //halted = usbd_pcd_ops.endpoint_halted ? usbd_pcd_ops.endpoint_halted(pcd, endpoint_index) : 0; + halted = usbd_pcd_ops.endpoint_halted ? usbd_pcd_ops.endpoint_halted(pcd, endpoint) : 0; + TRACE_MSG1(PCD, "BUS_ENDPOINT HALTED: %08x", bus->endpoint_array[endpoint_index].feature_setting); + + return halted || (endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)? 1 : 0); +} + +/*! bus_halt_endpoint - handle set/clear feature requests + * Used by the USB Device Core to set/clear endpoint halt status. + * + * We assume that if the udc driver does not implement anything then + * we should just return zero for ok. + */ +int bus_halt_endpoint (struct usbd_bus_instance *bus, int bEndpointAddress, int flag) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + int endpoint_index = pcd_search_endpoint_index(bus, bEndpointAddress); + struct usbd_endpoint_instance *endpoint = pcd_search_endpoint(bus, bEndpointAddress); + + TRACE_MSG1(PCD, "BUS_ENDPOINT HALT: endpoint: %p", (int) endpoint); + RETURN_EINVAL_UNLESS(endpoint); + endpoint->feature_setting = flag; + return usbd_pcd_ops.halt_endpoint ? usbd_pcd_ops.halt_endpoint(pcd, endpoint, flag) : 0; +} + +/*! pcd_disable_endpoints - disable udc and all endpoints + */ +static void pcd_disable_endpoints (struct usbd_bus_instance *bus) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + int i; + RETURN_IF (!bus || !bus->endpoint_array); + for (i = 1; i < usbd_pcd_ops.max_endpoints; i++) { + struct usbd_endpoint_instance *endpoint = (bus->endpoint_array + i); + CONTINUE_IF (!endpoint); + usbd_flush_endpoint (endpoint); + } +} + + +/*! bus_event_handler - handle generic bus event + * Called by usb core layer to inform bus of an event. + */ +int bus_event_handler (struct usbd_bus_instance *bus, usbd_device_event_t event, int data) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + int epn; + //struct usbd_endpoint_map *endpoint_map_array = bus->function_instance->endpoint_map_array; + + //TRACE_MSG1(PCD, "BUS_EVENT %x", event); + switch (event) { + case DEVICE_UNKNOWN: + break; + + case DEVICE_INIT: + TRACE_MSG0(PCD, "EVENT INIT"); + break; + + case DEVICE_CREATE: + TRACE_MSG0(PCD, "EVENT CREATE"); + pcd_disable_endpoints (bus); + if (usbd_pcd_ops.start) usbd_pcd_ops.start (pcd); + break; + + case DEVICE_HUB_CONFIGURED: + TRACE_MSG0(PCD, "EVENT HUB_CONFIGURED"); + //bi_connect (bus, NULL); + break; + + case DEVICE_RESET: + TRACE_MSG0(PCD, "EVENT RESET"); + + if (usbd_pcd_ops.set_address) usbd_pcd_ops.set_address (pcd, 0); + + if (usbd_pcd_ops.reset_ep) usbd_pcd_ops.reset_ep (pcd, 0); + + pcd_disable_endpoints (bus); + otg_event_irq(pcd->otg, BUS_RESET | BUS_SUSPENDED_, PCD, "DEVICE_RESET"); + + break; + + case DEVICE_ADDRESS_ASSIGNED: + TRACE_MSG1(PCD, "EVENT ADDRESSED: %d", data); + + otg_event_irq(pcd->otg, ADDRESSED, PCD, "ADDRESSED"); + + break; + + case DEVICE_CONFIGURED: + TRACE_MSG0(PCD, "EVENT CONFIGURED"); + otg_event_irq(pcd->otg, CONFIGURED, PCD, "CONFIGURED"); + // iterate across the physical endpoint instance array to enable the endpoints + if (usbd_pcd_ops.setup_ep) + for (epn = 1; epn < bus->endpoints; epn++) + usbd_pcd_ops.setup_ep (pcd, epn, bus->endpoint_array + epn); + break; + + case DEVICE_DE_CONFIGURED: + TRACE_MSG0(PCD, "EVENT DE-CONFIGURED"); + break; + + case DEVICE_SET_INTERFACE: + TRACE_MSG0(PCD, "EVENT SET INTERFACE"); + break; + + case DEVICE_SET_FEATURE: + TRACE_MSG0(PCD, "EVENT SET FEATURE"); + break; + + case DEVICE_CLEAR_FEATURE: + TRACE_MSG0(PCD, "EVENT CLEAR FEATURE"); + break; + + case DEVICE_BUS_INACTIVE: + TRACE_MSG0(PCD, "EVENT INACTIVE"); + TRACE_MSG1(PCD, "EVENT INACTIVE: state: %x", bus->device_state); + otg_event_irq(pcd->otg, BUS_SUSPENDED, PCD, "DEVICE_BUS_INACTIVE"); + break; + + case DEVICE_BUS_ACTIVITY: + TRACE_MSG0(PCD, "EVENT ACTIVITY"); + otg_event_irq(pcd->otg, BUS_SUSPENDED_, PCD, "DEVICE_BUS_ACTIVITY"); + break; + + case DEVICE_POWER_INTERRUPTION: + TRACE_MSG0(PCD, "POWER INTERRUPTION"); + break; + + case DEVICE_HUB_RESET: + TRACE_MSG0(PCD, "HUB RESET"); + break; + + case DEVICE_DESTROY: + TRACE_MSG0(PCD, "DEVICE DESTROY"); + //bi_disconnect (bus, NULL); + pcd_disable_endpoints (bus); + if (usbd_pcd_ops.stop) usbd_pcd_ops.stop (pcd); + break; + + case DEVICE_CLOSE: + TRACE_MSG0(PCD, "DEVICE CLOSE"); + break; + default: + TRACE_MSG1(PCD, "DEVICE UNKNOWN: %d", event); + break; + } + TRACE_MSG2(PCD, "EVENT bNumInterfaces: %x alternates: %x", bus->bNumInterfaces, bus->alternates); + return 0; +} + + +/*! bus_start_endpoint_in + */ +int bus_start_endpoint_in (struct usbd_bus_instance *bus, struct usbd_endpoint_instance *endpoint) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + unsigned long flags; + + RETURN_ZERO_IF (!endpoint); + ocd_ops.interrupts++; + + local_irq_save (flags); + + // call pcd_start_endpoint_in IFF we didn't previously have a tx urb + if (!endpoint->tx_urb && pcd_tx_next_irq (endpoint)) { + usbd_pcd_ops.start_endpoint_in (pcd, endpoint); + } + local_irq_restore (flags); + return 0; +} + +/*! bus_start_endpoint_out + */ +int bus_start_endpoint_out (struct usbd_bus_instance *bus, struct usbd_endpoint_instance *endpoint) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + unsigned long flags; + + RETURN_ZERO_IF (!endpoint); + ocd_ops.interrupts++; + local_irq_save (flags); + // call pcd_start_endpoint_OUT IFF we didn't previously have a rcv urb + if (!endpoint->rcv_urb && pcd_rcv_next_irq (endpoint)) { + usbd_pcd_ops.start_endpoint_out (pcd, endpoint); + } + local_irq_restore (flags); + return 0; +} + + +/*! bus_cancel_urb_irq - cancel sending an urb + * Used by the USB Device Core to cancel an urb. + */ +int bus_cancel_urb_irq (struct usbd_urb *urb) +{ + struct usbd_bus_instance *bus = urb->bus; + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + RETURN_EINVAL_IF (!urb); + TRACE_MSG1(PCD, "BUS_CANCEL URB: %x", (int)urb); + switch (urb->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + case USB_DIR_IN: + // is this the active urb? + if (urb->endpoint->tx_urb == urb) { + urb->endpoint->tx_urb = NULL; + TRACE_MSG1(PCD, "CANCEL IN URB: %02x", urb->endpoint->bEndpointAddress); + if (usbd_pcd_ops.cancel_in_irq) usbd_pcd_ops.cancel_in_irq (pcd, urb); + } + usbd_urb_finished_irq (urb, SEND_CANCELLED); + break; + + case USB_DIR_OUT: + // is this the active urb? + if (urb->endpoint->rcv_urb == urb) { + urb->endpoint->rcv_urb = NULL; + TRACE_MSG1(PCD, "CANCEL OUT URB: %02x", urb->endpoint->bEndpointAddress); + if (usbd_pcd_ops.cancel_out_irq) usbd_pcd_ops.cancel_out_irq (pcd, urb); + } + TRACE_MSG0(PCD, "CANCEL RECV URB"); + usbd_urb_finished_irq (urb, RECV_CANCELLED); + break; + } + urb->endpoint->last = urb->endpoint->sent = 0; + return 0; +} + +/*! pcd_rcv_finished_irq + */ +struct usbd_urb * pcd_rcv_finished_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad) +{ + struct usbd_urb *rcv_urb; + + // if we had an urb then update actual_length, dispatch if neccessary + if (likely ( (int) (rcv_urb = endpoint->rcv_urb))) { + struct usbd_bus_instance *bus = rcv_urb->bus; + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + + //TRACE_MSG5(PCD, "BUS_RCV COMPLETE: finished buffer: %x actual: %d len: %d bad: %d: status: %d", + // rcv_urb->buffer, rcv_urb->actual_length, len, urb_bad, rcv_urb->status); + + if (!urb_bad && !endpoint->rcv_error && (rcv_urb->bus->status == USBD_OK)) { + +#if 0 + int i; + u8 *cp = rcv_urb->buffer; + for (i = 0; i < rcv_urb->actual_length + len; i+= 8) { + + TRACE_MSG8(PCD, "RECV %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] + ); + } +#endif + // increment the received data size + rcv_urb->actual_length += len; + + endpoint->rcv_urb = NULL; + rcv_urb->jiffies = jiffies; + rcv_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum () : 0; + //TRACE_MSG1(PCD, "BUS_RCV COMPLETE: framenum: %x", (int) rcv_urb->framenum); + usbd_urb_finished_irq (rcv_urb, RECV_OK); + rcv_urb = NULL; + } + else { + rcv_urb->actual_length = 0; + //endpoint->rcv_error = 1; + } + } + + // if we don't have an urb see if we can get one + return pcd_rcv_next_irq (endpoint); +} + +/*! pcd_rcv_complete_irq + */ +struct usbd_urb * pcd_rcv_complete_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad) +{ + struct usbd_urb *rcv_urb; + + // if we had an urb then update actual_length, dispatch if neccessary + if (likely ( (int) (rcv_urb = endpoint->rcv_urb))) { + struct usbd_bus_instance *bus = rcv_urb->bus; + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + + TRACE_MSG4(PCD, "BUS_RCV COMPLETE: actual: %d len: %d bad: %d: status: %d", + rcv_urb->actual_length, len, urb_bad, rcv_urb->status); + + //TRACE_MSG4(PCD, "BUS_RCV COMPLETE: request: %d buffer: %d packet: %d transfer: %d", + // rcv_urb->request_length, rcv_urb->buffer_length, + // endpoint->wMaxPacketSize, endpoint->rcv_transferSize); + + // check the urb is ok, are we adding data less than the packetsize + if (!urb_bad && !endpoint->rcv_error && (rcv_urb->bus->status == USBD_OK) && (len <= endpoint->wMaxPacketSize)) { + + // increment the received data size + rcv_urb->actual_length += len; + + // if the current received data is short (less than full packetsize) which + // indicates the end of the bulk transfer, we have received the maximum + // transfersize, or if we do not have enough room to receive another packet + // then pass this data up to the function driver + + // XXX this needs to be fixed, for example the MSC driver + // has varying maximum sizes + + + if ( + ( (len < endpoint->wMaxPacketSize) || + (rcv_urb->actual_length >= endpoint->rcv_transferSize) || + (rcv_urb->actual_length >= rcv_urb->request_length) || + (rcv_urb->actual_length + endpoint->wMaxPacketSize > rcv_urb->buffer_length))) + { +#if 0 + int i; + u8 *cp = rcv_urb->buffer; + for (i = 0; i < rcv_urb->actual_length; i+= 8) + TRACE_MSG8(PCD, "RECV %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] + ); +#endif + endpoint->rcv_urb = NULL; + rcv_urb->jiffies = jiffies; + rcv_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum () : 0; + TRACE_MSG2(PCD, "BUS_RCV COMPLETE: finished length: %d framenum: %x", + rcv_urb->actual_length, (int) rcv_urb->framenum); + usbd_urb_finished_irq (rcv_urb, RECV_OK); + rcv_urb = NULL; + } + } + else { + rcv_urb->actual_length = 0; + //endpoint->rcv_error = 1; + } + } + + // if we don't have an urb see if we can get one + return pcd_rcv_next_irq (endpoint); +} + +/*! pcd_tx_complete_irq + */ +struct usbd_urb * pcd_tx_complete_irq (struct usbd_endpoint_instance *endpoint, int restart) +{ + struct usbd_urb *tx_urb; + + TRACE_MSG2(PCD, "tx_urb: %x restart: %d", endpoint->tx_urb, restart); + + // if we have a tx_urb advance or reset, finish if complete + if ( (tx_urb = endpoint->tx_urb)) { + + struct usbd_bus_instance *bus = tx_urb->bus; + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + + TRACE_MSG5(PCD, "BUS_TX CURRENT TX_URB: %p actual: %d sent: %d last: %d: flags: %d", + (int)endpoint->tx_urb, tx_urb->actual_length, endpoint->sent, + endpoint->last, tx_urb->flags); + + if (likely (!restart)) { + int sent = endpoint->last; + endpoint->sent += sent; + endpoint->last -= sent; + } + else { + TRACE_MSG0(PCD, "RESTARTING"); + endpoint->last = 0; + } + + //if ( ( (tx_urb->actual_length - endpoint->sent) <= 0) && ! (tx_urb->flags & USBD_URB_SENDZLP) ) { + + if ( (endpoint->sent >= tx_urb->actual_length) && ! (tx_urb->flags & USBD_URB_SENDZLP) ) { + tx_urb->jiffies = jiffies; + tx_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum () : 0; + TRACE_MSG2(PCD, "BUS_TX COMPLETE: finished tx_urb: %p framenum: %x", + (int)tx_urb, (int)tx_urb->framenum); + usbd_urb_finished_irq (tx_urb, SEND_FINISHED_OK); + endpoint->tx_urb = NULL; + endpoint->last = endpoint->sent = 0; + } + } + return pcd_tx_next_irq (endpoint); +} + +/* ******************************************************************************************* */ + +/*! pcd_disable - Stop using the USB Device Controller + * Stop using the USB Device Controller. + * Shutdown and free dma channels, de-register the interrupt handler. + */ +void pcd_disable (struct usbd_bus_instance *bus) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + TRACE_MSG0(PCD, "BUS_UDC DISABLE"); + if (usbd_pcd_ops.disable_ep) usbd_pcd_ops.disable_ep (pcd, 0); + pcd_disable_endpoints (bus); + + if (usbd_pcd_ops.disable) usbd_pcd_ops.disable (pcd); +} + +/* ************************************************************************************* */ +/*! bus_framenum + */ +int bus_framenum (void) +{ + return (pcd_ops.framenum) ? pcd_ops.framenum () : 0; +} + +/*! bus_interrupts + */ +u64 bus_interrupts (void) +{ + return ocd_ops.interrupts; +} + +/*! bus_ticks + */ +u64 bus_ticks (void) +{ + return (ocd_ops.ticks) ? ocd_ops.ticks () : 0; +} + +/*! bus_elapsed + */ +u64 bus_elapsed (u64 *t1, u64 *t2) +{ + return (ocd_ops.elapsed) ? (ocd_ops.elapsed (t1, t2)) : 0; +} + +/*! pcd_recv_setup_emulate_irq - emulate a device request + */ +int pcd_recv_setup_emulate_irq(struct usbd_bus_instance *bus, u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + struct usbd_device_request request; + + TRACE_MSG4(PCD, "bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x", bmRequestType, bRequest, wValue, wIndex); + request.bmRequestType = bmRequestType; + request.bRequest = bRequest; + request.wValue = cpu_to_le16(wValue); + request.wIndex = cpu_to_le16(wIndex); + request.wLength = cpu_to_le16(wLength); + return pcd_recv_setup_irq(pcd, &request); +} + +/*! pcd_check_device_feature - verify that feature is set or clear + * Check current feature setting and emulate SETUP Request to set or clear + * if required. + */ +void pcd_check_device_feature(struct usbd_bus_instance *bus, int feature, int flag) +{ + int status = bus->device_feature_settings & (1 << feature); + TRACE_MSG2(PCD, "BUS_CHECK FEATURE: feature: %x flag: %x", feature, flag); + if (!status && flag) + pcd_recv_setup_emulate_irq(bus, USB_REQ_HOST2DEVICE, USB_REQ_SET_FEATURE, feature, 0, 0); + else if (status && !flag) + pcd_recv_setup_emulate_irq(bus, USB_REQ_HOST2DEVICE, USB_REQ_CLEAR_FEATURE, feature, 0, 0); + TRACE_MSG1(PCD, "BUS_CHECK FEATURE: features; %08x", bus->device_feature_settings); +} + +/* ******************************************************************************************* */ +#if !defined(OTG_C99) +struct usbd_bus_operations bus_ops; + +void pcd_global_init(void) +{ + ZERO(bus_ops); + bus_ops.start_endpoint_in = bus_start_endpoint_in; + bus_ops.start_endpoint_out = bus_start_endpoint_out; + bus_ops.cancel_urb_irq = bus_cancel_urb_irq; + bus_ops.endpoint_halted = bus_endpoint_halted; + bus_ops.halt_endpoint = bus_halt_endpoint; + bus_ops.set_address = bus_set_address; + bus_ops.event_handler = bus_event_handler; + bus_ops.request_endpoints = bus_request_endpoints; + bus_ops.set_endpoints = bus_set_endpoints; + bus_ops.framenum = bus_framenum; + bus_ops.ticks = bus_ticks; + bus_ops.elapsed = bus_elapsed; + bus_ops.interrupts = bus_interrupts; + //bus_ops.device_feature = bus_device_feature; +} + +#else /* !defined(OTG_C99) */ +struct usbd_bus_operations bus_ops = { + .start_endpoint_in = bus_start_endpoint_in, + .start_endpoint_out = bus_start_endpoint_out, + .cancel_urb_irq = bus_cancel_urb_irq, + .endpoint_halted = bus_endpoint_halted, + .halt_endpoint = bus_halt_endpoint, + .set_address = bus_set_address, + .event_handler = bus_event_handler, + .request_endpoints = bus_request_endpoints, + .set_endpoints = bus_set_endpoints, + .framenum = bus_framenum, + .ticks = bus_ticks, + .elapsed = bus_elapsed, + .interrupts = bus_interrupts, + //.device_feature = bus_device_feature, +}; +#endif /* !defined(OTG_C99) */ + + +struct usbd_bus_driver bus_driver = { + bops: &bus_ops, +}; + + + +/* ******************************************************************************************* */ +/* Prevent overlapp of bi administrative functions mainly: + * bus_register_bh + * bus_deregister_bh + */ +//DECLARE_MUTEX (usbd_bi_sem); +struct usbd_bus_instance *usbd_bus; + +/*! pcd_startup_events + */ +void pcd_startup_events(struct usbd_bus_instance *bus) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + //TRACE_MSG0(PCD, "BI_STARTUP"); + if (usbd_pcd_ops.startup_events) { + TRACE_MSG0(PCD, "BI_STARTUP_EVENTS - udc"); + usbd_pcd_ops.startup_events(pcd); + } + else { + TRACE_MSG0(PCD, "BI_STARTUP_EVENTS - default"); + usbd_bus_event_handler_irq (bus, DEVICE_INIT, 0); + usbd_bus_event_handler_irq (bus, DEVICE_CREATE, 0); + usbd_bus_event_handler_irq (bus, DEVICE_HUB_CONFIGURED, 0); + usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); + } + //TRACE_MSG0(PCD, "BI_STARTUP: FINISHED"); +} + +/*! bus_register_bh + */ +void bus_register_bh(void *arg) +{ + struct pcd_instance *pcd = arg; + struct otg_instance *otg = pcd->otg; + struct usbd_bus_instance *bus; + struct usbd_endpoint_instance *endpoint; + unsigned long flags; + + TRACE_MSG1(PCD, "BUS_REGISTER_BH: pcd: %p", (int)arg); + RETURN_UNLESS(pcd); + + TRACE_MSG0(PCD, "BUS_REGISTER_BH"); + + bus_driver.name = usbd_pcd_ops.name; + bus_driver.max_endpoints = usbd_pcd_ops.max_endpoints; + bus_driver.maxpacketsize = usbd_pcd_ops.ep0_packetsize; + bus_driver.capabilities = usbd_pcd_ops.capabilities; + bus_driver.bMaxPower = usbd_pcd_ops.bMaxPower; + bus_driver.ports = usbd_pcd_ops.ports; + // XXX bus_driver.otg_bmAttributes = tcd_ops.bmAttributes; + bus_driver.otg_bmAttributes = usbd_pcd_ops.bmAttributes; + + TRACE_MSG1(PCD, "BUS_REGISTER_BH UDC Capabilities: %x", bus_driver.capabilities); + // XXX TRACE_MSG1(PCD, "BUS_REGISTER_BH UDC OTG Attributes: %x\n", tcd_ops.bmAttributes); + TRACE_MSG1(PCD, "BUS_REGISTER_BH UDC OTG Attributes: %x\n", usbd_pcd_ops.bmAttributes); + + // register this bus interface driver and create the device driver instance + if (! (bus = usbd_register_bus (&bus_driver, usbd_pcd_ops.ep0_packetsize))) { + TRACE_MSG0(PCD, "BUS_REGISTER_BH: register failed"); + printk (KERN_INFO "%s: failed\n", __FUNCTION__); + pcd_disable (NULL); + otg_event(pcd->otg, enable_otg_ | PCD_OK, PCD, "BUS_REGISTER_BH"); + return; + } + + bus->privdata = (void *) pcd; + pcd->bus = bus; + + //if (usbd_pcd_ops.serial_init ? usbd_pcd_ops.serial_init (pcd) : -EINVAL) { + // + //} + + if (serial_number_str && strlen(serial_number_str)) { + char *sp, *dp; + int i; + printk(KERN_INFO"%s:\n", __FUNCTION__); + printk(KERN_INFO"%s: serial_number_str: %s\n", __FUNCTION__, serial_number_str); + TRACE_MSG1(PCD, "prm serial_number_str: %s", serial_number_str); + for (sp = serial_number_str, dp = otg->serial_number, i = 0; + *sp && (i < (sizeof(otg->serial_number) - 1)); i++, sp++) + if (isxdigit(*sp)) *dp++ = toupper(*sp); + } + + + TRACE_MSG1(PCD, "otg serial_number_str: %s", pcd->otg->serial_number); + TRACE_MSG0(PCD, "BUS_REGISTER_BH: usbd_enable_function_irq"); + + if (usbd_enable_function_irq (bus, pcd->otg->function_name, pcd->otg->serial_number)) { + TRACE_MSG0(PCD, "BUS_REGISTER_BH: enable function failed"); + printk (KERN_INFO "%s: failed\n", __FUNCTION__); + pcd_disable (NULL); + otg_event(pcd->otg, enable_otg_ | PCD_OK, PCD, "BUS_REGISTER_BH"); + return; + } + + // setup endpoint zero + endpoint = bus->endpoint_array + 0; + endpoint->bEndpointAddress = 0; + + //endpoint->tx_attributes = 0; + // + + endpoint->wMaxPacketSize = usbd_pcd_ops.ep0_packetsize; + endpoint->rcv_transferSize = 4096; // XXX should this be higher + endpoint->wMaxPacketSize = usbd_pcd_ops.ep0_packetsize; + if (usbd_pcd_ops.setup_ep) usbd_pcd_ops.setup_ep (pcd, 0, endpoint); + + TRACE_MSG0(PCD, "BUS_REGISTER_BH: startup"); + + // hopefully device enumeration will finish this process + // XXX should this move to pcd_en? + pcd_startup_events (bus); + TRACE_MSG0(PCD, "BUS_REGISTER_BH: FINISHED - sending PCD_OK"); + //MOD_INC_USE_COUNT; + otg_event(pcd->otg, PCD_OK, PCD, "BUS_REGISTER_BH PCD_OK"); +} + +/*! bus_deregister_bh + */ +void bus_deregister_bh(void *arg) +{ + struct pcd_instance *pcd = arg; + struct usbd_bus_instance *bus; + struct bus_data *data; + unsigned long flags; + + //TRACE_MSG1(PCD, "BUS_DEREGISTER_BH: pcd: %x", pcd); + if (pcd && (bus = pcd->bus) && (usbd_bus_state_enabled == bus->bus_state)) { + + if (usbd_pcd_ops.disable) usbd_pcd_ops.disable (pcd); + + if (bus->device_state != STATE_ATTACHED) { + usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); + usbd_bus_event_handler_irq (bus, DEVICE_POWER_INTERRUPTION, 0); + usbd_bus_event_handler_irq (bus, DEVICE_HUB_RESET, 0); + } + usbd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0); + pcd_disable_endpoints (bus); + pcd_disable (bus); + + usbd_disable_function (bus); + bus->bus_state = usbd_bus_state_disabled; + + //TRACE_MSG1(PCD, "%s: BI_SEM UP", (int)__FUNCTION__); + //otg_event(pcd->otg, exit_ok, "BUS_DISABLE EXIT_OK"); + + //if (bus->serial_number_str) + // lkfree (pcd->bus->serial_number_str); + + usbd_deregister_bus (bus); + pcd->bus = NULL; + + TRACE_MSG0(PCD, "BUS_DEREGISTER_BH: FINISHED - sending PCD_OK"); + //MOD_DEC_USE_COUNT; + } + otg_event(pcd->otg, PCD_OK, PCD, "BUS_DEREGISTER_BH PCD_OK"); +} + +/* ************************************************************************************* */ + + +/*! pcd_en_func - enable + * + * This is called to enable / disable the PCD and USBD stack. + */ +void pcd_en_func (struct otg_instance *otg, u8 flag) +{ + struct pcd_instance *pcd = otg->pcd; + struct usbd_bus_instance *bus = pcd->bus; + + //TRACE_MSG0(PCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(PCD, "PCD_EN: SET"); +#ifdef CONFIG_OTG_DB1550_J15 + int ret = au_readl(SYS_PINFUNC); + au_writel((ret | 0x8000),SYS_PINFUNC); + ret = au_readl(SYS_PINFUNC); + TRACE_MSG1(PCD, "Set the OTG for device mode (sys_pfunc = %04x)", ret); +#endif + // check if we can see the UDC and register, then enable the function + if (usbd_pcd_ops.enable) usbd_pcd_ops.enable (pcd); + //BREAK_IF (pcd_enable_irq (bus)); + //BREAK_IF (usbd_enable_function_irq (bus, otg->function_name)); + + // XXX should this move to here + // pcd_startup_events (bus); + break; + + case RESET: + TRACE_MSG0(PCD, "PCD_EN: RESET"); + usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); + TRACE_MSG0(PCD, "PCD_EN: DESTROY"); + usbd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0); + TRACE_MSG0(PCD, "PCD_EN: FINISHED"); + // XXX need to set a flag here + break; + } +} + +/*! pcd_init_func - per peripheral controller common initialization + * + * This is called to initialize / de-initialize the PCD and USBD stack. + * + * We start work items to do this. + * + */ +void pcd_init_func (struct otg_instance *otg, u8 flag) +{ + struct pcd_instance *pcd = otg->pcd; + struct usbd_bus_instance *bus = pcd->bus; + //struct bus_data *data = NULL; + + //printk(KERN_INFO"%s:\n", __FUNCTION__); + + //TRACE_MSG0(PCD, "--"); + switch (flag) { + case SET: + TRACE_MSG0(PCD, "PCD_INIT: SET"); + PREPARE_WORK_ITEM(pcd->bh, bus_register_bh, pcd); + SCHEDULE_WORK(pcd->bh); + TRACE_MSG0(PCD, "BUS_REGISTER_SCHEDULE: finished"); + break; + + case RESET: + TRACE_MSG0(PCD, "PCD_INIT: RESET"); + PREPARE_WORK_ITEM(pcd->bh, bus_deregister_bh, pcd); + SCHEDULE_WORK(pcd->bh); + } +} + diff -uNr linux/drivers/no-otg/ocd/otg-pcd/tr-init-l24.c linux/drivers/otg/ocd/otg-pcd/tr-init-l24.c --- linux/drivers/no-otg/ocd/otg-pcd/tr-init-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-pcd/tr-init-l24.c 2006-09-01 21:41:32.000000000 +0200 @@ -0,0 +1,234 @@ +/* + * otg/ocd/otg-pcd/tr-init-l24.c - Traditional Device Peripheral and OTG Controller Drivers Module Initialization + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-pcd/tr-init-l24.c + * @brief Traditional device driver init. + * + * TR OTG PCD/OCD/TCD Initialization + * + * This file initializes all of the low level hardware drivers for a traditional device. + * + * Traditional USB Devices must implement the following: + * + * ocd_ops operations from ocd driver + * pcd_ops operations from pcd driver + * tcd_ops operations from tcd driver + * + * The OTG Controller driver (ocd) implements any required OTG timers and if + * necessary mediates access to and initializes the OTG and/or USB hardware. + * + * The Peripheral Controller Driver (pcd) implements the lowest layer of the + * USBD stack to send and receive data from the USB actings as a USB + * peripheral. + * + * The Transceiver Controller Driver (tcd) implements control of the OTG or + * USB transceiver. At a minimum for a traditional device this should + * implement VBUS sensing (sometimes known as cable detect) and where + * possible control over the DP+ pullup resistor. + * + * + * Notes + * + * 1. This is the linux 2.4 version. + * + * 2. This will optionally do an auto start with TR_INIT if the OCD_CAPBILITIES_AUTO + * flag is set. + * + * TODO + * + * 1. hook up serial_number_str to pcd.c for use with bus interface layer. + * + * @ingroup OTGPCD + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +EMBED_LICENSE(); + +MOD_PARM (serial_number_str, "s"); +MOD_PARM_DESC (serial_number_str, "Serial Number"); +MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MOD_DESCRIPTION ("Traditional USB Device"); + +char *serial_number_str; + +otg_tag_t OCD; +struct ocd_instance *ocd_instance; + +otg_tag_t PCD; +struct pcd_instance *pcd_instance; + +otg_tag_t TCD; +struct tcd_instance *tcd_instance; + + +/* ************************************************************************************* */ + +/* tr_ocd_start_timer + * Fake - Set or reset timer to interrupt in number of uS (micro-seconds). + * This is only suitable for MN or TR firmware. + */ +int tr_ocd_start_timer(struct otg_instance *otg, int usec) +{ + otg_event(otg, TMOUT, OCD, "FAKE TMOUT"); + return 0; +} + +/* ************************************************************************** */ +/* tr_tcd_en_func - used to enable or disable transciever + * + */ +void tr_tcd_en_func(struct otg_instance *otg, u8 flag) +{ + struct pcd_instance *pcd = (struct pcd_instance *) otg->pcd; + struct usbd_bus_instance *bus = pcd->bus; + + //printk(KERN_INFO"%s: start\n", __FUNCTION__); + TRACE_MSG0(TCD, "--"); + TRACE_MSG2(TCD, "pcd: %x bus: %x", pcd, pcd->bus); + + switch (flag) { + case PULSE: + case SET: + TRACE_MSG0(TCD, "TR_TCD_EN SET"); + /* enable clock interrupt */ + otg_event_set_irq(tcd_instance->otg, 1, 1, B_SESS_VLD, TCD, "B_SESS_VLD"); + break; + case RESET: + TRACE_MSG0(TCD, "TR_TCD_EN RESET"); + /* disable clock interrupt */ + break; + } +// TRACE_MSG2(TCD, "ep0 endpoint: %x bEndpointAddress: %02x BBB", bus->ep0->endpoint_map_array->endpoint, + // bus->ep0->endpoint_map_array->endpoint->bEndpointAddress); +} + + +/* ************************************************************************** */ + +/* tr_modexit - This is used as module exit, and as cleanup if modinit fails. + * + * Specifically for each driver: + * + * call ops.mod_exit + * reset instance address and ops table address in state machine to NULL + * invalidate tag + */ +static void tr_modexit (void) +{ + struct otg_instance *otg = ocd_instance->otg; + printk(KERN_INFO"%s\n", __FUNCTION__); + + TRACE_MSG1(TCD, "otg: %x", otg); + if (otg) { + TRACE_MSG0(TCD, "Calling otg_exit()"); + otg_exit(otg); + TRACE_MSG0(TCD, "back from otg_exit()"); + } + else + TRACE_MSG0(TCD, "did not call otg_exit()"); + +#if 0 + if ((ocd_ops.capabilities & OCD_CAPABILITIES_TR) && (ocd_ops.capabilities & OCD_CAPABILITIES_AUTO)) { + printk(KERN_INFO"%s calling otg_admin\n",__FUNCTION__); + otg_admin("exit_all", "Traditional Device Auto TR_INIT"); + } +#endif + + if (tcd_ops.mod_exit) tcd_ops.mod_exit(); + if (ocd_ops.mod_exit) ocd_ops.mod_exit(); + if (pcd_ops.mod_exit) pcd_ops.mod_exit(); + + tcd_instance = otg_set_tcd_ops(NULL); + ocd_instance = otg_set_ocd_ops(NULL); + pcd_instance = otg_set_pcd_ops(NULL); + + TCD = otg_trace_invalidate_tag(TCD); + OCD = otg_trace_invalidate_tag(OCD); + PCD = otg_trace_invalidate_tag(PCD); + printk(KERN_INFO"%s finished\n", __FUNCTION__); +} + + +/* tr_modinit - linux module initialization + * + * This needs to initialize the ocd, pcd and tcd drivers. + * + * Specifically for each driver: + * + * obtain tag + * pass ops table address to state machine and get instance address + * call ops.mod_init + * + */ +static int tr_modinit (void) +{ + struct otg_instance *otg; + printk(KERN_INFO"%s\n", __FUNCTION__); + + #if !defined(OTG_C99) + pcd_global_init(); + #endif /* !defined(OTG_C99) */ + + /* For each of ocd, pcd and tcd do: + * + * get tag + * update ops table with default functions where possible + * do otg_set_xxx_ops() as appropriate + * call xxx_ops.mod_init as appropriate + */ + + OCD = otg_trace_obtain_tag(); + PCD = otg_trace_obtain_tag(); + TCD = otg_trace_obtain_tag(); + + UNLESS(ocd_ops.start_timer) ocd_ops.start_timer = tr_ocd_start_timer; + UNLESS(pcd_ops.pcd_init_func) pcd_ops.pcd_init_func = pcd_init_func; + UNLESS(pcd_ops.pcd_en_func) pcd_ops.pcd_en_func = pcd_en_func; + UNLESS(tcd_ops.tcd_en_func) tcd_ops.tcd_en_func = tr_tcd_en_func; + + THROW_IF((ocd_ops.mod_init) ? ocd_ops.mod_init() : 0, error); + THROW_IF((pcd_ops.mod_init) ? pcd_ops.mod_init() : 0, error); + THROW_IF((tcd_ops.mod_init) ? tcd_ops.mod_init() : 0, error); + + THROW_UNLESS(ocd_instance = otg_set_ocd_ops(&ocd_ops), error); + THROW_UNLESS(pcd_instance = otg_set_pcd_ops(&pcd_ops), error); + THROW_UNLESS(tcd_instance = otg_set_tcd_ops(&tcd_ops), error); + + THROW_UNLESS(ocd_instance && (otg = ocd_instance->otg), error); + + CATCH(error) { + tr_modexit(); + return -EINVAL; + } + /* Success! + */ + otg_init(otg); +#if 0 + otg_start(otg, ocd_ops.capabilities & OCD_CAPABILITIES_TR); +#endif + return 0; +} + +module_init(tr_modinit); +MOD_EXIT (tr_modexit); + diff -uNr linux/drivers/no-otg/ocd/otg-tcd/tcd-init-l24.c linux/drivers/otg/ocd/otg-tcd/tcd-init-l24.c --- linux/drivers/no-otg/ocd/otg-tcd/tcd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-tcd/tcd-init-l24.c 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,95 @@ +/* + * otg/ocd/otg-tcd/tcd-init-l24.c - OTG Transceiver Controller Driver Module Initialization + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-tcd/tcd-init-l24.c + * @brief PCD only driver init. + * + * OTG TCD Initialization + * + * This file initializes the low level hardware drivers. + * + * This is the linux 2.4 version. + * + * @ingroup OTGTCD + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) +MODULE_LICENSE ("GPL"); +#endif +MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MODULE_DESCRIPTION ("USB On-The-Go TCD"); +#endif + +otg_tag_t TCD; + +extern struct tcd_ops tcd_ops; +struct tcd_instance *tcd_instance; + +//extern void db1550_tcd_global_init(void); + +/* ************************************************************************************* */ + +/* tcd_modexit - This is *only* used for drivers compiled and used as a module. + */ +static void tcd_modexit (void) +{ + struct otg_instance *otg = tcd_instance->otg; + //printk(KERN_INFO"%s\n", __FUNCTION__); + + if (otg) + otg_exit(otg); + + if (tcd_ops.mod_exit) tcd_ops.mod_exit(); + tcd_instance = otg_set_tcd_ops(NULL); + otg_trace_invalidate_tag(TCD); +} + +/* otg_modinit - linux module initialization + * + * This needs to initialize the hcd, pcd and tcd drivers. This includes tcd and possibly hcd + * for some architectures. + * + */ +static int tcd_modinit (void) +{ + printk(KERN_INFO"Initilize the tcd structure\n"); +// db1550_tcd_global_init(); + TCD = otg_trace_obtain_tag(); + THROW_UNLESS(tcd_instance = otg_set_tcd_ops(&tcd_ops), error); + THROW_IF((tcd_ops.mod_init) ? tcd_ops.mod_init() : 0, error); + //printk(KERN_INFO"%s finish\n", __FUNCTION__); + + TRACE_MSG0(TCD, "--"); + + CATCH(error) { + tcd_modexit(); + return -EINVAL; + } + return 0; +} + +module_init (tcd_modinit); +module_exit (tcd_modexit); + diff -uNr linux/drivers/no-otg/ocd/otg-tcd/tcd.c linux/drivers/otg/ocd/otg-tcd/tcd.c --- linux/drivers/no-otg/ocd/otg-tcd/tcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/ocd/otg-tcd/tcd.c 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,55 @@ +/* + * otg/ocd/otg-tcd/tcd.c - OTG Transceiver Controller Driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/ocd/otg-tcd/tcd.c + * @brief TCD Support + * Notes + * + * @ingroup OTGTCD + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +extern struct tcd_ops tcd_ops; + +/* ************************************************************************************* */ +void tcd_cable_event_irq (struct otg_instance *otg) +{ + TRACE_MSG1(TCD, "TCD_CABLE_EVENT: vbus: %d", tcd_vbus(otg)); + otg_event(otg, VBUS_VLD, TCD, "CABLE"); + //otg_b_sess_vld(otg, 1, tcd_vbus (otg), "Cable Event"); +} + +void tcd_cable_event (struct otg_instance *otg) +{ + unsigned long flags; + local_irq_save (flags); + tcd_cable_event_irq (otg); + local_irq_restore (flags); +} + +/* ************************************************************************************* */ +int tcd_vbus (struct otg_instance *otg) +{ + TRACE_MSG0(TCD, "OCD VBUS"); + return tcd_ops.vbus ? tcd_ops.vbus (otg) : 1; +} + + diff -uNr linux/drivers/no-otg/otg/hcd-hw.h linux/drivers/otg/otg/hcd-hw.h --- linux/drivers/no-otg/otg/hcd-hw.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/hcd-hw.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,101 @@ +/* + * otg/hcd/hc_xfer_hw.h - Generic transfer level USBOTG aware Host Controller Driver (HCD) + * + * Copyright (c) 2004 Belcarra Technologies + * + * By: + * Tom Rushworth , + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @file otg/otg/hcd-hw.h + * @brief Implements Generic Host Controller Driver + * + * @ingroup OTGHCD + */ +#ifndef HC_XFER_HW_H +#define HC_XFER_HW_H 1 + +/*===========================================================================* + * Functions provided by the hardware specific component (whatever HW it is). + * Each HW specific component provides these functions. + *===========================================================================*/ + +/*===========================================================================* + * For the generic transfer aware framework. + *===========================================================================*/ + +/* + * hcd_hw_max_active_transfers() + * Returns: the (constant) maximum number of concurrently active transfers the HW will handle. + */ +extern int hcd_hw_max_active_transfers(struct bus_hcpriv *bus_hcpriv); + +/* + * hcd_hw_start_transfer() + * Returns: tid >= 0 when the transfer starts - this is an index in [0..hcd_hw_max_active_transfers] + * -1 when the transfer is invalid, + * -2 when there are no resources for the transfer currently available. + */ +extern int hcd_hw_start_transfer(struct bus_hcpriv *bus_hcpriv, + int len, // length of data region + void *data, // virtual address of data region + int toggle, // toggle value to start the xfer with + int maxps, // max packet size of endpoint + int slow, // 1 ==> slow speed, 0 ==> full speed QQSV verify: (((urb->pipe) >> 26) & 1) + int endpoint, // endpoint number + int address, // USB device address + int pid, // {PID_OUT, PID_IN, PID_SETUP} + int format, // PIPE_{CONTROL,BULK,INTERRUPT,ISOCHRONOUS} + u32 other); // Values depending on format + +/* + * hcd_hw_unlink_urb() + * Called in irq_lock, calls back to xhc_transfer_complete(). + * Returns: 0 when transfer is unlinked, non-zero otherwise. + */ +extern int hcd_hw_unlink_urb(struct bus_hcpriv *bus_hcpriv, int tid); // tid is value returned by hcd_hw_start_transfer() + +extern u32 hcd_hw_frame_number(struct bus_hcpriv *bus_hcpriv); + +extern void hcd_hw_enable_interrupts(struct bus_hcpriv *bus_hcpriv); +extern void hcd_hw_disable_interrupts(struct bus_hcpriv *bus_hcpriv); + +extern void hcd_hw_get_ops(struct bus_hcpriv *bus_hcpriv); + +extern int hcd_hw_init(struct bus_hcpriv *bus_hcpriv); + +extern void hcd_hw_exit(struct bus_hcpriv *bus_hcpriv); + +/*===========================================================================* + * For the virtual root hub. + *===========================================================================*/ + +extern int hcd_hw_rh_num_ports(struct bus_hcpriv *bus_hcpriv); +extern u8 hcd_hw_rh_otg_capable_mask(struct bus_hcpriv *bus_hcpriv); + +extern char *hcd_hw_rh_string(struct bus_hcpriv *bus_hcpriv, int strno); +extern u16 hcd_hw_rh_idVendor(struct bus_hcpriv *bus_hcpriv); +extern u16 hcd_hw_rh_idProduct(struct bus_hcpriv *bus_hcpriv); +extern u16 hcd_hw_rh_bcdDevice(struct bus_hcpriv *bus_hcpriv); +extern u8 hcd_hw_rh_cfg_bmAttributes(struct bus_hcpriv *bus_hcpriv); +extern u8 hcd_hw_rh_cfg_MaxPower(struct bus_hcpriv *bus_hcpriv); +extern u16 hcd_hw_rh_hub_attributes(struct bus_hcpriv *bus_hcpriv); +extern u8 hcd_hw_rh_power_delay(struct bus_hcpriv *bus_hcpriv); +extern u8 hcd_hw_rh_hub_contr_current(struct bus_hcpriv *bus_hcpriv); +extern u8 hcd_hw_rh_DeviceRemovable(struct bus_hcpriv *bus_hcpriv); +extern u8 hcd_hw_rh_PortPwrCtrlMask(struct bus_hcpriv *bus_hcpriv); +extern u32 hcd_hw_rh_get_hub_change_status(struct bus_hcpriv *bus_hcpriv); +extern void hcd_hw_rh_hub_feature(struct bus_hcpriv *bus_hcpriv, int feat_selector, int set_flag); +extern void hcd_hw_hcd_en_func(struct otg_instance *oi, u8 on); +/* + * Note: in the following functions, portnum is 0-origin. + * (I.e., valid range is [0..(hcd_hw_rh_num_ports-1)].) + */ +extern u32 hcd_hw_rh_get_port_change_status(struct bus_hcpriv *bus_hcpriv, int portnum); +extern void hcd_hw_rh_port_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, u16 wIndex, int set_flag); + + +#endif diff -uNr linux/drivers/no-otg/otg/hcd-l26.h linux/drivers/otg/otg/hcd-l26.h --- linux/drivers/no-otg/otg/hcd-l26.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/hcd-l26.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,119 @@ +/* + * otg/hcd/hc_xfer.h - Generic transfer level USBOTG aware Host Controller Driver (HCD) + * + * Copyright (c) 2004 Belcarra Technologies + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otg/hcd-l26.h + * @brief Implements Linux version of Generic Host Controller driver + * + * @ingroup OTGHCD + */ +#ifndef HC_XFER_H +#define HC_XFER_H 1 + +/*===========================================================================* + * Data structures and functions provided by the generic transfer aware + * host controller framework. + * + * The generic framework must be linked with a hardware specific component, + * whose functions are specified in hc_xfer_hw.h, and a generic root hub + * component, whose functions are specified in hc_xfer_rh.h. + * + * The resulting module provides a table of operations to the usb core layer, + * passed to the core layer by a call to usb_alloc_bus(). + *===========================================================================*/ + +#define XHC_RH_DESCRIPTORS_SIZE (USB_DT_DEVICE_SIZE+USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE+USB_DT_ENDPOINT_SIZE+USB_DT_HUB_NONVAR_SIZE+2) + +typedef struct rh_hcpriv { + struct urb *int_urb; + struct usbd_device_descriptor *dd; + struct usbd_configuration_descriptor *cd; + struct usbd_interface_descriptor *id; + struct usbd_endpoint_descriptor *ed; + struct hub_descriptor *hd; + struct timer_list poll_timer; + //int poll_jiffies; + struct usb_device *dev; + int dev_registered; + u32 hub_change_status; // For shadowing the HW + u32 *port_change_status; // An array of [num_ports] values for shadowing the HW + u32 hub_port_change_status; + struct WORK_STRUCT psc_bh; + u8 curr_cfg; + u8 curr_itf; + //u8 num_ports; + u8 otg_device_mask; // (1 << port_num) bit set iff port_num is acting as a device, not host, and so not usable + u8 descriptors[XHC_RH_DESCRIPTORS_SIZE]; + int suspended; +} rh_hcpriv_t; + +typedef struct bus_hcpriv { // XFER level Host Controller Info + struct usb_bus *usb_bus; + void *hw_hci; // HW specific information for this HC + int root_hub_addr; // Initially 0, set by the virtual root hub when addressed. + struct rh_hcpriv *rh_hcpriv; // Generic root hub info (see hc_xfer_rh.[hc]) + int terminating; // Boolean, TRUE if shutting down. + //struct hcd_instance *hcd; // OTG Controller Info + int max_active_urbs; + struct urb **active_urbs; + int device_registered; // bus_device status + struct device bus_device; // Linux Driver Model info. + //struct hcd_ops otg_ops; // operations for OTG component. + struct usb_driver *usb_driver; + + u8 num_ports; + u8 otg_port; + u8 otg_capable_mask; + u16 rh_vendorid; + u16 rh_productid; + u16 rh_bcddevice; + char *rh_serial; + char *rh_product; + char *rh_manufacturer; + u8 rh_bmAttributes; + u8 rh_bMaxPower; + + struct usb_device *roothub_dev; + struct usb_device *first_dev; + + int max_active_transfers; + +} bus_hcpriv_t; + +typedef struct dev_hcpriv { // XFER level HCI per attached device info + struct usb_device *dev; + struct list_head queued_urbs_both[2][16]; + u32 num_urbs_both[2][16]; + u8 epq_state_both[2][16]; +} dev_hcpriv_t; + +#define EPQ_EMPTY 0 +#define EPQ_RUNNING 1 +#define EPQ_WAITING 2 + +/*===========================================================================* + * For the hardware specific component. + *===========================================================================*/ + +extern void hcd_transfer_complete(struct bus_hcpriv *bus_hcpriv, int transfer_id, int format, + int cc, u32 remaining, int next_toggle); + +/*===========================================================================* + * For the virtual root hub. + *===========================================================================*/ + +extern void hcd_rh_urb_complete(struct bus_hcpriv *bus_hcpriv, struct urb *urb); + +#include "../otg/otg-trace.h" +extern otg_tag_t xfer_hci_trace_tag; + + +#endif diff -uNr linux/drivers/no-otg/otg/hcd-rh.h linux/drivers/otg/otg/hcd-rh.h --- linux/drivers/no-otg/otg/hcd-rh.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/hcd-rh.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,55 @@ +/* + * otg/hcd/hc_xfer_rh.h - Generic transfer level USBOTG aware Host Controller Driver (HCD) + * + * Copyright (c) 2004 Belcarra Technologies + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otg/hcd-rh.h + * @brief Implements Generic Root Hub function for Generic Host Controller Driver. + * + * @ingroup OTGHCD + */ +#ifndef HC_XFER_RH_H +#define HC_XFER_RH_H 1 + +#define XHC_RH_STRING_CONFIGURATION 1 +#define XHC_RH_STRING_INTERFACE 2 +#define XHC_RH_STRING_SERIAL 3 +#define XHC_RH_STRING_PRODUCT 4 +#define XHC_RH_STRING_MANUFACTURER 5 + +/*===========================================================================* + * Functions provided by the generic virtual root hub. + *===========================================================================*/ + +/*===========================================================================* + * For the generic transfer aware framework. + *===========================================================================*/ + +/* + * hcd_rh_submit_urb() + * Returns: values as for the generic submit_urb() function. + */ +extern int hcd_rh_submit_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb, int mem_flags); + +extern int hcd_rh_unlink_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb); + +extern void hcd_rh_get_ops(struct bus_hcpriv *bus_hcpriv); + +extern int hcd_rh_init(struct bus_hcpriv *bus_hcpriv); + +extern void hcd_rh_exit(struct bus_hcpriv *bus_hcpriv); + +/*===========================================================================* + * For the hardware specific root hub component. + *===========================================================================*/ + +extern irqreturn_t hcd_rh_int_hndlr(int irq, void *dev_id, struct pt_regs *regs); + +#endif diff -uNr linux/drivers/no-otg/otg/otg-api.h linux/drivers/otg/otg/otg-api.h --- linux/drivers/no-otg/otg/otg-api.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-api.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,233 @@ +/* + * otg/otg-api.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/* + * Doxygen group definitions - N.B. These much each be in + * their own separate comment... + */ +/*! + * @defgroup onthegogroup On-The-Go + */ +/*! + * @defgroup functiongroup Function Drivers + */ +/*! + * @defgroup tcdgroup Transceiver Controller Drivers + */ +/*! + * @defgroup pcdgroup Peripheral Controller Drivers + */ +/*! + * @defgroup libgroup Architecture Libraries + */ +/*! + * @defgroup platformgroup Platform Drivers + */ + + +/*! + * @defgroup OTGCore Core + * @ingroup onthegogroup + */ + +/*! + * @file otg/otg/otg-api.h + * @brief Core Defines for USB OTG Core Layaer + * + * @ingroup OTGCore + */ + +/*! + * @name OTGCORE OTG API Definitions + * This contains the OTG API structures and definitions. + * @{ + */ + +#include + +#ifdef CONFIG_OTG_FW_MN +#include +#else /* CONFIG_OTG_FW_MN */ +#include +#endif /* CONFIG_OTG_FW_MN */ + + +struct otg_instance; +typedef void (*otg_output_proc_t) (struct otg_instance *, u8); + +extern struct otg_firmware *otg_firmware_loaded; +extern struct otg_firmware *otg_firmware_orig; +extern struct otg_firmware *otg_firmware_loading; + + +char * otg_get_state_name(int state); + +/*! + * @struct otg_instance + * + * This tracks the current OTG configuration and state + */ +struct otg_instance { + + u16 state; /*!< current state */ + int previous; /*!< previous state */ + + int active; /*!< used as semaphore */ + + u32 current_inputs; /*!< input settings */ + u64 outputs; /*!< output settings */ + + u64 tickcount; /*!< when we transitioned to current state */ + u64 bconn; /*!< when b_conn was set */ + + struct otg_state *current_outputs; /*!< output table entry for current state */ + struct otg_state *previous_outputs; /*!< output table entry for current state */ + + otg_output_proc_t otg_output_ops[MAX_OUTPUTS]; /*!< array of functions mapped to output numbers */ + + int (*start_timer) (struct otg_instance *, int); /*!< OCD function to start timer */ + u64 (*ticks) (void); /*!< OCD function to return ticks */ + u64 (*elapsed) ( u64 *, u64 *); /*!< OCD function to return elapsed ticks */ + + void * hcd; /*!< pointer to hcd instance */ + void * ocd; /*!< pointer to ocd instance */ + void * pcd; /*!< pointer to pcd instance */ + void * tcd; /*!< pointer to tcd instance */ + + char function_name[OTGADMIN_MAXSTR]; /*!< current function */ + char serial_number[OTGADMIN_MAXSTR]; /*!< current serial number */ +}; + +typedef int (*otg_event_t) (struct otg_instance *, int, char *); + +struct otg_event_info { + char *name; + otg_event_t event; +}; + + +/* 1 - used by external functions to pass in administrative commands as simple strings + */ +extern int otg_status(int, u32, u32, char *, int); +extern void otg_message(char *); + +/* 2 - used internally by any OTG stack component to single event from interrupt context + */ +extern int otg_event_irq(struct otg_instance *, u64, otg_tag_t, char *); + +/* 3 - used internally by any OTG stack component to single event from non-interrupt context, + */ +extern int otg_event(struct otg_instance *, u64, otg_tag_t, char *); + +/* 5 - used by PCD/TCD drivers to signal various OTG Transceiver events + */ +int otg_event_set_irq(struct otg_instance *, int, int, u32, otg_tag_t, char *); + + +extern void otg_init (struct otg_instance *otg); +extern void otg_exit (struct otg_instance *otg); + +/* message + */ +#if defined(OTG_LINUX) +extern int otg_message_init_l24(struct otg_instance *); +extern void otg_message_exit_l24(void); +#endif /* defined(OTG_LINUX) */ + +#if defined(OTG_WINCE) +extern int otg_message_init_w42(struct otg_instance *); +extern void otg_message_exit_w42(void); +#endif /* defined(OTG_WINCE) */ + +extern int otg_message_init(struct otg_instance *); +extern void otg_message_exit(void); + +extern int otg_write_message_irq(char *buf, int size); +extern int otg_write_message(char *buf, int size); +extern int otg_read_message(char *buf, int size); +extern int otg_data_queued(void); +extern unsigned int otg_message_block(void); +extern unsigned int otg_message_poll(struct file *, struct poll_table_struct *); + + +/* trace + */ +extern int otg_trace_init (void); +extern void otg_trace_exit (void); +#if defined(OTG_LINUX) +extern int otg_trace_init_l24(void); +extern void otg_trace_exit_l24(void); +#endif /* defined(OTG_LINUX) */ + +#if defined(OTG_WINCE) +extern int otg_trace_init_w42(void); +extern void otg_trace_exit_w42(void); +#endif /* defined(OTG_WINCE) */ + +extern int otgtrace_init (void); +extern void otgtrace_exit (void); +extern int otg_trace_proc_read (char *page, int count, int * pos); +extern int otg_trace_proc_write (const char *buf, int count, int * pos); + +/* usbp + */ +extern int usbd_device_init (void); +extern void usbd_device_exit (void); + + +/* + * otgcore/otg-mesg.c + */ + +extern void otg_message(char *buf); +extern void otg_mesg_get_status_update(struct otg_status_update *status_update); +extern void otg_mesg_get_firmware_info(struct otg_firmware_info *firmware_info); +extern int otg_mesg_set_firmware_info(struct otg_firmware_info *firmware_info); +extern struct otg_instance * mesg_otg_instance; + + + +/* + * otgcore/otg.c + */ +extern char * otg_get_state_names(int i); + +/* + * ops + */ + +extern struct hcd_ops *otg_hcd_ops; +extern struct ocd_ops *otg_ocd_ops; +extern struct pcd_ops *otg_pcd_ops; +extern struct tcd_ops *otg_tcd_ops; +extern struct usbd_ops *otg_usbd_ops; + +#define CORE core_trace_tag +extern otg_tag_t CORE; + +extern struct hcd_instance * otg_set_hcd_ops(struct hcd_ops *); +extern struct ocd_instance * otg_set_ocd_ops(struct ocd_ops *); +extern struct pcd_instance * otg_set_pcd_ops(struct pcd_ops *); +extern struct tcd_instance * otg_set_tcd_ops(struct tcd_ops *); +extern int otg_set_usbd_ops(struct usbd_ops *); + +extern void otg_get_trace_info(otg_trace_t *p); +extern int otg_tmr_id_gnd(void); +extern u64 otg_tmr_ticks(void); +extern u64 otg_tmr_elapsed(u64 *t1, u64 *t2); +extern u16 otg_tmr_framenum(void); +extern u32 otg_tmr_interrupts(void); + +extern void otg_write_info_message(char *msg); + +/* @} */ + + diff -uNr linux/drivers/no-otg/otg/otg-compat.h linux/drivers/otg/otg/otg-compat.h --- linux/drivers/no-otg/otg/otg-compat.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-compat.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,73 @@ +/* + * otg/otgcore/otg-compat.h + * + * Copyright (c) 2004 Belcarra + * + */ +/*! + * @file otg/otg/otg-compat.h + * @brief Common include file to determine and include appropriate OS compatibility file. + * + * + * @ingroup OTGCore + */ +#ifndef _OTG_COMPAT_H +#define _OTG_COMPAT_H 1 + +#if defined(_WIN32_WCE) +#define OTG_WINCE _WIN32_WCE +#endif /* defined(_WIN32_WCE) */ + + /* What operating system are we running under? */ + + /* Recursively include enough information to determine which release */ + + #if (__GNUC__ >=3) + #define GCC3 + #else + #define GCC2 + #endif + #include + #include + #include + +#if defined(__GNUC__) + #define OTG_LINUX + #ifndef CONFIG_OTG_NOC99 + #define OTG_C99 + #else + #endif + + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) + #define LINUX26 + #elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) + #define LINUX24 + #else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */ + #define LINUX24 + #define LINUX_OLD + #warning "Early unsupported release of Linux kernel" + #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */ + + /* We are running under a supported version of Linux */ + #include + +#elif defined(OTG_WINCE) + + /* We are running under a supported version of WinCE */ + #include + #include + +#else /* defined(OTG_WINCE) */ + + #error "Operating system not recognized" + +#endif /* defined(OTG_WINCE) */ + +#include + +#if !defined(OTG_C99) +#else /* !defined(OTG_C99) */ +#endif /* !defined(OTG_C99) */ + + +#endif /* _OTG_COMPAT_H */ diff -uNr linux/drivers/no-otg/otg/otg-fw-df.h linux/drivers/otg/otg/otg-fw-df.h --- linux/drivers/no-otg/otg/otg-fw-df.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-fw-df.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,53 @@ + +/* Generated by otg-states-h.awk + * + * Do not Edit thie file + */ + +/*! +* @file otg/otg/otg-fw-df.h +* @brief OTG Firmware - Firmware for df +* +* This file defines the OTG State Machine tests. +* +* @ingroup OTGFW +*/ + + +/*! +* @page OTGFW +* @section OTGFW - otg-fw-df.h +* This contains the input, output and timout definitions for the OTG state machine firmware +*/ +extern char otg_fw_name_df[]; +extern int otg_test_max_df; +extern struct otg_test otg_tests_df[]; +extern struct otg_output otg_outputs_df[]; + + /* + * Copyright (c) 2004 Belcarra + * + */ + /*! + * This is the default Firmware. It is included in the + * compiled modules and supports the auto Traditional USB + * mode. No user inputs are implemented. + */ + +#define invalid_state 0 /* State machine has been initialized. */ + +#define otg_disabled 1 /* State machine has been initialized. */ + +#define terminator_state 2 /* terminator */ + /* + * This is not an OTG State. It is used internally to mark the end of the + * list of states and inputs. + */ + +#define OTG_STATES_DF 3 + +extern struct otg_state otg_states_df[OTG_STATES_DF + 1]; + +extern char *otg_get_state_name_df(int); + +extern struct otg_firmware otg_firmware_df; diff -uNr linux/drivers/no-otg/otg/otg-fw-mn.h linux/drivers/otg/otg/otg-fw-mn.h --- linux/drivers/no-otg/otg/otg-fw-mn.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-fw-mn.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,201 @@ + +/* Generated by otg-states-h.awk + * + * Do not Edit thie file + */ + +/*! +* @file otg/otg/otg-fw-mn.h +* @brief OTG Firmware - Firmware for mn +* +* This file defines the OTG State Machine tests. +* +* @ingroup OTGFW +*/ + + +/*! +* @page OTGFW +* @section OTGFW - otg-fw-mn.h +* This contains the input, output and timout definitions for the OTG state machine firmware +*/ +extern char otg_fw_name_mn[]; +extern int otg_test_max_mn; +extern struct otg_test otg_tests_mn[]; +extern struct otg_output otg_outputs_mn[]; + + /* + * Copyright (c) 2004 Belcarra + */ + /*! + * This is the initialization set for pcd and tcd. + */ + +#define invalid_state 0 /* Un-initialized state. */ + /* + * This the initial state of the software when first loaded. + * It is not possible to return to this state. + */ + +#define otg_disabled 1 /* State Machine ready but disabled. */ + /* + * The USBOTG State Machine has been initialized but is inactive. + * This state may have arrived at from either the invalid_state or + * from the otg_disable state. + */ + +#define otg_disable_tcd 2 /* State Machine waiting for driver de-initialization. */ + /* + * The State Machine stops the device drivers and waits for them + * to signal that they have finished de-initializing. + * + * This state disables the TCD driver and waits for TCD ok. + */ + +#define otg_disable_pcd 3 /* State Machine waiting for driver de-initialization. */ + /* + * The State Machine stops the device drivers and waits for them + * to signal that they have finished de-initializing. + * + * This state disables the PCD driver and waits for PCD ok. + */ + +#define otg_enable_pcd 4 /* State Machine waiting for driver initialization. */ + /* + * The State Machine starts the device drivers and waits for them + * to signal that they have finished initializing. + * + * This state enables the PCD driver and waits for PCD ok. + */ + +#define otg_enable_tcd 5 /* State Machine waiting for driver initialization. */ + /* + * The State Machine starts the device drivers and waits for them + * to signal that they have finished initializing. + * + * This state enables the PCD driver and waits for PCD ok. + */ + /* + * Copyright (c) 2004 Belcarra + * + */ + /*! + * This is the minimal firmware. It can be included in the + * compiled modules and supports the auto Traditional USB + * mode. No user inputs are required for normal operation. + * + * The b_bus_drop input can be optionally used to disconnect and re-connect. + * + * The enable_otg input can be optionally used to disable and re-enable. + * Note that disable/enable will reset b_bus_drop. + * + */ + /*! + * @name Meta State - otg_init + * @{ + */ + +#define otg_enabled 6 /* State machine has been initialized. */ + /*! + * The State Machine has successfully started the device drivers and + * is waiting for an input event. Typically it will move from here to + * an idle state specific to the current conditions (a_idle, b_idle, mn_idle etc.) + * based on user request b_bus_drop. + * + */ + /* @} */ + /*! + * @name Meta State - b_idle + * @{ + */ + +#define mn_idle 7 /* In idle mode with bus_drop reset. */ + /*! + * The State Machine is in the idle state for a Traditional USB Device. + * It is waiting for Vbus to indicate that it has been plugged into a USB Host. + * + */ + +#define mn_bus_drop 8 /* In idle mode with bus_drop set. */ + /*! + * The State Machine is in the idle state for a Traditional USB Device. + * The B-Device applications has requested that the bus connection be dropped. + * Wait for either enable_otg reset or b_bus_drop reset. + */ + /* @} */ + /*! + * @name Meta State - b_peripheral + * @{ + */ + +#define mn_peripheral 9 /* Signal a connection and wait for packet traffic. */ + /*! + * The State Machine in the peripheral state for a Traditional USB Device. + * The D+ pullup is enabled and we are waiting for a BUS_RESET to + * indicate that the USB Host has recognized that a USB Device is attached. + */ + +#define mn_bus_reset 10 /* The bus has been reset. */ + /*! + * The State Machine in the bus_reset state for a Traditional USB Device. + * It is waiting to be enumerated and configured by the USB Host. + */ + +#define mn_addressed 11 /* The device has been addressed. */ + /*! + * The State Machine in the configured state for a Traditional USB Device. + * This means that there is an active session, there is packet traffic + * with this device. + */ + +#define mn_configured 12 /* The device has been configured. */ + /*! + * The State Machine in the configured state for a Traditional USB Device. + * This means that there is an active session, there is packet traffic + * with this device. + */ + +#define mn_discharge 13 /* Discharging Vbus */ + /*! + * The State Machine in the discharge state for a Traditional USB Device. + * The device has been unplugged. The Vbus discharge resistor will be enabled + * for the TLDISC_DSCHRG time period. + */ + /* @} */ + /*! + * @name Meta State - b_suspended + * @{ + */ + +#define mn_suspended 14 /* The bus has been suspended */ + /*! + * The State Machine in the suspend state for a Traditional USB Device. + */ + +#define mn_wakeup_enabled 15 /* Suspended with REMOTE WAKEUP enabled. */ + /*! + * The State Machine in the suspend state for a Traditional USB Device, + * prior to suspended the USB Host enabled Remote Wakeup by sending a + * set REMOTE WAKUP request. + */ + +#define mn_wakeup 16 /* Perform REMOTE WAKEUP procedure. */ + /*! + * The State Machine in the wakeup state for a Traditional USB Device, + * The REMOTE WAKEUP procedure will be performed. + */ + /* @} */ + +#define terminator_state 17 /* terminator */ + /*! + * This is not an OTG State. It is used internally to mark the end of the + * list of states and inputs. + */ + +#define OTG_STATES_MN 18 + +extern struct otg_state otg_states_mn[OTG_STATES_MN + 1]; + +extern char *otg_get_state_name_mn(int); + +extern struct otg_firmware otg_firmware_mn; diff -uNr linux/drivers/no-otg/otg/otg-fw.h linux/drivers/otg/otg/otg-fw.h --- linux/drivers/no-otg/otg/otg-fw.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-fw.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,1309 @@ + +/* Generated by otg-h.awk + * + * Do not Edit, see otg-state.awk + */ + + +/*! +* @defgroup OTGFW Firmware +* @ingroup onthegogroup +*/ + +/*! +* @file otg/otg/otg-fw.h +* @brief OTG Firmware - Input, Output and Timeout definitions +* This file defines the OTG State Machine input, output and timeout constants. +* +* +* @ingroup OTGFW +*/ + +/*! +* @name OTGFW +* @section OTGFW - otg-fw.h +*/ + +/*! +* @name OTGVERSION OTG Version +* Version information +* @{ +*/ + +#define OTG_VERSION_FW 200502152159L + +/*! @} */ + +/*! +* @name BASIC +* These are the provided for application layer compatibility. +* @{ +*/ +#ifdef OTG_APPLICATION +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; +typedef unsigned long long u64; +#include +#include +#endif /* OTG_APPLICATION/ */ + +/*! @} */ + +/*! +* @name OTGSTRUCT OTG Structures +* Basic OTG Structures +*/ +#define OTGADMIN_MAXSTR 48 + +/*! + * @struct otg_test + * + * This defines the tests that allow the state machine to move + * from state to state based on input events. + * + * Goto target: if ( + * (!test1 || (test1 & inputs)) && + * (!test2 || (test2 & inputs)) && + * + */ +typedef struct otg_test { + u16 test; /*!< Test number */ + u16 state; /*!< State Machine State */ + u32 target; /*!< Goto this target if */ + u64 test1; /*!< (!test1 || (test1 & inputs)) && */ + u64 test2; /*!< (!test2 || (test2 & inputs)) && */ + u64 test3; /*!< (!test3 || (test3 & inputs)) && */ + /* N.B. An empty test is ignored. */ +} otg_test_t; /*!< */ + + +/*! + * @struct otg_input_name + * This structure simply allows for a table of input names that can + * searched for by input mask + */ +typedef struct otg_input_name { + u32 value; /*!< Bit value */ + char name[OTGADMIN_MAXSTR]; /*!< Name of state. */ +} otg_input_name_t; + + +/*! + * @struct otg_state + * This defines an OTG State. Each state has a name, meta state, + * timeout, reset and outputs + * + * On entry to the state the reset mask is or'd into the current + * input mask, the settings defined in the output mask are used to. + * call the required output functions and then if present the timer + * is started with the timeout value (in uSec). + * + */ +typedef struct otg_state { + u16 state; /*!< State Machine State */ + u16 meta; /*!< Meta Machine State */ + char name[OTGADMIN_MAXSTR]; /*!< Name of state. */ + u32 tmout; /*!< Start timeout if non-zero */ + u64 reset; /*!< Or these inputs on entry to state */ + u64 outputs; /*!< Or these outputs on entry to state */ +} otg_state_t; + + +/*! + * @struct otg_ioctl_name + * This structure allows a table for a table of IOCTL event names that can be + * searched for by the ioctl command value. It also stores the actual + * input mask to set or reset when according to the ioctl arguement. + */ +typedef struct otg_ioctl_name { + u32 cmd; /*!< Ioctl cmd */ + u32 set; /*!< Signal to set/reset */ + char *name; /*!< Name of ioctl. */ +} otg_ioctl_name_t; + + +/*! + * @struct otg_admin_command + * This allows for a table of IOCTL admin commands. + */ +typedef struct otg_admin_command { + int n; /*!< Ioctl cmd */ + char string[OTGADMIN_MAXSTR]; /*!< Signal to set/reset */ +} otg_admin_command_t; + + +/*! + * @struct otg_status_update + * This is used by the OTG administrative programs to pass data to and received + * data from the OTG State Machine driver. + */ +typedef struct otg_status_update { + u16 state; /*!< current state */ + u16 meta; /*!< current meta state */ + u32 inputs; /*!< current inputs */ + u64 outputs; /*!< current outputs */ + u32 capabilities; /*!< */ + char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */ + char state_name[OTGADMIN_MAXSTR]; /*!< name of current state */ + char meta_name[OTGADMIN_MAXSTR]; /*!< name of current meta-state */ + char function_name[OTGADMIN_MAXSTR]; /*!< currently selected function */ +} otg_status_update_t; + + +/*! + * @struct otg_firmware_info + * This is used by the OTG Administrative program to pass firmware information + * to the OTG State Machine./ + */ +typedef struct otg_firmware_info { + int number_of_states; /*!< number of states */ + int number_of_tests; /*!< number of tests */ + char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */ +} otg_firmware_info_t; + + +/*! + * @struct otg_firmware + * This is used by the OTG Administrative program to pass firmware + * to the OTG State Machine./ + */ +typedef struct otg_firmware { + int number_of_states; /*!< number of states */ + int number_of_tests; /*!< number of tests */ + char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */ + struct otg_state *otg_states; /*!< output information */ + struct otg_test *otg_tests; /*!< test information */ +} otg_firmware_t; + +/*! @} */ + +/*! +* @name OTGTIME Time Macros +* Basic OTG Time Macros +*/ + +#define US(n) (n) /*!< micro-seconds */ + +#define MS(n) (1000 * US(n)) /*!< milli-seconds */ + +#define SEC(n) (1000 * MS(n)) /*!< seconds */ + +/*! @} */ + +/*! +* @name OTGOUTPUTM Output Macros +* OTG Output Macros +*/ + +/* OTG State Outputs + * + * Each state that we transition to defines new settings for each of the defined + * outputs. Each output setting can have four settings: + * + * + * + * + * + */ +#define NC ((u64)0x0) /*!< NC no change */ +#define SET ((u64)0x1) /*!< SET the output should be set (i.e. enabled) */ +#define RESET ((u64)0x2) /*!< RESET the output should be reset (i.e. disabled) */ +#define PULSE ((u64)0x3) /*!< OTHER special setting, for example used for pulse vbus */ +#define POWER ((u64)0x3) /*!< POWER */ + +#define _MASK(n) (((u64) 1) << n) /*!< Set nth bit in 64 bit mask */ +#define _NOT(n) (((u64) n) << 32) /*!< Set nth+32 bit in 64 bit mask */ +#define _ncmask(n) (NC) /*!< No output change mask */ +#define _setmask(n) (SET << (n*2)) /*!< Output set mask */ +#define _resetmask(n) (RESET << (n*2)) /*!< Output reset mask */ +#define _pulsemask(n) (PULSE << (n*2)) /*!< Output pulse mask */ +#define _powermask(n) (POWER << (n*2)) /*!< Output poser mask */ + +/*! @} */ + +/*! +* @name OTGNAMES OTG Names +* Basic OTG Name Tables +*/ + +extern struct otg_input_name otg_input_names[]; +extern struct otg_ioctl_name otg_ioctl_names[]; +extern struct otg_timeouts otg_timeouts[]; +extern struct otg_firmware *otg_firmware_loaded; +extern char *otg_output_names[]; + + +/*! @} */ + +/* Generated by otg-inputs-h.awk + * + * Do not Edit this file. + */ + + + /*! + * N.B. The OTG State Machine Documentation uses the syntax A and A/ + * to indicate an event being true or not true. Because a trailing + * slash is not legal in C we use A and A_ to indicate true and not true. + */ + + /*! + * @name OTG Transceiver Inputs + * These inputs reflect changes seen in the OTG Transceiver. These + * what are available in most common OTG transceivers, for example + * the ISP1301 or MAX3353. + * + * The following are typical Vbus comparators. + * + * ISP1301 MAX3353E + * B-Session end threshold 0.2V-0.8V 0.5V + * Session Valid Comparator 0.8V-2.0V 1.4V + * B-Session Valid 2.0V-4.0V N/A + * A-Device Vbus Valid Comparator 4.4V 4.6V + * + * @{ + */ +#define ID_GND _MASK( 0) /*!< otg_ok - ID is grounded */ +#define ID_GND_ _NOT(ID_GND) +#define ID_FLOAT _MASK( 1) /*!< otg_ok - ID is floating */ +#define ID_FLOAT_ _NOT(ID_FLOAT) +#define DP_HIGH _MASK( 2) /*!< otg_ok - DP is pulled high */ +#define DP_HIGH_ _NOT(DP_HIGH) +#define DM_HIGH _MASK( 3) /*!< otg_ok - DM pullup is pulled high */ +#define DM_HIGH_ _NOT(DM_HIGH) +#define B_SESS_END _MASK( 4) /*!< otg_ok - Vbus less than B-Session end thresshold */ +#define B_SESS_END_ _NOT(B_SESS_END) +#define A_SESS_VLD _MASK( 5) /*!< otg_ok - Vbus greater than Session valid threshold */ +#define A_SESS_VLD_ _NOT(A_SESS_VLD) +#define B_SESS_VLD _MASK( 6) /*!< otg_ok - Vbus greater than B-Session Valid threshold */ +#define B_SESS_VLD_ _NOT(B_SESS_VLD) +#define VBUS_VLD _MASK( 7) /*!< otg_ok - Vbus greater than A-Device Vbus Valid threshold */ +#define VBUS_VLD_ _NOT(VBUS_VLD) +#define SRP_DET _MASK( 8) /*!< a_idle - SRP Detected (Vbus pulsed) */ +#define SRP_DET_ _NOT(SRP_DET) +#define SE0_DET _MASK( 9) /*!< b_idle - SE0 Detected (Single Ended Zeros, DM and DP both low) */ +#define SE0_DET_ _NOT(SE0_DET) +#define SE1_DET _MASK(10) /*!< b_idle - SE1 Detected (Single Ended Ones, DM and DP both high) */ +#define SE1_DET_ _NOT(SE1_DET) +#define BDIS_ACON _MASK(10) /*!< a_hnp_wait - Auto DP pullup high after B-disconnect */ +#define BDIS_ACON_ _NOT(BDIS_ACON) +#define CR_INT_DET _MASK(10) /*!< ph_audio - 0.4V < DP < 0.6V */ +#define CR_INT_DET_ _NOT(CR_INT_DET) + /* @} */ + /*! + * @name Peripheral and Host driver signals. + * + * + * @{ + */ +#define HUB_PORT_CONNECT _MASK(12) /*!< otg_both - Port Connection */ +#define HUB_PORT_CONNECT_ _NOT(HUB_PORT_CONNECT) +#define BUS_RESET _MASK(13) /*!< otg_both - Bus has reset. */ +#define BUS_RESET_ _NOT(BUS_RESET) +#define ADDRESSED _MASK(14) /*!< otg_both - Device has been addressed (received SET ADDRESS request.) */ +#define ADDRESSED_ _NOT(ADDRESSED) +#define CONFIGURED _MASK(15) /*!< otg_both - Device has been configured (received SET CONFIG request.) */ +#define CONFIGURED_ _NOT(CONFIGURED) +#define NOT_SUPPORTED _MASK(16) /*!< otg_both - Peripheral not supported (no class driver.) */ +#define NOT_SUPPORTED_ _NOT(NOT_SUPPORTED) +#define BUS_SUSPENDED _MASK(17) /*!< otg_both - Bus has been suspended. */ +#define BUS_SUSPENDED_ _NOT(BUS_SUSPENDED) + /* @} */ + + /*! + * @name Administrative Policy Inputs + * These are only valid in the state indicated and + * can be enabled or disabled. + * + * @{ + */ +#define a_bcon_no_tmout_req _MASK(18) /*!< otg_host - Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode). */ +#define a_bcon_no_tmout_req_ _NOT(a_bcon_no_tmout_req) +#define a_hpwr_req _MASK(19) /*!< otg_host - Application on A-host wants external charge pump enabled. */ +#define a_hpwr_req_ _NOT(a_hpwr_req) +#define bus_drop _MASK(20) /*!< otg_ok - Application on Device needs to power down bus. */ +#define bus_drop_ _NOT(bus_drop) +#define a_bus_drop _MASK(20) /*!< otg_ok - Application on A-Device needs to power down bus. */ +#define a_bus_drop_ _NOT(a_bus_drop) +#define b_bus_drop _MASK(20) /*!< otg_ok - Application on B-Device needs to disconnect from bus. */ +#define b_bus_drop_ _NOT(b_bus_drop) +#define bus_req _MASK(21) /*!< otg_ok - Application on Device wants to use the bus. */ +#define bus_req_ _NOT(bus_req) +#define a_bus_req _MASK(21) /*!< otg_ok - Application on A-Device wants to act as host */ +#define a_bus_req_ _NOT(a_bus_req) +#define b_bus_req _MASK(21) /*!< otg_ok - Application on B-Device wants to act as host */ +#define b_bus_req_ _NOT(b_bus_req) +#define b_sess_req _MASK(21) /*!< otg_ok - Application on B-Device to perform SRP (alias for b_srp_req.) */ +#define b_sess_req_ _NOT(b_sess_req) +#define suspend_req _MASK(22) /*!< otg_host - Application on Device requests bus be suspended (alias for a_bus_req/.) */ +#define suspend_req_ _NOT(suspend_req) +#define a_suspend_req _MASK(22) /*!< otg_host - Application on A-host requests bus be suspended (alias for a_bus_req/.) */ +#define a_suspend_req_ _NOT(a_suspend_req) +#define b_suspend_req _MASK(22) /*!< otg_host - Application on B-host requests bus be suspended (alias for b_bus_req/.) */ +#define b_suspend_req_ _NOT(b_suspend_req) + /* @} */ + + /*! + * @name Administrative Action Inputs + * These are only valid in state indicated. The + * state machine tests must reset in states + * prior and after use. + * @{ + */ +#define set_remote_wakeup_cmd _MASK(24) /*!< a_host - A-Device will send Remote Wakeup Enable Request */ +#define set_remote_wakeup_cmd_ _NOT(set_remote_wakeup_cmd) +#define remote_wakeup_cmd _MASK(24) /*!< tr_configured - B-Device will perform Remote Wakeup. */ +#define remote_wakeup_cmd_ _NOT(remote_wakeup_cmd) +#define reset_remote_wakeup_cmd _MASK(25) /*!< a_host - A-Device will send Remote Wakeup Disable Request */ +#define reset_remote_wakeup_cmd_ _NOT(reset_remote_wakeup_cmd) +#define clr_err_cmd _MASK(25) /*!< a_vbus_err - A-Device ill clears Vbus overcurrent error. */ +#define clr_err_cmd_ _NOT(clr_err_cmd) +#define b_hnp_cmd _MASK(25) /*!< b_configured - B-Device will attempt HNP */ +#define b_hnp_cmd_ _NOT(b_hnp_cmd) +#define ph_int_cmd _MASK(25) /*!< ph_audio - B-Device will request Carkit interrupt */ +#define ph_int_cmd_ _NOT(ph_int_cmd) +#define ph_audio_cmd _MASK(25) /*!< ph_uart - Application on B-Device connected to Carkit requests audio mode. */ +#define ph_audio_cmd_ _NOT(ph_audio_cmd) +#define cr_int_cmd _MASK(25) /*!< cr_aud - Application on A-Device wants to emulate Carkit */ +#define cr_int_cmd_ _NOT(cr_int_cmd) +#define led_on_cmd _MASK(26) /*!< ph_uart - B-Device will enable Carkit LED */ +#define led_on_cmd_ _NOT(led_on_cmd) +#define led_off_cmd _MASK(27) /*!< ph_uart - B-Device will disable Carkit LED */ +#define led_off_cmd_ _NOT(led_off_cmd) + /* @} */ + + /*! + * @name Internal State + * Used to track status changes. + * @{ + */ +#define HNP_ENABLED _MASK(27) /*!< b_configured - B-HNP Enable Request sent (a-host) or received (b-peripheral). */ +#define HNP_ENABLED_ _NOT(HNP_ENABLED) +#define HNP_CAPABLE _MASK(28) /*!< otg_both - B-host Peripheral may do HNP */ +#define HNP_CAPABLE_ _NOT(HNP_CAPABLE) +#define HNP_SUPPORTED _MASK(28) /*!< otg_both - B-host can do HNP (A-Host Received HNP Supported SET FEATURE) */ +#define HNP_SUPPORTED_ _NOT(HNP_SUPPORTED) +#define REMOTE_WAKEUP_ENABLED _MASK(29) /*!< otg_configured - Remote Wakeup Enable Request received. */ +#define REMOTE_WAKEUP_ENABLED_ _NOT(REMOTE_WAKEUP_ENABLED) +#define REMOTE_CAPABLE _MASK(29) /*!< otg_both - Peripheral can do remote wakeup */ +#define REMOTE_CAPABLE_ _NOT(REMOTE_CAPABLE) + /* @} */ + + /*! + * @name Global Administration + * @{ + */ + /* @} */ + + /*! + * @name Driver Initialization Finished signals + * @{ + */ +#define PCD_OK _MASK(30) /*!< otg_driver - PCD Driver Initialization Finished. */ +#define PCD_OK_ _NOT(PCD_OK) +#define TCD_OK _MASK(30) /*!< otg_driver - TCD Driver Initialization Finished. */ +#define TCD_OK_ _NOT(TCD_OK) +#define HCD_OK _MASK(30) /*!< otg_driver - HCD Driver Initialization Finished. */ +#define HCD_OK_ _NOT(HCD_OK) +#define OCD_OK _MASK(30) /*!< otg_driver - OCD Driver Initialization Finished. */ +#define OCD_OK_ _NOT(OCD_OK) + /* @} */ + + /*! + * @name Timeout and enable + * @{ + */ +#define TMOUT _MASK(30) /*!< otg_all - Generic Timeout */ +#define TMOUT_ _NOT(TMOUT) +#define enable_otg _MASK(31) /*!< otg_all - Move State Machine otg_disabled state. */ +#define enable_otg_ _NOT(enable_otg) +#define AUTO _MASK(31) /*!< otg_all - Auto Return (enable_otg only true when active) */ +#define AUTO_ _NOT(AUTO) + /* @} */ + + /*! @name Timeouts C.f. OTG Table 5-2 A-Device Timing + * @{ + */ +#define Ta_wait_vrise _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ +#define Ta_wait_vrise_ _NOT(Ta_wait_vrise) +#define TA_WAIT_VRISE MS(100) +#define Ta_wait_vrise_200 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ +#define Ta_wait_vrise_200_ _NOT(Ta_wait_vrise_200) +#define TA_WAIT_VRISE_200 MS(200) +#define Ta_wait_vrise_400 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ +#define Ta_wait_vrise_400_ _NOT(Ta_wait_vrise_400) +#define TA_WAIT_VRISE_400 MS(400) +#define Ta_wait_vrise_500 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ +#define Ta_wait_vrise_500_ _NOT(Ta_wait_vrise_500) +#define TA_WAIT_VRISE_500 MS(500) +#define Ta_wait_vrise_800 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ +#define Ta_wait_vrise_800_ _NOT(Ta_wait_vrise_800) +#define TA_WAIT_VRISE_800 MS(800) +#define Ta_bcon_ldb _MASK(30) /*!< a_bcon_ldb - B-Connect Long Debounce */ +#define Ta_bcon_ldb_ _NOT(Ta_bcon_ldb) +#define TA_BCON_LDB MS(100) +#define Ta_wait_bcon _MASK(30) /*!< a_wait_bcon - Wait for 1 second for B-Connect */ +#define Ta_wait_bcon_ _NOT(Ta_wait_bcon) +#define TA_WAIT_BCON SEC(1) +#define Ta_wait_bcon_5 _MASK(30) /*!< a_wait_bcon - Wait for 5 second for B-Connect */ +#define Ta_wait_bcon_5_ _NOT(Ta_wait_bcon_5) +#define TA_WAIT_BCON_5 SEC(5) +#define Ta_wait_bcon_10 _MASK(30) /*!< a_wait_bcon - Wait for 10 second for B-Connect */ +#define Ta_wait_bcon_10_ _NOT(Ta_wait_bcon_10) +#define TA_WAIT_BCON_10 SEC(10) +#define Ta_aidl_bdis _MASK(30) /*!< a_hnp_wait - A-Idle to B-Disconnect */ +#define Ta_aidl_bdis_ _NOT(Ta_aidl_bdis) +#define TA_AIDL_BDIS MS(200) +#define Ta_bdis_acon _MASK(30) /*!< a_suspend - B-disconnect to A-Connect */ +#define Ta_bdis_acon_ _NOT(Ta_bdis_acon) +#define TA_BDIS_ACON MS(3) +#define Ta_bidl_adis_min _MASK(30) /*!< a_peripheral - B-Idle to A-Disconnect minimum (TODO) */ +#define Ta_bidl_adis_min_ _NOT(Ta_bidl_adis_min) +#define TA_BIDL_ADIS_MIN MS(3) +#define Ta_bcon_sdb _MASK(30) /*!< a_bcon_sdb - B-Connect Short Debounce */ +#define Ta_bcon_sdb_ _NOT(Ta_bcon_sdb) +#define TA_BCON_SDB US(2) +#define Ta_bcon_sdb_win _MASK(30) /*!< a_bcon_win - B-Connect Short Debounce Window */ +#define Ta_bcon_sdb_win_ _NOT(Ta_bcon_sdb_win) +#define TA_BCON_SDB_WIN MS(100) + /* @} */ + + /*! @name Timeouts C.f. OTG Table 5-3 B-Device Timing + * @{ + */ +#define Tb_se0_srp _MASK(30) /*!< b_srp_se0 - SE0 Time Before SRP */ +#define Tb_se0_srp_ _NOT(Tb_se0_srp) +#define TB_SE0_SRP MS(2) +#define Tb_data_pls _MASK(30) /*!< b_srp_init - Data-Line Pulse Time */ +#define Tb_data_pls_ _NOT(Tb_data_pls) +#define TB_DATA_PLS MS(7) +#define Tb_data_pls_min _MASK(30) /*!< a_srp_min - Data-Line Pulse minimum time */ +#define Tb_data_pls_min_ _NOT(Tb_data_pls_min) +#define TB_DATA_PLS_MIN MS(5) +#define Tb_data_pls_max _MASK(30) /*!< a_srp_wait - Data-Line Pulse maximum time */ +#define Tb_data_pls_max_ _NOT(Tb_data_pls_max) +#define TB_DATA_PLS_MAX MS(10) +#define Tb_srp_init _MASK(30) /*!< b_srp_init - SRP Initiate Time (TODO multi-state? Not-needed?) */ +#define Tb_srp_init_ _NOT(Tb_srp_init) +#define TB_SRP_INIT MS(100) +#define Tb_srp_fail_min _MASK(30) /*!< b_srp_wait - SRP Fail Time minimum (TODO) */ +#define Tb_srp_fail_min_ _NOT(Tb_srp_fail_min) +#define TB_SRP_FAIL_MIN SEC(5) +#define Tb_aidl_bdis_min _MASK(30) /*!< b_peripheral - A-idle to B-Disconnect minimum (TODO) */ +#define Tb_aidl_bdis_min_ _NOT(Tb_aidl_bdis_min) +#define TB_AIDL_BDIS_MIN MS(5) +#define Tb_aidl_bdis_max _MASK(30) /*!< b_peripheral - A-idle to B-Disconnect maximum (TODO) */ +#define Tb_aidl_bdis_max_ _NOT(Tb_aidl_bdis_max) +#define TB_AIDL_BDIS_MAX MS(150) +#define Tldisc_dschrg _MASK(30) /*!< b_dischrg - Local Disconnect to Data Line Discharge (TODO) */ +#define Tldisc_dschrg_ _NOT(Tldisc_dschrg) +#define TLDISC_DSCHRG US(25) +#define Tb_ase0_brst_min _MASK(30) /*!< b_wait_acon - A-SE0 to B-Reset minimum */ +#define Tb_ase0_brst_min_ _NOT(Tb_ase0_brst_min) +#define TB_ASE0_BRST_MIN US(3125) +#define Tb_acon_dbnc _MASK(30) /*!< b_acon_dbnc - A-Connect Debounce */ +#define Tb_acon_dbnc_ _NOT(Tb_acon_dbnc) +#define TB_ACON_DBNC US(2) +#define Tb_acon_bse0 _MASK(30) /*!< b_host_se0 - A-Connect to B-SE0 */ +#define Tb_acon_bse0_ _NOT(Tb_acon_bse0) +#define TB_ACON_BSE0 MS(1) +#define Tid_ldb _MASK(30) /*!< otg_enable - ID changes debounce */ +#define Tid_ldb_ _NOT(Tid_ldb) +#define TID_LDB MS(100) + /* @} */ + + /*! @name Timeouts for Carkit + * @{ + */ +#define Tph_bcon_ldb _MASK(30) /*!< ph_init - B-Connect Long Debounce */ +#define Tph_bcon_ldb_ _NOT(Tph_bcon_ldb) +#define TPH_BCON_LDB MS(100) +#define Tph_init_pls _MASK(30) /*!< ph_int - Timeout for Carkit interrupt. */ +#define Tph_init_pls_ _NOT(Tph_init_pls) +#define TPH_INIT_PLS US(500) +#define Tcr_uart_rsp _MASK(30) /*!< ph_int - Timeout for Carkit UART */ +#define Tcr_uart_rsp_ _NOT(Tcr_uart_rsp) +#define TCR_UART_RSP MS(30) +#define Tcr_aud_det _MASK(30) /*!< ph_audio_wait - Timeout waiting for Carkit Audio ack */ +#define Tcr_aud_det_ _NOT(Tcr_aud_det) +#define TCR_AUD_DET US(1000) +#define Tph_led_off _MASK(30) /*!< ph_uart - Timeout for LED off pulse */ +#define Tph_led_off_ _NOT(Tph_led_off) +#define TPH_LED_OFF US(10) +#define Tph_led_on _MASK(30) /*!< ph_uart - Timeout for LED on pulse */ +#define Tph_led_on_ _NOT(Tph_led_on) +#define TPH_LED_ON MS(4) +#define Tcr_led_off _MASK(30) /*!< ph_uart - Timeout for LED off pulse */ +#define Tcr_led_off_ _NOT(Tcr_led_off) +#define TCR_LED_OFF MS(4) +#define Tcr_led_on _MASK(30) /*!< ph_uart - Timeout for LED on pulse */ +#define Tcr_led_on_ _NOT(Tcr_led_on) +#define TCR_LED_ON MS(8) +#define Tcr_dm_disc _MASK(30) /*!< cr_aud - Timeout for DM disconnect */ +#define Tcr_dm_disc_ _NOT(Tcr_dm_disc) +#define TCR_DM_DISC MS(50) +#define Tph_mono_ack _MASK(30) /*!< cr_wait - Timeout for CR INT */ +#define Tph_mono_ack_ _NOT(Tph_mono_ack) +#define TPH_MONO_ACK MS(1) +#define Tph_aud_det _MASK(30) /*!< cr_ack - Timeout for CR ACK */ +#define Tph_aud_det_ _NOT(Tph_aud_det) +#define TPH_AUD_DET MS(100) + /* @} */ + + /*! @name Timeouts for timer test + * @{ + */ +#define Tzero _MASK(30) /*!< otg_enabled - Startup */ +#define Tzero_ _NOT(Tzero) +#define TZERO MS(2) +#define Tst_one_ms _MASK(30) /*!< tm_start - Timer test */ +#define Tst_one_ms_ _NOT(Tst_one_ms) +#define TST_ONE_MS MS(1) +#define Tst_ten_ms _MASK(30) /*!< tm_start - Timer test */ +#define Tst_ten_ms_ _NOT(Tst_ten_ms) +#define TST_TEN_MS MS(10) +#define Tst_one_second _MASK(30) /*!< tm_start - Timer test */ +#define Tst_one_second_ _NOT(Tst_one_second) +#define TST_ONE_SECOND SEC(1) +#define Tst_two_second _MASK(30) /*!< tm_start - Timer test */ +#define Tst_two_second_ _NOT(Tst_two_second) +#define TST_TWO_SECOND SEC(2) +#define Tst_four_second _MASK(30) /*!< tm_start - Timer test */ +#define Tst_four_second_ _NOT(Tst_four_second) +#define TST_FOUR_SECOND SEC(4) +#define Tst_eight_second _MASK(30) /*!< tm_start - Timer test */ +#define Tst_eight_second_ _NOT(Tst_eight_second) +#define TST_EIGHT_SECOND SEC(8) +#define Tst_ten_second _MASK(30) /*!< tm_start - Timer test */ +#define Tst_ten_second_ _NOT(Tst_ten_second) +#define TST_TEN_SECOND SEC(10) + /* @} */ + +/* Generated by otg-outputs-h.awk + * + * Do not Edit this file. + */ + + + + /* State Machine Outputs + */ + + /*! @name Driver Initialization Outputs + * N.B. tcd_en is used for older devices, to check if Vbus + * already enabled. + * @{ + */ + +#define TCD_INIT_OUT 0 /*!< Initiate Transceiver Controller Driver Initialization (or De-initialization.) */ +#define tcd_init_out _setmask(TCD_INIT_OUT) +#define tcd_init_out_ _resetmask(TCD_INIT_OUT) +#define tcd_init_out_set _setmask(TCD_INIT_OUT) +#define tcd_init_out_reset _resetmask(TCD_INIT_OUT) +#define tcd_init_out_power _powermask(TCD_INIT_OUT) + +#define PCD_INIT_OUT 1 /*!< Initiate Peripheral Controller Driver Initialization (or De-initialization.) */ +#define pcd_init_out _setmask(PCD_INIT_OUT) +#define pcd_init_out_ _resetmask(PCD_INIT_OUT) +#define pcd_init_out_set _setmask(PCD_INIT_OUT) +#define pcd_init_out_reset _resetmask(PCD_INIT_OUT) +#define pcd_init_out_power _powermask(PCD_INIT_OUT) + +#define HCD_INIT_OUT 2 /*!< Initiate Host Controller Driver Initialization (or De-initialization). */ +#define hcd_init_out _setmask(HCD_INIT_OUT) +#define hcd_init_out_ _resetmask(HCD_INIT_OUT) +#define hcd_init_out_set _setmask(HCD_INIT_OUT) +#define hcd_init_out_reset _resetmask(HCD_INIT_OUT) +#define hcd_init_out_power _powermask(HCD_INIT_OUT) + +#define OCD_INIT_OUT 3 /*!< Initiate OTG Controller Driver Initialization (or De-initialization). */ +#define ocd_init_out _setmask(OCD_INIT_OUT) +#define ocd_init_out_ _resetmask(OCD_INIT_OUT) +#define ocd_init_out_set _setmask(OCD_INIT_OUT) +#define ocd_init_out_reset _resetmask(OCD_INIT_OUT) +#define ocd_init_out_power _powermask(OCD_INIT_OUT) + +#define TCD_EN_OUT 4 /*!< Enable Transceiver Controller Driver */ +#define tcd_en_out _setmask(TCD_EN_OUT) +#define tcd_en_out_ _resetmask(TCD_EN_OUT) +#define tcd_en_out_set _setmask(TCD_EN_OUT) +#define tcd_en_out_reset _resetmask(TCD_EN_OUT) +#define tcd_en_out_power _powermask(TCD_EN_OUT) + +#define PCD_EN_OUT 5 /*!< Enable Peripheral Controller Driver */ +#define pcd_en_out _setmask(PCD_EN_OUT) +#define pcd_en_out_ _resetmask(PCD_EN_OUT) +#define pcd_en_out_set _setmask(PCD_EN_OUT) +#define pcd_en_out_reset _resetmask(PCD_EN_OUT) +#define pcd_en_out_power _powermask(PCD_EN_OUT) + +#define HCD_EN_OUT 6 /*!< Enable Host Controller Driver */ +#define hcd_en_out _setmask(HCD_EN_OUT) +#define hcd_en_out_ _resetmask(HCD_EN_OUT) +#define hcd_en_out_set _setmask(HCD_EN_OUT) +#define hcd_en_out_reset _resetmask(HCD_EN_OUT) +#define hcd_en_out_power _powermask(HCD_EN_OUT) + /* @) */ + + /*! @name Transceiver Controller Driver Outputs + * @{ + */ + +#define DRV_VBUS_OUT 7 /*!< A-Device will Drive Vbus to 5V through charge pump. */ +#define drv_vbus_out _setmask(DRV_VBUS_OUT) +#define drv_vbus_out_ _resetmask(DRV_VBUS_OUT) +#define drv_vbus_out_set _setmask(DRV_VBUS_OUT) +#define drv_vbus_out_reset _resetmask(DRV_VBUS_OUT) +#define drv_vbus_out_power _powermask(DRV_VBUS_OUT) + +#define CHRG_VBUS_OUT 8 /*!< B-Device will charge Vbus to 3.3V through resistor (SRP.) */ +#define chrg_vbus_out _setmask(CHRG_VBUS_OUT) +#define chrg_vbus_out_ _resetmask(CHRG_VBUS_OUT) +#define chrg_vbus_out_set _setmask(CHRG_VBUS_OUT) +#define chrg_vbus_out_reset _resetmask(CHRG_VBUS_OUT) +#define chrg_vbus_out_power _powermask(CHRG_VBUS_OUT) + +#define DISCHRG_VBUS_OUT 9 /*!< B-Device will discharge Vbus (enable dischage resistor.) */ +#define dischrg_vbus_out _setmask(DISCHRG_VBUS_OUT) +#define dischrg_vbus_out_ _resetmask(DISCHRG_VBUS_OUT) +#define dischrg_vbus_out_set _setmask(DISCHRG_VBUS_OUT) +#define dischrg_vbus_out_reset _resetmask(DISCHRG_VBUS_OUT) +#define dischrg_vbus_out_power _powermask(DISCHRG_VBUS_OUT) + +#define DM_PULLUP_OUT 10 /*!< DM pullup control - aka loc_carkit */ +#define dm_pullup_out _setmask(DM_PULLUP_OUT) +#define dm_pullup_out_ _resetmask(DM_PULLUP_OUT) +#define dm_pullup_out_set _setmask(DM_PULLUP_OUT) +#define dm_pullup_out_reset _resetmask(DM_PULLUP_OUT) +#define dm_pullup_out_power _powermask(DM_PULLUP_OUT) + +#define DM_PULLDOWN_OUT 11 /*!< DM pulldown control */ +#define dm_pulldown_out _setmask(DM_PULLDOWN_OUT) +#define dm_pulldown_out_ _resetmask(DM_PULLDOWN_OUT) +#define dm_pulldown_out_set _setmask(DM_PULLDOWN_OUT) +#define dm_pulldown_out_reset _resetmask(DM_PULLDOWN_OUT) +#define dm_pulldown_out_power _powermask(DM_PULLDOWN_OUT) + +#define DP_PULLUP_OUT 12 /*!< DP pullup control - aka loc_conn */ +#define dp_pullup_out _setmask(DP_PULLUP_OUT) +#define dp_pullup_out_ _resetmask(DP_PULLUP_OUT) +#define dp_pullup_out_set _setmask(DP_PULLUP_OUT) +#define dp_pullup_out_reset _resetmask(DP_PULLUP_OUT) +#define dp_pullup_out_power _powermask(DP_PULLUP_OUT) + +#define DP_PULLDOWN_OUT 13 /*!< DP pulldown control */ +#define dp_pulldown_out _setmask(DP_PULLDOWN_OUT) +#define dp_pulldown_out_ _resetmask(DP_PULLDOWN_OUT) +#define dp_pulldown_out_set _setmask(DP_PULLDOWN_OUT) +#define dp_pulldown_out_reset _resetmask(DP_PULLDOWN_OUT) +#define dp_pulldown_out_power _powermask(DP_PULLDOWN_OUT) + +#define CLR_OVERCURRENT_OUT 14 /*!< Clear overcurrent indication */ +#define clr_overcurrent_out _setmask(CLR_OVERCURRENT_OUT) +#define clr_overcurrent_out_ _resetmask(CLR_OVERCURRENT_OUT) +#define clr_overcurrent_out_set _setmask(CLR_OVERCURRENT_OUT) +#define clr_overcurrent_out_reset _resetmask(CLR_OVERCURRENT_OUT) +#define clr_overcurrent_out_power _powermask(CLR_OVERCURRENT_OUT) + +#define DM_DET_OUT 15 /*!< Enable B-Device D- High detect */ +#define dm_det_out _setmask(DM_DET_OUT) +#define dm_det_out_ _resetmask(DM_DET_OUT) +#define dm_det_out_set _setmask(DM_DET_OUT) +#define dm_det_out_reset _resetmask(DM_DET_OUT) +#define dm_det_out_power _powermask(DM_DET_OUT) + +#define DP_DET_OUT 16 /*!< Enable B-Device D+ High detect */ +#define dp_det_out _setmask(DP_DET_OUT) +#define dp_det_out_ _resetmask(DP_DET_OUT) +#define dp_det_out_set _setmask(DP_DET_OUT) +#define dp_det_out_reset _resetmask(DP_DET_OUT) +#define dp_det_out_power _powermask(DP_DET_OUT) + +#define CR_DET_OUT 17 /*!< Enable D+ CR detect */ +#define cr_det_out _setmask(CR_DET_OUT) +#define cr_det_out_ _resetmask(CR_DET_OUT) +#define cr_det_out_set _setmask(CR_DET_OUT) +#define cr_det_out_reset _resetmask(CR_DET_OUT) +#define cr_det_out_power _powermask(CR_DET_OUT) + +#define CHARGE_PUMP_OUT 18 /*!< Enable external charge pump. */ +#define charge_pump_out _setmask(CHARGE_PUMP_OUT) +#define charge_pump_out_ _resetmask(CHARGE_PUMP_OUT) +#define charge_pump_out_set _setmask(CHARGE_PUMP_OUT) +#define charge_pump_out_reset _resetmask(CHARGE_PUMP_OUT) +#define charge_pump_out_power _powermask(CHARGE_PUMP_OUT) + +#define BDIS_ACON_OUT 19 /*!< Enable auto A-connect after B-disconnect. */ +#define bdis_acon_out _setmask(BDIS_ACON_OUT) +#define bdis_acon_out_ _resetmask(BDIS_ACON_OUT) +#define bdis_acon_out_set _setmask(BDIS_ACON_OUT) +#define bdis_acon_out_reset _resetmask(BDIS_ACON_OUT) +#define bdis_acon_out_power _powermask(BDIS_ACON_OUT) + +#define MX21_VBUS_DRAIN 20 /*!< MX21 hack */ +#define mx21_vbus_drain _setmask(MX21_VBUS_DRAIN) +#define mx21_vbus_drain_ _resetmask(MX21_VBUS_DRAIN) +#define mx21_vbus_drain_set _setmask(MX21_VBUS_DRAIN) +#define mx21_vbus_drain_reset _resetmask(MX21_VBUS_DRAIN) +#define mx21_vbus_drain_power _powermask(MX21_VBUS_DRAIN) + +#define ID_PULLDOWN_OUT 21 /*!< Enable the ID to ground pulldown ( (CEA-936 - 5 wire carkit.) */ +#define id_pulldown_out _setmask(ID_PULLDOWN_OUT) +#define id_pulldown_out_ _resetmask(ID_PULLDOWN_OUT) +#define id_pulldown_out_set _setmask(ID_PULLDOWN_OUT) +#define id_pulldown_out_reset _resetmask(ID_PULLDOWN_OUT) +#define id_pulldown_out_power _powermask(ID_PULLDOWN_OUT) + +#define UART_OUT 22 /*!< Enable Transparent UART mode (CEA-936.) */ +#define uart_out _setmask(UART_OUT) +#define uart_out_ _resetmask(UART_OUT) +#define uart_out_set _setmask(UART_OUT) +#define uart_out_reset _resetmask(UART_OUT) +#define uart_out_power _powermask(UART_OUT) + +#define AUDIO_OUT 23 /*!< Enable Audio mode (CEA-936 CarKit interrupt detector.) */ +#define audio_out _setmask(AUDIO_OUT) +#define audio_out_ _resetmask(AUDIO_OUT) +#define audio_out_set _setmask(AUDIO_OUT) +#define audio_out_reset _resetmask(AUDIO_OUT) +#define audio_out_power _powermask(AUDIO_OUT) + +#define MONO_OUT 24 /*!< Enable Mono-Audio mode (CEA-936.) */ +#define mono_out _setmask(MONO_OUT) +#define mono_out_ _resetmask(MONO_OUT) +#define mono_out_set _setmask(MONO_OUT) +#define mono_out_reset _resetmask(MONO_OUT) +#define mono_out_power _powermask(MONO_OUT) + /* @) */ + + /*! @name Peripheral Controller Driver Outputs + * @{ + */ + +#define REMOTE_WAKEUP_OUT 25 /*!< Peripheral will perform remote wakeup. */ +#define remote_wakeup_out _setmask(REMOTE_WAKEUP_OUT) +#define remote_wakeup_out_ _resetmask(REMOTE_WAKEUP_OUT) +#define remote_wakeup_out_set _setmask(REMOTE_WAKEUP_OUT) +#define remote_wakeup_out_reset _resetmask(REMOTE_WAKEUP_OUT) +#define remote_wakeup_out_power _powermask(REMOTE_WAKEUP_OUT) + /* @) */ + + /*! @name Host Controller Driver Outputs + * @{ + */ + +#define LOC_SOF_OUT 26 /*!< Host will enable packet traffic. */ +#define loc_sof_out _setmask(LOC_SOF_OUT) +#define loc_sof_out_ _resetmask(LOC_SOF_OUT) +#define loc_sof_out_set _setmask(LOC_SOF_OUT) +#define loc_sof_out_reset _resetmask(LOC_SOF_OUT) +#define loc_sof_out_power _powermask(LOC_SOF_OUT) + +#define LOC_SUSPEND_OUT 27 /*!< Host will suspend bus. */ +#define loc_suspend_out _setmask(LOC_SUSPEND_OUT) +#define loc_suspend_out_ _resetmask(LOC_SUSPEND_OUT) +#define loc_suspend_out_set _setmask(LOC_SUSPEND_OUT) +#define loc_suspend_out_reset _resetmask(LOC_SUSPEND_OUT) +#define loc_suspend_out_power _powermask(LOC_SUSPEND_OUT) + +#define REMOTE_WAKEUP_EN_OUT 28 /*!< Host will send remote wakeup enable or disable request. */ +#define remote_wakeup_en_out _setmask(REMOTE_WAKEUP_EN_OUT) +#define remote_wakeup_en_out_ _resetmask(REMOTE_WAKEUP_EN_OUT) +#define remote_wakeup_en_out_set _setmask(REMOTE_WAKEUP_EN_OUT) +#define remote_wakeup_en_out_reset _resetmask(REMOTE_WAKEUP_EN_OUT) +#define remote_wakeup_en_out_power _powermask(REMOTE_WAKEUP_EN_OUT) + +#define HNP_EN_OUT 29 /*!< Host will send HNP enable request. */ +#define hnp_en_out _setmask(HNP_EN_OUT) +#define hnp_en_out_ _resetmask(HNP_EN_OUT) +#define hnp_en_out_set _setmask(HNP_EN_OUT) +#define hnp_en_out_reset _resetmask(HNP_EN_OUT) +#define hnp_en_out_power _powermask(HNP_EN_OUT) + +#define HPWR_OUT 30 /*!< Host will enable high power (external charge pump.) */ +#define hpwr_out _setmask(HPWR_OUT) +#define hpwr_out_ _resetmask(HPWR_OUT) +#define hpwr_out_set _setmask(HPWR_OUT) +#define hpwr_out_reset _resetmask(HPWR_OUT) +#define hpwr_out_power _powermask(HPWR_OUT) + /* @) */ +#define MAX_OUTPUTS 31 + +/* Generated by otg-ioctls-h.awk + * + * Do not Edit this file, + */ + + +#if defined(OTG_LINUX) + +#define OTGADMIN_MAGIC 'O' +#define OTGADMIN_VERSION _IOR(OTGADMIN_MAGIC, 1, u64) +#define OTGADMIN_STATUS _IOR(OTGADMIN_MAGIC, 3, struct otg_status_update) +#define OTGADMIN_SET_FUNCTION _IOW(OTGADMIN_MAGIC, 4, struct otg_admin_command) +#define OTGADMIN_GET_FUNCTION _IOR(OTGADMIN_MAGIC, 4, struct otg_admin_command) +#define OTGADMIN_SET_INFO _IOW(OTGADMIN_MAGIC, 5, struct otg_firmware_info) +#define OTGADMIN_GET_INFO _IOR(OTGADMIN_MAGIC, 5, struct otg_firmware_info) +#define OTGADMIN_SET_STATE _IOW(OTGADMIN_MAGIC, 6, struct otg_state) +#define OTGADMIN_GET_STATE _IOR(OTGADMIN_MAGIC, 6, struct otg_state) +#define OTGADMIN_SET_TEST _IOW(OTGADMIN_MAGIC, 7, struct otg_test) +#define OTGADMIN_GET_TEST _IOR(OTGADMIN_MAGIC, 7, struct otg_test) +#define OTGADMIN_SET_SERIAL _IOW(OTGADMIN_MAGIC, 8, struct otg_admin_command) +#define OTGADMIN_GET_SERIAL _IOR(OTGADMIN_MAGIC, 8, struct otg_admin_command) + + + + /*! + * Signal Sent: a_bcon_no_tmout_req + * State Valid: otg_host + * Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode). + */ +#define OTGADMIN_A_BCON_NO_TMOUT_REQ _IOW(OTGADMIN_MAGIC, 10, int) + + /*! + * Signal Sent: a_hpwr_req + * State Valid: otg_host + * Application on A-host wants external charge pump enabled. + */ +#define OTGADMIN_A_HPWR_REQ _IOW(OTGADMIN_MAGIC, 11, int) + + /*! + * Signal Sent: bus_drop + * State Valid: otg_ok + * Application on Device needs to power down bus. + */ +#define OTGADMIN_BUS_DROP _IOW(OTGADMIN_MAGIC, 12, int) + + /*! + * Signal Sent: a_bus_drop + * State Valid: otg_ok + * Application on A-Device needs to power down bus. + */ +#define OTGADMIN_A_BUS_DROP _IOW(OTGADMIN_MAGIC, 13, int) + + /*! + * Signal Sent: b_bus_drop + * State Valid: otg_ok + * Application on B-Device needs to disconnect from bus. + */ +#define OTGADMIN_B_BUS_DROP _IOW(OTGADMIN_MAGIC, 14, int) + + /*! + * Signal Sent: bus_req + * State Valid: otg_ok + * Application on Device wants to use the bus. + */ +#define OTGADMIN_BUS_REQ _IOW(OTGADMIN_MAGIC, 15, int) + + /*! + * Signal Sent: a_bus_req + * State Valid: otg_ok + * Application on A-Device wants to act as host + */ +#define OTGADMIN_A_BUS_REQ _IOW(OTGADMIN_MAGIC, 16, int) + + /*! + * Signal Sent: b_bus_req + * State Valid: otg_ok + * Application on B-Device wants to act as host + */ +#define OTGADMIN_B_BUS_REQ _IOW(OTGADMIN_MAGIC, 17, int) + + /*! + * Signal Sent: b_sess_req + * State Valid: otg_ok + * Application on B-Device to perform SRP (alias for b_srp_req.) + */ +#define OTGADMIN_B_SESS_REQ _IOW(OTGADMIN_MAGIC, 18, int) + + /*! + * Signal Sent: suspend_req + * State Valid: otg_host + * Application on Device requests bus be suspended (alias for a_bus_req/.) + */ +#define OTGADMIN_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 19, int) + + /*! + * Signal Sent: a_suspend_req + * State Valid: otg_host + * Application on A-host requests bus be suspended (alias for a_bus_req/.) + */ +#define OTGADMIN_A_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 20, int) + + /*! + * Signal Sent: b_suspend_req + * State Valid: otg_host + * Application on B-host requests bus be suspended (alias for b_bus_req/.) + */ +#define OTGADMIN_B_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 21, int) + + /*! + * Signal Sent: set_remote_wakeup_cmd + * State Valid: a_host + * A-Device will send Remote Wakeup Enable Request + */ +#define OTGADMIN_SET_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 22, int) + + /*! + * Signal Sent: remote_wakeup_cmd + * State Valid: tr_configured + * B-Device will perform Remote Wakeup. + */ +#define OTGADMIN_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 23, int) + + /*! + * Signal Sent: reset_remote_wakeup_cmd + * State Valid: a_host + * A-Device will send Remote Wakeup Disable Request + */ +#define OTGADMIN_RESET_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 24, int) + + /*! + * Signal Sent: clr_err_cmd + * State Valid: a_vbus_err + * A-Device ill clears Vbus overcurrent error. + */ +#define OTGADMIN_CLR_ERR_CMD _IOW(OTGADMIN_MAGIC, 25, int) + + /*! + * Signal Sent: b_hnp_cmd + * State Valid: b_configured + * B-Device will attempt HNP + */ +#define OTGADMIN_B_HNP_CMD _IOW(OTGADMIN_MAGIC, 26, int) + + /*! + * Signal Sent: ph_int_cmd + * State Valid: ph_audio + * B-Device will request Carkit interrupt + */ +#define OTGADMIN_PH_INT_CMD _IOW(OTGADMIN_MAGIC, 27, int) + + /*! + * Signal Sent: ph_audio_cmd + * State Valid: ph_uart + * Application on B-Device connected to Carkit requests audio mode. + */ +#define OTGADMIN_PH_AUDIO_CMD _IOW(OTGADMIN_MAGIC, 28, int) + + /*! + * Signal Sent: cr_int_cmd + * State Valid: cr_aud + * Application on A-Device wants to emulate Carkit + */ +#define OTGADMIN_CR_INT_CMD _IOW(OTGADMIN_MAGIC, 29, int) + + /*! + * Signal Sent: led_on_cmd + * State Valid: ph_uart + * B-Device will enable Carkit LED + */ +#define OTGADMIN_LED_ON_CMD _IOW(OTGADMIN_MAGIC, 30, int) + + /*! + * Signal Sent: led_off_cmd + * State Valid: ph_uart + * B-Device will disable Carkit LED + */ +#define OTGADMIN_LED_OFF_CMD _IOW(OTGADMIN_MAGIC, 31, int) + + /*! + * Signal Sent: enable_otg + * State Valid: otg_all + * Move State Machine otg_disabled state. + */ +#define OTGADMIN_ENABLE_OTG _IOW(OTGADMIN_MAGIC, 32, int) + +#define OTGADMIN_MAXNR 33 + + +#endif /* defined(OTG_LINUX) */ + +/* Generated by otg-iocontrol-h.awk + * + * Do not Edit this file, + */ + + +#if defined(OTG_WINCE) + +#define OTGADMIN_BASE$ 0x401 +#define _USBLAN_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(OTGADMIN_BASE, _Function, _Method, _Access) + +#define OTGADMIN_VERSION OTGADMIN_CTL_CODE(0x401, METHOD_BUFFERRED, FILE_READ_ACCESS) +#define OTGADMIN_STATUS OTGADMIN_CTL_CODE(0x403, METHOD_BUFFERRED, FILE_READ_ACCESS) +#define OTGADMIN_SET_FUNCTION OTGADMIN_CTL_CODE(0x404, METHOD_BUFFERRED, FILE_WRITE_ACCESS) +#define OTGADMIN_GET_FUNCTION OTGADMIN_CTL_CODE(0x404, METHOD_BUFFERRED, FILE_READ_ACCESS) +#define OTGADMIN_SET_INFO OTGADMIN_CTL_CODE(0x405, METHOD_BUFFERRED, FILE_WRITE_ACCESS) +#define OTGADMIN_GET_INFO OTGADMIN_CTL_CODE(0x405, METHOD_BUFFERRED, FILE_READ_ACCESS) +#define OTGADMIN_SET_STATE OTGADMIN_CTL_CODE(0x406, METHOD_BUFFERRED, FILE_WRITE_ACCESS) +#define OTGADMIN_GET_STATE OTGADMIN_CTL_CODE(0x406, METHOD_BUFFERRED, FILE_READ_ACCESS) +#define OTGADMIN_SET_TEST OTGADMIN_CTL_CODE(0x407, METHOD_BUFFERRED, FILE_WRITE_ACCESS) +#define OTGADMIN_GET_TEST OTGADMIN_CTL_CODE(0x407, METHOD_BUFFERRED, FILE_READ_ACCESS) + + + + /*! + * Signal Sent: a_bcon_no_tmout_req + * State Valid: otg_host + * Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode). + */ +#define OTGADMIN_A_BCON_NO_TMOUT_REQ OTGADMIN_CTL_CODE(, 0x40a, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: a_hpwr_req + * State Valid: otg_host + * Application on A-host wants external charge pump enabled. + */ +#define OTGADMIN_A_HPWR_REQ OTGADMIN_CTL_CODE(, 0x40b, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: bus_drop + * State Valid: otg_ok + * Application on Device needs to power down bus. + */ +#define OTGADMIN_BUS_DROP OTGADMIN_CTL_CODE(, 0x40c, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: a_bus_drop + * State Valid: otg_ok + * Application on A-Device needs to power down bus. + */ +#define OTGADMIN_A_BUS_DROP OTGADMIN_CTL_CODE(, 0x40d, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: b_bus_drop + * State Valid: otg_ok + * Application on B-Device needs to disconnect from bus. + */ +#define OTGADMIN_B_BUS_DROP OTGADMIN_CTL_CODE(, 0x40e, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: bus_req + * State Valid: otg_ok + * Application on Device wants to use the bus. + */ +#define OTGADMIN_BUS_REQ OTGADMIN_CTL_CODE(, 0x40f, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: a_bus_req + * State Valid: otg_ok + * Application on A-Device wants to act as host + */ +#define OTGADMIN_A_BUS_REQ OTGADMIN_CTL_CODE(, 0x410, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: b_bus_req + * State Valid: otg_ok + * Application on B-Device wants to act as host + */ +#define OTGADMIN_B_BUS_REQ OTGADMIN_CTL_CODE(, 0x411, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: b_sess_req + * State Valid: otg_ok + * Application on B-Device to perform SRP (alias for b_srp_req.) + */ +#define OTGADMIN_B_SESS_REQ OTGADMIN_CTL_CODE(, 0x412, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: suspend_req + * State Valid: otg_host + * Application on Device requests bus be suspended (alias for a_bus_req/.) + */ +#define OTGADMIN_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x413, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: a_suspend_req + * State Valid: otg_host + * Application on A-host requests bus be suspended (alias for a_bus_req/.) + */ +#define OTGADMIN_A_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x414, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: b_suspend_req + * State Valid: otg_host + * Application on B-host requests bus be suspended (alias for b_bus_req/.) + */ +#define OTGADMIN_B_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x415, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: set_remote_wakeup_cmd + * State Valid: a_host + * A-Device will send Remote Wakeup Enable Request + */ +#define OTGADMIN_SET_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x416, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: remote_wakeup_cmd + * State Valid: tr_configured + * B-Device will perform Remote Wakeup. + */ +#define OTGADMIN_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x417, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: reset_remote_wakeup_cmd + * State Valid: a_host + * A-Device will send Remote Wakeup Disable Request + */ +#define OTGADMIN_RESET_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x418, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: clr_err_cmd + * State Valid: a_vbus_err + * A-Device ill clears Vbus overcurrent error. + */ +#define OTGADMIN_CLR_ERR_CMD OTGADMIN_CTL_CODE(, 0x419, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: b_hnp_cmd + * State Valid: b_configured + * B-Device will attempt HNP + */ +#define OTGADMIN_B_HNP_CMD OTGADMIN_CTL_CODE(, 0x41a, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: ph_int_cmd + * State Valid: ph_audio + * B-Device will request Carkit interrupt + */ +#define OTGADMIN_PH_INT_CMD OTGADMIN_CTL_CODE(, 0x41b, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: ph_audio_cmd + * State Valid: ph_uart + * Application on B-Device connected to Carkit requests audio mode. + */ +#define OTGADMIN_PH_AUDIO_CMD OTGADMIN_CTL_CODE(, 0x41c, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: cr_int_cmd + * State Valid: cr_aud + * Application on A-Device wants to emulate Carkit + */ +#define OTGADMIN_CR_INT_CMD OTGADMIN_CTL_CODE(, 0x41d, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: led_on_cmd + * State Valid: ph_uart + * B-Device will enable Carkit LED + */ +#define OTGADMIN_LED_ON_CMD OTGADMIN_CTL_CODE(, 0x41e, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: led_off_cmd + * State Valid: ph_uart + * B-Device will disable Carkit LED + */ +#define OTGADMIN_LED_OFF_CMD OTGADMIN_CTL_CODE(, 0x41f, METHOD_BUFFERRED, FILE_READ_ACCESS) + + /*! + * Signal Sent: enable_otg + * State Valid: otg_all + * Move State Machine otg_disabled state. + */ +#define OTGADMIN_ENABLE_OTG OTGADMIN_CTL_CODE(, 0x420, METHOD_BUFFERRED, FILE_READ_ACCESS) + +#define OTGADMIN_MAXNR 33 + + +#endif /* defined(OTG_WINCE) */ + +/* Generated by otg-metas-h.awk + * + * Do not Edit thie file + */ + + + /*! @name OTG Figure 6-2 Meta States + * + * These are the Meta States defined in the 2.0 OTG Specification + * Figure 6.2 - Dual-Role A-Device. + * @{ + */ + +#define m_a_idle 0 /* */ + +#define m_a_wait_vrise 1 /* */ + +#define m_a_wait_bcon 2 /* */ + +#define m_a_host 3 /* */ + +#define m_a_suspend 4 /* */ + +#define m_a_peripheral 5 /* */ + +#define m_a_wait_vfall 6 /* */ + +#define m_a_vbus_err 7 /* */ + /* @} */ + /*! @name OTG Figure 6-3 Meta States + * + * These are the Meta States defined in the 2.0 OTG Specification + * Figure 6.3 - Dual-Role B-Device. + * @{ + */ + +#define m_b_idle 8 /* */ + +#define m_b_srp_init 9 /* */ + +#define m_b_peripheral 10 /* */ + +#define m_b_suspend 11 /* */ + +#define m_b_wait_acon 12 /* */ + +#define m_b_host 13 /* */ + +#define m_b_suspended 14 /* */ + /* @} */ + /*! @name Carkit Meta States Figure 7-7 + * + * @{ + */ + +#define m_ph_disc 15 /* Equivalent to b_peripheral */ + +#define m_ph_init 16 /* */ + +#define m_ph_uart 17 /* */ + +#define m_ph_aud 18 /* */ + +#define m_ph_wait 19 /* */ + +#define m_ph_exit 20 /* */ + +#define m_cr_init 21 /* */ + +#define m_cr_uart 22 /* */ + +#define m_cr_aud 23 /* */ + +#define m_cr_ack 24 /* */ + +#define m_cr_wait 25 /* */ + +#define m_cr_disc 26 /* */ + /* @} */ + /*! @name Additional states used locally. + * + * @{ + */ + +#define m_otg_init 27 /* */ + +#define m_usb_accessory 28 /* */ + +#define m_usb_factory 29 /* */ + +#define m_unknown 30 /* */ + /* @} */ + +#define OTG_METAS_FW 31 + +extern char *otg_meta_names[]; diff -uNr linux/drivers/no-otg/otg/otg-hcd.h linux/drivers/otg/otg/otg-hcd.h --- linux/drivers/no-otg/otg/otg-hcd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-hcd.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,85 @@ +/* + * otg/otg-hcd.h - OTG Host Controller Driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * + */ +/*! + * @defgroup OTGHCD Host Controller Driver Support + * @ingroup onthegogroup + */ +/*! + * @file otg/otg/otg-hcd.h + * @brief Defines common to On-The-Go Host Controller Support + * + * This file defines the hcd_ops and hcd_instance structures. + * + * The hcd_ops structure contains all of the output functions that will + * be called as required by the OTG event handler when changing states. + * + * The hcd_instance structure is used to maintain the global data + * required by the host controller drivers. + * + * @ingroup OTGHCD + */ + +/*! + * @name HCD Host Controller Driver + * @{ + */ +struct hcd_instance; + +/*! + * @struct hcd_ops + * The pcd_ops structure contains pointers to all of the functions implemented for the + * linked in driver. Having them in this structure allows us to easily determine what + * functions are available without resorting to ugly compile time macros or ifdefs + * + * There is only one instance of this, defined in the device specific lower layer. + */ +struct hcd_ops { + + /* mandatory */ + int max_ports; /*!< maximum number of ports available */ + u32 capabilities; /*!< UDC Capabilities - see usbd-bus.h for details */ + char *name; /*!< name of controller */ + + /* Driver Initialization - by degrees + */ + int (*mod_init) (void); /*!< HCD Module Initialization */ + void (*mod_exit) (void); /*!< HCD Module Exit */ + + + otg_output_proc_t hcd_init_func; /*!< OTG calls to initialize or de-initialize the HCD */ + otg_output_proc_t hcd_en_func; /*!< OTG calls to enable or disable the HCD */ + otg_output_proc_t loc_sof_func; /*!< OTG calls to into a_host or b_host state - attempt to use port */ + otg_output_proc_t loc_suspend_func; /*!< OTG calls to suspend bus */ + otg_output_proc_t remote_wakeup_en_func; /*!< OTG calls to issue SET FEATURE REMOTE WAKEUP */ + otg_output_proc_t hnp_en_func; /*!< OTG calls to issues SET FEATURE B_HNP_ENABLE */ +}; + +/*! + * @struct hcd_instance + */ +struct hcd_instance { + struct otg_instance *otg; /*!< pointer to OTG Instance */ + void * privdata; /*!< pointer to private data for PCD */ + struct WORK_STRUCT bh; /*!< work structure for bottom half handler */ +}; + +#define HCD hcd_trace_tag +extern otg_tag_t HCD; +extern struct hcd_ops hcd_ops; +extern struct hcd_instance *hcd_instance; + +extern void hcd_init_func(struct otg_instance *, u8 ); +extern void hcd_en_func(struct otg_instance *, u8 ); + +/* @} */ + diff -uNr linux/drivers/no-otg/otg/otg-linux.h linux/drivers/otg/otg/otg-linux.h --- linux/drivers/no-otg/otg/otg-linux.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-linux.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,284 @@ +/* + * otg/otg/otg-linux.h + * + * Copyright (c) 2004 Belcarra + * + */ + +/*! + * @file otg/otg/otg-linux.h + * @brief Linux OS Compatibility defines + * + * @ingroup OTGCore + */ +#ifndef _OTG_LINUX_H +#define _OTG_LINUX_H 1 + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! @name Compilation Related + */ + +/*! @{ */ +#undef PRAGMAPACK +#define PACKED __attribute__((packed)) +#define INLINE __inline__ + +/*! @} */ + +/*! @name Memory Allocation Primitives + * + * CKMALLOC() + * LSTRDUP() + * LKFREE() + * LIST_ENTRY() + * LIST_FOR_EACH() + */ + + /*! @{ */ + +#if defined(LINUX26) + #include +#define GET_KERNEL_PAGE() __get_free_page(GFP_KERNEL) + +#else /* LINUX26 */ + + #include + #define GET_KERNEL_PAGE() get_free_page(GFP_KERNEL) +#endif /* LINUX26 */ + + + +// Common to all supported versions of Linux ?? + +#define CKMALLOC(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f) +#define LSTRDUP(str) _lstrdup(__FUNCTION__, __LINE__, str) +#define LKFREE(p) _lkfree(__FUNCTION__, __LINE__, p) + +#define ckmalloc(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f) +#define lstrdup(str) _lstrdup(__FUNCTION__, __LINE__, str) +#define lkfree(p) _lkfree(__FUNCTION__, __LINE__, p) + +#define OTG_MALLOC_TEST +#undef OTG_MALLOC_DEBUG + +#ifdef OTG_MALLOC_TEST + extern int otg_mallocs; +#endif + + +static INLINE void *_ckmalloc (const char *func, int line, int n, int f) +{ + void *p; + if ((p = kmalloc (n, f)) == NULL) { + return NULL; + } + memset (p, 0, n); + #ifdef OTG_MALLOC_TEST + ++otg_mallocs; + #endif + #ifdef OTG_MALLOC_DEBUG + printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs); + #endif + return p; +} + +static INLINE char *_lstrdup (const char *func, int line, char *str) +{ + int n; + char *s; + if (str && (n = strlen (str) + 1) && (s = kmalloc (n, GFP_ATOMIC))) { +#ifdef OTG_MALLOC_TEST + ++otg_mallocs; +#endif +#ifdef OTG_MALLOC_DEBUG + printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, s, func, line, otg_mallocs); +#endif + return strcpy (s, str); + } + return NULL; +} + +static INLINE void _lkfree (const char *func, int line, void *p) +{ + if (p) { +#ifdef OTG_MALLOC_TEST + --otg_mallocs; +#endif +#ifdef OTG_MALLOC_DEBUG + printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs); +#endif + kfree (p); +#ifdef MALLOC_TEST + if (otg_mallocs < 0) { + printk(KERN_INFO"%s: %p %s %d %d otg_mallocs less zero!\n", __FUNCTION__, p, func, line, otg_mallocs); + } +#endif +#ifdef OTG_MALLOC_DEBUG + else { + printk(KERN_INFO"%s: %s %d NULL\n", __FUNCTION__, func, line); + } +#endif + } +} + +#if 1 +#include +#define LIST_NODE struct list_head +#define LIST_NODE_INIT(name) struct list_head name = {&name, &name} +#define LIST_ENTRY(pointer, type, member) list_entry(pointer, type, member) +#define LIST_FOR_EACH(cursor, head) list_for_each(cursor, head) +#define LIST_ADD_TAIL(n,h) list_add_tail(n,h) +#define LIST_DEL(h) list_del(&(h)) +#else +#include +#endif + +/*! @} */ + + +/*! @name Atomic Operations + * + * atomic_post_inc() + * atomic_pre_dec() + */ +/*! @{ */ +static __inline__ int atomic_post_inc(volatile atomic_t *v) +{ + unsigned long flags; + int result; + local_irq_save(flags); + result = (v->counter)++; + local_irq_restore(flags); + return(result); +} + +static __inline__ int atomic_pre_dec(volatile atomic_t *v) +{ + unsigned long flags; + int result; + local_irq_save(flags); + result = --(v->counter); + local_irq_restore(flags); + return(result); +} +/*! @} */ + + + +/*!@name Scheduling Primitives + * + * WORK_STRUCT + * WORK_ITEM + * + * SCHEDULE_TIMEOUT()\n + * SET_WORK_ARG()\n + * SCHEDULE_WORK()\n + * SCHEDULE_IMMEDIATE_WORK()\n + * NO_WORK_DATA()\n + * MOD_DEC_USE_COUNT\n + * MOD_INC_USE_COUNT\n + */ + +/*! @{ */ + +static void inline SCHEDULE_TIMEOUT(int seconds){ + schedule_timeout( seconds * HZ ); +} + + +/* Separate Linux 2.4 and 2.6 versions of scheduling primitives */ +#if defined(LINUX26) + #include + + #define WORK_STRUCT work_struct + #define WORK_ITEM work_struct + typedef struct WORK_ITEM WORK_ITEM; + #if 0 + #define PREPARE_WORK_ITEM(__item,__routine,__data) INIT_WORK((__item),(__routine),(__data)) + #else + #include + #define PREPARE_WORK_ITEM(__item,__routine,__data) __prepare_work(&(__item),(__routine),(__data)) + static inline void __prepare_work(struct work_struct *_work, + void (*_routine), + void * _data){ + INIT_LIST_HEAD(&_work->entry); + _work->pending = 0; + _work->func = _routine; + _work->data = _data; + init_timer(&_work->timer); + } + #endif + #undef PREPARE_WORK + typedef void (* WORK_PROC)(void *); + + #define SET_WORK_ARG(__item, __data) (__item).data = __data + + #define SCHEDULE_WORK(item) schedule_work(&item) + #define SCHEDULE_IMMEDIATE_WORK(item) SCHEDULE_WORK((item)) + #define PENDING_WORK_ITEM(item) (item.pending != 0) + #define NO_WORK_DATA(item) (!item.data) + #define _MOD_DEC_USE_COUNT //Not used in 2.6 + #define _MOD_INC_USE_COUNT //Not used in 2.6 + +#else /* LINUX26 */ + + #define WORK_STRUCT tq_struct + #define WORK_ITEM tq_struct + typedef struct WORK_ITEM WORK_ITEM; + #define PREPARE_WORK_ITEM(item,work_routine,work_data) { item.routine = work_routine; item.data = work_data; } + #define SET_WORK_ARG(__item, __data) (__item).data = __data + #define NO_WORK_DATA(item) (!(item).data) + #define SCHEDULE_WORK(item) schedule_task(&item) + #define PENDING_WORK_ITEM(item) (item.sync != 0) + #define _MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT + #define _MOD_INC_USE_COUNT MOD_INC_USE_COUNT + + typedef void (* WORK_PROC)(void *); + + #if !defined(IRQ_HANDLED) + // Irq's + typedef void irqreturn_t; + #define IRQ_NONE + #define IRQ_HANDLED + #define IRQ_RETVAL(x) + #endif +#endif /* LINUX26 */ + +/*! @} */ + +/*!@name Semaphores + * + * up() + * down() + */ +/*! @{ */ +#define UP(s) up(s) +#define DOWN(s) down(s) +/*! @} */ + +/*! @name Printk + * + * PRINTK() + */ +/*! @{ */ +#define PRINTK(s) printk(s) +/*! @} */ + + +#endif /* _OTG_LINUX_H */ diff -uNr linux/drivers/no-otg/otg/otg-list.h linux/drivers/otg/otg/otg-list.h --- linux/drivers/no-otg/otg/otg-list.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-list.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,69 @@ +#ifndef _OTG_LIST_H +#define _OTG_LIST_H 1 + +/* + This file defines doubly linked list capabilities + needed. Many operating systems have built in capabilities + which match and may possibly be borrowed. + + + Here is a typical implementation +*/ + +struct otg_list_node { + struct otg_list_node *previous, *next; +}; +typedef struct otg_list_node otg_list_node_t, *otg_list_node_ptr; + +//A macro to define an list node within a structure +#define OTG_LIST_HEAD struct otg_list_node +#define OTG_LIST_HEAD_INIT(name) struct otg_list_node name = {&name, &name}; +//Assume that an OTG_LIST_HEAD pointer is embedded as structure +// Recover numerical offset of member "member" within a pointer to a "type" +#define __OTG_PTR_OFFSET(type, member) (u32) ( &(((type *) 0)->member) ) +//Recover typed pointer from pointer to interior of type at the given offset +#define __OTG_PTR_CONTAINER(ptr, type, member) (type *) ((char *) ptr - __OTG_PTR_OFFSET(type,member)) +// member "member" of type "type". Recover a pointer to the beginning of the structure + +#define OTG_LIST_ENTRY(pointer, type, member) __OTG_PTR_CONTAINER(pointer, type, member) + +//Recover list header embedded in structure which is a member of a list +#define OTG_LIST_MEMBER(pointer, member) &(ptr->member) +//Add an entry at the logical end of the list + +//#define INLINE inline +#define OTG_LIST_ADD_TAIL(new_entry, list_head) __otg_list_add_tail(new_entry, list_head) +static INLINE void __otg_list_add_tail(struct otg_list_node *new_entry, struct otg_list_node *list_head) +{ + struct otg_list_node *old_previous = list_head->previous; + old_previous->next = new_entry; + new_entry->next = list_head; + new_entry->previous = old_previous; + list_head -> previous = new_entry; + +} + +//Delete an entry from the surrounding list +#define OTG_LIST_DEL_ENTRY(del_entry) __otg_list_del_entry(&(del_entry)) + +static void INLINE __otg_list_del_entry(struct otg_list_node *del_entry){ + //Remove from list + del_entry->previous->next = del_entry->next; + del_entry->next->previous = del_entry->previous; + //Invalidate the entry + del_entry->previous = NULL; + del_entry->next = NULL; +} + +#define OTG_LIST_FOR_EACH(cursor, list) for(cursor=(list)->next; cursor != (list); cursor=cursor->next) + +#undef LIST_HEAD +#define LIST_HEAD OTG_LIST_HEAD +#undef LIST_HEAD_INIT +#define LIST_HEAD_INIT(name) OTG_LIST_HEAD_INIT(name) +#define LIST_ENTRY(pointer,type,member) OTG_LIST_ENTRY(pointer,type,member) +#define LIST_ADD_TAIL(new_entry, list_head) OTG_LIST_ADD_TAIL(new_entry, list_head) +#define LIST_DEL_ENTRY(del_entry) OTG_LIST_DEL_ENTRY(del_entry) +#define LIST_FOR_EACH(cursor, list) OTG_LIST_FOR_EACH(cursor, list) + +#endif /*_OTG_LIST_H */ diff -uNr linux/drivers/no-otg/otg/otg-module.h linux/drivers/otg/otg/otg-module.h --- linux/drivers/no-otg/otg/otg-module.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-module.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,71 @@ +/* + * otg/otg/otg-module.h + * + * Copyright (c) 2004 Belcarra + * + */ +/*! + * @file otg/otg/otg-module.h + * @brief Linux Module OS Compatibility defines + * + * + * @ingroup OTGCore + */ +#ifndef _OTG_MODULE_H +#define _OTG_MODULE_H 1 + +#if defined(OTG_LINUX) + #if !defined(_LINUX_MODULE_H) + #include + #endif /* _LINUX_MODULE_H */ + + #if defined(MODULE) + + #define MOD_EXIT(exit_routine) module_exit(exit_routine) + #define MOD_INIT(init_routine) module_init(init_routine) + #define MOD_PROC(proc) (proc) + #define MOD_AUTHOR(string) MODULE_AUTHOR(string) + #define MOD_PARM(param, type) MODULE_PARM(param, type) + #define MOD_PARM_DESC(param,desc) MODULE_PARM_DESC(param, desc) + #define MOD_DESCRIPTION(description) MODULE_DESCRIPTION(description) + #define OTG_EXPORT_SYMBOL(symbol) EXPORT_SYMBOL(symbol) + #define OTG_EPILOGUE 1 /* EPILOGUE ROUTINE NEEDED */ + + #else /* defined(MODULE) */ + + #define MOD_EXIT(exit_routine) + #define MOD_INIT(init_routine) module_init(init_routine) + #define MOD_PROC(proc) NULL + #define MOD_AUTHOR(string) + #define MOD_PARM(param, type) + #define MOD_PARM_DESC(param,desc) + #define MOD_DESCRIPTION(description) + #define OTG_EXPORT_SYMBOL(symbol) //EXPORT_SYMBOL(symbol) + #undef EXPORT_SYMBOL + #define OTG_EPILOGUE 0 /* EPILOGUE ROUTINE NOT NEEDED */ + + #endif /* defined(MODULE) */ + + //#undef MODULE_AUTHOR + //#undef MODULE_DESCRIPTION + //#undef MODULE_PARM_DESC + //#undef MODULE_PARM + #if defined(LINUX24) || defined(LINUX26) + #include + #if defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17)) + //"GPL License applies under certain cirumstances; consult your vendor for details" + #define EMBED_LICENSE() MODULE_LICENSE ("GPL") + #else + #define EMBED_LICENSE() //Operation not supported for earlier Linux kernels + #endif + + #else /* defined(LINUX24) || defined(LINUX26) */ + #error "Need to define EMBED_LICENSE for the current operating system" + #endif /* defined(LINUX24) || defined(LINUX26) */ + #define EMBED_MODULE_INFO(section,moduleinfo) static char __##section##_module_info[] = moduleinfo "balden@belcarra.com" + #define EMBED_USBD_INFO(moduleinfo) EMBED_MODULE_INFO(usbd,moduleinfo) + #define GET_MODULE_INFO(section) __##section##_module_info +#endif /* defined(OTG_LINUX) */ + + +#endif diff -uNr linux/drivers/no-otg/otg/otg-ocd.h linux/drivers/otg/otg/otg-ocd.h --- linux/drivers/no-otg/otg/otg-ocd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-ocd.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,94 @@ +/* + * otg/otg-ocd.h - OTG Controller Driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * + */ +/*! + * @defgroup OTGOCD OTG Controller Driver Support + * @ingroup onthegogroup + */ +/*! + * @file otg/otg/otg-ocd.h + * @brief Defines common to On-The-Go OTG Controller Support + * + * This file defines the ocd_ops and ocd_instance structures. + * + * The ocd_ops structure contains all of the output functions that will + * be called as required by the OTG event handler when changing states. + * + * The ocd_instance structure is used to maintain the global data + * required by the OTG controller drivers. + * + * @ingroup OTGOCD + */ + +/*! + * @name OCD OTG Controller Driver + * @{ + */ +//struct ocd_instance; + +struct ocd_instance { + struct otg_instance *otg; + void * privdata; +}; + + +typedef int (*otg_timer_callback_proc_t) (void *); + +#define OCD_CAPABILITIES_DR 1 << 0 +#define OCD_CAPABILITIES_PO 1 << 1 +#define OCD_CAPABILITIES_TR 1 << 2 +#define OCD_CAPABILITIES_HOST 1 << 3 + +#define OCD_CAPABILITIES_AUTO 1 << 4 + + +/*! + * @struct ocd_ops + * The ocd_ops structure contains pointers to all of the functions implemented for the + * linked in driver. Having them in this structure allows us to easily determine what + * functions are available without resorting to ugly compile time macros or ifdefs + * + * There is only one instance of this, defined in the device specific lower layer. + */ +struct ocd_ops { + + u32 capabilities; /* OCD Capabilities */ + + /* Driver Initialization - by degrees + */ + int (*mod_init) (void); /*!< OCD Module Initialization */ + void (*mod_exit) (void); /*!< OCD Module Exit */ + + otg_output_proc_t ocd_init_func; /*!< OTG calls to initialize or de-initialize the OCD */ + + int (*start_timer) (struct otg_instance *, int);/*!< called by OTG to start timer */ + u64 (*ticks) (void); /*!< called by OTG to fetch current ticks, typically micro-seconds when available */ + u64 (*elapsed) ( u64 *, u64 *); /*!< called by OTG to get micro-seconds elapsed between two ticks */ + u32 interrupts; /*!< called by OTG to get number of interrupts */ +}; + + +#if 0 +struct ocd_instance { + struct otg_instance *otg; + void * privdata; +}; +#endif + +#ifndef OTG_APPLICATION + +//#define OCD ocd_trace_tag +extern otg_tag_t OCD; +extern struct ocd_ops ocd_ops; +extern struct ocd_instance *ocd_instance; +#endif +/* @} */ diff -uNr linux/drivers/no-otg/otg/otg-p2k.h linux/drivers/otg/otg/otg-p2k.h --- linux/drivers/no-otg/otg/otg-p2k.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-p2k.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,208 @@ +/* + * otg/otg/otg-p2k.h + * + * Copyright (c) 2004 Belcarra + * + */ +/*! + * @file otg/otg/otg-p2k.h + * @brief Linux OS Compatibility defines + * + * @ingroup OTGCore + */ + +#ifndef _OTG_P2K_H 1 +#define _OTG_P2K_H 1 + +/*!@name Common Platform 2000 includes + */ +/*! * @{ */ + +/* UNKNOWN SO FAR */ +/*! @} */ + +/*!@name Compilation Related + */ + +/*! @{ */ +#define PRAGMAPACK +#define PACKED +#define INLINE __inline + +/*! @} */ + +/*!@name OTG int typedefs + */ +/*! * @{ */ +typedef unsigned short u16; +typedef unsigned char u8; +typedef unsigned long int u32; +typedef __int64 u64; +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned long int __u32; +typedef __int64 __u64; + +/*! @} */ + +/*!@name Memory Allocation Primitives + * + * CKMALLOC() + * LSTRDUP() + * LKFREE() + * LIST_ENTRY() + */ +/*! @{ */ + +#define GFP_KERNEL 0 +#define GFP_ATOMIC 0 + +#define CKMALLOC(n,f) _ckmalloc(__FILE__, __LINE__, n, f) +#define LSTRDUP(str) _lstrdup(__FILE__, __LINE__, str) +#define LKFREE(p) _lkfree(__FILE__, __LINE__, p) + +#define OTG_MALLOC_TEST +#undef OTG_MALLOC_DEBUG + +#ifdef OTG_MALLOC_TEST + extern int otg_mallocs; +#endif + +static INLINE void *_ckmalloc (const char *func, int line, int n, int f) +{ + + return 0; +} +static INLINE char *_lstrdup (const char *func, int line, char *str) +{ + + return 0; +} +static INLINE void _lkfree (const char *func, int line, void *p) +{ +} + +// XXX +#define LIST_ENTRY(p, t, m) \ + ((t *)((char *)(p)-(unsigned long)(&((t *)0)->m))) + +// XXX +#define LIST_FOR_EACH(p, h) \ + for (p = (h)->next, (p->next); p != (h); \ + p = p->next, (p->next)) + +#define LIST_ADD_TAIL(n,h) (0) +#define LIST_ADD_TAIL(n,h) (0) +#define LIST_DEL(h) (0) + + +/*! @} */ + + +/*! @name Atomic Operations + * + * atomic_post_inc() + * atomic_pre_dec() + */ +/*! @{ */ + +/* @} */ + + + +/*! @name Scheduling Primitives + * + * WORK_STRUCT + * WORK_ITEM + * + * SCHEDULE_TIMEOUT() + * SET_WORK_ARG() + * SCHEDULE_WORK() + * SCHEDULE_IMMEDIATE_WORK() + * NO_WORK_DATA() + * MOD_DEC_USE_COUNT + * MOD_INC_USE_COUNT + */ +/*! @{ */ +#define HZ 60 +#define SCHEDULE_TIMEOUT(seconds) + + #define SCHEDULE_WORK(item) (0) +#define PENDING_WORK_ITEM(item) (0) +#define PREPARE_WORK_ITEM(__item,__routine,__data) (0) + +/*! @} */ + + +/*! @name Semaphores + * + * up() + * down() + */ +/*! @{ */ +#define UP(s) +#define DOWN(s) +/*! @} */ + + +/*!@name Printk + * + * PRINTK() + */ +/*! @{ */ +//#define PRINTK(s) printk(s) +// DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s"), _T(msg))); + +/*! @} */ + +/*! @name Little Endian Macros + * / + *! @{ + */ + +__inline u16 cpu_to_le16 ( u16 x ) +{ + return (x<<8) | (x>>8); +} + +__inline u16 le16_to_cpu ( u16 val ) +{ + return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8)); +} + +__inline u32 le32_to_cpu (u32 val) +{ + return (((val) & 0xff000000) >> 24) | (((val) & 0x00ff0000) >> 8) | + (((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24); +} +/*! @} */ + +/*! @name little endian macros + */ +/*! @{ */ +#define local_irq_save(f) f = 0; __asm cli +#define local_irq_restore(f) __asm sti + +/*! @} */ + +/*! @name Ioctl + */ +/*! @{ */ +#define _IOR(m,n,s) ((m)|(n)) +#define _IOW(m,n,s) ((m)|(n)) +#define _IOCSIZE(a) (sizeof(a)) +#define copy_from_user(d,s,n) memcpy(d,s,n) +#define copy_to_user(d,s,n) memcpy(d,s,n) + +/*! @} */ + +/*! @name ERRNO + */ +/*! @{ */ +#define EINVAL WSAEINVAL + +/*! @} */ + + + +#endif /* _OTG_P2K_H */ diff -uNr linux/drivers/no-otg/otg/otg-pcd.h linux/drivers/otg/otg/otg-pcd.h --- linux/drivers/no-otg/otg/otg-pcd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-pcd.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,84 @@ +/* + * otg/otg-pcd.h - OTG Peripheral Controller Driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @defgroup OTGPCD Peripheral Controller Driver Support + * @ingroup onthegogroup + */ +/*! + * @file otg/otg/otg-pcd.h + * @brief Defines common to On-The-Go Peripheral Controller Support + * + * This file defines the pcd_ops and pcd_instance structures. + * + * The pcd_ops structure contains all of the output functions that will + * be called as required by the OTG event handler when changing states. + * + * The pcd_instance structure is used to maintain the global data + * required by the peripheral controller drivers. + * + * @ingroup OTGPCD + */ + +/*! + * @name PCD Peripheral Controller Driver + * @{ + */ +typedef u16 (*framenum_t)(void); + +/*! + * @struct pcd_ops + * + * The pcd_ops structure contains pointers to all of the functions implemented for the + * linked in driver. Having them in this structure allows us to easily determine what + * functions are available without resorting to ugly compile time macros or ifdefs + * + * There is only one instance of this, defined in the device specific lower layer. + */ +struct pcd_ops { + + int (*mod_init) (void); /*!< PCD Module Initialization */ + void (*mod_exit) (void); /*!< PCD Module Exit */ + + otg_output_proc_t pcd_init_func; /*!< OTG calls to initialize or de-initialize the PCD */ + otg_output_proc_t pcd_en_func; /*!< OTG calls to enable or disable the PCD */ + otg_output_proc_t remote_wakeup_func; /*!< OTG calls to have PCD perform remote wakeup */ + + framenum_t framenum; /*!< OTG calls to get current USB Frame number */ +}; + +#define PCD pcd_trace_tag +extern otg_tag_t PCD; +extern struct pcd_ops pcd_ops; +extern struct pcd_instance *pcd_instance; + +/*! + * @struct pcd_instance + */ +struct pcd_instance { + struct otg_instance *otg; /*!< pointer to OTG Instance */ + struct usbd_bus_instance *bus; /*!< pointer to usb bus instance */ + void * privdata; /*!< pointer to private data for PCD */ + int pcd_exiting; /*!< non-zero if OTG is unloading */ + struct WORK_STRUCT bh; /*!< work structure for bottom half handler */ + +}; + + +#if !defined(OTG_C99) +extern void pcd_global_init(void); +#endif /* !defined(OTG_C99) */ +extern void pcd_init_func(struct otg_instance *, u8 ); +extern void pcd_en_func(struct otg_instance *, u8 ); + +/* @} */ + diff -uNr linux/drivers/no-otg/otg/otg-tcd.h linux/drivers/otg/otg/otg-tcd.h --- linux/drivers/no-otg/otg/otg-tcd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-tcd.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,114 @@ +/* + * otg/otg-tcd.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @defgroup OTGTCD Transceiver Controller Driver Support + * @ingroup onthegogroup + */ +/*! + * @file otg/otg/otg-tcd.h + * @brief Defines common to On-The-Go Transceiver Controller Support + * + * This file defines the tcd_ops and tcd_instance structures. + * + * The tcd_ops structure contains all of the output functions that will + * be called as required by the OTG event handler when changing states. + * + * The tcd_instance structure is used to maintain the global data + * required by the transceiver controller drivers. + * + * @ingroup OTGTCD + */ + +/*! + * @name TCD Transceiver Controller Driver + * @{ + */ + + +/*! + * @struct tcd_ops + * The pcd_ops structure contains pointers to all of the functions implemented for the + * linked in driver. Having them in this structure allows us to easily determine what + * functions are available without resorting to ugly compile time macros or ifdefs + * + * There is only one instance of this, defined in the device specific lower layer. + */ +struct tcd_ops { + u64 initial_state; + + void * privdata; + + /* Driver Initialization - by degrees + */ + int (*mod_init) (void); /*!< TCD Module Initialization */ + void (*mod_exit) (void); /*!< TCD Module Exit */ + + + /* 3. called for usbd_vbus() if defined */ + int (*vbus) (struct otg_instance *); /* return non-zero if the Vbus valid */ + int (*id) (struct otg_instance *); /* return non-zero if the ID valid */ + + /* 4. called by otg event to control outputs + */ + + otg_output_proc_t tcd_init_func; /*!< OTG calls to initialize or de-initialize the HCD */ + otg_output_proc_t tcd_en_func; /*!< OTG calls to enable or disable the TCD */ + otg_output_proc_t chrg_vbus_func; /*!< OTG calls to enable or disable charging Vbus */ + otg_output_proc_t drv_vbus_func; /*!< OTG calls to enable or disable Vbus */ + otg_output_proc_t dischrg_vbus_func; /*!< OTG calls to enable or disable discharging Vbus */ + otg_output_proc_t dp_pullup_func; /*!< OTG calls to enable or disable D+ pullup (aka loc_conn) */ + otg_output_proc_t dm_pullup_func; /*!< OTG calls to enable or disable D- pullup (aka loc_carkit) */ + otg_output_proc_t dp_pulldown_func; /*!< OTG calls to enable or disable D+ pulldown (aka loc_conn) */ + otg_output_proc_t dm_pulldown_func; /*!< OTG calls to enable or disable D- pulldown (aka loc_carkit) */ + otg_output_proc_t peripheral_host_func; /*!< OTG calls to enable or disable D- pulldown (aka loc_carkit) */ + otg_output_proc_t overcurrent_func; /*!< OTG calls to clear overcurrent indication */ + otg_output_proc_t dm_det_func; /*!< OTG calls to enable or disable D+ pullup detection */ + otg_output_proc_t dp_det_func; /*!< OTG calls to enable or disable D- pullup detection */ + otg_output_proc_t cr_det_func; /*!< OTG calls to enable or disable D+ CRINT detection */ + otg_output_proc_t charge_pump_func; /*!< OTG calls to enable or disable external charge pump */ + otg_output_proc_t bdis_acon_func; /*!< OTG calls to enable or disable the BDIS ACON feature */ + otg_output_proc_t mx21_vbus_drain_func; /*!< OTG calls to enable or disable the mx21 vbus drain feature */ + otg_output_proc_t id_pulldown_func; /*!< OTG calls to enable or disable ID pulldown to ground */ + otg_output_proc_t audio_func; /*!< OTG calls to enable or disable audio function */ + otg_output_proc_t uart_func; /*!< OTG calls to enable or disable uart function */ + otg_output_proc_t mono_func; /*!< OTG calls to enable or disable mono function */ +}; + +/*! + * @struct hcd_instance + */ +struct tcd_instance { + struct otg_instance *otg; /*!< pointer to OTG Instance */ + struct WORK_STRUCT bh; /*!< work structure for bottom half handler */ +}; + + +extern struct trace_ops trace_ops; +extern struct tcd_ops tcd_ops; + + +extern struct tcd_instance *tcd_instance; +extern int tcd_vbus (struct otg_instance *otg); + +/* pcd_cable_event[_irq] - these can be called in a traditional PCD implementation + * to set b_sess_vld appropriately when vbus is sensed (pcd must implement ocd_ops.vbus) + */ +extern void tcd_cable_event_irq (struct otg_instance *); +extern void tcd_cable_event (struct otg_instance *); + +//extern void tcd_init(struct otg_instance *, u8 ); + +#define TCD tcd_trace_tag +extern otg_tag_t TCD; + +/* @} */ diff -uNr linux/drivers/no-otg/otg/otg-trace.h linux/drivers/otg/otg/otg-trace.h --- linux/drivers/no-otg/otg/otg-trace.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-trace.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,404 @@ +/* + * otg/otgcore/otg-trace.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + */ +/*! + * @file otg/otg/otg-trace.h + * @brief Core Defines for USB OTG Core Layaer + * + * Fast Trace Utility + * This set of definitions and code is meant to provide a _fast_ debugging facility + * (much faster than printk) so that time critical code can be debugged by looking + * at a trace of events provided by reading a file in the procfs without affecting + * the timing of events in the critical code. + * + * The mechanism used it to allocate a (large) ring buffer of relatively small structures + * that include location and high-res timestamp info, and up to 8 bytes of optional + * data. Values are stored and timestamps are taken as the critical code runs, but + * data formatting and display are done during the procfs read, when more time is + * available :). + * + * Note that there is usually some machine dependent code involved in getting the + * high-res timestamp, and there may be other bits used just to keep the overall + * time impact as low as possible. + * + * Varargs up to 9 arguments are now supported, but you have to supply the number + * of args, since examining the format string for the number at trace event time + * was deemed too expensive time-wise. + * + * + * @ingroup OTGCore + */ + +#ifndef OTG_TRACE_H +#define OTG_TRACE_H 1 + +//#include + + + +typedef u8 otg_tag_t; // 1-origin tags (0 ==> invalid/unused trace entry. Max is 32. + +#ifdef PRAGMAPACK +#pragma pack(push,1) +#endif /* PRAGMAPACK */ +typedef enum otg_trace_types { + otg_trace_msg_invalid_n, + otg_trace_msg_va_start_n, + otg_trace_msg_va_list_n, + otg_trace_setup_n, + otg_trace_msg_n, + otg_trace_msg32_n, + otg_trace_msg16_n, + otg_trace_msg8_n +} PACKED otg_trace_types_t; +#ifdef PRAGMAPACK +#pragma pack(pop) +#endif /* PRAGMAPACK */ + +typedef struct otg_trace_msg { + char *msg; +} otg_trace_msg_t; + +typedef struct otg_trace_msg32 { + char *msg; + u32 val; +} otg_trace_msg32_t; + +typedef struct otg_trace_msg16 { + char *msg; + u16 val0; + u16 val1; +} otg_trace_msg16_t; + +typedef struct otg_trace_msg8 { + char *msg; + u8 val0; + u8 val1; + u8 val2; + u8 val3; +} otg_trace_msg8_t; + +#define OTG_TRACE_MAX_IN_VA 7 +typedef struct otg_trace_msg_va_list { + u32 val[OTG_TRACE_MAX_IN_VA]; +} otg_trace_msg_va_list_t; + +typedef struct otg_trace_msg_va_start { + const char *function; + u32 interrupts; + u64 ticks; + u16 framenum; + u16 dummy; + + union { + otg_trace_msg_t msg; + otg_trace_msg8_t msg8; + otg_trace_msg16_t msg16; + otg_trace_msg32_t msg32; + +#if !defined(BELCARRA_DEBUG) + struct usbd_device_request setup; +#else + unsigned char setup[8]; +#endif /* !defined(BELCARRA_DEBUG) */ + + } trace; +} PACKED otg_trace_msg_va_start_t; + +typedef struct trace { + otg_trace_types_t otg_trace_type; + otg_tag_t tag; + u8 va_num_args; + u8 in_interrupt:1; + u8 id_gnd:1; + union { + otg_trace_msg_va_start_t s; + otg_trace_msg_va_list_t l; + } va; +} PACKED otg_trace_t; + +#define TRACE_MAX_IS_2N 1 +#define TRACE_MAX 0x00008000 +#define TRACE_MASK 0x00007FFF +//#define TRACE_MAX 0x000080 +//#define TRACE_MASK 0x00007F + + +#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) + +#if !defined(OLD_TRACE_API) + +// The new API hides otg_trace_get_next(), and adds otg_trace_setup() as part of the core. + +#if !defined(BELCARRA_DEBUG) +extern void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup); +#define TRACE_SETUP(tag,setup) otg_trace_setup(tag,__FUNCTION__,setup) +#endif +#define TRACE_MSG0(tag, msg) otg_trace_msg(tag, __FUNCTION__, 0, msg, 0) +#define TRACE_FMSG0(tag, f, msg) otg_trace_msg(tag, f, 0, msg, 0) + +#else + +extern int otg_trace_first; +extern int otg_trace_last_read; +extern int otg_trace_next; + +#if 0 + +extern otg_trace_t *otg_traces; + + +otg_trace_t *otg_trace_get_next(otg_tag_t tag, const char *fn, otg_trace_types_t otg_trace_type, int n); + +#if !defined(BELCARRA_DEBUG) +/* + * Trace a setup packet. + * The BELCARRA_DEBUG guard is so the rest of these functions can be used + * outside of a USB context, such as debugging TTY flow control. + */ +static __inline__ void otg_trace_setup(otg_tag_t tag, const char *fn, struct usbd_device_request *setup) +{ + if (otg_traces) { + otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_setup_n,1); + memcpy(&p->va.s.trace.setup, setup, sizeof(struct usbd_device_request)); + } +} +#define TRACE_SETUP(tag,setup) otg_trace_setup(tag,__FUNCTION__,setup) +#endif /* !defined(BELCARRA_DEBUG) */ + +/* + * Trace a single non-parameterized message. + */ +static __inline__ void otg_trace_msg_0(otg_tag_t tag, const char *fn, char *msg) +{ + if (otg_traces) { + otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg_n,1); + p->va.s.trace.msg.msg = msg; + } +} +#define TRACE_MSG0(tag, msg) otg_trace_msg_0(tag, __FUNCTION__, msg) +#define TRACE_FMSG0(tag, f, msg) otg_trace_msg_0(tag, f, msg) + +/* + * Trace a message with a single 32-bit value. + */ +static __inline__ void otg_trace_msg_1xU32(otg_tag_t tag, const char *fn, char *fmt, u32 val) +{ + if (otg_traces) { + otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg32_n,1); + p->va.s.trace.msg32.val = val; + p->va.s.trace.msg32.msg = fmt; + } +} +#define OTRACE_MSG1(tag,fmt,val) otg_trace_msg_1xU32(tag,__FUNCTION__,fmt,(u32)val) + +/* + * Trace a message with two 16-bit values. + */ +static __inline__ void otg_trace_msg_2xU16(otg_tag_t tag, const char *fn, char *fmt, u16 val0, u16 val1) +{ + if (otg_traces) { + otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg16_n,1); + p->va.s.trace.msg16.val0 = val0; + p->va.s.trace.msg16.val1 = val1; + p->va.s.trace.msg16.msg = fmt; + } +} +#define OTRACE_MSG2(tag,fmt,val0,val1) otg_trace_msg_2xU16(tag,__FUNCTION__,fmt,(u16)val0,(u16)val1) + +/* + * Trace a message with four 8-bit values. + */ +static __inline__ void otg_trace_msg_4xU8(otg_tag_t tag, const char *fn, char *fmt, u8 val0, u8 val1, u8 val2, u8 val3) +{ + if (otg_traces) { + otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg8_n,1); + p->va.s.trace.msg8.val0 = val0; + p->va.s.trace.msg8.val1 = val1; + p->va.s.trace.msg8.val2 = val2; + p->va.s.trace.msg8.val3 = val3; + p->va.s.trace.msg8.msg = fmt; + } +} +#define OTRACE_MSG4(tag,fmt,val0,val1,val2,val3) otg_trace_msg_4xU8(tag,__FUNCTION__,fmt,(u8)val0,(u8)val1,(u8)val2,(u8)val3) +#endif + +#endif + +extern void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, ...); + +#define eprintf(format, args...) printk (KERN_INFO format , ## args) + +#define TRACE_MSG(tag, nargs, fmt, args...) otg_trace_msg(tag, __FUNCTION__, nargs, fmt, ## args) + +#define TRACE_MSG1(tag, fmt, a1) \ + otg_trace_msg(tag, __FUNCTION__, 1, fmt, a1) +#define TRACE_MSG2(tag, fmt, a1, a2) \ + otg_trace_msg(tag, __FUNCTION__, 2, fmt, a1, a2) +#define TRACE_MSG3(tag, fmt, a1, a2, a3) \ + otg_trace_msg(tag, __FUNCTION__, 3, fmt, a1, a2, a3) +#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) \ + otg_trace_msg(tag, __FUNCTION__, 4, fmt, a1, a2, a3, a4) +#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \ + otg_trace_msg(tag, __FUNCTION__, 5, fmt, a1, a2, a3, a4, a5) +#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \ + otg_trace_msg(tag, __FUNCTION__, 6, fmt, a1, a2, a3, a4, a5, a6) +#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \ + otg_trace_msg(tag, __FUNCTION__, 7, fmt, a1, a2, a3, a4, a5, a6, a7) +#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \ + otg_trace_msg(tag, __FUNCTION__, 8, fmt, a1, a2, a3, a4, a5, a6, a7, a8) +#if 0 +#define TRACE_MSG9(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + otg_trace_msg(tag, __FUNCTION__, 9, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) +#endif +#define TRACE_FMSG1(tag, f, fmt, a1) \ + otg_trace_msg(tag, f, 1, fmt, a1) +#define TRACE_FMSG2(tag, f, fmt, a1, a2) \ + otg_trace_msg(tag, f, 2, fmt, a1, a2) +#define TRACE_FMSG3(tag, f, fmt, a1, a2, a3) \ + otg_trace_msg(tag, f, 3, fmt, a1, a2, a3) +#define TRACE_FMSG4(tag, f, fmt, a1, a2, a3, a4) \ + otg_trace_msg(tag, f, 4, fmt, a1, a2, a3, a4) +#define TRACE_FMSG5(tag, f, fmt, a1, a2, a3, a4, a5) \ + otg_trace_msg(tag, f, 5, fmt, a1, a2, a3, a4, a5) +#define TRACE_FMSG6(tag, f, fmt, a1, a2, a3, a4, a5, a6) \ + otg_trace_msg(tag, f, 6, fmt, a1, a2, a3, a4, a5, a6) +#define TRACE_FMSG7(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7) \ + otg_trace_msg(tag, f, 7, fmt, a1, a2, a3, a4, a5, a6, a7) +#define TRACE_FMSG8(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \ + otg_trace_msg(tag, f, 8, fmt, a1, a2, a3, a4, a5, a6, a7, a8) +#if 0 +#define TRACE_FMSG9(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + otg_trace_msg(tag, f, 9, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) +#endif +extern otg_tag_t otg_trace_obtain_tag(void); +extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); + +#elif defined(OTG_WINCE) + +#define TRACE_MSG0(tag, msg) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s"), _T(msg))) + +#define TRACE_MSG1(tag, fmt, a1) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d"), _T(fmt), a1)) + +#define TRACE_MSG2(tag, fmt, a1, a2) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d"), _T(fmt), a1, a2)) + +#define TRACE_MSG3(tag, fmt, a1, a2, a3) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d"), _T(fmt), a1, a2, a3)); + +#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d"), _T(fmt), a1, a2, a3, a4)); + +#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5)); + +#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6)); + +#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6, a7)); + +#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6, a7, a8)); + +extern otg_tag_t otg_trace_obtain_tag(void); +extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); + +/* +#define TRACE_OTG_CURRENT(o) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CURRENT - %s (%s) RESET: %08x SET: %08x"),\ + otg_state_names[o->state], \ + otg_state_names[o->previous], \ + (u32)(~o->current_inputs), \ + (u32)(o->current_inputs))\ + ); + +#define TRACE_OTG_CHANGE(o) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CHANGE - %s (%s) RESET: %08x SET: %08x"),\ + otg_state_names[o->state], \ + otg_state_names[o->previous], \ + (u32)(~o->current_inputs), \ + (u32)(o->current_inputs))\ + ); + +#define TRACE_OTG_NEW(o) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - NEW - %s (%s) RESET: %08x SET: %08x"),\ + otg_state_names[o->state], \ + otg_state_names[o->previous], \ + (u32)(~o->current_inputs), \ + (u32)(o->current_inputs))\ + ); + +#define TRACE_OTG_INPUTS(u) \ + DEBUGMSG(ZONE_INIT,(_T("OTGCORE - INPUTS - RESET: %08x SET: %08x"),\ + (u32)(u >> 32), \ + (u32)(u & 0xffffffff))\ + ); +*/ + +extern otg_tag_t otg_trace_obtain_tag(void); +extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); + +#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ + +#define TRACE_SETUP(tag,setup) + +//#define TRACE_MSG(tag,nargs,fmt,...) +//#define TRACE_FMSG(tag, nargs,fmt,...) + +#define TRACE_MSG0(tag, fmt) +#define TRACE_MSG1(tag, fmt, a1) +#define TRACE_MSG2(tag, fmt, a1, a2) +#define TRACE_MSG3(tag, fmt, a1, a2, a3) +#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) +#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) +#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) +#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) +#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) +//#define TRACE_MSG9(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) + +#define TRACE_FMSG0(tag, f, fmt) +#define TRACE_FMSG1(tag, f, fmt, a1) +#define TRACE_FMSG2(tag, f, fmt, a1, a2) +#define TRACE_FMSG3(tag, f, fmt, a1, a2, a3) +#define TRACE_FMSG4(tag, f, fmt, a1, a2, a3, a4) +#define TRACE_FMSG5(tag, f, fmt, a1, a2, a3, a4, a5) +#define TRACE_FMSG6(tag, f, fmt, a1, a2, a3, a4, a5, a6) +#define TRACE_FMSG7(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7) +#define TRACE_FMSG8(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8) +//#define TRACE_FMSG9(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) + +//static inline otg_tag_t otg_trace_obtain_tag(void) +//{ +// return 0; +//} + +//static inline otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag) +//{ +// return 0; +//} + +extern otg_tag_t otg_trace_obtain_tag(void); +extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); + +#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ + +#define USBD usbd_trace_tag +extern otg_tag_t USBD; + +//#define ACM acm_trace_tag +//extern otg_tag_t ACM; + +//#define NETWORK network_trace_tag +//extern otg_tag_t NETWORK; + +#endif /* OTG_TRACE_H */ diff -uNr linux/drivers/no-otg/otg/otg-utils.h linux/drivers/otg/otg/otg-utils.h --- linux/drivers/no-otg/otg/otg-utils.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/otg-utils.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,151 @@ +/* + * otg/otg/otg-utils.h + * + * Basic utilities for OTG package + * Copyright (c) 2004 Belcarra + */ +/*! + * @file otg/otg/otg-utils.h + * @brief Useful macros. + * + * + * @ingroup OTGCore + */ + + +#if !defined(_OTG_UTILS_H) +#define _OTG_UTILS_H 1 + +/*! @name Min, Max and zero + * @{ + */ +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define ZERO(a) memset(&a, 0, sizeof(a)) + +/* @} */ + +/*! @name TRUE and FALSE + * + * @{ + */ +#if !defined(TRUE) +#define TRUE 1 +#endif + +#if !defined(FALSE) +#define FALSE 0 +#endif +/* @} */ + + +/*! @name CATCH and THROW + * + * These macros implement a conceptually clean way to use + * C's goto to implement a standard error handler. The typical forms are: + * + * THROW(error); // continue execution in CATCH statement + * THROW_IF(test, error); // continue execution in CATCH statement if test true + * THROW_UNLESS(test, error); // continue execution in CATCH statement if test false + * + * // program execution will continue after the CATCH statement; + * CATCH(error) { + * // handle error here + * } + * + * @{ + */ +#define CATCH(x) while(0) x: +#define THROW(x) goto x +#define THROW_IF(e, x) if (e) goto x; +#define THROW_UNLESS(e, x) UNLESS (e) goto x; +/* @} */ + + +/*! @name UNLESS + * + * This implements a simple equivalent to negated if (expression). + * + * Unless expression then statement else statement. + * + * @{ + */ +#define unless(x) if(!(x)) +#define UNLESS(x) if(!(x)) +/* @} */ + +/*! @name BREAK_ and CONTINUE_ + * + * These implement a conditional break and continue statement. + * + * @{ + */ +#define BREAK_UNLESS(x) UNLESS (x) break; +#define BREAK_IF(x) if (x) break; +#define CONTINUE_IF(x) if (x) continue; +#define CONTINUE_UNLESS(x) UNLESS (x) continue; + +/* @} */ + + +/*! @name RETURN_ + * + * These implement conditional return statement. + * @{ + */ +#define RETURN_IF(x) if (x) return; +#define RETURN_ZERO_IF(x) if (x) return 0; +#define RETURN_NULL_IF(x) if (x) return NULL; +#define RETURN_EBUSY_IF(x) if (x) return -EBUSY; +#define RETURN_EFAULT_IF(x) if (x) return -EFAULT; +#define RETURN_EINVAL_IF(x) if (x) return -EINVAL; +#define RETURN_ENOMEM_IF(x) if (x) return -ENOMEM; +#define RETURN_EAGAIN_IF(x) if (x) return -EAGAIN; +#define RETURN_ETIMEDOUT_IF(x) if (x) return -ETIMEDOUT; +#define RETURN_EPIPE_IF(x) if (x) return -EPIPE; +#define RETURN_IRQ_HANDLED_IF(x) if (x) return IRQ_HANDLED; +#define RETURN_TRUE_IF(x) if (x) return TRUE; +#define RETURN_FALSE_IF(x) if (x) return FALSE; + +#define RETURN_UNLESS(x) UNLESS (x) return; +#define RETURN_NULL_UNLESS(x) UNLESS (x) return NULL; +#define RETURN_ZERO_UNLESS(x) UNLESS (x) return 0; +#define RETURN_EBUSY_UNLESS(x) UNLESS (x) return -EBUSY; +#define RETURN_EFAULT_UNLESS(x) UNLESS (x) return -EFAULT; +#define RETURN_EINVAL_UNLESS(x) UNLESS (x) return -EINVAL; +#define RETURN_ENOMEM_UNLESS(x) UNLESS (x) return -ENOMEM; +#define RETURN_EAGAIN_UNLESS(x) UNLESS (x) return -EAGAIN; +#define RETURN_ETIMEDOUT_UNLESS(x) UNLESS (x) return -ETIMEDOUT; +#define RETURN_EPIPE_UNLESS(x) UNLESS (x) return -EPIPE; +#define RETURN_IRQ_HANDLED_UNLESS(x) UNLESS (x) return IRQ_HANDLED; +#define RETURN_TRUE_UNLESS(x) UNLESS (x) return TRUE; +#define RETURN_FALSE_UNLESS(x) UNLESS (x) return FALSE; + +/* @} */ + + +/*! @name likely and unlikely + * + * Under linux these can be used to provide a hint to GCC that + * the expression being evaluated is probably going to evaluate + * to true (likely) or false (unlikely). The intention is that + * GCC can then provide optimal code for that. + * @{ + */ +#ifndef likely +#define likely(x) x +#endif + +#ifndef unlikely +#define unlikely(x) x +#endif +/* @} */ + + +#endif /* _OTG_UTILS_H */ + diff -uNr linux/drivers/no-otg/otg/pcd-include.h linux/drivers/otg/otg/pcd-include.h --- linux/drivers/no-otg/otg/pcd-include.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/pcd-include.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,36 @@ +/* + * otg/udc.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otg/pcd-include.h + * @brief Linux OS Precompiled Headers + * + * @ingroup OTGCore + */ + +#include +#include + +#include +#include +#include + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + diff -uNr linux/drivers/no-otg/otg/usbp-bus.h linux/drivers/otg/otg/usbp-bus.h --- linux/drivers/no-otg/otg/usbp-bus.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/usbp-bus.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,354 @@ +/* + * otg/otg/usbp-bus.h - USB Device Bus Interface Driver Interface + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @file otg/otg/usbp-bus.h + * @brief Bus interface Defines for USB Device Core Layaer + * + * This file contains the USB Peripheral Driver definitions. + * + * The + * + * This is the interface between the bottom of the USB Core and the top of + * the Bus Interace Drivers and is comprised of: + * + * o public functions exported by the USB Core layer + * + * o structures and functions passed by the Function Driver to the USB + * Core layer + * + * USB Peripheral Controller Drivers are structured such that the upper edge + * implements interfaces to the USB Peripheral Core layer to provide low + * level USB services and the lower edge interfaces to the actual USB + * Hardware (typically called the USB Device Controller or UDC.) + * + * The peripheral controller layer primarily deals with endpoints. + * There is one control endpoint and one or more data endpoints. + * + * The control endpoint is special, with received data processed with + * the built in EP0 function. Which in turn will pass requests to + * interface functions as appropriate. + * + * Data endpoints are controlled by interface driver and have a + * function callback specified by the interface driver to perform + * whatever needs to be done when data is sent or received. + * + * + * + * @ingroup USBP + */ + +struct usbd_endpoint_map; +struct usbd_endpoint_instance; +struct usbd_bus_instance; +struct usbd_urb; + +/*! + * USB Bus Interface Driver structures + * + * Driver description: + * + * struct usbd_bus_operations + * struct usbd_bus_driver + * + */ + +extern int usbd_maxstrings; + + +/*! + * exported to otg + */ +struct usbd_ops { + int (*admin) (char *); + char * (*function_name) (int); +}; + + + +/*! Operations that the slave layer or function driver can use to interact + * with the bus interface driver. + * + * The send_urb() function is used by the usb-device endpoint 0 driver and + * function drivers to submit data to be sent. + * + * The cancel_urb() function is used by the usb-device endpoint 0 driver and + * function drivers to remove previously queued data to be sent. + * + * The endpoint_halted() function is used by the ep0 control function to + * check if an endpoint is halted. + * + * The endpoint_feature() function is used by the ep0 control function to + * set/reset device features on an endpoint. + * + * The device_event() function is used by the usb device core to tell the + * bus interface driver about various events. + */ +struct usbd_bus_operations { + //int (*xbus_serial_number) (struct usbd_bus_instance *, char *); + + int (*start_endpoint_in) (struct usbd_bus_instance *, struct usbd_endpoint_instance *); + int (*start_endpoint_out) (struct usbd_bus_instance *, struct usbd_endpoint_instance *); + int (*cancel_urb_irq) (struct usbd_urb *); + //int (*device_feature) (struct usbd_bus_instance *, int, int); + int (*endpoint_halted) (struct usbd_bus_instance *, int, int); + int (*halt_endpoint) (struct usbd_bus_instance *, int, int, int); + int (*set_address) (struct usbd_bus_instance *, int); + int (*event_handler) (struct usbd_bus_instance *, usbd_device_event_t, int); + int (*request_endpoints) (struct usbd_bus_instance *, struct usbd_endpoint_map *, + int, struct usbd_endpoint_request *); + int (*set_endpoints) (struct usbd_bus_instance *, int , struct usbd_endpoint_map *); + int (*framenum) (void); + u64 (*ticks) (void); + u64 (*elapsed) (u64 *, u64 *); + u64 (*interrupts) (void); +}; + +/*! Endpoint Map + * + * An array of these structures is created by the bus interface driver to + * show what endpoints have been configured for the function driver. + * + * This is the logical array of endpoints that are indexed into by the + * function layer using the logical endpoint numbers defined by the function + * drivers. It maps these numbers into a real physical endpoint. + * + * Fields that can be different for Full speed versus High speed are + * represented with an array both values are available. + * + * For interfaces with multiple alternate settings, each separate + * combinantion of interface_num/altsetting/endpoint must be specified. + * It is up to the lower layers to determine if / when overlapped + * endpoints can be re-used. + * + */ +struct usbd_endpoint_map { + u8 configuration; + u8 interface_num; + u8 alternate; + u8 bEndpointAddress[2]; // logical endpoint address + u16 wMaxPacketSize[2]; // packetsSize for requested endpoint + u8 bmAttributes[2]; // requested endpoint type + u16 transferSize[2]; // transferSize for bulk transfers + u8 physicalEndpoint[2]; // physical endpoint number + struct usbd_endpoint_instance *endpoint; +}; + + +/*! Endpoint configuration + * + * Per endpoint configuration data. Used to track which actual physical + * endpoints. + * + * The bus layer maintains an array of these to represent all physical + * endpoints. + * + */ +struct usbd_endpoint_instance { + int bEndpointAddress; // logical endpoint address + int physical_endpoint; // physical endpoint address - bus interface specific + int bmAttributes; // endpoint type + u16 wMaxPacketSize; // packet size for requested endpoint + u32 feature_setting; // save set feature information + + // control + int state; // available for use by bus interface driver + + // receive side + struct urb_link rdy; // empty urbs ready to receive + struct usbd_urb *rcv_urb; // active urb + u32 rcv_transferSize; // maximum transfer size from function driver + int rcv_error; // current bulk-in has an error + + // transmit side + struct urb_link tx; // urbs ready to transmit + struct usbd_urb *tx_urb; // active urb + u32 tx_transferSize; // maximum transfer size from function driver + + u32 sent; // data already sent + u32 last; // data sent in last packet XXX do we need this + + void *privdata; + +}; + +/*! @name endpoint zero states + * @{ + */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 +#define DATA_STATE_PENDING_XMIT 5 +#define WAIT_FOR_IN_STATUS 6 +/*! @} */ + +/*! Bus Interface data structure + * + * Keep track of specific bus interface. + * + * This is passed to the usb-device layer when registering. It contains all + * required information about each real bus interface found such that the + * usb-device layer can create and maintain a usb-device structure. + * + * Note that bus interface registration is incumbent on finding specific + * actual real bus interfaces. There will be a registration for each such + * device found. + * + * The max_tx_endpoints and max_rx_endpoints are the maximum number of + * possible endpoints that this bus interface can support. The default + * endpoint 0 is not included in these counts. + * + */ +struct usbd_bus_driver { + char *name; + u8 max_endpoints; // maximimum number of rx enpoints + u8 otg_bmAttributes; + u8 maxpacketsize; + u8 HighSpeedCapable; + struct usbd_device_description *device_description; + struct usbd_bus_operations *bops; + void *privdata; + u32 capabilities; + u8 bMaxPower; + u8 ports; +}; + +/*! + * Bus state + * + * enabled or disabled + */ +typedef enum usbd_bus_state { + usbd_bus_state_unknown, + usbd_bus_state_disabled, + usbd_bus_state_enabled +} usbd_bus_state_t; + + +/*! + * @name UDC Capabilities + * @{ + */ +#define HAVE_CABLE_IRQ 0x0001 +#define REMOTE_WAKEUP_SUPPORTED 0x0002 +#define ROOT_HUB 0x0004 +/*! @} */ + +/*! Bus Interface configuration structure + * + * This is allocated for each configured instance of a bus interface driver. + * + * It contains a pointer to the appropriate bus interface driver. + * + * The privdata pointer may be used by the bus interface driver to store private + * per instance state information. + */ +struct usbd_bus_instance { + + struct usbd_bus_driver *driver; + + int endpoints; + struct usbd_endpoint_instance *endpoint_array; // array of available configured endpoints + + struct urb_link finished; // processed urbs + + //char *serial_number_str; + + usbd_device_status_t status; // device status + usbd_bus_state_t bus_state; + usbd_device_state_t device_state; // current USB Device state + usbd_device_state_t suspended_state; // previous USB Device state + + struct usbd_function_instance *ep0; // ep0 configuration + struct usbd_function_instance *function_instance; + + + u8 HighSpeedFlag; + + u8 ConfigurationValue; // current set configuration (zero is default) + u8 bNumInterfaces; // number of interfaces in the current configuration + u8 *alternates; // array[0..interfaces-1] of alternate settings + // for each interface + + u32 device_feature_settings;// save set feature information + u8 otg_bmAttributes; + + struct WORK_STRUCT device_bh; // runs as bottom half, equivalent to interrupt time + + void *privdata; // private data for the bus interface + char *arg; +}; + + + + +/*! bus driver registration + * + * Called by bus interface drivers to register themselves when loaded + * or de-register when unloading. + */ +struct usbd_bus_instance *usbd_register_bus (struct usbd_bus_driver *, int); +void usbd_deregister_bus (struct usbd_bus_instance *); + + +/*! Enable/Disable Function + * + * Called by a bus interface driver to select and enable a specific function + * driver. + */ +int usbd_enable_function_irq (struct usbd_bus_instance *, char *, char *); +void usbd_disable_function (struct usbd_bus_instance *); + + +/*! + * usbd_configure_device is used by function drivers (usually the control endpoint) + * to change the device configuration. + * + * usbd_device_event is used by bus interface drivers to tell the higher layers that + * certain events have taken place. + */ +void usbd_bus_event_handler (struct usbd_bus_instance *, usbd_device_event_t, int); +void usbd_bus_event_handler_irq (struct usbd_bus_instance *, usbd_device_event_t, int); + + +/*! + * usbd_device_request - process a received urb + * @urb: pointer to an urb structure + * + * Used by a USB Bus interface driver to pass received data in a URB to the + * appropriate USB Function driver. + * + * This function must return 0 for success and -EINVAL if the request + * is to be stalled. + * + * Not that if the SETUP is Host to Device with a non-zero wLength then there + * *MUST* be a valid receive urb queued OR the request must be stalled. + */ +int usbd_device_request (struct usbd_bus_instance*, struct usbd_device_request *); + +/*! + * Device I/O + */ +void usbd_urb_finished (struct usbd_urb *, int ); +void usbd_urb_finished_irq (struct usbd_urb *, int ); +struct usbd_urb *usbd_first_urb_detached_irq (urb_link * hd); + +/*! flush endpoint + */ +void usbd_flush_endpoint_irq (struct usbd_endpoint_instance *); +void usbd_flush_endpoint (struct usbd_endpoint_instance *); +int usbd_find_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress); + + + diff -uNr linux/drivers/no-otg/otg/usbp-chap9.h linux/drivers/otg/otg/usbp-chap9.h --- linux/drivers/no-otg/otg/usbp-chap9.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/usbp-chap9.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,1577 @@ +/* + * otg/otg/usbp-chap9.h + * + * Copyright (c) 2004 Belcarra + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @defgroup USBP USB Peripheral Support + * @ingroup onthegogroup + */ + + +/*! + * @file otg/otg/usbp-chap9.h + * @brief Chapter 9 and other class descriptor structure definitions. + * + * USB Descriptors are used to build a configuration database for each USB + * Function driver. + * + * @ingroup USBP + */ + +/*! + * @name Device and/or Interface Class Codes + */ + + /*! @{ */ + + +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff +/*! @} */ + + +/*! @name USB Device Request Types (bmRequestType) + *C.f. USB 2.0 Table 9-2 +*/ + +/*! @{ */ + +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define USB_RECIP_HUB 0x00 +#define USB_RECIP_PORT 0x03 + +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 +#define USB_ENDPOINT_OPT 0x40 +/*! @} */ + +/*! @name Descriptor types + * C.f. USB Table 9-5 + */ + +/*! @{ */ + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIGURATION 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIGURATION 0x07 +#define USB_DT_INTERFACE_POWER 0x08 +#define USB_DT_OTG 0x09 + + +#define USB_DT_HID (USB_TYPE_CLASS | 0x01) +#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) +#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) +/*! @} */ + +/*! @name Descriptor sizes per descriptor type + */ + /*! @{ */ + +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 +#define USB_DT_HID_SIZE 9 +/*! @} */ + +/*! @name Endpoints + */ +/*! @{ */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_CONTROL 0x00 +#define USB_ENDPOINT_ISOCHRONOUS 0x01 +#define USB_ENDPOINT_BULK 0x02 +#define USB_ENDPOINT_INTERRUPT 0x03 +/*! @} */ + +/*! + * @name USB Packet IDs (PIDs) + */ +/*! @{ */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ +/*! @} */ + +/*! * @name Standard requests + */ + + /*! @{ */ + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C +/*! @} */ + +/*! + * @name Hub requests + */ + /*! @{ */ +#define USB_REQ_CLEAR_TT_BUFFER 0x08 +#define USB_REQ_RESET_TT 0x09 +#define USB_REQ_GET_TT_STATE 0x0A +#define USB_REQ_STOP_TT 0x0B +/*! @} */ + +/*! + * @name HID requests + */ + /*! @{ */ +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_GET_IDLE 0x02 +#define USB_REQ_GET_PROTOCOL 0x03 +#define USB_REQ_SET_REPORT 0x09 +#define USB_REQ_SET_IDLE 0x0A +#define USB_REQ_SET_PROTOCOL 0x0B +/*! @} */ + + +/*! + * @name USB Spec Release number + */ + /*! @{ */ + +#define USB_BCD_VERSION 0x0200 +/*! @} */ + +/*! + * @name Audio + */ + /*! @{ */ + +#define CS_AUDIO_UNDEFINED 0x20 +#define CS_AUDIO_DEVICE 0x21 +#define CS_AUDIO_CONFIGURATION 0x22 +#define CS_AUDIO_STRING 0x23 +#define CS_AUDIO_INTERFACE 0x24 +#define CS_AUDIO_ENDPOINT 0x25 + +#define AUDIO_HEADER 0x01 +#define AUDIO_INPUT_TERMINAL 0x02 +#define AUDIO_OUTPUT_TERMINAL 0x03 +#define AUDIO_MIXER_UNIT 0x04 +#define AUDIO_SELECTOR_UNIT 0x05 +#define AUDIO_FEATURE_UNIT 0x06 +#define AUDIO_PROCESSING_UNIT 0x07 +#define AUDIO_EXTENSION_UNIT 0x08 +/*! @} */ + + +/*! + * @name Device Requests (c.f Table 9-2) + */ + /*! @{ */ + +#define USB_REQ_DIRECTION_MASK 0x80 +#define USB_REQ_TYPE_MASK 0x60 +#define USB_REQ_RECIPIENT_MASK 0x1f + +#define USB_REQ_DEVICE2HOST 0x80 +#define USB_REQ_HOST2DEVICE 0x00 + +#define USB_REQ_TYPE_STANDARD 0x00 +#define USB_REQ_TYPE_CLASS 0x20 +#define USB_REQ_TYPE_VENDOR 0x40 + +#define USB_REQ_RECIPIENT_DEVICE 0x00 +#define USB_REQ_RECIPIENT_INTERFACE 0x01 +#define USB_REQ_RECIPIENT_ENDPOINT 0x02 +#define USB_REQ_RECIPIENT_OTHER 0x03 +/*! @} */ + +/*! + * @name Hub Recipients + */ + /*! @{ */ +#define USB_REQ_RECIPIENT_HUB 0x00 +#define USB_REQ_RECIPIENT_PORT 0x03 +/*! @} */ + +/*! + * @name get status bits + */ + /*! @{ */ + +#define USB_STATUS_SELFPOWERED 0x01 +#define USB_STATUS_REMOTEWAKEUP 0x02 + +#define USB_STATUS_HALT 0x01 +/*! @} */ + +/*! + * @name Convert Feature Selector to a Status Flag Setting + */ + /*! @{ */ +#define FEATURE(f) (1 << f) +/*! @} */ + +/*! + * @name standard feature selectors + */ + /*! @{ */ +#define USB_ENDPOINT_HALT 0x00 +#define USB_DEVICE_REMOTE_WAKEUP 0x01 +#define USB_TEST_MODE 0x02 +/*! @} */ + +/*! + * @name Hub Feature Selectors C.f. Table 11-17 + */ + /*! @{ */ +#if !defined(C_HUB_LOCAL_POWER) +#define C_HUB_LOCAL_POWER 0x0 +#endif +#if !defined(C_HUB_OVER_CURRENT) +#define C_HUB_OVER_CURRENT 0x1 +#endif + +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 + +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 +#define PORT_HIGH_SPEED 10 + +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +#define PORT_TEST 21 +#define PORT_INDICATOR 22 + +/*! @} */ + +#ifdef PRAGMAPACK +#pragma pack(push,1) +#endif + + +struct usbd_device_request { + u8 bmRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} PACKED; + + + +struct usbd_otg_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bmAttributes; +} PACKED; + +/*! + * @name OTG + */ + /*! @{ */ +#define USB_OTG_HNP_SUPPORTED 0x02 +#define USB_OTG_SRP_SUPPORTED 0x01 +/*! @} */ + +/*! + * @name OTG Feature selectors + */ + /*! @{ */ +#define USB_OTG_B_HNP_ENABLE 0x03 +#define USB_OTG_A_HNP_SUPPORT 0x04 +#define USB_OTG_A_ALT_HNP_ENABLE 0x05 +/*! @} */ + +/*! + * @name Class-Specific Request Codes + * C.f. CDC Table 46 + */ + /*! @{ */ + +#define CDC_CLASS_REQUEST_SEND_ENCAPSULATED 0x00 +#define CDC_CLASS_REQUEST_GET_ENCAPSULATED 0x01 + +#define CDC_CLASS_REQUEST_SET_COMM_FEATURE 0x02 +#define CDC_CLASS_REQUEST_GET_COMM_FEATURE 0x03 +#define CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE 0x04 + +#define CDC_CLASS_REQUEST_SET_LINE_CODING 0x20 +#define CDC_CLASS_REQUEST_GET_LINE_CODING 0x21 + +#define CDC_CLASS_REQUEST_SET_CONTROL_LINE_STATE 0x22 +#define CDC_CLASS_REQUEST_SEND_BREAK 0x23 +/*! @} */ + +/*! + * @name Notification codes + * c.f. CDC Table 68 + */ + /*! @{ */ + +#define CDC_NOTIFICATION_NETWORK_CONNECTION 0x00 +#define CDC_NOTIFICATION_RESPONSE_AVAILABLE 0x01 +#define CDC_NOTIFICATION_AUX_JACK_HOOK_STATE 0x08 +#define CDC_NOTIFICATION_RING_DETECT 0x09 +#define CDC_NOTIFICATION_SERIAL_STATE 0x20 +#define CDC_NOTIFICATION_CALL_STATE_CHANGE 0x28 +#define CDC_NOTIFICATION_LINE_STATE_CHANGE 0x29 +#define CDC_NOTIFICATION_CONNECTION_SPEED_CHANGE 0x2a +/*! @} */ + +struct hub_descriptor { + u8 bDescLength; + u8 bDescriptorType; + u8 bNbrPorts; + u16 wHubCharacteristics; + u8 bPwrOn2PwrGood; + u8 bHubContrCurrent; + u8 DeviceRemovable; + u8 PortPwrCtrlMask; +} PACKED; + +/*! + * @name Hub Descriptor C.f. 11.23.2.1 Table 11-13 + * N.B. This assumes 1-8 ports, DeviceRemovable and PortPwrCtrlMask + * must be sized for the number of if ports > 8. + */ + /*! @{ */ + + +#define HUB_GANGED_POWER 0x00 +#define HUB_INDIVIDUAL_POWER 0x01 +#define HUB_COMPOUND_DEVICE 0x04 +#define HUB_GLOBAL_OVERCURRENT 0x00 +#define HUB_INDIVIDUAL_OVERCURRENT 0x08 +#define HUB_NO_OVERCURRENT 0x10 +#define HUB_TT_8 0x00 +#define HUB_TT_16 0x20 +#define HUB_TT_24 0x40 +#define HUB_TT_32 0x60 +#define HUB_INDICATORS_SUPPORTED 0x80 +/*! @} */ + +/*! + * @name HID - Class Descriptors + * C.f. 7.1.1 + */ + /*! @{ */ + +#define HID 0x21 +#define HID_REPORT 0x22 +#define HID_PHYSICAL 0x23 +/*! @} */ + +/*! + * name HID Descriptor + * C.f. E.8 + */ + /*! @{ */ + + +struct hid_descriptor { + u8 bLength; + u8 bDescriptorType; + u16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bReportType; + u16 wItemLength; +} PACKED; + +/*! @} */ + +/*! + * name ACM - Line Coding structure + * C.f. CDC Table 50 + */ + /*! @{ */ + + +struct cdc_acm_line_coding { + u32 dwDTERate; + u8 bCharFormat; + u8 bParityType; + u8 bDataBits; +} PACKED; + + /*!@} */ + +/*! + * @name ACM - Line State + * C.f. CDC Table 51 + */ + /*! @{ */ +typedef u16 cdc_acm_bmLineState; +#define CDC_LINESTATE_D1_RTS (1 << 1) +#define CDC_LINESTATE_D0_DTR (1 << 0) +/*! @} */ + +/*! + * Serial State - C.f. 6.3.5 + */ +struct acm_serial_state { + u16 uart_state_bitmap; +}; + + +/*! @name ACM - UART State + * C.f. Table 69 - UART State Bitmap Values + */ +/*! @{ */ +typedef u16 cdc_acm_bmUARTState; +#define CDC_UARTSTATE_BOVERRUN (1 << 6) +#define CDC_UARTSTATE_BPARITY (1 << 5) +#define CDC_UARTSTATE_BFRAMING (1 << 4) +#define CDC_UARTSTATE_BRINGSIGNAL (1 << 3) +#define CDC_UARTSTATE_BBREAK (1 << 2) +#define CDC_UARTSTATE_BTXCARRIER_DSR (1 << 1) +#define CDC_UARTSTATE_BRXCARRIER_DCD (1 << 0) +/*! @} */ + +/* !USB Notification + * + */ + +struct cdc_notification_descriptor { + u8 bmRequestType; + u8 bNotification; + u16 wValue; + u16 wIndex; + u16 wLength; + u8 data[2]; +} PACKED; + + + + +/*! + * @name Communications Class Types + * + * c.f. CDC USB Class Definitions for Communications Devices + * c.f. WMCD USB CDC Subclass Specification for Wireless Mobile Communications Devices + * + * @{ + */ + +#define CLASS_BCD_VERSION 0x0110 +/*! @} */ + +/*! @name c.f. CDC 4.1 Table 14 */ + +/*! @{ */ + +#define AUDIO_CLASS 0x01 +#define COMMUNICATIONS_DEVICE_CLASS 0x02 +/*! @} */ + +/*! @name c.f. CDC 4.2 Table 15 + */ + /*! @{ */ +#define COMMUNICATIONS_INTERFACE_CLASS 0x02 +/*! @} */ + +/*! @name c.f. CDC 4.3 Table 16 + */ + /*! @{ */ +#define COMMUNICATIONS_NO_SUBCLASS 0x00 +#define COMMUNICATIONS_DLCM_SUBCLASS 0x01 +#define COMMUNICATIONS_ACM_SUBCLASS 0x02 +#define COMMUNICATIONS_TCM_SUBCLASS 0x03 +#define COMMUNICATIONS_MCCM_SUBCLASS 0x04 +#define COMMUNICATIONS_CCM_SUBCLASS 0x05 +#define COMMUNICATIONS_ENCM_SUBCLASS 0x06 +#define COMMUNICATIONS_ANCM_SUBCLASS 0x07 + +#define AUDIO_CONTROL_SUBCLASS 0x01 +#define AUDIO_STREAMING_SUBCLASS 0x02 +/*! @} */ + +/*! @name c.f. WMCD 5.1 + */ + /*! @{ */ +#define COMMUNICATIONS_WHCM_SUBCLASS 0x08 +#define COMMUNICATIONS_DMM_SUBCLASS 0x09 +#define COMMUNICATIONS_MDLM_SUBCLASS 0x0a +#define COMMUNICATIONS_OBEX_SUBCLASS 0x0b +/*! @} */ + +/*! @name c.f. CDC 4.6 Table 18 + */ + /*! @{ */ +#define DATA_INTERFACE_CLASS 0x0a +/*! @} */ + +/*! @name c.f. CDC 4.7 Table 19 + */ + /*! @{ */ +#define COMMUNICATIONS_NO_PROTOCOL 0x00 +/*! @} */ + + +/*! @name c.f. CDC 5.2.3 Table 24 + * c.f. Audio Appendix A.4 + */ + /*! @{ */ +#define CS_UNDEFINED 0x20 +#define CS_DEVICE 0x21 +#define CS_CONFIG 0x22 +#define CS_STRING 0x23 +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 +/*! @} */ + +/*! + * @name Audio Interface Class - c.f Appendix A.1 + */ + /*! @{ */ +#define AUDIO_INTERFACE_CLASS 0x01 +/*! @} */ + +/*! @name Audio Interface Subclass - c.f Appendix A.2 + */ + /*! @{ */ +#define AUDIO_SUBCLASS_UNDEFINED 0x00 +#define AUDIO_AUDIOCONTROL 0x01 +#define AUDIO_AUDIOSTREAMING 0x02 +#define AUDIO_MIDISTREAMING 0x03 +/*! @} */ + +/*! @name Audio Interface Proctol - c.f. Appendix A.3 + */ + /*! @{ */ +#define AUDIO_PR_PROTOCOL_UNDEFINED 0x00 +/*! @} */ + +/*! @name Audio Class-Specific AC Interface Descriptor Subtypes - c.f. A.5 + */ + /*! @{ */ +#define AUDIO_AC_DESCRIPTOR_UNDEFINED 0x00 +#define AUDIO_AC_HEADER 0x01 +#define AUDIO_AC_INPUT_TERMINAL 0x02 +#define AUDIO_AC_OUTPUT_TERMINAL 0x03 +#define AUDIO_AC_MIXER_UNIT 0x04 +#define AUDIO_AC_SELECTOR_UNIT 0x05 +#define AUDIO_AC_FEATURE_UNIT 0x06 +#define AUDIO_AC_PROCESSING_UNIT 0x07 +#define AUDIO_AC_EXTENSION_UNIT 0x08 +/*! @} */ + +/*! @name Audio Class-Specific AS Interface Descriptor Subtypes - c.f. A.6 + */ + /*! @{ */ +#define AUDIO_AS_DESCRIPTOR_UNDEFINED 0x00 +#define AUDIO_AS_GENERAL 0x01 +#define AUDIO_AS_FORMAT_TYPE 0x02 +#define AUDIO_AS_FORMAT_UNSPECFIC 0x03 +/*! @} */ + +/*! @name Audio Class Processing Unit Processing Types - c.f. A.7 + */ + /*! @{ */ +#define AUDIO_PROCESS_UNDEFINED 0x00 +#define AUDIO_UP_DOWN_MUIX_PROCESS 0x01 +#define AUDIO_DOLBY_PROLOGIC_PROCESS 0x02 +#define AUDIO_3D_STEREO_EXTENDER_PROCESS 0x03 +#define AUDIO_REVERBERATION_PROCESS 0x04 +#define AUDIO_CHORUS_PROCESS 0x05 +#define AUDIO_DYN_RANGE_COMP_PROCESS 0x06 +/*! @} */ + +/*! @name Audio Class-Specific Endpoint Descriptor Subtypes - c.f. A.8 + */ + /*! @{ */ +#define AUDIO_DESCRIPTOR_UNDEFINED 0x00 +#define AUDIO_EP_GENERAL 0x01 +/*! @} */ + +/*! @name Audio Class-Specific Request Codes - c.f. A.9 + */ + /*! @{ */ +#define AUDIO_REQUEST_CODE_UNDEFINED 0x00 +#define AUDIO_SET_CUR 0x01 +#define AUDIO_GET_CUR 0x81 +#define AUDIO_SET_MIN 0x02 +#define AUDIO_GET_MIN 0x82 +#define AUDIO_SET_MAX 0x03 +#define AUDIO_GET_MAX 0x83 +#define AUDIO_SET_RES 0x04 +#define AUDIO_GET_RES 0x84 +#define AUDIO_SET_MEM 0x05 +#define AUDIO_GET_MEM 0x85 +#define AUDIO_GET_STAT 0xff +/*! @} */ + +/*! + * @name bDescriptorSubtypes + * + * c.f. CDC 5.2.3 Table 25 + * c.f. WMCD 5.3 Table 5.3 + */ + /*! @{ */ + +#define USB_ST_HEADER 0x00 +#define USB_ST_CMF 0x01 +#define USB_ST_ACMF 0x02 +#define USB_ST_DLMF 0x03 +#define USB_ST_TRF 0x04 +#define USB_ST_TCLF 0x05 +#define USB_ST_UF 0x06 +#define USB_ST_CSF 0x07 +#define USB_ST_TOMF 0x08 +#define USB_ST_USBTF 0x09 +#define USB_ST_NCT 0x0a +#define USB_ST_PUF 0x0b +#define USB_ST_EUF 0x0c +#define USB_ST_MCMF 0x0d +#define USB_ST_CCMF 0x0e +#define USB_ST_ENF 0x0f +#define USB_ST_ATMNF 0x10 + +#define USB_ST_WHCM 0x11 +#define USB_ST_MDLM 0x12 +#define USB_ST_MDLMD 0x13 +#define USB_ST_DMM 0x14 +#define USB_ST_OBEX 0x15 +#define USB_ST_CS 0x16 +#define USB_ST_CSD 0x17 +#define USB_ST_TCM 0x18 +/*! @} */ + +/*! + * @name Class Descriptor Description Structures... + * c.f. CDC 5.1 + * c.f. WCMC 6.7.2 + * + * XXX add the other dozen class descriptor description structures.... + * + */ + /*! @{ */ +struct usbd_header_functional_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u16 bcdCDC; +}; + +struct usbd_call_management_functional_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bmCapabilities; + u8 bDataInterface; +}; + +struct usbd_abstract_control_functional_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bmCapabilities; +}; + +struct usbd_union_functional_functor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bMasterInterface; + u8 bSlaveInterface[1]; + //u8 bSlaveInterface[0]; // XXX FIXME +}; + +struct usbd_ethernet_networking_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + char *iMACAddress; + u8 bmEthernetStatistics; + u16 wMaxSegmentSize; + u16 wNumberMCFilters; + u8 bNumberPowerFilters; +}; + +struct usbd_mobile_direct_line_model_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u16 bcdVersion; + u8 bGUID[16]; +}; + +struct usbd_mobile_direct_line_model_detail_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bGuidDescriptorType; + u8 bDetailData[2]; + //u8 bDetailData[0]; // XXX FIXME +}; +#if 0 +struct usbd_header_description { + u8 bDescriptorSubtype; + u16 bcdCDC; +}; + +struct usbd_call_management_description { + u8 bmCapabilities; + u8 bDataInterface; +}; + +struct usbd_abstract_control_description { + u8 bmCapabilities; +}; + +struct usbd_union_function_description { + u8 bMasterInterface; + u8 bSlaveInterface[1]; + //u8 bSlaveInterface[0]; // XXX FIXME +}; + +struct usbd_ethernet_networking_description { + char *iMACAddress; + u8 bmEthernetStatistics; + u16 wMaxSegmentSize; + u16 wNumberMCFilters; + u8 bNumberPowerFilters; +}; + +struct usbd_mobile_direct_line_model_description { + u16 bcdVersion; + u8 bGUID[16]; +}; + +struct usbd_mobile_direct_line_model_detail_description { + u8 bGuidDescriptorType; + u8 bDetailData[2]; + //u8 bDetailData[0]; // XXX FIXME +}; + +struct usbd_class_description { + u8 bDescriptorSubtype; + u8 elements; + union { + struct usbd_header_description header; + struct usbd_call_management_description call_management; + struct usbd_abstract_control_description abstract_control; + struct usbd_union_function_description union_function; + struct usbd_ethernet_networking_description ethernet_networking; + struct usbd_mobile_direct_line_model_description mobile_direct; + struct usbd_mobile_direct_line_model_detail_description mobile_direct_detail; + } description; +}; +#endif +/*! @} */ + +/*! + * @name Endpoint Modifiers + * static struct usbd_endpoint_description function_default_A_1[] = { + * + * {this_endpoint: 0, attributes: CONTROL, max_size: 8, polling_interval: 0 }, + * {this_endpoint: 1, attributes: BULK, max_size: 64, polling_interval: 0, direction: IN}, + * {this_endpoint: 2, attributes: BULK, max_size: 64, polling_interval: 0, direction: OUT}, + * {this_endpoint: 3, attributes: INTERRUPT, max_size: 8, polling_interval: 0}, + * + * + */ + /*! @{ */ + +#define CONTROL 0x00 +#define ISOCHRONOUS 0x01 +#define BULK 0x02 +#define INTERRUPT 0x03 +/*! @} */ + + +/*! @name configuration modifiers + */ + +/* @{ */ +#define BMATTRIBUTE_RESERVED 0x80 +#define BMATTRIBUTE_SELF_POWERED 0x40 +#define BMATTRIBUTE_REMOTE_WAKEUP 0x20 + +#if 0 +/* + * The UUT tester specifically tests for MaxPower to be non-zero (> 0). + */ +#if !defined(CONFIG_OTG_MAXPOWER) || (CONFIG_OTG_MAXPOWER == 0) + #define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED + #define BMAXPOWER 1 +#else + #define BMATTRIBUTE BMATTRIBUTE_RESERVED + #define BMAXPOWER CONFIG_USBD_MAXPOWER +#endif +#endif +/*! @} */ + + + +/*! + * @name Standard Usb Descriptor Structures + */ + /*! @{ */ + +struct usbd_endpoint_descriptor { + u8 bLength; + u8 bDescriptorType; // 0x5 + u8 bEndpointAddress; + u8 bmAttributes; + u16 wMaxPacketSize; + u8 bInterval; +} PACKED; + +struct usbd_interface_descriptor { + u8 bLength; + u8 bDescriptorType; // 0x04 + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; +} PACKED; + +struct usbd_configuration_descriptor { + u8 bLength; + u8 bDescriptorType; // 0x2 + u16 wTotalLength; + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; +} PACKED; + +struct usbd_device_descriptor { + u8 bLength; + u8 bDescriptorType; // 0x01 + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u16 idVendor; + u16 idProduct; + u16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} PACKED; + +struct usbd_string_descriptor { + u8 bLength; + u8 bDescriptorType; // 0x03 + u16 wData[0]; +} PACKED; + +struct usbd_device_qualifier_descriptor { + u8 bLength; + u8 bDescriptorType; + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u8 bNumConfigurations; + u8 bReserved; +} PACKED; + +struct usbd_generic_descriptor { + u8 bLength; + u8 bDescriptorType; +} PACKED; + +struct usbd_generic_class_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; +} PACKED; +/*! @} */ + +/*! + * @name Communications Class Dscriptor Structures + * c.f. CDC 5.2 Table 25c + */ + +/*! @{*/ + +struct usbd_class_function_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; +} PACKED; + +struct usbd_class_function_descriptor_generic { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bmCapabilities; +} PACKED; + +struct usbd_class_header_function_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x00 + u16 bcdCDC; +} PACKED; + +struct usbd_class_call_management_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x01 + u8 bmCapabilities; + u8 bDataInterface; +} PACKED; + +struct usbd_class_abstract_control_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x02 + u8 bmCapabilities; +} PACKED; + +struct usbd_class_direct_line_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x03 +} PACKED; + +struct usbd_class_telephone_ringer_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x04 + u8 bRingerVolSeps; + u8 bNumRingerPatterns; +} PACKED; + +struct usbd_class_telephone_call_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x05 + u8 bmCapabilities; +} PACKED; + +struct usbd_class_union_function_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x06 + u8 bMasterInterface; + u8 bSlaveInterface0[0]; +} PACKED; + +struct usbd_class_country_selection_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x07 + u8 iCountryCodeRelDate; + u16 wCountryCode0[0]; +} PACKED; + + +struct usbd_class_telephone_operational_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x08 + u8 bmCapabilities; +} PACKED; + + +struct usbd_class_usbd_terminal_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x09 + u8 bEntityId; + u8 bInterfaceNo; + u8 bOutInterfaceNo; + u8 bmOptions; + u8 bChild0[0]; +} PACKED; + +struct usbd_class_network_channel_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x0a + u8 bEntityId; + u8 iName; + u8 bChannelIndex; + u8 bPhysicalInterface; +} PACKED; + +struct usbd_class_protocol_unit_function_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x0b + u8 bEntityId; + u8 bProtocol; + u8 bChild0[0]; +} PACKED; + +struct usbd_class_extension_unit_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x0c + u8 bEntityId; + u8 bExtensionCode; + u8 iName; + u8 bChild0[0]; +} PACKED; + +struct usbd_class_multi_channel_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x0d + u8 bmCapabilities; +} PACKED; + +struct usbd_class_capi_control_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x0e + u8 bmCapabilities; +} PACKED; + +struct usbd_class_ethernet_networking_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x0f + u8 iMACAddress; + u32 bmEthernetStatistics; + u16 wMaxSegmentSize; + u16 wNumberMCFilters; + u8 bNumberPowerFilters; +} PACKED; + +struct usbd_class_atm_networking_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x10 + u8 iEndSystermIdentifier; + u8 bmDataCapabilities; + u8 bmATMDeviceStatistics; + u16 wType2MaxSegmentSize; + u16 wType3MaxSegmentSize; + u16 wMaxVC; +} PACKED; + + +struct usbd_class_mdlm_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x12 + u16 bcdVersion; + u8 bGUID[16]; +} PACKED; + +/* +struct usbd_class_mdlmd_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x13 + u8 bGuidDescriptorType; + u8 bDetailData[0]; +} PACKED; +*/ + +struct usbd_class_blan_descriptor { + u8 bFunctionLength; // 0x7 + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x13 + u8 bGuidDescriptorType; + u8 bmNetworkCapabilities; + u8 bmDataCapabilities; + u8 bPad; +} PACKED; + +struct usbd_class_safe_descriptor { + u8 bFunctionLength; // 0x6 + u8 bDescriptorType; + u8 bDescriptorSubtype; // 0x13 + u8 bGuidDescriptorType; + u8 bmNetworkCapabilities; + u8 bmDataCapabilities; +} PACKED; + +/*@}*/ + + +/*! + * @name Descriptor Union Structures + */ + /*! @{ */ + +struct usbd_descriptor { + union { + struct usbd_generic_descriptor generic_descriptor; + struct usbd_generic_class_descriptor generic_class_descriptor; + struct usbd_endpoint_descriptor endpoint_descriptor; + struct usbd_interface_descriptor interface_descriptor; + struct usbd_configuration_descriptor configuration_descriptor; + struct usbd_device_descriptor device_descriptor; + struct usbd_string_descriptor string_descriptor; + } descriptor; + +} PACKED; + +struct usbd_class_descriptor { + union { + struct usbd_class_function_descriptor function; + struct usbd_class_function_descriptor_generic generic; + struct usbd_class_header_function_descriptor header_function; + struct usbd_class_call_management_descriptor call_management; + struct usbd_class_abstract_control_descriptor abstract_control; + struct usbd_class_direct_line_descriptor direct_line; + struct usbd_class_telephone_ringer_descriptor telephone_ringer; + struct usbd_class_telephone_operational_descriptor telephone_operational; + struct usbd_class_telephone_call_descriptor telephone_call; + struct usbd_class_union_function_descriptor union_function; + struct usbd_class_country_selection_descriptor country_selection; + struct usbd_class_usbd_terminal_descriptor usb_terminal; + struct usbd_class_network_channel_descriptor network_channel; + struct usbd_class_extension_unit_descriptor extension_unit; + struct usbd_class_multi_channel_descriptor multi_channel; + struct usbd_class_capi_control_descriptor capi_control; + struct usbd_class_ethernet_networking_descriptor ethernet_networking; + struct usbd_class_atm_networking_descriptor atm_networking; + struct usbd_class_mdlm_descriptor mobile_direct; + //struct usbd_class_mdlmd_descriptor mobile_direct_detail; + struct usbd_class_blan_descriptor mobile_direct_blan_detail; + struct usbd_class_safe_descriptor mobile_direct_safe_detail; + } descriptor; + +} PACKED; +/*! @} */ + +/*! + * @name Audio Status Word Format - c.f. Table 3.1 + */ + /*! @{ */ + +#define AUDIO_STATUS_INTERRUPT_PENDING 1<<7 +#define AUDIO_STATUS_MEMORY_CONTENT_CHANGED 1<<6 +#define AUDIO_STATUS_ORIGINATOR_AUDIO_CONTROL_INTERFACE 0 +#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_INTERFACE 1 +#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_ENDPOINT 2 +#define AUDIO_STATUS_ORIGINATOR_AUDIOCONTROL_INTERFACE 0 + +struct usbd_audio_status_word { + u8 bStatusType; + u8 bOriginator; +} PACKED; + +struct usbd_audio_ac_interface_header_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u16 bcdADC; + u16 wTotalLength; + u8 binCollection; + u8 bainterfaceNr[0]; +} PACKED; + +struct usbd_audio_input_terminal_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bTerminalID; + u16 wTerminalType; + u8 bAssocTerminal; + u8 bNrChannels; + u16 wChannelConfig; + u8 iChannelNames; + u8 iTerminal; +} PACKED; + +struct usbd_audio_input_terminal_description { + struct usbd_audio_input_terminal_descriptor *audio_input_terminal_descriptor; + u16 wTerminalType; + u16 wChannelConfig; + char * iChannelNames; + char * iTerminal; +}; + +struct usbd_audio_output_terminal_descriptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bTerminalID; + u16 wTerminalType; + u8 bAssocTerminal; + u8 bSourceID; + u8 iTerminal; +} PACKED; + +struct usbd_audio_output_terminal_description { + struct usbd_audio_output_terminal_descriptor *audio_output_terminal_descriptor; + u16 wTerminalType; + char * iTerminal; +}; + +struct usbd_audio_mixer_unit_descriptor_a { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bUniteID; + u8 bNrinPins; +} PACKED; + +struct usbd_audio_mixer_unit_descriptor_b { + u8 baSourceID; + u8 bNrChannels; + u16 wChannelConfig; + u8 iChannelNames; + u8 bmControls; + u8 iMixer; +} PACKED; + +struct usbd_audio_mixer_unit_description { + struct usbd_audio_mixer_unit_descriptor *audio_mixer_unit_descriptor; + u16 wChannelConfig; + char * iMixer; +}; + + +struct usbd_audio_as_general_interface_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bTerminalLink; + u8 bDelay; + u16 wFormatTag; +} PACKED; + +struct usbd_audio_as_general_interface_description { + struct usbd_audio_as_general_interface_descriptor *audio_as_general_interface_descriptor; + u16 wFormatTag; +}; + +struct usbd_audio_as_format_type_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bFormatType; + u8 bNrChannels; + u8 bSubFrameSize; + u8 bBitResolution; + u8 bSamFreqType; + u8 iSamFreq[3]; +} PACKED; + +struct usbd_audio_as_format_type_description { + struct usbd_audio_as_format_type_descriptor *audio_as_format_type_descriptor; + u16 wFormatTag; +}; + + + + +struct usbd_audio_descriptor { + union { + struct usbd_audio_ac_interface_header_descriptor header; + struct usbd_audio_input_terminal_descriptor input; + struct usbd_audio_output_terminal_descriptor output; + } descriptor; +} PACKED; + + + +struct usbd_ac_endpoint_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bEndpointAddress; + u8 bmAttributes; + u16 wMaxPacketSize; + u8 bInterval; + u8 bRefresh; + u8 bSynchAddress; +} PACKED; + + +struct usbd_as_iso_endpoint_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bmAttributes; + u8 bLockDelayUnits; + u16 wLockDelay; +} PACKED; +/*! @}*/ + + +/*! + * @name MCPC + */ + /*! @{ */ + +/*! struct usbd_interface_association + * Used in AB-1 or DL-1 interface bundles. + */ +struct usbd_interface_association_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bFirstInterface; + u8 bInterfaceCount; + u8 bFunctionClass; + u8 bFunctionSubClass; + u8 bFunctionProcotol; + u8 iFunction; +} PACKED; + +struct usbd_mobile_abstract_control_model_specific_desciptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubType; + u8 bType; + u8 bMode_1; +}; + +struct usbd_mobile_direct_line_model_specific_desciptor { + u8 bFunctionLength; + u8 bDescriptorType; + u8 bDescriptorSubType; + u8 bType; + u8 bMode_N[2]; +}; + + +/*! @} */ + +/*! + * Device Events + * + * These are defined in the USB Spec (c.f USB Spec 2.0 Figure 9-1). + * + * There are additional events defined to handle some extra actions we need to have handled. + * + */ +typedef enum usbd_device_event { + + DEVICE_UNKNOWN, // bi - unknown event + DEVICE_INIT, // bi - initialize + DEVICE_CREATE, // bi - + DEVICE_HUB_CONFIGURED, // bi - bus has been plugged int + DEVICE_RESET, // bi - hub has powered our port + + DEVICE_ADDRESS_ASSIGNED,// ep0 - set address setup received + DEVICE_CONFIGURED, // ep0 - set configure setup received + DEVICE_SET_INTERFACE, // ep0 - set interface setup received + + DEVICE_SET_FEATURE, // ep0 - set feature setup received + DEVICE_CLEAR_FEATURE, // ep0 - clear feature setup received + + DEVICE_DE_CONFIGURED, // ep0 - set configure setup received for ?? + + DEVICE_BUS_INACTIVE, // bi - bus in inactive (no SOF packets) + DEVICE_BUS_ACTIVITY, // bi - bus is active again + + DEVICE_POWER_INTERRUPTION,// bi - hub has depowered our port + DEVICE_HUB_RESET, // bi - bus has been unplugged + DEVICE_DESTROY, // bi - device instance should be destroyed + DEVICE_CLOSE, // bi - device instance should be destroyed + +} usbd_device_event_t; + +/*! USB Request Block structure + * + * This is used for both sending and receiving data. + * + * The callback function is used to let the function driver know when + * transmitted data has been sent. + * + * The callback function is set by the alloc_recv function when an urb is + * allocated for receiving data for an endpoint and used to call the + * function driver to inform it that data has arrived. + * + * Note that for OUT urbs the buffer is always allocated to a multiple of + * the packetsize that is 1 larger than the requested size. This prevents + * overflow if the host unexpectedly sends a full sized packet when we are + * expecting a short one (the host is always right..) + * + * USBD_URB_SENDZLP - set this flag if a ZLP (zero length packet) should be + * sent after the data is sent. This is required if sending data that is a + * multiple of the packetsize but less than the maximum transfer size for + * the protocol in use. A ZLP is required to ensure that the host + * recognizes a short transfer. + * + */ + +typedef struct urb_link { + struct urb_link *next; + struct urb_link *prev; +} urb_link; + + +/*! URB Status + * + * This defines the current status of a pending or finshed URB. + * + */ +typedef enum usbd_urb_status { + SEND_IN_QUEUE, + SEND_IN_PROGRESS, + SEND_FINISHED_OK, + SEND_FINISHED_ERROR, + SEND_CANCELLED, + SEND_STALLED, + SEND_DISABLED, + RECV_IN_QUEUE, + RECV_IN_PROGRESS, + RECV_OK, + RECV_ERROR, + RECV_CANCELLED, + RECV_STALLED, + RECV_DISABLED +} usbd_urb_status_t; + +#define USBD_URB_SENDZLP 0x01 + +struct usbd_urb { + + struct usbd_bus_instance *bus; + struct usbd_function_instance *function_instance; + struct usbd_endpoint_instance *endpoint; + + int (*callback) (struct usbd_urb *, int); + + u8 *buffer; // data received (OUT) or being sent (IN) + u32 request_length; // maximum data expected for OUT + u32 buffer_length; // allocated size of buffer + u32 actual_length; // actual data received (OUT or being sent (IN) + u32 flags; + + void *function_privdata; + void *bus_privdata; + void *pcimap; + + struct urb_link link; + usbd_urb_status_t status; // what is the current status of the urb + time_t jiffies; // timestamp for when urb was started or finished + u16 framenum; // SOF framenum when urb was finished +}; + +/*! Endpoint Request + * + * An array of these structures is initialized by each function driver to specify the endpoints + * it requires. + * + * The bus interface driver will attempt to fulfill these requests with the actual endpoints it + * has available. + * + * Note that in most cases the bEndpointAddress should be left as zero except for specialized + * function drivers that both require a specific value and know ahead of time that the specified + * value is legal for the bus interface driver being used. + */ +struct usbd_endpoint_request { + u8 configuration; /* configuration endpoint will be in */ + u8 interface_num; /* interface endpoint will be in */ + u8 alternate; /* altsetting for this request */ + u8 bmAttributes; /* endpoint type AND direction */ + u16 fs_requestedTransferSize; /* max full speed transfer size for this endpoint */ + u16 hs_requestedTransferSize; /* max high speed transfer size for this endpoint */ + u8 bEndpointAddress; /* specific bEndpointAddress function driver requires */ + u8 physical; /* physical endpoint used */ +}; + +/*! + * Device status + * + * Overall state, we use this to show when we are suspended. + * This is required because when resumed we need to go back + * to previous state. + */ +typedef enum usbd_device_status { + USBD_OPENING, // we are currently opening + USBD_RESETING, // we are currently opening + USBD_OK, // ok to use + USBD_SUSPENDED, // we are currently suspended + USBD_CLOSING, // we are currently closing + USBD_CLOSED, // we are currently closing + USBD_UNKNOWN, +} usbd_device_status_t; + + +/*! + * Device State (c.f USB Spec 2.0 Figure 9-1) + * + * What state the usb device is in. + * + * Note the state does not change if the device is suspended, we simply set a + * flag to show that it is suspended. + * + */ +typedef enum usbd_device_state { + STATE_INIT, // just initialized + STATE_CREATED, // just created + STATE_ATTACHED, // we are attached + STATE_POWERED, // we have seen power indication (electrical bus signal) + STATE_DEFAULT, // we been reset + STATE_ADDRESSED, // we have been addressed (in default configuration) + STATE_CONFIGURED, // we have seen a set configuration device command + STATE_SUSPENDED, // device has been suspended + STATE_UNKNOWN, // destroyed +} usbd_device_state_t; + + + +#ifdef PRAGMAPACK +#pragma pack(pop) +#endif + + diff -uNr linux/drivers/no-otg/otg/usbp-func.h linux/drivers/otg/otg/usbp-func.h --- linux/drivers/no-otg/otg/usbp-func.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/usbp-func.h 2006-09-01 21:41:33.000000000 +0200 @@ -0,0 +1,428 @@ +/* + * otg/otg/usbp-func.h - USB Device Function Driver Interface + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * + */ + +/*! + * @file otg/otg/usbp-func.h + * @brief Function Driver related structures and definitions. + * + * This file contains the USB Function Driver and USB Interface Driver + * definitions. + * + * The USBOTG support allows for implementation of composite devices. + * + * From USB 2.0, C.f. 5.2.3: + * + * A Device that has multiple interfaces controlled independantly of + * each other is referred to as a composite device. A composite device + * has only a single device. + * + * In this implemenation the function portion of the device is split into + * two types of drivers: + * + * - USB Interface Driver + * - USB Function Driver + * + * + * USB Interface Driver + * + * An USB Interface Driver specifies the Interface related descriptors and + * implements the data handling processes for each of the data endpoints + * associated with the interface (or interfaces) required for the function. + * It typically will also implement the upper edge interface to the OS. + * + * The USB Interface Driver will also handle non-standard device requests + * and feature requests that are for the interface or an endpoint associated + * with one of the interfaces associated with the driver. + * + * Each interface implemented by an USB Interface Driver will have a + * interface instance that stores a pointer to the parent USB Function + * Driver and has an array of pointers to endpoint instances for the data + * endpoints associated with the interface. + * + * + * USB Function Driver + * + * A USB Function Driver specifies the device and configuration descriptors. + * A configuration descriptor is assembled from the interface descriptors + * from the USB Interface Driver (or drivers) that it is making available to + * the USB Host. + * + * The USB Function Driver handles non-standard device requests and feature + * requests for the device. + * + * Each function driver has a function instance that maintains an array + * of pointers to configuration instances and a pointer to the device + * descriptor. + * + * Each configuration instance maintains an array of pointers to the + * interface instances for that configuration and a pointer to the + * complete configuration descriptor. + * + * @ingroup USBP + */ + +/*! + * This file contains the USB Device Core Common definitions. It also contains definitions for + * functions that are commonly used by both Function Drivers and Bus Interface Drivers. + * + * Specifically: + * + * o public functions exported by the USB Core layer + * + * o common structures and definitions + * + */ + + + +struct usbd_bus_instance; +struct usbd_endpoint_instance; +struct usbd_function_instance; + +/*! + * USB Function Driver types + */ +typedef enum usbd_function_types { + function_simple, + function_interface, + function_composite +} usbd_function_types_t; + + +/*! + * USB Function Driver structures + * + * Descriptors: + * struct usbd_endpoint_description + * struct usbd_interface_description + * struct usbd_configuration_description + * + * Driver description: + * struct usbd_function_driver + * struct usbd_function_operations + * + */ + +struct usbd_function_operations { + + int (*function_enable) (struct usbd_function_instance *); + void (*function_disable) (struct usbd_function_instance *); + + /* + * All of the following may be called from an interrupt context + */ + void (*event_handler) (struct usbd_function_instance *, usbd_device_event_t, int); + int (*device_request) (struct usbd_function_instance *, struct usbd_device_request *); + int (*set_configuration) (struct usbd_function_instance *, int configuration); + + int (*set_interface) (struct usbd_function_instance *, int interface, int altsetting); + + void * (*get_descriptor)(struct usbd_function_instance *, int descriptor_type, int descriptor_index); + int * (*set_descriptor)(struct usbd_function_instance *, int descriptor_type, int descriptor_index, void *); + + void (*endpoint_cleared)(struct usbd_function_instance*, int wIndex, int); + +}; + + +/*! + * function driver definitions + */ +struct usbd_alternate_instance { + struct usbd_interface_descriptor *interface_descriptor; + int classes; + struct usbd_generic_class_descriptor **class_list; + int endpoints; + struct usbd_endpoint_descriptor **endpoint_list; + u8 *endpoint_indexes; +}; + + +/*! + * usb device description structures + */ + +struct usbd_alternate_description { + struct usbd_interface_descriptor *interface_descriptor; + char *iInterface; + + // list of CDC class descriptions for this alternate interface + u8 classes; + struct usbd_generic_class_descriptor **class_list; + + // list of endpoint descriptions for this alternate interface + u8 endpoints; + struct usbd_endpoint_descriptor **endpoint_list; + + // list of indexes into endpoint request map for each endpoint descriptor + u8 *endpoint_indexes; +}; + +typedef struct usbd_generic_class_descriptor usbd_class_descriptor_t; +typedef struct usbd_endpoint_descriptor usbd_endpoint_descriptor_t; + +struct usbd_interface_description { + // list of alternate interface descriptions for this interface + u8 alternates; + struct usbd_alternate_description *alternate_list; +}; + +struct usbd_configuration_description { + struct usbd_configuration_descriptor *configuration_descriptor; + char *iConfiguration; + // list of interface descriptons for this configuration + //u8 bNumInterfaces; + //struct usbd_interface_description *interface_list; + int configuration_type; +}; + +struct usbd_device_description { + struct usbd_device_descriptor *device_descriptor; + #ifdef CONFIG_OTG_HIGH_SPEED + struct usbd_device_qualifier_descriptor *device_qualifier_descriptor; + #endif /* CONFIG_OTG_HIGH_SPEED */ + struct usbd_otg_descriptor *otg_descriptor; + u8 *iManufacturer; + u8 *iProduct; + u8 *iSerialNumber; + //u8 endpointsRequested; + //struct usbd_endpoint_request *requestedEndpoints; +}; + + +struct usbd_interfaces_instance { + u8 alternates; + struct usbd_alternate_instance *alternates_instance_array; +}; + +struct usbd_configuration_instance { + int bNumInterfaces; + struct usbd_configuration_descriptor *configuration_descriptor; + struct usbd_interfaces_instance *interfaces_instance_array; + struct usbd_function_driver *function_driver; +}; + + +/*! Function Driver data structure + * + * Function driver and its configuration descriptors. + * + * This is passed to the usb-device layer when registering. It contains all + * required information about the function driver for the usb-device layer + * to use the function drivers configuration data and to configure this + * function driver an active configuration. + * + * Note that each function driver registers itself on a speculative basis. + * Whether a function driver is actually configured will depend on the USB + * HOST selecting one of the function drivers configurations. + * + * This may be done multiple times WRT to either a single bus interface + * instance or WRT to multiple bus interface instances. In other words a + * multiple configurations may be selected for a specific bus interface. Or + * the same configuration may be selected for multiple bus interfaces. + * + */ +struct usbd_function_driver { + const char *name; + struct usbd_function_operations *fops; // functions + + // device & configuration descriptions + struct usbd_device_description *device_description; + struct usbd_configuration_description *configuration_description; + int bNumConfigurations; + + u16 idVendor; + u16 idProduct; + u16 bcdDevice; + + u8 bNumInterfaces; + struct usbd_interface_description *interface_list; + + u8 endpointsRequested; + struct usbd_endpoint_request *requestedEndpoints; + + // constructed descriptors + struct usbd_device_descriptor *device_descriptor; +#ifdef CONFIG_OTG_HIGH_SPEED + struct usbd_device_qualifier_descriptor *device_qualifier_descriptor; +#endif /* CONFIG_OTG_HIGH_SPEED */ + struct usbd_otg_descriptor *otg_descriptor; + struct usbd_configuration_instance *configuration_instance_array; +}; + +/*! Function configuration structure + * + * This is allocated for each configured instance of a function driver. + * + * It stores pointers to the usbd_function_driver for the appropriate function, + * and pointers to the USB HOST requested usbd_configuration_description and + * usbd_interface_description. + * + * The privdata pointer may be used by the function driver to store private + * per instance state information. + * + * The endpoint map will contain a list of all endpoints for the configuration + * driver, and only related endpoints for an interface driver. + * + * The interface driver array will be NULL for an interface driver. + */ +struct usbd_function_instance { + char * name; + LIST_NODE instances; // linked list (was drivers in usbd_function_driver) + struct usbd_bus_instance *bus; + struct usbd_function_driver *function_driver; + void *privdata; // private data for the function + + usbd_function_types_t function_type; + + int configuration; // configuration driver only + int altsetting; // interface driver only + + char **interfaces_list; + + int interfaces; + struct usbd_function_instance **interfaces_array; + + int endpoints; + struct usbd_endpoint_request *requestedEndpoints; + struct usbd_endpoint_map *endpoint_map_array; + +}; + + +struct usbd_function_instance; + +/*! + * @name Function Driver Registration + * + * Called by function drivers to register themselves when loaded + * or de-register when unloading. + * @{ + */ +struct usbd_function_instance * usbd_register_function (struct usbd_function_driver *, char *, void *); +struct usbd_function_instance * usbd_register_interface (struct usbd_function_driver *, char *, void *); +struct usbd_function_instance * usbd_register_composite (struct usbd_function_driver *function_driver, char*, char **, void *); + +void usbd_deregister_function (struct usbd_function_instance *); +struct usbd_function_instance *usbd_find_function (char *name); +struct usbd_function_instance *usbd_find_interface(char *name); +/*! @} */ + + +/*! + * @name String Descriptor database + * + * @{ + */ + +struct usbd_string_descriptor *usbd_get_string_descriptor (u8); +u8 usbd_realloc_string (u8, char *); +u8 usbd_alloc_string (char *); +void usbd_free_string_descriptor(u8 ); + +//extern struct usbd_string_descriptor **usb_strings; +//void usbd_free_descriptor_strings (struct usbd_descriptor *); + +/*! @} */ + + + +/*! + * @name Device Information + * + * @} + */ +int usbd_high_speed(struct usbd_function_instance *); +int usbd_bmaxpower(struct usbd_function_instance *); +int usbd_endpoint_wMaxPacketSize(struct usbd_function_instance *, int, int); +int usbd_endpoint_zero_wMaxPacketSize(struct usbd_function_instance *, int); +int usbd_endpoint_bEndpointAddress(struct usbd_function_instance *, int, int); +int usbd_get_bMaxPower(struct usbd_function_instance *); +/*! @} */ + + +/*! + * @name Device Control + * + * @} + */ +usbd_device_state_t usbd_get_device_state(struct usbd_function_instance *); +usbd_device_status_t usbd_get_device_status(struct usbd_function_instance *); +int usbd_framenum(struct usbd_function_instance *); +u64 usbd_ticks(struct usbd_function_instance *); +u64 usbd_elapsed(struct usbd_function_instance *, u64 *, u64 *); + +/*! @} */ + +/*! + * @name Endpoint I/O + * + * @} + */ + +struct usbd_urb *usbd_alloc_urb (struct usbd_function_instance *, int, int length, int (*callback) (struct usbd_urb *, int)); +struct usbd_urb *usbd_alloc_urb_ep0 (struct usbd_function_instance *, int length, int (*callback) (struct usbd_urb *, int)); +void usbd_free_urb (struct usbd_urb *urb); +int usbd_start_in_urb (struct usbd_urb *urb); +int usbd_start_out_urb (struct usbd_urb *); +int usbd_cancel_urb(struct usbd_urb *); +int usbd_halt_endpoint (struct usbd_function_instance *function, int endpoint_index); +int usbd_endpoint_halted (struct usbd_function_instance *function, int endpoint); + + +/*! @} */ + + +/*! + * @name endpoint information + * + * Used by function drivers to get specific endpoint information + * @{ + */ + +int usbd_endpoint_transferSize(struct usbd_function_instance *, int, int); +int usbd_endpoint_interface(struct usbd_function_instance *, int); +int usbd_interface_AltSetting(struct usbd_function_instance *, int); +int usbd_ConfigurationValue(struct usbd_function_instance *); +void usbd_endpoint_update(struct usbd_function_instance *, int , struct usbd_endpoint_descriptor *, int); +/*! @} */ + +//int usbd_remote_wakeup_enabled(struct usbd_function_instance *); + +/*! + * @name device information + * + * Used by function drivers to get device feature information + * @{ + */ +int usbd_otg_bmattributes(struct usbd_function_instance *); +/*! @} */ + +/*! + * @name usbd_feature_enabled - return non-zero if specified feature has been enabled + * @{ + */ +int usbd_feature_enabled(struct usbd_function_instance *function, int f); +/*! @} */ + +/* DEPRECATED */ +/*! + * @name Access to function privdata (DEPRECATED) + * @{ + */ +void *usbd_function_get_privdata(struct usbd_function_instance *function); +void usbd_function_set_privdata(struct usbd_function_instance *function, void *privdata); +void usbd_flush_endpoint_index (struct usbd_function_instance *, int ); +int usbd_old_get_descriptor (struct usbd_function_instance *function, u8 *buffer, int max, int descriptor_type, int index); +/*! @} */ + diff -uNr linux/drivers/no-otg/otg/usbp-pcd.h linux/drivers/otg/otg/usbp-pcd.h --- linux/drivers/no-otg/otg/usbp-pcd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otg/usbp-pcd.h 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,279 @@ +/* + * otg/otg/usbp-pcd.h - OTG Peripheral Controller Driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @file otg/otg/usbp-pcd.h + * @brief Peripheral Controller Driver Related Structures and Definitions. + * + * @ingroup USBP + */ + +/*! + * The pcd_ops structure contains pointers to all of the functions implemented for the + * linked in driver. Having them in this structure allows us to easily determine what + * functions are available without resorting to ugly compile time macros or ifdefs + * + * There is only one instance of this, defined in the device specific lower layer. + */ +struct usbd_pcd_ops { + + /* mandatory */ + u8 bmAttributes; + int max_endpoints; + int ep0_packetsize; + u32 capabilities; /* UDC Capabilities - see usbd-bus.h for details */ + u8 bMaxPower; + char *name; + u8 ports; + + + /* 3. called by ot_init() if defined */ + int (* serial_init) (struct pcd_instance *); /* get device serial number if available */ + + /* 4. called from usbd_enable_function_irq() - REQUIRED */ + int (*request_endpoints) /* process endpoint_request list and return endpoint_map */ + (struct pcd_instance *, + struct usbd_endpoint_map *, /* showing allocated endpoints with attributes, addresses */ + int, struct usbd_endpoint_request*); /* and other associated information */ + + /* 5. called from usbd_enable_function_irq() - REQUIRED */ + int (*set_endpoints) /* setup required endpoints in hardware */ + (struct pcd_instance *, int , struct usbd_endpoint_map *); + + /* 6. called by xxx() if defined */ + void (*enable) (struct pcd_instance *); /* enable the UDC */ + + /* 7. called by xxx() if defined */ + void (*disable) (struct pcd_instance *); /* disable the UDC */ + + /* 8. called by xxx() if defined */ + void (*start) (struct pcd_instance *); /* called for DEVICE_CREATE to start the UDC */ + void (*stop) (struct pcd_instance *); /* called for DEVICE_DESTROY to stop the UDC */ + void (*disable_ep) + (struct pcd_instance *, + unsigned int ep); /* called for DEVICE_DESTROTY to disable endpoint zero */ + + /* 9. called by xxx() if defined */ + void (*set_address) + (struct pcd_instance *, + unsigned char address); /* called for DEVICE_RESET and DEVICE_ADDRESS_ASSIGNED to */ + + + /* 10. called by xxx() if defined */ + void (*setup_ep) + (struct pcd_instance *, + unsigned int ep, /* called for DEVICE_CONFIGURED to setup specified endpoint for use */ + struct usbd_endpoint_instance *endpoint); + + /* 11. called by xxx() if defined */ + void (*reset_ep) + (struct pcd_instance *, + unsigned int ep); /* called for DEVICE_RESET to reset endpoint zero */ + + + /* 12. called by usbd_send_urb() - REQUIRED */ + void (*start_endpoint_in) /* start an IN urb */ + (struct pcd_instance *, + struct usbd_endpoint_instance *); + + /* 13. called by usbd_start_recv() - REQUIRED */ + void (*start_endpoint_out) /* start an OUT urb */ + (struct pcd_instance *, + struct usbd_endpoint_instance *); + + /* 14. called by usbd_cancel_urb_irq() */ + void (*cancel_in_irq) + (struct pcd_instance *, + struct usbd_urb *urb); /* cancel active urb for IN endpoint */ + void (*cancel_out_irq) + (struct pcd_instance *, + struct usbd_urb *urb); /* cancel active urb for OUT endpoint */ + + + /* 15. called from ep0_recv_setup() for GET_STATUS request */ + int (*endpoint_halted) + (struct pcd_instance *, + struct usbd_endpoint_instance *); /* Return non-zero if requested endpoint is halted */ + int (*halt_endpoint) + (struct pcd_instance *, + struct usbd_endpoint_instance *, + int); /* Return non-zero if requested endpoint clear fails */ + + /* 16 */ + void (*startup_events) (struct pcd_instance *); /* perform UDC specific USB events */ + + /* 17. root hub operations + */ + int (*hub_status) (struct pcd_instance *); + int (*port_status) (struct pcd_instance *, int); + int (*hub_feature) (struct pcd_instance *, int, int); + int (*port_feature) (struct pcd_instance *, int, int, int); + int (*get_ports_status) (struct pcd_instance *); + int (*wait_for_change) (struct pcd_instance *); +}; + +extern struct usbd_pcd_ops usbd_pcd_ops; + + +/*! pcd_rcv_next_irq - get the current or next urb to receive into + * Called by UDC driver to get current receive urb or next available receive urb. + */ +static __inline__ struct usbd_urb * pcd_rcv_next_irq (struct usbd_endpoint_instance *endpoint) +{ + if (!endpoint->rcv_urb) + if ( (endpoint->rcv_urb = usbd_first_urb_detached_irq (&endpoint->rdy))) + endpoint->rcv_urb->status = RECV_IN_PROGRESS; + + //TRACE_MSG1(PCD, "BUS_RCV_URB: %p", endpoint->rcv_urb); + return endpoint->rcv_urb; +} + + +/*! pcd_rcv_complete_irq - complete a receive operation + * Called by UDC driver to show completion of outstanding I/O, this will + * update the urb length and if necessary will finish the urb passing it to the + * bottom half which will in turn call the function driver callback. + */ +struct usbd_urb * pcd_rcv_complete_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad); +struct usbd_urb * pcd_rcv_finished_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad); + + +/*! pcd_rcv_cancelled_irq - cancel current receive urb + * Called by UDC driver to cancel the current receive urb. + */ +static __inline__ void pcd_rcv_cancelled_irq (struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *rcv_urb; + + TRACE_MSG1(PCD, "BUS_RCV CANCELLED: %p", (int) endpoint->rcv_urb); + RETURN_IF (! (rcv_urb = endpoint->rcv_urb)); + //TRACE_MSG1(PCD, "rcv_urb: %p", (int)endpoint->rcv_urb); + usbd_urb_finished_irq (rcv_urb, RECV_CANCELLED); + endpoint->sent = endpoint->last = 0; + endpoint->rcv_urb = NULL; +} + + +/*! pcd_tx_next_irq - get the current or next urb to transmit + * Called by UDC driver to get current transmit urb or next available transmit urb. + */ +static __inline__ struct usbd_urb * pcd_tx_next_irq (struct usbd_endpoint_instance *endpoint) +{ + if (!endpoint->tx_urb) + if ( (endpoint->tx_urb = usbd_first_urb_detached_irq (&endpoint->tx))) { +#if 0 + int i; + u8 *cp = endpoint->tx_urb->buffer; + + TRACE_MSG2(PCD, "NEXT TX: length: %d flags: %x", + endpoint->tx_urb->actual_length, endpoint->tx_urb->flags); + + for (i = 0; i < endpoint->tx_urb->actual_length; i+= 8) + + TRACE_MSG8(PCD, "SENT %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] + ); +#endif + endpoint->tx_urb->status = SEND_IN_PROGRESS; + } + //TRACE_MSG1(PCD, "BUS_TX NEXT TX_URB: %p", (int)endpoint->tx_urb); + return endpoint->tx_urb; +} + + +/*! pcd_tx_complete_irq - complete a transmit operation + * Called by UDC driver to show completion of an outstanding I/O, this will update the urb sent + * information and if necessary will finish the urb passing it to the bottom half which will in + * turn call the function driver callback. + */ +struct usbd_urb * pcd_tx_complete_irq (struct usbd_endpoint_instance *endpoint, int restart); + + +/*! pcd_tx_cancelled_irq - finish pending tx_urb with SEND_CANCELLED + * Called by UDC driver to cancel the current transmit urb. + */ +static __inline__ void pcd_tx_cancelled_irq (struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb; + + TRACE_MSG1(PCD, "BUS_TX CANCELLED: %p", (int) endpoint->tx_urb); + RETURN_IF (! (tx_urb = endpoint->tx_urb)); + usbd_urb_finished_irq (tx_urb, SEND_CANCELLED); + endpoint->sent = endpoint->last = 0; + endpoint->tx_urb = NULL; +} + + +/*! pcd_tx_sendzlp - test if we need to send a ZLP + * This has a side-effect of reseting the USBD_URB_SENDZLP flag. + */ +static __inline__ int pcd_tx_sendzlp (struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *tx_urb = endpoint->tx_urb; + RETURN_FALSE_IF (!tx_urb || (tx_urb->actual_length < endpoint->sent) || ! (tx_urb->flags & USBD_URB_SENDZLP)); + tx_urb->flags &= ~USBD_URB_SENDZLP; + return TRUE; +} + + +/*! pcd_rcv_complete_irq - complete a receive + * Called from rcv interrupt to complete. + */ +static __inline__ void pcd_rcv_fast_complete_irq (struct usbd_endpoint_instance *endpoint, struct usbd_urb *rcv_urb) +{ + //TRACE_MSG1(PCD, "BUS_RCV FAST COMPLETE: %d", rcv_urb->actual_length); + usbd_urb_finished_irq (rcv_urb, RECV_OK); +} + +/*! pcd_recv_setup - process a device request + * Note that we verify if a receive urb has been queued for H2D with non-zero wLength + * and return -EINVAL to stall if the upper layers have not properly tested for and + * setup a receive urb in this case. + */ +static __inline__ int pcd_recv_setup_irq (struct pcd_instance *pcd, struct usbd_device_request *request) +{ + struct usbd_bus_instance *bus = pcd->bus; + struct usbd_endpoint_instance *endpoint = bus->endpoint_array + 0; + TRACE_SETUP (PCD, request); + RETURN_EINVAL_IF (usbd_device_request(bus, request)); // fail if already failed + + //TRACE_MSG2(PCD, "BUS_RECV SETUP: RCV URB: %x TX_URB: %x", (int)endpoint->rcv_urb, (int)endpoint->tx_urb); + RETURN_ZERO_IF ( (request->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_DEVICE2HOST); + RETURN_ZERO_IF (!le16_to_cpu (request->wLength)); + RETURN_EINVAL_IF (!endpoint->rcv_urb); + return 0; +} + +/*! pcd_recv_setup_emulate_irq - emulate a device request + * Called by the UDC driver to inject a SETUP request. This is typically used + * by drivers for UDC's that do not pass all SETUP requests to us but instead give us + * a configuration change interrupt. + */ +int pcd_recv_setup_emulate_irq(struct usbd_bus_instance *, u8, u8, u16, u16, u16); + +/*! pcd_ep0_reset_irq - reset ep0 endpoint + */ +static void __inline__ pcd_ep0_reset_endpoint_irq (struct usbd_endpoint_instance *endpoint) +{ + TRACE_MSG0(PCD, "BUS_EP0 RESET"); + pcd_tx_cancelled_irq (endpoint); + pcd_rcv_cancelled_irq (endpoint); + endpoint->sent = endpoint->last = 0; +} + +/*! pcd_check_device_feature - verify that feature is set or clear + * Check current feature setting and emulate SETUP Request to set or clear + * if required. + */ +void pcd_check_device_feature(struct usbd_bus_instance *, int , int ); + diff -uNr linux/drivers/no-otg/otgcore/Makefile linux/drivers/otg/otgcore/Makefile --- linux/drivers/no-otg/otgcore/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/Makefile 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,107 @@ +# +# 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 := otgcore_target.o + +# Objects that export symbols. + +export-objs := core-init-l24.o otg.o otg-trace.o otg-mesg.o usbp-bops.o usbp-fops.o otg-mesg-l24.o + +# Multipart objects. (core layer) + +list-multi := otgtrace.o otgcore.o otgadmin.o + +otgcore-objs := core-init-l24.o otg.o \ + otg-trace.o otg-trace-l24.o \ + otg-mesg.o otg-mesg-l24.o \ + otg-fw.o \ + usbp-fops.o usbp-bops.o + + +usbprocfs-objs := usbp-procfs.o + + +ifeq ("$(CONFIG_OTG_FW_MN)", "y") +otgcore-objs += otg-fw-mn.o +else +otgcore-objs += otg-fw-df.o +endif + + +# Optional parts of multipart objects. + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_OTG) += otgcore.o +obj-$(CONFIG_OTG_PROCFSM) += usbprocfs.o + + +# Object files in subdirectories + + +# 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 +OTG=$(TOPDIR)/drivers/otg +#USBDCORE_DIR=$(OTG)/usbdcore +OTGCORE_DIR=$(OTG)/otgcore +EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) +EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) + +vpath %.c $(OTGCORE_DIR) + +# Link rules for multi-part drivers. + +otgadmin.o: $(otgadmin-objs) + $(LD) -r -o $@ $(otgadmin-objs) + +otgcore.o: $(otgcore-objs) + $(LD) -r -o $@ $(otgcore-objs) + +otgtrace.o: $(otgtrace-objs) + $(LD) -r -o $@ $(otgtrace-objs) + +usbprocfs.o: $(usbprocfs-objs) + $(LD) -r -o $@ $(usbprocfs-objs) + diff -uNr linux/drivers/no-otg/otgcore/Makefile-l26 linux/drivers/otg/otgcore/Makefile-l26 --- linux/drivers/no-otg/otgcore/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/Makefile-l26 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,24 @@ +# +# Belcarra OTG - On-The-Go +# +# Copyright (c) 2004 Belcarra Technologies Corp + +OTG=$(TOPDIR)/drivers/otg +EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) +EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) + +#otgadmin-objs := admin-init-l24.o otg-admin.o otg-decode.o +#obj-$(CONFIG_OTG) += otgadmin.o + +otgcore-objs := core-init-l24.o otg.o \ + otg-trace.o otg-trace-l24.o \ + otg-mesg.o otg-mesg-l24.o \ + otg-fw.o \ + usbp-bops.o \ + usbp-fops.o \ + +obj-$(CONFIG_OTG) += otgcore.o + +#otgtrace-objs := otg-trace.o +#obj-$(CONFIG_OTG_TRACE) += otgtrace.o + diff -uNr linux/drivers/no-otg/otgcore/TODO-USBP.txt linux/drivers/otg/otgcore/TODO-USBP.txt --- linux/drivers/no-otg/otgcore/TODO-USBP.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/TODO-USBP.txt 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,51 @@ +USBD Core Architecture ToDo Stuart Lynne +Belcarra Fri Aug 13 14:37:13 PDT 2004 + + +The following need to be addressed to support ongoing development and support +for the USB Device core and functions. + + +Specifically: + - finish acm, make CDC compliant, support all possible operations + - enhance network, add NDIS support, + - wmc - implement wmc + - obex + - mass storage + + +These require: + + - enhancements to allow building composite functions + - common routines for handling CDC class requirements + + +1. interface list + + - currently we have an endpoint list that allows each endpoint to be + mapped to a function + + - we need to do the same for interfaces, allow each interface to be + owned by a function, this will allow ep0 to multiplex per + interface standard requests to the proper function driver + + +2. common functions + + - a common send_interrupt_urb function + + int usbd_send_interrupt(struct usbd_function_instance *, + int, void *, int, int (*callback) (struct usbd_urb *, int)); + + + - this creates an interrupt urb on the required endpoint, copies the + data in, and starts it with the appropriate callback etc. + + + - notification + + usbd_network_notfication + usbd_response_available + usbd_serial_state + + diff -uNr linux/drivers/no-otg/otgcore/core-init-l24.c linux/drivers/otg/otgcore/core-init-l24.c --- linux/drivers/no-otg/otgcore/core-init-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/core-init-l24.c 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,173 @@ +/* + * otg/otg/core-init-l24.c - OTG Peripheral Controller Driver Module Initialization + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This is the linux 2.4 version. + * + */ +/*! + * @file otg/otgcore/core-init-l24.c + * @brief OTG Core Linux initialization. + * + * This file is the starting point for defining the Linux OTG Core + * driver. It references and starts all of the other components that + * must be "linked" into the OTGCORE mdoule. + * + * @ingroup OTGCore + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EMBED_LICENSE(); // When supported by the OS, embed license information in the binary + + +#ifdef OTG_MALLOC_TEST +int otg_mallocs; +#endif /* OTG_MALLOC_TEST */ + +otg_tag_t CORE; + +#ifdef CONFIG_OTG_FW_MN +struct otg_firmware *otg_firmware_loaded = &otg_firmware_mn; +struct otg_firmware *otg_firmware_orig = &otg_firmware_mn; +#else /* CONFIG_OTG_FW_MN */ +struct otg_firmware *otg_firmware_loaded = &otg_firmware_df; +struct otg_firmware *otg_firmware_orig = &otg_firmware_df; +#endif /* CONFIG_OTG_FW_MN */ + +struct otg_firmware *otg_firmware_loading; + + +struct otg_instance otg_instance_private; + +struct hcd_instance hcd_instance_private = { otg: &otg_instance_private, }; +struct ocd_instance ocd_instance_private = { otg: &otg_instance_private, }; +struct pcd_instance pcd_instance_private = { otg: &otg_instance_private, }; +struct tcd_instance tcd_instance_private = { otg: &otg_instance_private, }; + +struct otg_instance otg_instance_private = { + hcd: &hcd_instance_private, + ocd: &ocd_instance_private, + pcd: &pcd_instance_private, + tcd: &tcd_instance_private, +}; + +struct otg_instance *otg_instance = &otg_instance_private; + +char * otg_get_state_name(int state) +{ + struct otg_state *otg_state; + if (!otg_firmware_loaded || (state >= otg_firmware_loaded->number_of_states)) + return "UNKNOWN_STATE"; + + otg_state = otg_firmware_loaded->otg_states + state; + return otg_state->name; +} + + +/* ************************************************************************************* */ + + +/* otg_modinit - linux module initialization + */ +static int otg_modinit (void) +{ + int message_init = 0; + int message_init_l24 = 0; + int trace_init = 0; + int trace_init_l24 = 0; + + + printk(KERN_INFO"%s: \n", __FUNCTION__); + + trace_init = otg_trace_init(); + trace_init_l24 = otg_trace_init_l24(); + + CORE = otg_trace_obtain_tag(); + + TRACE_MSG0(CORE,"--"); + otg_instance->current_outputs = otg_firmware_loaded->otg_states; + otg_instance->previous_outputs = otg_firmware_loaded->otg_states; + + /* initialize otg-mesg and usbp + */ + THROW_IF((message_init = otg_message_init(otg_instance)), error); + THROW_IF((message_init_l24 = otg_message_init_l24(otg_instance)), error); + THROW_IF(usbd_device_init(), error); + + otg_set_ocd_ops(NULL); + otg_set_tcd_ops(NULL); + otg_set_hcd_ops(NULL); + otg_set_pcd_ops(NULL); + + CATCH(error) { + CORE = otg_trace_invalidate_tag(CORE); + if (trace_init_l24 )otg_trace_exit_l24(); + if (trace_init) otg_trace_exit(); + if (message_init_l24 )otg_message_exit_l24(); + if (message_init) otg_message_exit(); + return -EINVAL; + } + return 0; +} +module_init (otg_modinit); + + +#if OTG_EPILOGUE /* Set nonzero in when -DMODULE is in force */ +/* otg_modexit - This is *only* used for drivers compiled and used as a module. + */ +static void otg_modexit (void) +{ + printk(KERN_INFO"%s: \n", __FUNCTION__); + + usbd_device_exit(); + otg_message_exit_l24(); + + CORE = otg_trace_invalidate_tag(CORE); + otg_trace_exit(); + + if (otg_firmware_loading) { + lkfree(otg_firmware_loading->otg_states); + lkfree(otg_firmware_loading->otg_tests); + lkfree(otg_firmware_loading); + } + if (otg_firmware_loaded && (otg_firmware_loaded != otg_firmware_orig)) { + lkfree(otg_firmware_loaded->otg_states); + lkfree(otg_firmware_loaded->otg_tests); + lkfree(otg_firmware_loaded); + } +} +#endif + +#if defined (CONFIG_OTG_USBD_PM) +int usbd_load(char * arg) { return 0; } +int usbd_unload(char *arg) { return 0; } +OTG_EXPORT_SYMBOL(usbd_load); +OTG_EXPORT_SYMBOL(usbd_unload); +#endif + +MOD_EXIT(otg_modexit); +OTG_EXPORT_SYMBOL(otg_get_state_name); + +#ifdef OTG_MALLOC_TEST +OTG_EXPORT_SYMBOL(otg_mallocs); +#endif /* OTG_MALLOC_TEST */ + diff -uNr linux/drivers/no-otg/otgcore/hub.c linux/drivers/otg/otgcore/hub.c --- linux/drivers/no-otg/otgcore/hub.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/hub.c 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,607 @@ +/* + * otg/otgcore/hub.c + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This is a root hub function. + * + */ +/*! + * @file otg/otgcore/hub.c + * @brief A simple hub function driver. + * + * + * @ingroup USBDCore + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_OTG_ROOT_HUB +/* + * Root Hub Configuration + * + * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions + * Data Interface Alternate description(s) + * Interface description(s) + * Configuration description(s) + * Device Description + */ + +#define BULK_INT 0x00 +#define ENDPOINTS 0x01 + +#if 0 + +Configuration descriptor [1 ] 09 02 19 00 01 01 02 00 00 + 09 02 19 00 01 01 00 40 00 + +Interface descriptor [1:1:0 ] 09 04 00 00 01 09 00 00 03 +Endpoint descriptor [1:1:0:1] 07 05 81 03 10 00 ff + +#endif + +#if defined(OTG_WINCE) + +static struct hub_descriptor rh_hub_descriptor; +struct usbd_endpoint_descriptor rh_data; + +static struct usbd_endpoint_descriptor *rh_default[] = { &rh_data, }; +u8 rh_indexes[] = { BULK_INT, }; + +static struct usbd_interface_descriptor rh_data_alternate_descriptor; +static struct usbd_alternate_description rh_data_alternate_descriptions[1]; +}; + +static struct usbd_interface_description rh_interfaces[1]; + +struct usbd_configuration_descriptor rh_configuration_descriptor; + +struct usbd_configuration_description rh_description[1]; + +static struct usbd_device_descriptor rh_device_descriptor; +#ifdef CONFIG_OTG_HIGH_SPEED +static struct usbd_device_qualifier_descriptor rh_device_qualifier_descriptor; +#endif /* CONFIG_OTG_HIGH_SPEED */ + +static struct usbd_endpoint_request rh_endpoint_requests[ENDPOINTS+1] = { + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, + { 0, }, +}; + +static struct usbd_otg_descriptor rh_otg_descriptor; + +struct usbd_device_description rh_device_description; + +void hub_global_init(void) +{ + ZERO(rh_hub_descriptor); + rh_hub_descriptor.bDescLength = 0x09; + rh_hub_descriptor.bDescriptorType = USB_DT_HUB; + rh_hub_descriptor.bNbrPorts = 0x00; + rh_hub_descriptor.wHubCharacteristics = HUB_GANGED_POWER | HUB_GLOBAL_OVERCURRENT; + rh_hub_descriptor.bPwrOn2PwrGood = 0x01; + rh_hub_descriptor.bHubContrCurrent = 0x01; + rh_hub_descriptor.DeviceRemovable = 0x00; + rh_hub_descriptor.PortPwrCtrlMask = 0xff; + + ZERO(rh_data); + rh_data.bLength: 0x07; + rh_data.bDescriptorType = USB_DT_ENDPOINT; + rh_data.bEndpointAddress = USB_DIR_IN; + rh_data.bmAttributes = INTERRUPT; + rh_data.wMaxPacketSize = __constant_cpu_to_le16(0x4); + rh_data.bInterval: 0xff; + + ZERO(rh_data_alternate_desriptor); + rh_data_alternate_descriptor.bLength = 0x09; + rh_data_alternate_descriptor.bDescriptorType = USB_DT_INTERFACE; + rh_data_alternate_descriptor.bNumEndpoints = 0x01; + rh_data_alternate_descriptor.bInterfaceClass = USB_CLASS_HUB; + + ZERO(rh_data_alternate_descriptions); + rh_data_alternate_descriptors[0].iInterface =Root hub - Status"; + rh_data_alternate_descriptors[0].interface_descriptor = &rh_data_alternate_descriptor; + rh_data_alternate_descriptors[0].endpoints = sizeof (rh_default) / sizeof(struct usbd_endpoint_descriptor *); + rh_data_alternate_descriptors[0].endpoint_list = rh_default; + rh_data_alternate_descriptors[0].endpoint_indexes = rh_indexes; + + ZERO(rh_interfaces); + + rh_interfaces[0].alternates = sizeof (rh_data_alternate_descriptions) / sizeof (struct usbd_alternate_description); + rh_interfaces[0].alternate_list = rh_data_alternate_descriptions; + + + ZERO(rh_configuration_descriptor); + rh_configuration_descriptor.bLength = 0x09; + rh_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION; + rh_configuration_descriptor.bNumInterfaces = sizeof (rh_interfaces) / sizeof (struct usbd_interface_description); + rh_configuration_descriptor.bConfigurationValue = 0x01; + + + ZERO(rh_description); + rh_description[0].configuration_descriptor = &rh_configuration_descriptor; + rh_description[0].iConfiguration = Root Hub"; + rh_description[0].bNumInterfaces = sizeof (rh_interfaces) / sizeof (struct usbd_interface_description); + rh_description[0].interface_list = rh_interfaces; + + ZERO(rh_device_descriptor); + + rh_device_descriptor.bLength = sizeof(struct usbd_device_descriptor); + rh_device_descriptor.bDescriptorType = USB_DT_DEVICE; + rh_device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); + rh_device_descriptor.bDeviceClass = USB_CLASS_HUB; + + +#ifdef CONFIG_OTG_HIGH_SPEED + ZERO(rh_device_qualifier_descriptor); + rh_device_qualifier_descriptor.bLength = sizeof(struct usbd_device_qualifier_descriptor); + rh_device_qualifier_descriptor.bDescriptorType = USB_DT_DEVICE_QUALIFIER; + rh_device_qualifier_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); + rh_device_qualifier_descriptor.bDeviceClass = USB_CLASS_HUB; +#endif /* CONFIG_OTG_HIGH_SPEED */ + + ZERO(rh_otg_descriptor); + rh_otg_descriptor.bLength = sizeof(struct usbd_otg_descriptor); + rh_otg_descriptor.bDescriptorType = USB_DT_OTG; + rh_otg_descriptor.bmAttributes = 0; + + + ZERO(rh_device_description); + rh_device_description.device_descriptor = &rh_device_descriptor; + #ifdef CONFIG_OTG_HIGH_SPEED + rh_device_description.device_qualifier_descriptor = &rh_device_qualifier_descriptor; + #endif /* CONFIG_OTG_HIGH_SPEED */ + rh_device_description.otg_descriptor = &rh_otg_descriptor; + rh_device_description.iManufacturer = ""; + rh_device_description.iProduct = "root hub"; + rh_device_description.endpointsRequested = ENDPOINTS; + rh_device_description.requestedEndpoints = rh_endpoint_requests; + +} +#else /* defined(OTG_WINCE) */ + +static struct hub_descriptor rh_hub_descriptor = { + bDescLength: 0x09, + bDescriptorType: USB_DT_HUB, + bNbrPorts: 0x00, + wHubCharacteristics: HUB_GANGED_POWER | HUB_GLOBAL_OVERCURRENT, + bPwrOn2PwrGood: 0x01, + bHubContrCurrent: 0x01, + DeviceRemovable: 0x00, + PortPwrCtrlMask: 0xff, +}; + +struct usbd_endpoint_descriptor rh_data = { + bLength: 0x07, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIR_IN, + bmAttributes: INTERRUPT, + wMaxPacketSize: __constant_cpu_to_le16(0x4), + bInterval: 0xff, +}; + +static struct usbd_endpoint_descriptor *rh_default[] = { &rh_data, }; +u8 rh_indexes[] = { BULK_INT, }; + +static struct usbd_interface_descriptor rh_data_alternate_descriptor = { + bLength: 0x09, + bDescriptorType: USB_DT_INTERFACE, + bNumEndpoints: 0x01, + bInterfaceClass: USB_CLASS_HUB, +}; + +static struct usbd_alternate_description rh_data_alternate_descriptions[] = { + { iInterface:"Root hub - Status", + interface_descriptor: &rh_data_alternate_descriptor, + endpoints:sizeof (rh_default) / sizeof(struct usbd_endpoint_descriptor *), + endpoint_list: rh_default, + endpoint_indexes: rh_indexes, + }, +}; + +static struct usbd_interface_description rh_interfaces[] = { + { alternates:sizeof (rh_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), + alternate_list:rh_data_alternate_descriptions,}, +}; + + +struct usbd_configuration_descriptor rh_configuration_descriptor = { + bLength: 0x09, + bDescriptorType: USB_DT_CONFIGURATION, + bNumInterfaces: sizeof (rh_interfaces) / sizeof (struct usbd_interface_description), + bConfigurationValue: 0x01, +}; + +struct usbd_configuration_description rh_description[] = { + { configuration_descriptor: &rh_configuration_descriptor, + iConfiguration:"Root Hub", + bNumInterfaces:sizeof (rh_interfaces) / sizeof (struct usbd_interface_description), + interface_list:rh_interfaces,}, +}; + +static struct usbd_device_descriptor rh_device_descriptor = { + bLength: sizeof(struct usbd_device_descriptor), + bDescriptorType: USB_DT_DEVICE, + bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), + bDeviceClass: USB_CLASS_HUB, +}; + +#ifdef CONFIG_OTG_HIGH_SPEED +static struct usbd_device_qualifier_descriptor rh_device_qualifier_descriptor = { + bLength: sizeof(struct usbd_device_qualifier_descriptor), + bDescriptorType: USB_DT_DEVICE_QUALIFIER, + bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), + bDeviceClass: USB_CLASS_HUB, +}; +#endif /* CONFIG_OTG_HIGH_SPEED */ + +static struct usbd_endpoint_request rh_endpoint_requests[ENDPOINTS+1] = { + { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, + { 0, }, +}; + +static struct usbd_otg_descriptor rh_otg_descriptor = { + bLength : sizeof(struct usbd_otg_descriptor), + bDescriptorType: USB_DT_OTG, + bmAttributes: 0, +}; + +struct usbd_device_description rh_device_description = { + device_descriptor: &rh_device_descriptor, + #ifdef CONFIG_OTG_HIGH_SPEED + device_qualifier_descriptor: &rh_device_qualifier_descriptor, + #endif /* CONFIG_OTG_HIGH_SPEED */ + otg_descriptor: &rh_otg_descriptor, + iManufacturer: "", + iProduct: "root hub", + endpointsRequested: ENDPOINTS, + requestedEndpoints: rh_endpoint_requests, +}; +#endif /* defined(OTG_WINCE) */ + +/* root hub Private variables ******************************************************************** */ + +struct rh_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) + u8 ports; + u8 hub_features; + u32 *port_features; + u32 ports_status; + struct WORK_STRUCT bh; +}; + +struct rh_private rh_private; + +/* Transmit Function *************************************************************************** */ + +void rh_task(void *data) +{ + + //struct rh_private *rh = (struct rh_private *)data; + //struct usbd_function_instance *function = rh->function; + //struct usbd_bus_instance *bus = function->bus; + + printk(KERN_INFO"%s: start\n", __FUNCTION__); +#if 0 + wait_queue_head_t rh_wait_queue; + init_waitqueue_head(&rh_wait_queue); + + while (rh->bh.data && rh->connected) { + u32 ports_status = cpu_to_le32(bus->driver->bops->get_ports_status(bus)); + + printk(KERN_INFO"%s: status: %08x\n", __FUNCTION__, ports_status); + + if (rh->ports_status != ports_status) { + struct usbd_urb *urb = usbd_alloc_urb (function, BULK_INT, 4, NULL); + BREAK_UNLESS(urb); + memcpy(urb->buffer, &ports_status, 4); + urb->actual_length = 4; + if (usbd_send_urb(urb)) { + printk(KERN_INFO"%s: usbd_send_urb failed\n", __FUNCTION__); + usbd_dealloc_urb (urb); + } + } + rh->ports_status = ports_status; + interruptible_sleep_on_timeout(&rh_wait_queue, 10000); + } +#endif + printk(KERN_INFO"%s: exiting\n", __FUNCTION__); +} + +/* USB Device Functions ************************************************************************ */ + +/* rh_event_irq - process a device event + */ +void rh_event_irq (struct usbd_function_instance *function, usbd_device_event_t event, int data) +{ + struct rh_private *rh = &rh_private; + + switch (event) { + case DEVICE_CONFIGURED: + rh->connected = 1; + + SET_WORK_ARG(rh->bh, &rh_private); +// rh->bh.data = &rh_private; + SCHEDULE_WORK(rh_private.bh); +// schedule_task(&rh_private.bh); + break; + + case DEVICE_RESET: + case DEVICE_DE_CONFIGURED: + BREAK_UNLESS(rh->connected); + rh->connected = 0; + rh->bh.data = 0; + break; + default: + break; + } +} + +/* copy_config + * @urb: pointer to urb + * @data: pointer to configuration data + * @length: length of data + * + * Copy configuration data to urb transfer buffer if there is room for it. + */ +static int copy_report (struct usbd_urb *urb, void *data, int size, int max_buf) +{ + int available; + int length = size; + + //printk(KERN_INFO"%s: data: %p size: %d max: %d\n", __FUNCTION__, data, size, max_buf); + RETURN_EINVAL_UNLESS (urb); + RETURN_EINVAL_UNLESS (data); + RETURN_EINVAL_UNLESS (size); + + RETURN_EINVAL_IF ((available = max_buf - urb->actual_length) <= 0); + + //printk(KERN_INFO"%s: length: %d available: %d\n", __FUNCTION__, length, available); + + length = (length < available) ? length : available; + memcpy (urb->buffer + urb->actual_length, data, length); + urb->actual_length += length; + return 0; +} + +/* rh_recv_setup_irq - called to indicate urb has been received + */ +int rh_recv_setup_irq (struct usbd_function_instance *function, struct usbd_device_request *request) +{ + struct usbd_bus_instance *bus = function->bus; + u8 bRequest = request->bRequest; + u8 bmRequestType = request->bmRequestType; + u16 wValue = le16_to_cpu(request->wValue); + u16 wIndex = le16_to_cpu(request->wIndex); + u16 wLength = le16_to_cpu(request->wLength); + + u8 direction = bmRequestType & USB_REQ_DIRECTION_MASK; + u8 recipient = bmRequestType & USB_REQ_RECIPIENT_MASK; + + //printk(KERN_INFO"%s:\n", __FUNCTION__); + /* verify that this is a class request + */ + RETURN_EINVAL_UNLESS((request->bmRequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS); + + //printk(KERN_INFO"%s: valid direction: %x recipient: %x bRequest: %02x\n", + // __FUNCTION__, direction, recipient, bRequest); + + if (USB_REQ_HOST2DEVICE == direction) { + + //printk(KERN_INFO"%s: H2D\n", __FUNCTION__); + + /* these do not require a reply + */ + int setclear_flag = USB_REQ_SET_FEATURE == bRequest; + switch (recipient) { + case USB_RECIP_HUB: + switch (request->bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + bus->driver->bops->hub_feature(bus, wValue, setclear_flag); + } + return 0; + case USB_RECIP_PORT: + switch (request->bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + bus->driver->bops->port_feature(bus, wValue, wIndex, setclear_flag); +#if 0 + case USB_REQ_CLEAR_TT_BUFFER: + case USB_REQ_RESET_TT: + case USB_REQ_STOP_TT: +#endif + } + return 0; + } + } + else { + //printk(KERN_INFO"%s: D2H\n", __FUNCTION__); + + /* these do require a reply + */ + struct usbd_urb *urb; + int rc = 0; + u32 status; + + RETURN_EINVAL_UNLESS(wLength); + RETURN_EINVAL_UNLESS((urb = usbd_alloc_urb_ep0(function, wLength, NULL))); + + switch (recipient) { + case USB_RECIP_HUB: + //printk(KERN_INFO"%s: HUB\n", __FUNCTION__); + switch (request->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + BREAK_UNLESS (copy_report(urb, (void *)&rh_hub_descriptor, sizeof(rh_hub_descriptor), wLength)); + rc = -EINVAL; + break; + + case USB_REQ_GET_STATUS: + status = cpu_to_le16(bus->driver->bops->hub_status(bus)); + urb->actual_length = 4; + memcpy(urb->buffer, &status, 4); + break; + } + break; + case USB_RECIP_PORT: + //printk(KERN_INFO"%s: PORT\n", __FUNCTION__); + switch (request->bRequest) { + case USB_REQ_GET_STATUS: + status = cpu_to_le16(bus->driver->bops->port_status(bus, wIndex)); + urb->actual_length = 4; + memcpy(urb->buffer, &status, 4); + break; +#if 0 + case USB_REQ_GET_TT_STATE: +#endif + } + break; + default: + rc = -EINVAL; + } + UNLESS (rc) + RETURN_ZERO_IF(!usbd_send_urb(urb)); + /* error */ + usbd_dealloc_urb(urb); + } + return -EINVAL; +} + + +static int rh_function_enable (struct usbd_function_instance *function) +{ + struct rh_private *rh = &rh_private; + struct usbd_bus_instance *bus = function->bus; + int ports; + + printk (KERN_INFO "%s:\n", __FUNCTION__); + + if (rh->port_features) + lkfree(rh->port_features); + + rh->ports = ports = bus->driver->ports; + rh->port_features = ckmalloc(4 * ports, GFP_KERNEL); + rh_private.bh.data = &rh_private; + + rh_hub_descriptor.bNbrPorts = ports; + + printk (KERN_INFO "%s: ports: %d\n", __FUNCTION__, ports); + + //schedule_task(&rh_private.bh); + + printk (KERN_INFO "%s: finished\n", __FUNCTION__); + + return 0; +} + +static void rh_function_disable (struct usbd_function_instance *function) +{ + struct rh_private *rh = &rh_private; + printk (KERN_INFO "%s:\n", __FUNCTION__); + if (rh->port_features) + lkfree(rh->port_features); + printk (KERN_INFO "%s: finished\n", __FUNCTION__); +} + +#if defined(OTG_WINCE) + +struct otg_instance otg_instance_private; + +void hub_ops_init(void) +{ + ZERO(otg_instance_private); + otg_instance_private.hcd = &hcd_instance_privatej +} + +#else /* defined(OTG_WINCE) */ + +static struct usbd_function_operations function_ops = { + event_irq: rh_event_irq, + recv_setup_irq: rh_recv_setup_irq, + function_enable: rh_function_enable, + function_disable: rh_function_disable, +}; + +static struct usbd_function_driver function_driver = { + name:"root hub", + fops:&function_ops, + device_description:&rh_device_description, + bNumConfigurations:sizeof (rh_description) / sizeof (struct usbd_configuration_description), + configuration_description:rh_description, + idVendor: __constant_cpu_to_le16(0x15ec), + idProduct: __constant_cpu_to_le16(0xf010), + bcdDevice: __constant_cpu_to_le16(0x000), +}; +#endif /* defined(OTG_WINCE) */ + +/* USB Module init/exit ************************************************************************ */ +#if defined(OTG_WINCE) + + +#else /* defined(OTG_WINCE) */ + + +/* + * rh_modinit - module init + */ +int rh_init (void) +{ + printk (KERN_INFO "%s:\n", __FUNCTION__); + + THROW_IF (usbd_register_function (&function_driver), error); + + printk (KERN_INFO "%s: registered\n", __FUNCTION__); + + rh_private.usb_driver_registered++; + +// rh_private.bh.sync = 0; +// rh_private.bh.routine = rh_task; + PREPARE_WORK_ITEM(rh_private.bh,rh_task,NULL); + + CATCH(error) { + if (rh_private.usb_driver_registered) { + usbd_deregister_function (&function_driver); + rh_private.usb_driver_registered = 0; + } + return -EINVAL; + } + printk (KERN_INFO "%s: finished\n", __FUNCTION__); + return 0; +} + +/* rh_modexit - module cleanup + */ +void rh_exit (void) +{ + //while (rh_private.bh.sync) + while (PENDING_WORK_ITEM(rh_private.bh)) + schedule_timeout(HZ); + + //rh_private.bh.data = 0; + SET_WORK_ARG(rh_private.bh,0); + while (PENDING_WORK_ITEM(rh_private.bh)) + schedule_timeout(HZ); + + if (rh_private.usb_driver_registered) + usbd_deregister_function (&function_driver); +} +#endif /* defined(OTG_WINCE) */ +#endif /* CONFIG_OTG_ROOT_HUB */ + diff -uNr linux/drivers/no-otg/otgcore/otg-fw-df.c linux/drivers/otg/otgcore/otg-fw-df.c --- linux/drivers/no-otg/otgcore/otg-fw-df.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg-fw-df.c 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,121 @@ + +/* Generated by otg-tests-c.awk + * + * Do not Edit, see otg-state.awk + */ + + +/*! +* @file otg/otgcore/otg-fw-df.c +* @brief OTG Firmware - Firmware for df +* +* This file defines the OTG State Machine tests. +* +* +* @ingroup OTGFW +*/ + +/*! +* @page OTGFW +* @section OTGFW - otg-fw-df.c +* This contains the input, output and timout definitions for the OTG state machine firmware +*/ + + +#ifdef OTG_APPLICATION +#define NULL 0 +#include "otg-fw.h" +#include "otg-fw-df.h" +#else /* OTG_APPLICATION/ */ +#include +#include +#include +#include +#include +//#include +#include +#include +#endif /* OTG_APPLICATION */ + +char otg_fw_name_df[] = "otg_df"; + + +struct otg_test otg_tests_df[] = { + /* + * Copyright (c) 2004 Belcarra + * + */ + /*! + * This is the default Firmware. It is included in the + * compiled modules and supports the auto Traditional USB + * mode. No user inputs are implemented. + */ + { /* */ + 0, /* .test */ + invalid_state, /* .state */ + otg_disabled, /* .target */ + enable_otg, /* .test1 */ + }, + /* + * This is not an OTG State. It is used internally to mark the end of the + * list of states and inputs. + */ + { /* */ + 1, /* .test */ + terminator_state, /* .state */ + invalid_state, /* .target */ + 0, /* .test1 */ + }, + {2, invalid_state,}, + +}; + +#define OTG_TESTS_DF 2 + +int otg_test_max_df = 2; + + /* eof */ + +/* Generated by otg-info-c.awk + * + * Do not Edit, see otg-state.awk + */ + +struct otg_state otg_states_df[OTG_STATES_DF + 1] = { + { /* 0 */ + invalid_state, /* .state */ + m_otg_init, /* .meta */ + "invalid_state", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + }, + { /* 1 */ + otg_disabled, /* .state */ + m_otg_init, /* .meta */ + "otg_disabled", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + }, + { /* 2 */ + terminator_state, /* .state */ + m_otg_init, /* .meta */ + "terminator_state", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + /* .outputs */ + 0, + }, + + {0, 0, "", 0, 0,}, + +}; + +struct otg_firmware otg_firmware_df = { + OTG_STATES_DF, /* number of states */ + OTG_TESTS_DF, /* number of tests */ + "otg-df", /* name of firmware */ + otg_states_df, /* struct otg_state * */ + otg_tests_df, /* struct otg_test * */ +}; + +/* eof */ diff -uNr linux/drivers/no-otg/otgcore/otg-fw-mn.c linux/drivers/otg/otgcore/otg-fw-mn.c --- linux/drivers/no-otg/otgcore/otg-fw-mn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg-fw-mn.c 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,511 @@ + +/* Generated by otg-tests-c.awk + * + * Do not Edit, see otg-state.awk + */ + + +/*! +* @file otg/otgcore/otg-fw-mn.c +* @brief OTG Firmware - Firmware for mn +* +* This file defines the OTG State Machine tests. +* +* +* @ingroup OTGFW +*/ + +/*! +* @page OTGFW +* @section OTGFW - otg-fw-mn.c +* This contains the input, output and timout definitions for the OTG state machine firmware +*/ + + +#ifdef OTG_APPLICATION +#define NULL 0 +#include "otg-fw.h" +#include "otg-fw-mn.h" +#else /* OTG_APPLICATION/ */ +#include +#include +#include +#include +#include +//#include +#include +#include +#endif /* OTG_APPLICATION */ + +char otg_fw_name_mn[] = "otg_mn"; + + +struct otg_test otg_tests_mn[] = { + /* + * Copyright (c) 2004 Belcarra + */ + /*! + * This is the initialization set for pcd and tcd. + */ + /* + * This the initial state of the software when first loaded. + * It is not possible to return to this state. + */ + { /* Initialize by sending the otg_enable signal. */ + 0, /* .test */ + invalid_state, /* .state */ + otg_disabled, /* .target */ + enable_otg, /* .test1 */ + }, + /* + * The USBOTG State Machine has been initialized but is inactive. + * This state may have arrived at from either the invalid_state or + * from the otg_disable state. + */ + { /* Initialize by sending the otg_enable signal. */ + 1, /* .test */ + otg_disabled, /* .state */ + otg_enable_pcd, /* .target */ + enable_otg, /* .test1 */ + }, + /* + * The State Machine stops the device drivers and waits for them + * to signal that they have finished de-initializing. + * + * This state disables the TCD driver and waits for TCD ok. + */ + { /* Wait for ok from de-initializing drivers. */ + 2, /* .test */ + otg_disable_tcd, /* .state */ + otg_disable_pcd, /* .target */ + TCD_OK, /* .test1 */ + }, + /* + * The State Machine stops the device drivers and waits for them + * to signal that they have finished de-initializing. + * + * This state disables the PCD driver and waits for PCD ok. + */ + { /* Wait for ok from de-initializing drivers. */ + 3, /* .test */ + otg_disable_pcd, /* .state */ + otg_disabled, /* .target */ + PCD_OK, /* .test1 */ + }, + /* + * The State Machine starts the device drivers and waits for them + * to signal that they have finished initializing. + * + * This state enables the PCD driver and waits for PCD ok. + */ + { /* Wait for ok from initializing drivers. */ + 4, /* .test */ + otg_enable_pcd, /* .state */ + otg_enable_tcd, /* .target */ + PCD_OK, /* .test1 */ + }, + /* + * The State Machine starts the device drivers and waits for them + * to signal that they have finished initializing. + * + * This state enables the PCD driver and waits for PCD ok. + */ + { /* Wait for ok from initializing drivers. */ + 5, /* .test */ + otg_enable_tcd, /* .state */ + otg_enabled, /* .target */ + TCD_OK, /* .test1 */ + }, + /* + * Copyright (c) 2004 Belcarra + * + */ + /*! + * This is the minimal firmware. It can be included in the + * compiled modules and supports the auto Traditional USB + * mode. No user inputs are required for normal operation. + * + * The b_bus_drop input can be optionally used to disconnect and re-connect. + * + * The enable_otg input can be optionally used to disable and re-enable. + * Note that disable/enable will reset b_bus_drop. + * + */ + /*! + * The State Machine has successfully started the device drivers and + * is waiting for an input event. Typically it will move from here to + * an idle state specific to the current conditions (a_idle, b_idle, mn_idle etc.) + * based on user request b_bus_drop. + * + */ + { /* Check for disable. */ + 6, /* .test */ + otg_enabled, /* .state */ + otg_disable_tcd, /* .target */ + enable_otg_, /* .test1 */ + }, + { /* Move to idle. */ + 7, /* .test */ + otg_enabled, /* .state */ + mn_idle, /* .target */ + AUTO | AUTO_, /* .test1 */ + }, + /*! + * The State Machine is in the idle state for a Traditional USB Device. + * It is waiting for Vbus to indicate that it has been plugged into a USB Host. + * + */ + { /* Check for disable (must be done for check for bus_drop.) */ + 8, /* .test */ + mn_idle, /* .state */ + otg_disable_tcd, /* .target */ + enable_otg_, /* .test1 */ + }, + { /* Check for b_bus_drop */ + 9, /* .test */ + mn_idle, /* .state */ + mn_bus_drop, /* .target */ + b_bus_drop, /* .test1 */ + }, + { /* move to peripheral mode when SESSION valid. */ + 10, /* .test */ + mn_idle, /* .state */ + mn_peripheral, /* .target */ + b_bus_drop_, /* .test1 */ + B_SESS_VLD, /* .test2 */ + }, + /*! + * The State Machine is in the idle state for a Traditional USB Device. + * The B-Device applications has requested that the bus connection be dropped. + * Wait for either enable_otg reset or b_bus_drop reset. + */ + { /* Wait for b_bus_drop/ or enable_otg/ */ + 11, /* .test */ + mn_bus_drop, /* .state */ + mn_idle, /* .target */ + b_bus_drop_ | enable_otg_, /* .test1 */ + }, + /*! + * The State Machine in the peripheral state for a Traditional USB Device. + * The D+ pullup is enabled and we are waiting for a BUS_RESET to + * indicate that the USB Host has recognized that a USB Device is attached. + */ + { /* Move to idle if we loose any of these inputs. */ + 12, /* .test */ + mn_peripheral, /* .state */ + mn_idle, /* .target */ + enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ + }, + { /* Move to next state if bus reset is seen. */ + 13, /* .test */ + mn_peripheral, /* .state */ + mn_bus_reset, /* .target */ + BUS_RESET, /* .test1 */ + }, + /*! + * The State Machine in the bus_reset state for a Traditional USB Device. + * It is waiting to be enumerated and configured by the USB Host. + */ + { /* Move to idle via discharge, if we loose any of these inputs. */ + 14, /* .test */ + mn_bus_reset, /* .state */ + mn_discharge, /* .target */ + enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ + }, + { /* Progress if we are addressed. */ + 15, /* .test */ + mn_bus_reset, /* .state */ + mn_addressed, /* .target */ + ADDRESSED, /* .test1 */ + }, + /*! + * The State Machine in the configured state for a Traditional USB Device. + * This means that there is an active session, there is packet traffic + * with this device. + */ + { /* Move to idle via discharge, if we loose any of these inputs. */ + 16, /* .test */ + mn_addressed, /* .state */ + mn_discharge, /* .target */ + enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ + }, + { /* Progress if we are configured. */ + 17, /* .test */ + mn_addressed, /* .state */ + mn_configured, /* .target */ + CONFIGURED, /* .test1 */ + }, + /*! + * The State Machine in the configured state for a Traditional USB Device. + * This means that there is an active session, there is packet traffic + * with this device. + */ + { /* */ + 18, /* .test */ + mn_configured, /* .state */ + mn_suspended, /* .target */ + BUS_SUSPENDED, /* .test1 */ + }, + { /* Move to idle via discharge, if we loose any of these inputs. */ + 19, /* .test */ + mn_configured, /* .state */ + mn_discharge, /* .target */ + enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ + }, + /*! + * The State Machine in the discharge state for a Traditional USB Device. + * The device has been unplugged. The Vbus discharge resistor will be enabled + * for the TLDISC_DSCHRG time period. + */ + { /* Progress to idle on timeout. */ + 20, /* .test */ + mn_discharge, /* .state */ + mn_idle, /* .target */ + Tldisc_dschrg, /* .test1 */ + }, + /*! + * The State Machine in the suspend state for a Traditional USB Device. + */ + { /* Move to idle via discharge, if we loose any of these inputs. */ + 21, /* .test */ + mn_suspended, /* .state */ + mn_discharge, /* .target */ + enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ + }, + { /* Check for a resumed bus. */ + 22, /* .test */ + mn_suspended, /* .state */ + mn_configured, /* .target */ + BUS_SUSPENDED_, /* .test1 */ + }, + { /* Is remote wakeup enabled? */ + 23, /* .test */ + mn_suspended, /* .state */ + mn_wakeup_enabled, /* .target */ + REMOTE_WAKEUP_ENABLED, /* .test1 */ + }, + /*! + * The State Machine in the suspend state for a Traditional USB Device, + * prior to suspended the USB Host enabled Remote Wakeup by sending a + * set REMOTE WAKUP request. + */ + { /* Move to idle via discharge, if we loose any of these inputs. */ + 24, /* .test */ + mn_wakeup_enabled, /* .state */ + mn_discharge, /* .target */ + enable_otg_ | b_bus_req_ | B_SESS_VLD_, /* .test1 */ + }, + { /* Check for a resumed bus. */ + 25, /* .test */ + mn_wakeup_enabled, /* .state */ + mn_suspended, /* .target */ + BUS_SUSPENDED_, /* .test1 */ + }, + { /* Remote wakeup requested? */ + 26, /* .test */ + mn_wakeup_enabled, /* .state */ + mn_wakeup, /* .target */ + remote_wakeup_cmd, /* .test1 */ + }, + /*! + * The State Machine in the wakeup state for a Traditional USB Device, + * The REMOTE WAKEUP procedure will be performed. + */ + { /* Automatic return. */ + 27, /* .test */ + mn_wakeup, /* .state */ + mn_wakeup_enabled, /* .target */ + AUTO | AUTO_, /* .test1 */ + }, + /*! + * This is not an OTG State. It is used internally to mark the end of the + * list of states and inputs. + */ + { /* */ + 28, /* .test */ + terminator_state, /* .state */ + invalid_state, /* .target */ + 0, /* .test1 */ + }, + {29, invalid_state,}, + +}; + +#define OTG_TESTS_MN 29 + +int otg_test_max_mn = 29; + + /* eof */ + +/* Generated by otg-info-c.awk + * + * Do not Edit, see otg-state.awk + */ + +struct otg_state otg_states_mn[OTG_STATES_MN + 1] = { + { /* 0 */ + invalid_state, /* .state */ + m_otg_init, /* .meta */ + "invalid_state", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + }, + { /* 1 */ + otg_disabled, /* .state */ + m_otg_init, /* .meta */ + "otg_disabled", /* .name */ + 0, /* .tmout */ + enable_otg_, /* .reset */ + /* .outputs */ + chrg_vbus_out_ | drv_vbus_out_ | dp_pullup_out_ | loc_sof_out_, + }, + { /* 2 */ + otg_disable_tcd, /* .state */ + m_otg_init, /* .meta */ + "otg_disable_tcd", /* .name */ + 0, /* .tmout */ + TCD_OK_, /* .reset */ + /* .outputs */ + tcd_init_out_, + }, + { /* 3 */ + otg_disable_pcd, /* .state */ + m_otg_init, /* .meta */ + "otg_disable_pcd", /* .name */ + 0, /* .tmout */ + PCD_OK_, /* .reset */ + /* .outputs */ + pcd_init_out_, + }, + { /* 4 */ + otg_enable_pcd, /* .state */ + m_otg_init, /* .meta */ + "otg_enable_pcd", /* .name */ + 0, /* .tmout */ + PCD_OK_, /* .reset */ + /* .outputs */ + chrg_vbus_out_ | drv_vbus_out_ | dp_pullup_out_ | loc_sof_out_ | pcd_init_out, + }, + { /* 5 */ + otg_enable_tcd, /* .state */ + m_otg_init, /* .meta */ + "otg_enable_tcd", /* .name */ + 0, /* .tmout */ + TCD_OK_, /* .reset */ + /* .outputs */ + chrg_vbus_out_ | drv_vbus_out_ | dp_pullup_out_ | loc_sof_out_ | tcd_init_out, + }, + { /* 6 */ + otg_enabled, /* .state */ + m_otg_init, /* .meta */ + "otg_enabled", /* .name */ + 0, /* .tmout */ + b_bus_drop_, /* .reset */ + }, + { /* 7 */ + mn_idle, /* .state */ + m_b_idle, /* .meta */ + "mn_idle", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + /* .outputs */ + dp_pullup_out_ | pcd_en_out_ | tcd_en_out_power | dischrg_vbus_out_, + }, + { /* 8 */ + mn_bus_drop, /* .state */ + m_b_idle, /* .meta */ + "mn_bus_drop", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + }, + { /* 9 */ + mn_peripheral, /* .state */ + m_b_peripheral, /* .meta */ + "mn_peripheral", /* .name */ + 0, /* .tmout */ + BUS_RESET_, /* .reset */ + /* .outputs */ + dp_pullup_out | pcd_en_out | dischrg_vbus_out_, + }, + { /* 10 */ + mn_bus_reset, /* .state */ + m_b_peripheral, /* .meta */ + "mn_bus_reset", /* .name */ + 0, /* .tmout */ + BUS_RESET_ | ADDRESSED_, /* .reset */ + }, + { /* 11 */ + mn_addressed, /* .state */ + m_b_peripheral, /* .meta */ + "mn_addressed", /* .name */ + 0, /* .tmout */ + ADDRESSED_ | CONFIGURED_, /* .reset */ + /* .outputs */ + dp_pullup_out | pcd_en_out | dischrg_vbus_out_, + }, + { /* 12 */ + mn_configured, /* .state */ + m_b_peripheral, /* .meta */ + "mn_configured", /* .name */ + 0, /* .tmout */ + CONFIGURED_ | BUS_SUSPENDED_, /* .reset */ + /* .outputs */ + dp_pullup_out | pcd_en_out | dischrg_vbus_out_, + }, + { /* 13 */ + mn_discharge, /* .state */ + m_b_peripheral, /* .meta */ + "mn_discharge", /* .name */ + TLDISC_DSCHRG, /* .tmout */ + 0, /* .reset */ + /* .outputs */ + dischrg_vbus_out | pcd_en_out_, + }, + { /* 14 */ + mn_suspended, /* .state */ + m_b_suspended, /* .meta */ + "mn_suspended", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + }, + { /* 15 */ + mn_wakeup_enabled, /* .state */ + m_b_suspended, /* .meta */ + "mn_wakeup_enabled", /* .name */ + 0, /* .tmout */ + remote_wakeup_cmd_, /* .reset */ + /* .outputs */ + remote_wakeup_out_, + }, + { /* 16 */ + mn_wakeup, /* .state */ + m_b_suspended, /* .meta */ + "mn_wakeup", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + /* .outputs */ + remote_wakeup_out, + }, + { /* 17 */ + terminator_state, /* .state */ + m_otg_init, /* .meta */ + "terminator_state", /* .name */ + 0, /* .tmout */ + 0, /* .reset */ + /* .outputs */ + 0, + }, + + {0, 0, "", 0, 0,}, + +}; + +struct otg_firmware otg_firmware_mn = { + OTG_STATES_MN, /* number of states */ + OTG_TESTS_MN, /* number of tests */ + "otg-mn", /* name of firmware */ + otg_states_mn, /* struct otg_state * */ + otg_tests_mn, /* struct otg_test * */ +}; + +/* eof */ diff -uNr linux/drivers/no-otg/otgcore/otg-fw.c linux/drivers/otg/otgcore/otg-fw.c --- linux/drivers/no-otg/otgcore/otg-fw.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg-fw.c 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,251 @@ + +/* Generated by otg-fw-c.awk + * + * Do not Edit, see otg-state.awk + */ + +/*! +* @file otg/otgcore/otg-fw.c +* @brief OTG Firmware - Input, Output and Timeout definitions +* +* This file defines the OTG State Machine input, output and timeout constants. +* +* +* @ingroup OTGFW +*/ + +/*! +* @page OTGFW +* @section OTGFW - otg-fw.c +* This contains the input, output and timout definitions for the OTG state machine firmware +*/ + + +#ifdef OTG_APPLICATION +#include "otg-fw.h" +#else /* OTG_APPLICATION/ */ +#include +#include +#include +#include +#include +#include +#endif /* OTG_APPLICATION */ + +#define OTG_VERSION_FWC 200502152159L + + +#if OTG_VERSION_FWC != OTG_VERSION_FW +#error OTG_VERSION_FWC != OTG_VERSION_FW +#endif /* OTG_VERSION_FWC != OTG_VERSION_FW */ + +/* Generated by otg-input-name-c.awk + * + * Do not Edit, see otg-state.awk + */ + + +/* #include "otg-fw.h" */ + +#define OTG_VERSION_INPUT_TABLE 200502152159L + + +#if OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW +#error OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW +#endif /* OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW */ + + +struct otg_input_name otg_input_names[] = { + {ID_GND, "ID_GND",}, + {ID_FLOAT, "ID_FLOAT",}, + {DP_HIGH, "DP_HIGH",}, + {DM_HIGH, "DM_HIGH",}, + {B_SESS_END, "B_SESS_END",}, + {A_SESS_VLD, "A_SESS_VLD",}, + {B_SESS_VLD, "B_SESS_VLD",}, + {VBUS_VLD, "VBUS_VLD",}, + {SRP_DET, "SRP_DET",}, + {SE0_DET, "SE0_DET",}, + {SE1_DET, "SE1_DET",}, + {BDIS_ACON, "BDIS_ACON",}, + {CR_INT_DET, "CR_INT_DET",}, + {HUB_PORT_CONNECT, "HUB_PORT_CONNECT",}, + {BUS_RESET, "BUS_RESET",}, + {ADDRESSED, "ADDRESSED",}, + {CONFIGURED, "CONFIGURED",}, + {NOT_SUPPORTED, "NOT_SUPPORTED",}, + {BUS_SUSPENDED, "BUS_SUSPENDED",}, + {a_bcon_no_tmout_req, "a_bcon_no_tmout_req",}, + {a_hpwr_req, "a_hpwr_req",}, + {bus_drop, "bus_drop",}, + {a_bus_drop, "a_bus_drop",}, + {b_bus_drop, "b_bus_drop",}, + {bus_req, "bus_req",}, + {a_bus_req, "a_bus_req",}, + {b_bus_req, "b_bus_req",}, + {b_sess_req, "b_sess_req",}, + {suspend_req, "suspend_req",}, + {a_suspend_req, "a_suspend_req",}, + {b_suspend_req, "b_suspend_req",}, + {set_remote_wakeup_cmd, "set_remote_wakeup_cmd",}, + {remote_wakeup_cmd, "remote_wakeup_cmd",}, + {reset_remote_wakeup_cmd, "reset_remote_wakeup_cmd",}, + {clr_err_cmd, "clr_err_cmd",}, + {b_hnp_cmd, "b_hnp_cmd",}, + {ph_int_cmd, "ph_int_cmd",}, + {ph_audio_cmd, "ph_audio_cmd",}, + {cr_int_cmd, "cr_int_cmd",}, + {led_on_cmd, "led_on_cmd",}, + {led_off_cmd, "led_off_cmd",}, + {HNP_ENABLED, "HNP_ENABLED",}, + {HNP_CAPABLE, "HNP_CAPABLE",}, + {HNP_SUPPORTED, "HNP_SUPPORTED",}, + {REMOTE_WAKEUP_ENABLED, "REMOTE_WAKEUP_ENABLED",}, + {REMOTE_CAPABLE, "REMOTE_CAPABLE",}, + {PCD_OK, "PCD_OK",}, + {TCD_OK, "TCD_OK",}, + {HCD_OK, "HCD_OK",}, + {OCD_OK, "OCD_OK",}, + {TMOUT, "TMOUT",}, + {enable_otg, "enable_otg",}, + {AUTO, "AUTO",}, + {0, "0",}, + +}; + + /* eof */ + +/* Generated by otg-ioctls-names-c.awk + * + * Do not Edit, see otg-state.awk + */ +#if defined(OTG_LINUX) || defined(OTG_WINCE) + + +/* #include "otg-fw.h" */ + +#define OTG_VERSION_IOCTL_TABLE 200502152159L + + +#if OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW +#error OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW +#endif /* OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW */ + +struct otg_ioctl_name otg_ioctl_names[] = { + {OTGADMIN_A_BCON_NO_TMOUT_REQ, a_bcon_no_tmout_req, "OTGADMIN_A_BCON_NO_TMOUT_REQ",}, + {OTGADMIN_A_HPWR_REQ, a_hpwr_req, "OTGADMIN_A_HPWR_REQ",}, + {OTGADMIN_BUS_DROP, bus_drop, "OTGADMIN_BUS_DROP",}, + {OTGADMIN_A_BUS_DROP, a_bus_drop, "OTGADMIN_A_BUS_DROP",}, + {OTGADMIN_B_BUS_DROP, b_bus_drop, "OTGADMIN_B_BUS_DROP",}, + {OTGADMIN_BUS_REQ, bus_req, "OTGADMIN_BUS_REQ",}, + {OTGADMIN_A_BUS_REQ, a_bus_req, "OTGADMIN_A_BUS_REQ",}, + {OTGADMIN_B_BUS_REQ, b_bus_req, "OTGADMIN_B_BUS_REQ",}, + {OTGADMIN_B_SESS_REQ, b_sess_req, "OTGADMIN_B_SESS_REQ",}, + {OTGADMIN_SUSPEND_REQ, suspend_req, "OTGADMIN_SUSPEND_REQ",}, + {OTGADMIN_A_SUSPEND_REQ, a_suspend_req, "OTGADMIN_A_SUSPEND_REQ",}, + {OTGADMIN_B_SUSPEND_REQ, b_suspend_req, "OTGADMIN_B_SUSPEND_REQ",}, + {OTGADMIN_SET_REMOTE_WAKEUP_CMD, set_remote_wakeup_cmd, "OTGADMIN_SET_REMOTE_WAKEUP_CMD",}, + {OTGADMIN_REMOTE_WAKEUP_CMD, remote_wakeup_cmd, "OTGADMIN_REMOTE_WAKEUP_CMD",}, + {OTGADMIN_RESET_REMOTE_WAKEUP_CMD, reset_remote_wakeup_cmd, "OTGADMIN_RESET_REMOTE_WAKEUP_CMD",}, + {OTGADMIN_CLR_ERR_CMD, clr_err_cmd, "OTGADMIN_CLR_ERR_CMD",}, + {OTGADMIN_B_HNP_CMD, b_hnp_cmd, "OTGADMIN_B_HNP_CMD",}, + {OTGADMIN_PH_INT_CMD, ph_int_cmd, "OTGADMIN_PH_INT_CMD",}, + {OTGADMIN_PH_AUDIO_CMD, ph_audio_cmd, "OTGADMIN_PH_AUDIO_CMD",}, + {OTGADMIN_CR_INT_CMD, cr_int_cmd, "OTGADMIN_CR_INT_CMD",}, + {OTGADMIN_LED_ON_CMD, led_on_cmd, "OTGADMIN_LED_ON_CMD",}, + {OTGADMIN_LED_OFF_CMD, led_off_cmd, "OTGADMIN_LED_OFF_CMD",}, + {OTGADMIN_ENABLE_OTG, enable_otg, "OTGADMIN_ENABLE_OTG",}, + {0, 0, "",}, + +}; + + /* eof */ +#endif /* defined(OTG_LINUX) || defined(OTG_WINCE) */ + +/* Generated by otg-output-names-c.awk + * + * Do not Edit, see otg-state.awk + */ + +char *otg_output_names[] = { + "tcd_init_out", /* 0 Initiate Transceiver Controller Driver Initialization (or De-initialization.) */ + "pcd_init_out", /* 1 Initiate Peripheral Controller Driver Initialization (or De-initialization.) */ + "hcd_init_out", /* 2 Initiate Host Controller Driver Initialization (or De-initialization). */ + "ocd_init_out", /* 3 Initiate OTG Controller Driver Initialization (or De-initialization). */ + "tcd_en_out", /* 4 Enable Transceiver Controller Driver */ + "pcd_en_out", /* 5 Enable Peripheral Controller Driver */ + "hcd_en_out", /* 6 Enable Host Controller Driver */ + "drv_vbus_out", /* 7 A-Device will Drive Vbus to 5V through charge pump. */ + "chrg_vbus_out", /* 8 B-Device will charge Vbus to 3.3V through resistor (SRP.) */ + "dischrg_vbus_out", /* 9 B-Device will discharge Vbus (enable dischage resistor.) */ + "dm_pullup_out", /* 10 DM pullup control - aka loc_carkit */ + "dm_pulldown_out", /* 11 DM pulldown control */ + "dp_pullup_out", /* 12 DP pullup control - aka loc_conn */ + "dp_pulldown_out", /* 13 DP pulldown control */ + "clr_overcurrent_out", /* 14 Clear overcurrent indication */ + "dm_det_out", /* 15 Enable B-Device D- High detect */ + "dp_det_out", /* 16 Enable B-Device D+ High detect */ + "cr_det_out", /* 17 Enable D+ CR detect */ + "charge_pump_out", /* 18 Enable external charge pump. */ + "bdis_acon_out", /* 19 Enable auto A-connect after B-disconnect. */ + "mx21_vbus_drain", /* 20 MX21 hack */ + "id_pulldown_out", /* 21 Enable the ID to ground pulldown ( (CEA-936 - 5 wire carkit.) */ + "uart_out", /* 22 Enable Transparent UART mode (CEA-936.) */ + "audio_out", /* 23 Enable Audio mode (CEA-936 CarKit interrupt detector.) */ + "mono_out", /* 24 Enable Mono-Audio mode (CEA-936.) */ + "remote_wakeup_out", /* 25 Peripheral will perform remote wakeup. */ + "loc_sof_out", /* 26 Host will enable packet traffic. */ + "loc_suspend_out", /* 27 Host will suspend bus. */ + "remote_wakeup_en_out", /* 28 Host will send remote wakeup enable or disable request. */ + "hnp_en_out", /* 29 Host will send HNP enable request. */ + "hpwr_out", /* 30 Host will enable high power (external charge pump.) */ + NULL, + +}; + + /* eof */ + +/* Generated by otg-meta-names-c.awk + * + * Do not Edit, see otg-state.awk + */ + + + +char *otg_meta_names[] = { + "a_idle", /* 0 - */ + "a_wait_vrise", /* 1 - */ + "a_wait_bcon", /* 2 - */ + "a_host", /* 3 - */ + "a_suspend", /* 4 - */ + "a_peripheral", /* 5 - */ + "a_wait_vfall", /* 6 - */ + "a_vbus_err", /* 7 - */ + "b_idle", /* 8 - */ + "b_srp_init", /* 9 - */ + "b_peripheral", /* 10 - */ + "b_suspend", /* 11 - */ + "b_wait_acon", /* 12 - */ + "b_host", /* 13 - */ + "b_suspended", /* 14 - */ + "ph_disc", /* 15 - Equivalent to b_peripheral */ + "ph_init", /* 16 - */ + "ph_uart", /* 17 - */ + "ph_aud", /* 18 - */ + "ph_wait", /* 19 - */ + "ph_exit", /* 20 - */ + "cr_init", /* 21 - */ + "cr_uart", /* 22 - */ + "cr_aud", /* 23 - */ + "cr_ack", /* 24 - */ + "cr_wait", /* 25 - */ + "cr_disc", /* 26 - */ + "otg_init", /* 27 - */ + "usb_accessory", /* 28 - */ + "usb_factory", /* 29 - */ + "unknown", /* 30 - */ + + "", +}; + +/* eof */ diff -uNr linux/drivers/no-otg/otgcore/otg-mesg-l24.c linux/drivers/otg/otgcore/otg-mesg-l24.c --- linux/drivers/no-otg/otgcore/otg-mesg-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg-mesg-l24.c 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,399 @@ +/* + * otg/otgcore/otg-mesg-l24.c - OTG state machine Administration + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otgcore/otg-mesg-l24.c + * @brief OTG Core Linux Message Interface. + * + * Implement linux char device via /proc/otg-mesg. + * + * @ingroup OTGCore + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +wait_queue_head_t otg_message_queue; + +/*! + * otg_message() - queue message data + * @param buf message to send. + */ +void otg_message (char *buf) +{ + char time_buf[64]; + static u64 save_ticks; + u64 new_ticks; + u64 ticks; + + new_ticks = otg_tmr_ticks(); + + otg_write_message(buf, strlen(buf)); + + ticks = otg_tmr_elapsed(&new_ticks, &save_ticks); + save_ticks = new_ticks; + + if (ticks < 10000) + sprintf(time_buf, " %d uS\n", ticks); + else if (ticks < 10000000) + //sprintf(time_buf, " %d mS\n", ticks / 1000); + sprintf(time_buf, " %d mS\n", ticks >> 10); + else + //sprintf(time_buf, " %d S\n", ticks / 1000000); + sprintf(time_buf, " %d S\n", ticks >> 20); + + otg_write_message(time_buf, strlen(time_buf)); + + + //TRACE_MSG0(CORE, "wakeup "); + //printk(KERN_INFO"%s: wakeup\n", __FUNCTION__); + wake_up_interruptible(&otg_message_queue); +} + +/*! + * otg_message_block() - implement block + */ +unsigned int otg_message_block(void) +{ + int count = otg_data_queued(); + if (count) return count; + //TRACE_MSG0(CORE, "sleeping "); + //printk(KERN_INFO"%s: sleeping\n", __FUNCTION__); + interruptible_sleep_on(&otg_message_queue); + return otg_data_queued(); +} + +/*! + * otg_message_poll() - implement poll + * @param filp file pointer + * @param wait poll table structure + */ +unsigned int otg_message_poll(struct file *filp, struct poll_table_struct *wait) +{ + //TRACE_MSG0(CORE, "polling "); + poll_wait(filp, &otg_message_queue, wait); + return otg_data_queued() ? POLLIN | POLLRDNORM : 0; +} + +/*! + * otg_message_ioctl_internal() - ioctl call + * @param cmd ioctl command. + * @param arg ioctl arguement. + * @return non-zero for error. + */ +int otg_message_ioctl_internal(unsigned int cmd, unsigned long arg) +{ + int i; + int len; + int flag; + struct otg_admin_command admin_command; + struct otg_status_update status_update; + struct otg_firmware_info firmware_info; + struct otg_state otg_state; + struct otg_test otg_test; + struct otg_ioctl_name *otg_ioctl_name; + static char func_buf[32]; + char *sp, *dp; + char *name; + + switch (cmd) { + + case OTGADMIN_GET_FUNCTION: + //TRACE_MSG0(CORE, "OTGADMIN_GET_FUNCTION"); + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd))); + name = otg_usbd_ops->function_name(admin_command.n); + admin_command.string[0] = '\0'; + if (name) + strncat(admin_command.string, name, sizeof(admin_command.string)); + + RETURN_EINVAL_IF(copy_to_user((void *)arg, &admin_command, _IOC_SIZE(cmd))); + return 0; + + case OTGADMIN_SET_FUNCTION: + //TRACE_MSG0(CORE, "OTGADMIN_SET_FUNCTION"); + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + memset(&admin_command, 0x41, sizeof(struct otg_admin_command)); + RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd))); + len = sizeof(mesg_otg_instance->function_name); + admin_command.string[len] = '\0'; + //TRACE_MSG1(CORE, "Setting function: \"%s\"", mesg_otg_instance->function_name); + strncpy(mesg_otg_instance->function_name, admin_command.string, len); + mesg_otg_instance->function_name[len-1] = '\0'; + return 0; + + case OTGADMIN_GET_SERIAL: + //TRACE_MSG0(CORE, "OTGADMIN_GET_SERIAL"); + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + strncpy(admin_command.string, mesg_otg_instance->serial_number, sizeof(admin_command.string)); + admin_command.string[sizeof(admin_command.string) - 1] = '\0'; + RETURN_EINVAL_IF(copy_to_user((void *)arg, &admin_command, _IOC_SIZE(cmd))); + return 0; + + case OTGADMIN_SET_SERIAL: + //TRACE_MSG0(CORE, "OTGADMIN_SET_SERIAL"); + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd))); + admin_command.string[sizeof(admin_command.string) - 1] = '\0'; + printk(KERN_INFO"%s: string: %s\n", __FUNCTION__, admin_command.string); + for (sp = admin_command.string, dp = mesg_otg_instance->serial_number, i = 0; + *sp && (i < (sizeof(admin_command.string) - 1)); i++, sp++) + if (isxdigit(*sp)) *dp++ = toupper(*sp); + + *sp = '\0'; + //TRACE_MSG1(CORE, "serial_number: %s", mesg_otg_instance->serial_number); + printk(KERN_INFO"%s: serial: %s\n", __FUNCTION__, mesg_otg_instance->serial_number); + return 0; + + case OTGADMIN_STATUS: + //TRACE_MSG0(CORE, "OTGADMIN_STATUS"); + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + memset(&status_update, 0, sizeof(struct otg_status_update)); + + otg_mesg_get_status_update(&status_update); + + RETURN_EINVAL_IF(copy_to_user((void *)arg, &status_update, _IOC_SIZE(cmd))); + return 0; + + case OTGADMIN_GET_INFO: + //TRACE_MSG0(CORE, "OTGADMIN_GET_INFO"); + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + + otg_mesg_get_firmware_info(&firmware_info); + + RETURN_EINVAL_IF(copy_to_user((void *)arg, &firmware_info, _IOC_SIZE(cmd))); + //TRACE_MSG0(CORE, "OTGADMIN_GET_INFO: finished"); + return 0; + + case OTGADMIN_SET_INFO: + //TRACE_MSG0(CORE, "OTGADMIN_SET_INFO"); + memset(&firmware_info, 0x41, sizeof(firmware_info)); + RETURN_EINVAL_IF(copy_from_user(&firmware_info, (void *)arg, _IOC_SIZE(cmd))); + + + return otg_mesg_set_firmware_info(&firmware_info); + return 0; + + case OTGADMIN_GET_STATE: + case OTGADMIN_SET_STATE: + //TRACE_MSG0(CORE, "OTGADMIN_XXX_STATE"); + RETURN_EINVAL_IF(copy_from_user(&otg_state, (void *)arg, _IOC_SIZE(cmd))); + + switch (cmd) { + case OTGADMIN_GET_STATE: + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + //TRACE_MSG0(CORE, "OTGADMIN_GET_STATE"); + RETURN_EINVAL_UNLESS (otg_state.state < otg_firmware_loaded->number_of_states); + memcpy(&otg_state, otg_firmware_loaded->otg_states + otg_state.state, sizeof(otg_state)); + RETURN_EINVAL_IF(copy_to_user((void *)arg, &otg_state, _IOC_SIZE(cmd))); + break; + case OTGADMIN_SET_STATE: + RETURN_EINVAL_UNLESS(otg_firmware_loading); + //TRACE_MSG0(CORE, "OTGADMIN_SET_STATE"); + RETURN_EINVAL_UNLESS (otg_state.state < otg_firmware_loading->number_of_states); + memcpy(otg_firmware_loading->otg_states + otg_state.state, &otg_state, sizeof(otg_state)); + break; + } + return 0; + + + case OTGADMIN_GET_TEST: + case OTGADMIN_SET_TEST: + RETURN_EINVAL_IF(copy_from_user(&otg_test, (void *)arg, _IOC_SIZE(cmd))); + + switch (cmd) { + case OTGADMIN_GET_TEST: + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + //TRACE_MSG1(CORE, "OTGADMIN_GET_TEST: %d", otg_test.test); + RETURN_EINVAL_UNLESS (otg_test.test < otg_firmware_loaded->number_of_tests); + memcpy(&otg_test, otg_firmware_loaded->otg_tests + otg_test.test, sizeof(otg_test)); + RETURN_EINVAL_IF(copy_to_user((void *)arg, &otg_test, _IOC_SIZE(cmd))); + break; + case OTGADMIN_SET_TEST: + RETURN_EINVAL_UNLESS(otg_firmware_loading); + //TRACE_MSG1(CORE, "OTGADMIN_SET_TEST : %d", otg_test.test); + RETURN_EINVAL_UNLESS (otg_test.test < otg_firmware_loading->number_of_tests); + memcpy(otg_firmware_loading->otg_tests + otg_test.test, &otg_test, sizeof(otg_test)); + break; + } + return 0; + + default: + //TRACE_MSG0(CORE, "OTGADMIN_"); + RETURN_EINVAL_UNLESS(otg_firmware_loaded); + for (otg_ioctl_name = otg_ioctl_names; otg_ioctl_name && otg_ioctl_name->cmd; otg_ioctl_name++) { + //TRACE_MSG4(CORE, "lookup: %04x %04x %08x %08x", + // _IOC_NR(cmd), _IOC_NR(otg_ioctl_name->cmd), cmd, otg_ioctl_name->cmd); + BREAK_IF(_IOC_NR(otg_ioctl_name->cmd) == _IOC_NR(cmd)); + } + //TRACE_MSG3(CORE, "checking %d %08x %08x", _IOC_NR(cmd), otg_ioctl_name->cmd, cmd); + RETURN_EINVAL_UNLESS(otg_ioctl_name->cmd); + __get_user(flag, (int *)arg); + //TRACE_MSG3(CORE, "%s %08x flag: %d", otg_ioctl_name->name, otg_ioctl_name->set, flag); + otg_event (mesg_otg_instance, flag ? otg_ioctl_name->set : _NOT(otg_ioctl_name->set), CORE, otg_ioctl_name->name); + return 0; + } + return 0; +} + + + +/*! + * otg_message_ioctl() - ioctl + * @param inode inode structure. + * @param filp file. + * @param cmd ioctl command. + * @param arg ioctl argument. + * @return non-zero for error. + */ +int otg_message_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int i; + int len; + int flag; + struct otg_admin_command admin_command; + struct otg_status_update status_update; + struct otg_firmware_info firmware_info; + struct otg_state otg_state; + struct otg_test otg_test; + //struct otg_ioctl_map *map = ioctl_map; + struct otg_ioctl_name *otg_ioctl_name; + + static char func_buf[32]; + + //TRACE_MSG6(CORE, "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) == OTGADMIN_MAGIC); + RETURN_EINVAL_UNLESS (_IOC_NR(cmd) <= OTGADMIN_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 otg_message_ioctl_internal(cmd, arg); +} + + +/*! + * otg_message_proc_read() - implement proc file system read. + * Standard proc file system read function. + * @param file file. + * @param buf buffer to fill + * @param count size of buffer + * @param pos file position + * @return number of bytes or negative number for error + */ +static ssize_t otg_message_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0, avail; + char *bp,*ep; + struct list_head *lhd; + int state; + + // get a page, max 4095 bytes of data... + RETURN_ENOMEM_UNLESS ((page = GET_KERNEL_PAGE())); + + bp = (char *) page; + + len = bp - (char *) page; + + if (( avail = otg_message_block())) { + + len += otg_read_message(bp, 4095); + + if (len > count) + len = -EINVAL; + else if ((len > 0) && copy_to_user (buf, (char *) page, len)) + len = -EFAULT; + + } + + free_page (page); + return len; +} + +/*! otg_message_proc_ioctl() - + * @param inode inode structure. + * @param filp file. + * @param cmd ioctl command. + * @param arg ioctl argument. + * @return non-zero for error. + */ +int otg_message_proc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + return otg_message_ioctl(inode, filp, cmd, arg); +} + +/*! + * @param filp file. + * @param wait buffer to fill + * @return number of bytes or negative number for error + */ +unsigned int otg_message_proc_poll(struct file *filp, struct poll_table_struct *wait) +{ + return otg_message_poll(filp, wait); +} + +/*! + * @var otg_message_proc_switch_functions + */ +static struct file_operations otg_message_proc_switch_functions = { + ioctl:otg_message_proc_ioctl, + poll:otg_message_proc_poll, + read:otg_message_proc_read, +}; + + +/*! + * otg_message_init_l24() - initialize + */ +int otg_message_init_l24(struct otg_instance *otg) +{ + struct proc_dir_entry *message = NULL; + init_waitqueue_head(&otg_message_queue); + + THROW_IF (!(message = create_proc_entry ("otg_message", 0666, 0)), error); + message->proc_fops = &otg_message_proc_switch_functions; + CATCH(error) { + printk(KERN_ERR"%s: creating /proc/otg_message failed\n", __FUNCTION__); + if (message) + remove_proc_entry("otg_message", NULL); + return -EINVAL; + } + return 0; +} + +/*! + * otg_message_exit_l24() - exit + */ +void otg_message_exit_l24(void) +{ + remove_proc_entry("otg_message", NULL); +} + +EXPORT_SYMBOL(otg_message); + + diff -uNr linux/drivers/no-otg/otgcore/otg-mesg.c linux/drivers/otg/otgcore/otg-mesg.c --- linux/drivers/no-otg/otgcore/otg-mesg.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg-mesg.c 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,369 @@ +/* + * otg/otgcore/otg-mesg.c - OTG state machine Administration + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otgcore/otg-mesg.c + * @brief OTG Core Message Facility. + * + * The OTG Core Message Facility has two functions: + * + * 1. implement a message queue for messages from the otg state machine to the otg + * management application + * + * 2. implement an ioctl control mechanism allowing the otg managment application + * to pass data and commands to the otg state machine + * + * @ingroup OTGCore + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(OTG_LINUX) +#include +#include +#endif /* defined(OTG_LINUX) */ + +//#define OTG_MESSAGE_BUF_SIZE 512 +#define OTG_MESSAGE_BUF_SIZE 2048 + +struct otg_instance * mesg_otg_instance; + +char *otg_message_buf; // buffer +char *otg_message_head; // next available empty space +char *otg_message_tail; // next available data if not equal to head + +/*! + * otg_data_avail() - amount of data in buffer + * space data + * tp hp 20 - (10 - 5) = 15 (10 - 5) + * 0......5.data.10......20 + * + * hp tp (10 - 5) = 5 20 - (10 - 5) + * 0.data.5......10.data.20 + * + * @return Bytes available in message ring buffer. + */ +int otg_data_avail(void) +{ + int avail = (otg_message_head >= otg_message_tail) ? + (otg_message_head - otg_message_tail) : + OTG_MESSAGE_BUF_SIZE - (otg_message_tail - otg_message_head) ; + + //TRACE_MSG4(CORE, "buf: %p head: %p tail: %p data avail: %d", otg_message_buf, otg_message_head, otg_message_tail, avail); + return avail; +} + +/*! + * otg_space_avail() - amount of space in buffer + * + * @return Space available in message ring buffer. + */ +int otg_space_avail(void) +{ + int avail = (otg_message_head >= otg_message_tail) ? + OTG_MESSAGE_BUF_SIZE - (otg_message_head - otg_message_tail) : + (otg_message_tail - otg_message_head) ; + + //TRACE_MSG4(CORE, "buf: %p head: %p tail: %p space avail: %d", otg_message_buf, otg_message_head, otg_message_tail, avail); + return avail; +} + +/*! + * otg_get_byte() - return next byte + * + * @return Next byte in message ring buffer + */ +char otg_get_byte(void) +{ + char c = *otg_message_tail; + + //TRACE_MSG3(CORE, "buf: %p head: %p tail: %p", otg_message_buf, otg_message_head, otg_message_tail); + if ((otg_message_tail - otg_message_buf) == OTG_MESSAGE_BUF_SIZE) + otg_message_tail = otg_message_buf; + else + otg_message_tail++; + + return c; +} + +/*! + * otg_put_byte() - put byte into buffer + * + * Put byte into message ring buffer + * + * @param byte put into message ring. + */ +void otg_put_byte(char byte) +{ + *otg_message_head = byte; + if ((otg_message_head - otg_message_buf) == OTG_MESSAGE_BUF_SIZE) + otg_message_head = otg_message_buf; + else + otg_message_head++; + +} + +/*! + * otg_write_message_irq() - queue message data (interrupt version) + * + * Attempt to write message data into ring buffer, return amount + * of data actually written. + * + * @param buf - pointer to data + * @param size - amount of available data + * @return number of bytes that where not written. + */ +int otg_write_message_irq (char *buf, int size) +{ + struct pcd_instance *pcd = NULL; + struct usbd_bus_instance *bus = NULL; + + pcd = (struct pcd_instance *)mesg_otg_instance->pcd; + if (pcd) + bus = pcd->bus; + + if (size >= (OTG_MESSAGE_BUF_SIZE - 2)) + return size; + + while (otg_space_avail() < (size + 1)) + otg_get_byte(); + + + /* copy, note that we don't have to test for space as we have + * already ensured that there is enough room for all of the data + */ + while (size--) + otg_put_byte(*buf++); + + //otg_put_byte('\n'); + + return size; +} + +/*! + * otg_write_message() - queue message data + * + * Attempt to write message data into ring buffer, return amount + * of data actually written. + * + * @param buf - pointer to data + * @param size - amount of available data + * @return number of bytes that where not written. + */ +int otg_write_message (char *buf, int size) +{ + int rc; + unsigned long flags; + local_irq_save (flags); + rc = otg_write_message_irq(buf, size); + local_irq_restore (flags); + return size; +} + +/*! + * otg_read_message() - get queued message data + * + * @param buf - pointer to data + * @param size - amount of available data + * @return the number of bytes read. + */ +int otg_read_message(char *buf, int size) +{ + int count = 0; + unsigned long flags; + + local_irq_save (flags); + + /* Copy bytes to the buffer while there is data available and space + * to put it in the buffer + */ + for ( ; otg_data_avail() && size--; count++) + BREAK_IF((*buf++ = otg_get_byte()) == '\n'); + + local_irq_restore (flags); + return count; +} + +/*! + * otg_data_queued() - return amount of data currently queued + * + * @return Amount of data currently queued in mesage ring buffer. + */ +int otg_data_queued(void) +{ + int count; + unsigned long flags; + local_irq_save (flags); + count = otg_data_avail(); + local_irq_restore (flags); + //printk(KERN_INFO"%s: count: %d\n", __FUNCTION__, count); + return count; +} + + +/*! + * otg_message_init() - initialize + * @param otg OTG instance + */ +int otg_message_init(struct otg_instance *otg) +{ + struct proc_dir_entry *message = NULL; + otg_message_buf = otg_message_head = otg_message_tail = NULL; + THROW_UNLESS((otg_message_buf = CKMALLOC(OTG_MESSAGE_BUF_SIZE + 16, GFP_KERNEL)), error); + otg_message_head = otg_message_tail = otg_message_buf ; + + mesg_otg_instance = otg; + CATCH(error) { + #if defined(OTG_LINUX) + printk(KERN_ERR"%s: creating /proc/otg_message failed\n", __FUNCTION__); + #endif /* defined(OTG_LINUX) */ + #if defined(OTG_WINCE) + DEBUGMSG(ZONE_INIT, (_T("otg_message_init: creating //proc//otg_message failed\n")) ); + #endif /* defined(OTG_LINUX) */ + return -EINVAL; + } + return 0; +} + +/*! + * otg_message_exit() - exit + */ +void otg_message_exit(void) +{ + mesg_otg_instance = NULL; + if (otg_message_buf) + LKFREE(otg_message_buf); + otg_message_buf = otg_message_head = otg_message_tail = NULL; +} + + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ + +/*! + * otg_mesg_get_status_update() - get status update + * @param status_update + */ +void otg_mesg_get_status_update(struct otg_status_update *status_update) +{ + + TRACE_MSG0(CORE, "--"); + status_update->state = mesg_otg_instance->state; + status_update->meta = mesg_otg_instance->current_outputs->meta; + status_update->inputs = mesg_otg_instance->current_inputs; + status_update->outputs = mesg_otg_instance->outputs; + status_update->capabilities = otg_ocd_ops ? otg_ocd_ops->capabilities : 0; + + strncpy(status_update->fw_name, otg_firmware_loaded->fw_name, OTGADMIN_MAXSTR); + strncpy(status_update->state_name, otg_get_state_name(mesg_otg_instance->state), OTGADMIN_MAXSTR); + + if (mesg_otg_instance->function_name) + strncpy(status_update->function_name, mesg_otg_instance->function_name, OTGADMIN_MAXSTR); + + TRACE_MSG5(CORE, "state: %02x meta: %02x inputs: %08x outputs: %04x", + status_update->state, status_update->meta, status_update->inputs, + status_update->outputs, status_update->capabilities); +} + + +/*! + * otg_mesg_get_firmware_info() - get firmware info + * @param firmware_info + */ +void otg_mesg_get_firmware_info(struct otg_firmware_info *firmware_info) +{ + memset(firmware_info, 0, sizeof(firmware_info)); + // XXX irq lock + + firmware_info->number_of_states = otg_firmware_loaded->number_of_states; + firmware_info->number_of_tests = otg_firmware_loaded->number_of_tests; + memcpy(&firmware_info->fw_name, otg_firmware_loaded->fw_name, sizeof(firmware_info->fw_name)); + +} + +/*! + * otg_mesg_set_firmware_info() - set firmware info + * @param firmware_info + */ +int otg_mesg_set_firmware_info(struct otg_firmware_info *firmware_info) +{ + + if (otg_firmware_loading) { + + if ( (otg_firmware_loading->number_of_states == firmware_info->number_of_states) && + (otg_firmware_loading->number_of_tests == firmware_info->number_of_tests) + ) { + TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: loaded"); + otg_firmware_loaded = otg_firmware_loading; + otg_firmware_loading = NULL; + return 0; + } + else { + TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: abandoned"); + LKFREE(otg_firmware_loading->otg_states); + LKFREE(otg_firmware_loading->otg_tests); + LKFREE(otg_firmware_loading); + return -EINVAL; + } + } + + TRACE_MSG2(CORE, "OTGADMIN_SET_INFO: otg_firmware_loaded: %x otg_firmware_orig: %x", + otg_firmware_loaded, otg_firmware_orig); + if (otg_firmware_loaded && (otg_firmware_loaded != otg_firmware_orig)) { + LKFREE(otg_firmware_loaded->otg_states); + LKFREE(otg_firmware_loaded->otg_tests); + LKFREE(otg_firmware_loaded); + otg_firmware_loaded = otg_firmware_orig; + TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: unloaded"); + } + if (firmware_info->number_of_states && firmware_info->number_of_tests) { + int size; + otg_firmware_loaded = NULL; + TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: setup"); + THROW_UNLESS((otg_firmware_loading = CKMALLOC(sizeof(struct otg_firmware), GFP_KERNEL)), error); + size = sizeof(struct otg_state) * firmware_info->number_of_states; + THROW_UNLESS((otg_firmware_loading->otg_states = CKMALLOC(size, GFP_KERNEL)), error); + size = sizeof(struct otg_test) * firmware_info->number_of_tests; + THROW_UNLESS((otg_firmware_loading->otg_tests = CKMALLOC(size, GFP_KERNEL)), error); + + otg_firmware_loading->number_of_states = firmware_info->number_of_states; + otg_firmware_loading->number_of_tests = firmware_info->number_of_tests; + memcpy(otg_firmware_loading->fw_name, &firmware_info->fw_name, sizeof(firmware_info->fw_name)); + + CATCH(error) { + TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: setup error"); + if (otg_firmware_loading) { + if (otg_firmware_loading->otg_states) LKFREE(otg_firmware_loading->otg_states); + if (otg_firmware_loading->otg_tests) LKFREE(otg_firmware_loading->otg_tests); + LKFREE(otg_firmware_loading); + return -EINVAL; + } + } + + } + return 0; +} + + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ + diff -uNr linux/drivers/no-otg/otgcore/otg-trace-l24.c linux/drivers/otg/otgcore/otg-trace-l24.c --- linux/drivers/no-otg/otgcore/otg-trace-l24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg-trace-l24.c 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,174 @@ +/* + * otg/otgcore/otg-trace-l24.c + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + */ +/*! + * @file otg/otgcore/otg-trace-l24.c + * @brief OTG Core Linux Debug Trace Facility + * + * This allows access to the OTG Core Trace via /proc/trace_otg. Each time + * this file is opened and read it will contain a snapshot of the most + * recent trace messages. + * + * Reading the trace resets it, the next read will open a new snapshot. + * + * N.B. There is no protection from multiple reads. + * + * N.B. This is for debugging and is not normally enabled for production software. + * + * @ingroup OTGCore + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) + +DECLARE_MUTEX(otg_trace_sem_l24); + + +/* * + * otg_trace_proc_read_l24 - implement proc file system read. + * @file + * @buf + * @count + * @pos + * + * Standard proc file system read function. + */ +static ssize_t otg_trace_proc_read_l24 (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + int oindex; + int rc; + + u64 ticks = 0; + + unsigned char *cp; + int skip = 0; + + otg_trace_t px; + otg_trace_t ox; + otg_trace_t vx; + otg_trace_t *o, *p; + + RETURN_ENOMEM_IF (!(page = GET_KERNEL_PAGE())); + + _MOD_INC_USE_COUNT; + + // Lock out tag changes while reading + DOWN(&otg_trace_sem_l24); + len = otg_trace_proc_read((char *)page, count, (int *)pos); + + if (len > count) + len = -EINVAL; + + else if (len > 0 && copy_to_user (buf, (char *) page, len)) + len = -EFAULT; + + free_page (page); + + UP(&otg_trace_sem_l24); + free_page (page); + _MOD_DEC_USE_COUNT; + return len; +} + +#define MAX_TRACE_CMD_LEN 64 +/* * + * otg_trace_proc_write_l24 - implement proc file system write. + * @file + * @buf + * @count + * @pos + * + * Proc file system write function. + */ +static ssize_t otg_trace_proc_write_l24 (struct file *file, const char *buf, size_t count, loff_t * pos) +{ + char command[MAX_TRACE_CMD_LEN+1]; + size_t l = MIN(count, MAX_TRACE_CMD_LEN); + + RETURN_ZERO_UNLESS(count); + RETURN_EINVAL_IF (copy_from_user (command, buf, l)); + + + RETURN_EINVAL_IF(copy_from_user (command, buf, l)); + + return otg_trace_proc_write(command, l, (int *)pos); +#if 0 + else { + n -= l; + while (n > 0) { + if (copy_from_user (&c, buf + (count - n), 1)) { + count = -EFAULT; + break; + } + n -= 1; + } + // Terminate command[] + if (l > 0 && command[l-1] == '\n') { + l -= 1; + } + command[l] = 0; + } +#endif +} + +/* Module init ************************************************************** */ + + + +static struct file_operations otg_trace_proc_operations_functions = { + read: otg_trace_proc_read_l24, + write: otg_trace_proc_write_l24, +}; + +/*! otg_trace_init_l24 + * + * Return non-zero if not successful. + */ +int otg_trace_init_l24 (void) +{ + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("trace_otg", 0, 0)) == NULL) + printk(KERN_INFO"%s PROC FS failed\n", "trace_otg"); + else + p->proc_fops = &otg_trace_proc_operations_functions; + + return 0; +} + +/*! otg_trace_exit_l24 - remove procfs entry, free trace data space. + */ +void otg_trace_exit_l24 (void) +{ + otg_trace_t *p; + unsigned long flags; + remove_proc_entry ("trace_otg", NULL); +} + +#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ +int otg_trace_init_l24 (void) {return 0;} +void otg_trace_exit_l24 (void) {} +#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ + diff -uNr linux/drivers/no-otg/otgcore/otg-trace.c linux/drivers/otg/otgcore/otg-trace.c --- linux/drivers/no-otg/otgcore/otg-trace.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg-trace.c 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,615 @@ +/* + * otg/otgcore/otg-trace.c + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + */ +/*! + * @file otg/otgcore/otg-trace.c + * @brief OTG Core Debug Trace Facility + * + * This implements the OTG Core Trace Facility. + * + * @ingroup OTGCore + */ + +#include +#include + +#if defined(OTG_LINUX) +#include +#include +#endif /* defined(OTG_LINUX) */ + +#include +#include +#include + +#include +#include + +#define STATIC static + +#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) + +DECLARE_MUTEX(otg_trace_sem); + +static int otg_trace_exiting; + +#define OTG_TRACE_NAME "trace_otg" + +typedef struct otg_trace_snapshot { + int first; + int next; + int total; + otg_trace_t *traces; +} otg_trace_snapshot; + +otg_trace_snapshot otg_snapshot1; +otg_trace_snapshot otg_snapshot2; + +static otg_trace_snapshot *otg_trace_current_traces; +static otg_trace_snapshot *otg_trace_other_traces; + +STATIC otg_trace_snapshot *rel_snapshot(otg_trace_snapshot *tss) +{ + RETURN_NULL_UNLESS(tss); + if (tss->traces) { + vfree(tss->traces); + tss->traces = NULL; + } + //vfree(tss); + return NULL; +} + +STATIC void init_snapshot(otg_trace_snapshot *tss) +{ + tss->first = tss->next = tss->total = 0; + memset(tss->traces,0,sizeof(otg_trace_t) * TRACE_MAX); +} + +STATIC otg_trace_snapshot *get_snapshot(otg_trace_snapshot *tss) +{ + //otg_trace_snapshot *tss; + unsigned long flags; + //UNLESS ((tss = vmalloc(sizeof(otg_trace_snapshot)))) { + // printk(KERN_ERR "Cannot allocate memory for OTG trace snapshot.\n"); + // return NULL; + //} + + UNLESS ((tss->traces = vmalloc(sizeof(otg_trace_t) * TRACE_MAX))) { + printk(KERN_ERR "Cannot allocate memory for OTG trace log.\n"); + return rel_snapshot(tss); + } + init_snapshot(tss); + return tss; +} + +/*! otg_trace_get_next + */ +STATIC otg_trace_t *otg_trace_get_next(otg_trace_snapshot *tss, otg_tag_t tag, const char *fn, otg_trace_types_t otg_trace_type, int n) +{ + otg_trace_t *p; + + // Get the next (n) trace slots. + + p = tss->traces + tss->next; + + while (n-- > 0) { + tss->next = (tss->next + 1) & TRACE_MASK; + if (tss->next == tss->first) { + // We have wraparound, bump tss->first + tss->first = (tss->first + 1) & TRACE_MASK; + } + tss->total++; + } + // End of next trace slot. + + otg_get_trace_info(p); + p->otg_trace_type = otg_trace_type; + p->tag = tag; + p->va.s.function = fn; + + return p; +} + +/* otg_trace_next_va + */ +STATIC otg_trace_t *otg_trace_next_va(otg_trace_snapshot *tss, otg_trace_t *p) +{ + /* Return the next trace slot in a va group. + No need to be atomic, just worry about wraparound. */ + int nxt = (p - tss->traces); + nxt = (nxt + 1) & TRACE_MASK; + p = tss->traces + nxt; + return(p); +} + +/* otg_trace_setup + */ +void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup) +{ + otg_trace_t *p; + otg_trace_snapshot *tss; + unsigned long flags; + local_irq_save(flags); + UNLESS ((tss = otg_trace_current_traces)) { + local_irq_restore(flags); + return; + } + // Reserve the trace slots. + UNLESS ((p = otg_trace_get_next(tss,tag,fn,otg_trace_setup_n,1))) { + local_irq_restore(flags); + return; + } + p->va_num_args = 0; + memcpy(&p->va.s.trace.setup, setup, sizeof(p->va.s.trace.setup) /*sizeof(struct usbd_device_request)*/); + local_irq_restore(flags); +} + +/* otg_trace_msg + */ +void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, ...) +{ + int n; + otg_trace_t *p; + otg_trace_snapshot *tss; + unsigned long flags; + + // Figure out how many trace slots we're going to need. + n = 1; + if (nargs > 1) { + n = 2; + // Too many arguments, bail. + RETURN_IF (nargs > (OTG_TRACE_MAX_IN_VA+1)); + } + + local_irq_save(flags); + UNLESS ((tss = otg_trace_current_traces)) { + local_irq_restore(flags); + return; + } + // Reserve the trace slots. + UNLESS((p = otg_trace_get_next(tss,tag,fn,otg_trace_msg_va_start_n,n))) { + local_irq_restore(flags); + return; + } + p->va.s.trace.msg32.msg = fmt; + p->va_num_args = nargs; + if (nargs > 0) { + va_list ap; + va_start(ap,fmt); + p->va.s.trace.msg32.val = va_arg(ap,u32); + if (--nargs > 0) { + // We need the next trace slot. + int i; + while (nargs >= 8) { + p = otg_trace_next_va(tss,p); + p->otg_trace_type = otg_trace_msg_va_list_n; + for (i = 0; i < 8; i++) p->va.l.val[i] = va_arg(ap,u32); + nargs -= 8; + } + p = otg_trace_next_va(tss,p); + p->otg_trace_type = otg_trace_msg_va_list_n; + for (i = 0; i < nargs; i++) p->va.l.val[i] = va_arg(ap,u32); + } + va_end(ap); + } + local_irq_restore(flags); +} + +/* Proc Filesystem *************************************************************************** */ + +/* int slot_in_data + */ +STATIC int slot_in_data(otg_trace_snapshot *tss, int slot) +{ + // Return 1 if slot is in the valid data region, 0 otherwise. + return (((tss->first < tss->next) && (slot >= tss->first) && (slot < tss->next)) || + ((tss->first > tss->next) && ((slot < tss->next) || (slot >= tss->first)))); + +} + +/*! otg_trace_read_slot + * + * Place a slot pointer in *ps (and possibly *pv), with the older entry (if any) in *po. + * Return: -1 at end of data + * 0 if not a valid enty + * 1 if entry valid but no older valid entry + * 2 if both entry and older are valid. + */ +STATIC int otg_trace_read_slot(otg_trace_snapshot *tss, int index, otg_trace_t **ps, otg_trace_t **po) +{ + int res; + int previous; + otg_trace_t *s,*o; + + *ps = *po = NULL; + index = (tss->first + index) & TRACE_MASK; + // Are we at the end of the data? + if (!slot_in_data(tss,index)) { + // End of data. + return(-1); + } + /* Nope, there's data to show. */ + s = tss->traces + index; + if (!s->tag || + s->otg_trace_type == otg_trace_msg_invalid_n || + s->otg_trace_type == otg_trace_msg_va_list_n) { + /* Ignore this slot, it's not valid, or is part + of a previously processed varargs. */ + return(0); + } + *ps = s; + res = 1; + // Is there a previous event (for "ticks" calculation)? + previous = (index - 1) & TRACE_MASK; + if (previous != tss->next && tss->total > 1) { + // There is a valid previous event. + res = 2; + o = tss->traces + previous; + /* If the previous event was a varargs event, we want + a copy of the first slot, not the last. */ + while (o->otg_trace_type == otg_trace_msg_va_list_n) { + previous = (previous - 1) & TRACE_MASK; + if (previous == tss->next) { + res = 1; + o = NULL; + break; + } + o = tss->traces + previous; + } + *po = o; + } + return(res); +} + + +/*! otg_trace_proc_read - implement proc file system read. + * + * Standard proc file system read function. + * @file + * @buf + * @count + * @pos + */ +int otg_trace_proc_read(char *page, int count, int * pos) +{ + int len = 0; + int index; + int oindex; + int rc; + + u64 ticks = 0; + + unsigned char *cp; + int skip = 0; + + otg_trace_t *o; + otg_trace_t *s; + otg_trace_snapshot *tss; + + DOWN(&otg_trace_sem); + + /* Grab the current snapshot, and replace it with the other. This + * needs to be atomic WRT otg_trace_msg() calls. + */ + UNLESS (*pos) { + unsigned long flags; + tss = otg_trace_other_traces; + init_snapshot(tss); // clear previous + local_irq_save(flags); + otg_trace_other_traces = otg_trace_current_traces; // swap snapshots + otg_trace_current_traces = tss; + local_irq_restore(flags); + } + + tss = otg_trace_other_traces; + + /* Get the index of the entry to read - this needs to be atomic WRT tag operations. + */ + s = o = NULL; + oindex = index = (*pos)++; + do { + rc = otg_trace_read_slot(tss,index,&s,&o); + switch (rc) { + case -1: // End of data. + UP(&otg_trace_sem); + return(0); + + case 0: // Invalid slot, skip it and try the next. + index = (*pos)++; + break; + + case 1: // s valid, o NULL + case 2: // s and o valid + break; + } + } while (rc == 0); + + len = 0; + + UNLESS (oindex) + len += sprintf((char *) page + len, "Tag Index Ints Framenum Ticks\n"); + + /* If there is a previous trace event, we want to calculate how many + * ticks have elapsed siince it happened. Unfortunately, determining + * if there _is_ a previous event isn't obvious, since we have to watch + * out for startup, varargs and wraparound. + */ + if (o) { + + ticks = otg_tmr_elapsed(&s->va.s.ticks, &o->va.s.ticks); + + if ((o->va.s.interrupts != s->va.s.interrupts) || (o->tag != s->tag) || (o->in_interrupt != s->in_interrupt)) + skip++; + } + + len += sprintf((char *) page + len, "%s%02x %c %8u %8d ", skip?"\n":"", + s->tag, s->id_gnd ? 'A' : 'B', index, s->va.s.interrupts); + + len += sprintf((char *) page + len, "%03x ", s->va.s.framenum); + + if (ticks < 10000) + len += sprintf((char *) page + len, "%8duS", ticks); + else if (ticks < 10000000) + len += sprintf((char *) page + len, "%8dmS", ticks >> 10); + else + len += sprintf((char *) page + len, "%8dS ", ticks >> 20); + + len += sprintf((char *) page + len, " %s ", s->in_interrupt ? "--" : "=="); + len += sprintf((char *) page + len, "%-24s: ",s->va.s.function); + + switch (s->otg_trace_type) { + case otg_trace_msg_invalid_n: + case otg_trace_msg_va_list_n: + len += sprintf((char *) page + len, " -- N/A"); + break; + + case otg_trace_msg_va_start_n: + if (s->va_num_args <= 1) + len += sprintf((char *) page + len, s->va.s.trace.msg32.msg, s->va.s.trace.msg32.val); + + else { + otg_trace_t *v = otg_trace_next_va(tss,s); + len += sprintf((char *) page + len, s->va.s.trace.msg32.msg, s->va.s.trace.msg32.val, + v->va.l.val[0], v->va.l.val[1], v->va.l.val[2], v->va.l.val[3], + v->va.l.val[4], v->va.l.val[5], v->va.l.val[6]); + } + break; + + case otg_trace_msg_n: + len += sprintf((char *) page + len, s->va.s.trace.msg.msg); + break; + + case otg_trace_msg32_n: + len += sprintf((char *) page + len, s->va.s.trace.msg32.msg, s->va.s.trace.msg32.val); + break; + + case otg_trace_msg16_n: + len += sprintf((char *) page + len, s->va.s.trace.msg16.msg, s->va.s.trace.msg16.val0, s->va.s.trace.msg16.val1); + break; + + case otg_trace_msg8_n: + len += sprintf((char *) page + len, s->va.s.trace.msg8.msg, + s->va.s.trace.msg8.val0, s->va.s.trace.msg8.val1, s->va.s.trace.msg8.val2, + s->va.s.trace.msg8.val3); + break; + + case otg_trace_setup_n: + cp = (unsigned char *)&s->va.s.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; + } + len += sprintf((char *) page + len, "\n"); + + UP(&otg_trace_sem); + return len; +} + +/*! otg_trace_proc_write - implement proc file system write. + * @file + * @buf + * @count + * @pos + * + * Proc file system write function. + */ +int otg_trace_proc_write (const char *buf, int count, int * pos) +{ +#define MAX_TRACE_CMD_LEN 64 + char command[MAX_TRACE_CMD_LEN+1]; + size_t n = count; + size_t l; + char c; + + if (n > 0) { + l = MIN(n,MAX_TRACE_CMD_LEN); + if (copy_from_user (command, buf, l)) + count = -EFAULT; + else { + n -= l; + while (n > 0) { + if (copy_from_user (&c, buf + (count - n), 1)) { + count = -EFAULT; + break; + } + n -= 1; + } + // Terminate command[] + if (l > 0 && command[l-1] == '\n') { + l -= 1; + } + command[l] = 0; + } + } + + if (0 >= count) { + printk(KERN_INFO"%s: count <= 0 %d\n", __FUNCTION__, count); + return count; + } + + + return count; +} + +static u32 otg_tags_mask = 0x0; +extern void otg_trace_exit (void); + +/*! otg_trace_obtain_tag(void) + */ +otg_tag_t otg_trace_obtain_tag(void) +{ + /* Return the next unused tag value [1..32] if one is available, + * return 0 if none is available. + */ + otg_tag_t tag = 0; + DOWN(&otg_trace_sem); + if (otg_tags_mask == 0xffffffff || otg_trace_exiting) { + // They're all in use. + UP(&otg_trace_sem); + return tag; + } + if (otg_tags_mask != 0x00000000) { + // 2nd or later tag, search for an unused one + while (otg_tags_mask & (0x1 << tag)) { + tag += 1; + } + } + + // Successful 1st or later tag. + otg_tags_mask |= 0x1 << tag; + UP(&otg_trace_sem); + return tag + 1; +} + +/*! otg_trace_invalidate_tag(otg_tag_t tag) + */ +otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag) +{ + otg_trace_t *p,*e; + otg_trace_snapshot *css,*oss; + unsigned long flags; + + // Nothing to do + RETURN_ZERO_IF ((0 >= tag) || (tag > 32)); + + // Grab a local copy of the snapshot pointers. + local_irq_save(flags); + css = otg_trace_current_traces; + oss = otg_trace_other_traces; + local_irq_restore(flags); + + + // Run through all the trace messages, and invalidate any with the given tag. + + DOWN(&otg_trace_sem); + if (css) + for (e = TRACE_MAX + (p = css->traces); p < e; p++) { + if (p->tag == tag) { + p->otg_trace_type = otg_trace_msg_invalid_n; + p->tag = 0; + } + } + if (oss) + for (e = TRACE_MAX + (p = oss->traces); p < e; p++) { + if (p->tag == tag) { + p->otg_trace_type = otg_trace_msg_invalid_n; + p->tag = 0; + } + } + + // Turn off the tag + otg_tags_mask &= ~(0x1 << (tag-1)); + if (otg_tags_mask == 0x00000000) { /* otg_trace_exit (); */ } + UP(&otg_trace_sem); + return 0; +} + + + +/* Module init ************************************************************** */ + +#define OT otg_trace_init_test +otg_tag_t OT; + +/*! otg_trace_init + * + * Return non-zero if not successful. + */ +int otg_trace_init (void) +{ + otg_trace_exiting = 0; + otg_trace_current_traces = otg_trace_other_traces = NULL; + UNLESS ((otg_trace_current_traces = get_snapshot(&otg_snapshot1)) && + (otg_trace_other_traces = get_snapshot(&otg_snapshot2))) + { + printk(KERN_ERR"%s: malloc failed (2x) %d\n", __FUNCTION__, sizeof(otg_trace_t) * TRACE_MAX); + otg_trace_current_traces = rel_snapshot(otg_trace_current_traces); + otg_trace_other_traces = rel_snapshot(otg_trace_other_traces); + return -ENOMEM; + } + OT = otg_trace_obtain_tag(); + RETURN_ENOMEM_IF(!OT); + TRACE_MSG0(OT,"--"); + return 0; +} + +/*! otg_trace_exit - remove procfs entry, free trace data space. + */ +void otg_trace_exit (void) +{ + otg_trace_t *p; + otg_tag_t tag; + + unsigned long flags; + otg_trace_snapshot *c,*o; + + otg_trace_invalidate_tag(OT); + otg_trace_exiting = 1; + + local_irq_save (flags); + + /* Look for any outstanding tags. */ + for (tag = 32; otg_tags_mask && tag > 0; tag--) + if (otg_tags_mask & (0x1 << (tag-1))) + otg_trace_invalidate_tag(tag); + + c = otg_trace_current_traces; + o = otg_trace_other_traces; + otg_trace_current_traces = otg_trace_other_traces = NULL; + local_irq_restore (flags); + + c = rel_snapshot(c); + o = rel_snapshot(o); +} + +#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ +int otg_trace_init (void) {return 0;} +void otg_trace_exit (void) {} +otg_tag_t otg_trace_obtain_tag(void) { return 0; } +void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup) { } +void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, ...) { } +otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag) { return 0; } +#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ + +OTG_EXPORT_SYMBOL(otg_trace_setup); +OTG_EXPORT_SYMBOL(otg_trace_msg); +OTG_EXPORT_SYMBOL(otg_trace_obtain_tag); +OTG_EXPORT_SYMBOL(otg_trace_invalidate_tag); + +#if 0 +// These are only needed when debugging the trace code itself. +OTG_EXPORT_SYMBOL(otg_trace_get_next); +OTG_EXPORT_SYMBOL(otg_trace_next_va); +OTG_EXPORT_SYMBOL(slot_in_data); +OTG_EXPORT_SYMBOL(otg_trace_read_slot); +OTG_EXPORT_SYMBOL(otg_trace_proc_read); +#endif + diff -uNr linux/drivers/no-otg/otgcore/otg.c linux/drivers/otg/otgcore/otg.c --- linux/drivers/no-otg/otgcore/otg.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/otg.c 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,936 @@ +/* + * otg/otgcore/otg.c - OTG state machine + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otgcore/otg.c + * @brief OTG Core State Machine Event Processing + * + * The OTG State Machine receives "input" information passed to it + * from the other drivers (pcd, tcd, hcd and ocd) that describe what + * is happening. + * + * The State Machine uses the inputs to move from state to state. Each + * state defines four things: + * + * 1. Reset - what inputs to set or reset on entry to the state. + * + * 2. Outputs - what output functions to call to set or reset. + * + * 3. Timeout - an optional timeout value to set + * + * 4. Tests - a series of tests that allow the state machine to move to + * new states based on current or new inputs. + * + * @ingroup OTGCore + */ +#ifndef OTG_REGRESS + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +struct hcd_ops *otg_hcd_ops; +struct ocd_ops *otg_ocd_ops; +struct pcd_ops *otg_pcd_ops; +struct tcd_ops *otg_tcd_ops; +struct usbd_ops *otg_usbd_ops; + +extern struct otg_instance otg_instance_private; +extern struct hcd_instance hcd_instance_private; +extern struct ocd_instance ocd_instance_private; +extern struct pcd_instance pcd_instance_private; +extern struct tcd_instance tcd_instance_private; + +#if 0 +/* otg_vbus - return vbus status + * + * XXX Should we default to set or reset if there is no vbus function + */ +int otg_vbus(struct otg_instance *otg) +{ + TRACE_MSG2(CORE, "otg_tcd_ops: %p vbus: %p", otg_tcd_ops, otg_tcd_ops ? otg_tcd_ops->vbus : NULL); + return (otg_tcd_ops && otg_tcd_ops->vbus) ? otg_tcd_ops->vbus(otg) : 1; +} +#endif + +#if defined (OTG_WINCE) +#define TRACE_SM_CURRENT(t, m, o) \ + +#else +#define TRACE_SM_CURRENT(t, m, o) \ + TRACE_MSG5(t, "SM_CURRENT: RESET: %08x SET: %08x %s (%s) %s",\ + (u32)(~o->current_inputs), \ + (u32)(o->current_inputs), \ + o->current_outputs->name, \ + o->previous_outputs->name, \ + m \ + ); +#endif //OTG_WINCE +#define TRACE_SM_CHANGE(t, m, o) \ + TRACE_MSG5(t, "SM_CHANGE: RESET: %08x SET: %08x %s (%s) %s",\ + (u32)(~o->current_inputs), \ + (u32)(o->current_inputs), \ + o->current_outputs->name, \ + o->previous_outputs->name, \ + m \ + ); + +#define TRACE_SM_NEW(t, m, o) \ + TRACE_MSG5(t, "SM_NEW: RESET: %08x SET: %08x %s (%s) %s",\ + (u32)(~o->current_inputs), \ + (u32)(o->current_inputs), \ + o->current_outputs->name, \ + o->previous_outputs->name, \ + m \ + ); + + +#define TRACE_SM_INPUTS(t, m, u, c) \ + TRACE_MSG4(t, "SM_INPUTS: RESET: %08x SET: %08x CUR: %08x %s",\ + (u32)(u >> 32), \ + (u32)(u & 0xffffffff), \ + c, \ + m \ + ) + +#endif + + + +DECLARE_MUTEX (otg_sem); + +/*! + * Output change lookup table - this maps the current output and the desired + * output to what should be done. This is just so that we don't redo changes, + * if the output does not change we do not want to re-call the output function + */ +u8 otg_output_lookup[4][4] = { + /* NC SET RESET PULSE, <--- current/ new */ + { NC, NC, NC, PULSE, }, /* NC */ + { SET, NC, SET, PULSE, }, /* SET */ + { RESET, RESET, NC, PULSE, }, /* RESET */ + { PULSE, PULSE, PULSE, PULSE, }, /* UNKNOWN */ +}; + + +/*! + * otg_new() - check OTG new inputs and select new state to be in + * + * This is used by the OTG event handler to see if the state should + * change based on the current input values. + * + * @param otg pointer to the otg instance. + * @return either the next OTG state or invalid_state if no change. + */ +static int otg_new(struct otg_instance *otg) +{ + int state = otg->state; + struct otg_test *otg_test = otg_firmware_loaded ? otg_firmware_loaded->otg_tests : NULL; + u64 input_mask = ((u64)otg->current_inputs) | ((u64)(~otg->current_inputs) << 32); // get a copy of current inputs + + UNLESS(otg_test) return invalid_state; + + TRACE_MSG3(CORE, "OTG_NEW: %s inputs: %08x %08x", otg_get_state_name(state), + (u32)(input_mask>>32&0xffffffff), (u32)(input_mask&0xffffffff)); + + /* iterate across the otg inputs table, for each entry that matches the current + * state, test the input_mask to see if a state transition is required. + */ + for (; otg_test->target != invalid_state; otg_test++) { + + u64 test1 = otg_test->test1; + u64 test2 = otg_test->test2; + u64 test3 = otg_test->test3; + + CONTINUE_UNLESS(otg_test->state == state); // skip states that don't match + + + TRACE_MSG7(CORE, "OTG_NEW: %s (%s, %d) test1: %08x %08x test2 %08x %08x", + otg_get_state_name(otg_test->state), + otg_get_state_name(otg_test->target), + otg_test->test, + (u32)(test1>>32&0xffffffff), (u32)(test1&0xffffffff), + (u32)(test2>>32&0xffffffff), (u32)(test2&0xffffffff) + ); + + /* the otg inputs table has multiple masks that define between multiple tests. Each + * test is a simple OR to check if specific inputs are present. + */ + CONTINUE_UNLESS ( + (!otg_test->test1 || ((test1 = otg_test->test1 & input_mask))) && + (!otg_test->test2 || ((test2 = otg_test->test2 & input_mask))) && + (!otg_test->test3 || ((test3 = otg_test->test3 & input_mask))) /*&& + (!otg_test->test4 || (otg_test->test4 & input_mask)) */ ); + + TRACE_MSG7(CORE, "OTG_NEW: GOTO %s test1: %08x %08x test2 %08x %08x test3 %08x %08x", + otg_get_state_name(otg_test->target), + (u32)(test1>>32&0xffffffff), (u32)(test1&0xffffffff), + (u32)(test2>>32&0xffffffff), (u32)(test2&0xffffffff), + (u32)(test3>>32&0xffff), (u32)(test3&0xffff) + ); + + return otg_test->target; + } + return invalid_state; +} + +char mbuf[96]; + +/*! + * otg_write_state_message_irq() - + * + * Send a message to the otg management application. + * + * @param msg + * @param reset + * @param inputs + */ +void otg_write_state_message_irq(char *msg, u64 reset, u32 inputs) +{ + sprintf(mbuf, "State: %s reset: %08x set: %08x inputs: %08x ", msg, (u32)(reset>>32), (u32)(reset&0xffffffff), inputs); + mbuf[95] = '\0'; + otg_message(mbuf); +} + +/*! + * otg_write_output_message_irq() - + * + * Send a message to the otg management application. + * + * @param msg + * @param val + */ +void otg_write_output_message_irq(char *msg, int val) +{ + strcpy(mbuf, "Output: "); + strncat(mbuf, msg, 64 - strlen(mbuf)); + if (val == 2) strncat(mbuf, "/", 64 - strlen(mbuf)); + mbuf[95] = '\0'; + otg_message(mbuf); +} + +/*! + * otg_write_timer_message_irq() - + * + * Send a message to the otg management application. + * + * @param msg + * @param val + */ +void otg_write_timer_message_irq(char *msg, int val) +{ + if (val < 10000) + sprintf(mbuf, "Timer: %s %d uS %u", msg, val, otg_tmr_ticks()); + else if (val < 10000000) + //sprintf(time_buf, " %d mS\n", ticks / 1000); + sprintf(mbuf, "Timer: %s %d mS %u", msg, val >> 10, otg_tmr_ticks()); + else + //sprintf(time_buf, " %d S\n", ticks / 1000000); + sprintf(mbuf, "Timer: %s %d S %u", msg, val >> 20, otg_tmr_ticks()); + + mbuf[95] = '\0'; + otg_message(mbuf); +} + +/*! + * otg_write_info_message_irq() - + * + * Send a message to the otg management application. + * + * @param msg + */ +void otg_write_info_message(char *msg) +{ + unsigned long flags; + local_irq_save (flags); + sprintf(mbuf, "Info: %s", msg); + mbuf[95] = '\0'; + otg_message(mbuf); + local_irq_restore (flags); +} + +/*! + * otg_write_reset_message_irq() - + * + * Send a message to the otg management application. + * + * @param msg + * @param val + */ +void otg_write_reset_message(char *msg, int val) +{ + unsigned long flags; + local_irq_save (flags); + sprintf(mbuf, "State: %s reset: %x", msg, val); + mbuf[95] = '\0'; + otg_message(mbuf); + local_irq_restore (flags); +} + +OTG_EXPORT_SYMBOL(otg_write_info_message); + +char otg_change_names[4] = { + '_', ' ', '/', '#', +}; + +/*! + * otg_do_event_irq() - process OTG events and determine OTG outputs to reflect new state + * + * This function is passed a mask with changed input values. This + * is passed to the otg_new() function to see if the state should + * change. If it changes then the output functions required for + * the new state are called. + * + * @param otg pointer to the otg instance. + * @param inputs input mask + * @param tag trace tag to use for tracing + * @param msg message for tracing + * @return non-zero if state changed. + */ +static int otg_do_event_irq(struct otg_instance *otg, u64 inputs, otg_tag_t tag, char *msg) +{ + u32 current_inputs = otg->current_inputs; + int current_state = otg->state; + u32 inputs_set = (u32)(inputs & 0xffffffff); // get changed inputs that SET something + u32 inputs_reset = (u32) (inputs >> 32); // get changed inputs that RESET something + int target; + + RETURN_ZERO_UNLESS(otg && inputs);; + TRACE_SM_INPUTS(tag, msg, inputs, current_inputs); + + /* Special Overrides - These are special tests that satisfy specific injunctions from the + * OTG Specification. + */ + if (inputs_set & inputs_reset) { // verify that there is no overlap between SET and RESET masks + TRACE_MSG1(CORE, "OTG_EVENT: ERROR attempting to set and reset the same input: %08x", inputs_set & inputs_reset); + return 0; + } + /* don't allow bus_req and bus_drop to be set at the same time + */ + if ((inputs_set & (bus_req | bus_drop)) == (bus_req | bus_drop) ) { + TRACE_MSG2(CORE, "OTG_EVENT: ERROR attempting to set both bus_req and bus_drop: %08x %08x", + inputs_set, (bus_req | bus_drop)); + return 0; + } + /* set bus_drop_ if bus_req, set bus_req_ if bus_drop + */ + if (inputs_set & bus_req) { + inputs |= bus_drop_; + TRACE_MSG1(CORE, "OTG_EVENT: forcing bus_drop/: %08x", inputs_set); + } + if (inputs_set & bus_drop) { + inputs |= bus_req_; + TRACE_MSG1(CORE, "OTG_EVENT: forcing bus_req/: %08x", inputs_set); + } + + otg->current_inputs &= ~inputs_reset; + otg->current_inputs |= inputs_set; + TRACE_MSG3(CORE, "OTG_EVENT: reset: %08x set: %08x inputs: %08x", inputs_reset, inputs_set, otg->current_inputs); + + TRACE_SM_CHANGE(tag, msg, otg); + + RETURN_ZERO_IF(otg->active); + + otg->active++; + + /* Search the state input change table to see if we need to change to a new state. + * This may take several iterations. + */ + while (invalid_state != (target = otg_new(otg))) { + + struct otg_state *otg_state; + int original_state = otg->state; + u64 current_outputs; + u64 output_results; + u64 new_outputs; + int i; + + /* if previous output started timer, then cancel timer before proceeding + */ + if ((otg_state = otg->current_outputs) && otg_state->tmout && otg->start_timer) { + TRACE_MSG0(CORE, "reseting timer"); + otg->start_timer(otg, 0); + } + + BREAK_UNLESS(otg_firmware_loaded); + + BREAK_UNLESS(target < otg_firmware_loaded->number_of_states); + + otg_state = otg_firmware_loaded->otg_states + target; + + BREAK_UNLESS(otg_state->name); + + otg->previous = otg->state; + otg->state = target; + otg->tickcount = otg_tmr_ticks(); + + + otg->previous_outputs= otg->current_outputs; + //otg->current_outputs = NULL; + + + /* A matching input table rule has been found, we are transitioning to a new + * state. We need to find the new state entry in the output table. + * XXX this could be a table lookup instead of linear search. + */ + + current_outputs = otg->outputs; + new_outputs = otg_state->outputs; + output_results = 0; + + TRACE_SM_NEW(tag, msg, otg); + + /* reset any inputs that the new state want's reset + */ + otg_do_event_irq(otg, otg_state->reset | TMOUT_, tag, otg_state->name); + + otg_write_state_message_irq(otg_state->name, otg_state->reset, otg->current_inputs); + +#if 1 + switch (target) { /* C.f. 6.6.1.12 b_conn reset */ + case otg_disabled: + otg->current_inputs = 0; + otg->outputs = 0; + break; + default: + //otg_event_irq(otg, not(b_conn), "a_host/ & a_suspend/: set b_conn/"); + break; + } +#endif + TRACE_MSG5(CORE, "OTG_EVENT: CALLING OUTPUT MAX:%d OUTPUTS CURRENT: %08x %08x NEW: %08x %08x", + MAX_OUTPUTS, + (u32)(current_outputs >> 32), + (u32)(current_outputs & 0xffffffff), + (u32)(new_outputs >> 32), + (u32)(new_outputs & 0xffffffff) + ); + + /* Iterate across the outputs bitmask, calling the appropriate functions + * to make required changes. The current outputs are saved and we DO NOT + * make calls to change output values until they change again. + */ + for (i = 0; i < MAX_OUTPUTS; i++) { + + u8 current_output = (u8)(current_outputs & 0x3); + u8 new_output = (u8)(new_outputs & 0x3); + u8 changed = otg_output_lookup[new_output][current_output]; + + output_results >>= 2; + + switch (changed) { + case NC: + output_results |= ((current_outputs & 0x3) << (MAX_OUTPUTS * 2)); + break; + case SET: + case RESET: + output_results |= (((u64)changed) << (MAX_OUTPUTS * 2)); + case PULSE: + + + otg_write_output_message_irq(otg_output_names[i], changed); + + //TRACE_MSG3(CORE, "OTG_EVENT: CHECKING OUTPUT %s %d %s", + // otg_state->name, i, otg_output_names[i]); + + BREAK_UNLESS(otg->otg_output_ops[i]); // Check if we have an output routine + + TRACE_MSG7(CORE, "OTG_EVENT: CALLING OUTPUT %s %d %s%c cur: %02x new: %02x chng: %02x", + otg_state->name, i, otg_output_names[i], + otg_change_names[changed&0x3], + current_output, new_output, + changed); + + otg->otg_output_ops[i](otg, changed); // Output changed, call function to do it + + //TRACE_MSG2(CORE, "OTG_EVENT: OUTPUT FINISED %s %s", otg_state->name, otg_output_names[i]); + break; + } + + current_outputs >>= 2; + new_outputs >>= 2; + } + + output_results >>= 2; + otg->outputs = output_results; + otg->current_outputs = otg_state; + + //TRACE_MSG2(CORE, "OTG_EVENT: STATE: otg->current_outputs: %08x %08x", + // (u32)(otg->outputs & 0xffffffff), (u32)(otg->outputs >> 32)); + + if (((otg->outputs & pcd_en_out_set) == pcd_en_out_set) && + ((otg->outputs & hcd_en_out_set) == hcd_en_out_set)) + { + TRACE_MSG0(CORE, "WARNING PCD_EN and HCD_EN both set"); + printk(KERN_INFO "%s: WARNING PCD_EN and HCD_EN both set\n", __FUNCTION__); + } + + /* start timer? + */ + CONTINUE_UNLESS(otg_state->tmout); + TRACE_MSG1(CORE, "setting timer: %d", otg_state->tmout); + if (otg->start_timer) { + otg_write_timer_message_irq(otg_state->name, otg_state->tmout); + otg->start_timer(otg, otg_state->tmout); + } + else { + //TRACE_MSG0(CORE, "NO TIMER"); + otg_do_event_irq(otg, TMOUT_, CORE, otg_state->name); + } + + } + //TRACE_MSG0(CORE, "finishing"); + otg->active = 0; + + if (current_state == otg->state) + TRACE_MSG0(CORE, "OTG_EVENT: NOT CHANGED"); + + otg->active = 0; + return 0; +} + +/*! + * otg_write_input_message_irq() - + + * @param inputs input mask + * @param tag trace tag to use for tracing + * @param msg message for tracing + */ +void otg_write_input_message_irq(u64 inputs, otg_tag_t tag, char *msg) +{ + u32 reset = (u32)(inputs >> 32); + u32 set = (u32) (inputs & 0xffffffff); + + char buf[64]; + //snprintf(buf, sizeof(buf), "Input: %08x %08x %s", *inputs >> 32, *inputs & 0xffffffff, msg ? msg : "NULL"); + TRACE_MSG3(tag, "Inputs: %08x %08x %s", set, reset, msg ? msg : "NULL"); + #ifdef OTG_WINCE + sprintf(buf, "Inputs: %08x %08x %s", reset, set, msg ? msg : "NULL"); + #else /* OTG_WINCE */ + snprintf(buf, sizeof(buf), "Inputs: %08x %08x %s", reset, set, msg ? msg : "NULL"); + #endif /* OTG_WINCE */ + buf[63] = '\0'; + otg_message(buf); +} + + +/*! + * otg_event_input_message_irq() - + * @param otg pointer to the otg instance. + * @param inputs input mask + * @param tag trace tag to use for tracing + * @param msg message for tracing + * @return non-zero if state changed. + */ +int otg_event_irq(struct otg_instance *otg, u64 inputs, otg_tag_t tag, char *msg) +{ + TRACE_SM_INPUTS(tag, msg, inputs, otg->current_inputs); + //otg_write_input_message_irq((u32)(inputs >> 32), (u32) (inputs & 0xffffffff), tag, msg); + otg_write_input_message_irq(inputs, tag, msg); + return otg_do_event_irq(otg, inputs, tag, msg); +} + +/*! + * otg_event() - + * @param otg pointer to the otg instance. + * @param inputs input mask + * @param tag trace tag to use for tracing + * @param msg message for tracing + * @return non-zero if state changed. + */ +int otg_event(struct otg_instance *otg, u64 inputs, otg_tag_t tag, char *msg) +{ + int rc; + unsigned long flags; + RETURN_ZERO_UNLESS(otg); + local_irq_save (flags); + rc = otg_event_irq(otg, inputs, tag, msg); + local_irq_restore (flags); + return rc; +} + + +/*! + * otg_event_set_irq() - + * @param otg pointer to the otg instance. + * @param changed + * @param flag + * @param input input mask + * @param tag trace tag to use for tracing + * @param msg message for tracing + * @return non-zero if state changed. + */ +int otg_event_set_irq(struct otg_instance *otg, int changed, int flag, u32 input, otg_tag_t tag, char *msg) +{ + RETURN_ZERO_UNLESS(changed); + TRACE_MSG4(tag, "%s: %08x changed: %d flag: %d", msg, input, changed, flag); + return otg_event(otg, flag ? input : _NOT(input), tag, msg); +} + + +/*! + * otg_init() - create and initialize otg instance + * + * Called to initialize the OTG state machine. This will cause the state + * to change from the invalid_state to otg_disabled. + * + * If the device has the OCD_CAPABILITIES_AUTO flag set then + * an initial enable_otg event is generated. This will cause + * the state to move from otg_disabled to otg_enabled. The + * requird drivers will be initialized by calling the appropriate + * output functions: + * + * - pcd_init_func + * - hcd_init_func + * - tcd_init_func + * + * @param otg pointer to the otg instance. + */ +void otg_init (struct otg_instance *otg) +{ + DOWN(&otg_sem); + otg->outputs = tcd_init_out_ | pcd_init_out_ | hcd_init_out_; + + /* This will move the state machine into the otg_disabled state + */ + otg_event(otg, enable_otg, CORE, "enable_otg"); // XXX + + if (otg_ocd_ops->capabilities & OCD_CAPABILITIES_AUTO) + otg_event(otg, AUTO, CORE, "AUTO"); // XXX + + UP(&otg_sem); +} + + +/*! + * otg_exit() + * + * This is called by the driver that started the state machine to + * cause it to exit. The state will move to otg_disabled. + * + * The appropriate output functions will be called to disable the + * peripheral drivers. + * + * @param otg pointer to the otg instance. + */ +void otg_exit (struct otg_instance *otg) +{ + //TRACE_MSG0(CORE, "DOWN OTG_SEM"); + DOWN(&otg_sem); + + //TRACE_MSG0(CORE, "OTG_EXIT"); + //otg_event(otg, exit_all | not(a_bus_req) | a_bus_drop | not(b_bus_req), "tcd_otg_exit"); + + otg_event(otg, enable_otg_, CORE, "enable_otg_"); + + while (otg->state != otg_disabled) { + // TRACE_MSG pointless here, module will be gon before we can look at it. + #ifdef OTG_WINCE + //DEBUGMSG(KERN_ERR"%s: waiting for state machine to disable\n", __FUNCTION__); + #else /* OTG_WINCE */ + printk(KERN_ERR"%s: waiting for state machine to disable\n", __FUNCTION__); + #endif /* OTG_WINCE */ + SCHEDULE_TIMEOUT(10 * HZ); + } + //TRACE_MSG0(CORE, "UP OTG_SEM"); + UP(&otg_sem); +} + + +OTG_EXPORT_SYMBOL(otg_event); +OTG_EXPORT_SYMBOL(otg_event_irq); +OTG_EXPORT_SYMBOL(otg_init); +OTG_EXPORT_SYMBOL(otg_exit); +OTG_EXPORT_SYMBOL(otg_event_set_irq); + +/* ********************************************************************************************* */ + +/*! + * otg_tcd_init_func() - + * + * This is a default tcd_init_func. It will be used + * if no other is provided. + * + * @param otg pointer to the otg instance. + * @param flag set or reset + */ +void otg_tcd_init_func (struct otg_instance *otg, u8 flag) +{ + otg_event(otg, TCD_OK, CORE, "TCD_OK"); +} + +/*! + * otg_pcd_init_func() - + * + * This is a default pcd_init_func. It will be used + * if no other is provided. + * + * @param otg pointer to the otg instance. + * @param flag set or reset + */ +void otg_pcd_init_func (struct otg_instance *otg, u8 flag) +{ + otg_event(otg, PCD_OK, CORE, "PCD_OK"); +} + +/*! + * otg_hcd_init_func() - + * + * This is a default hcd_init_func. It will be used + * if no other is provided. + * + * @param otg pointer to the otg instance. + * @param flag set or reset + */ +void otg_hcd_init_func (struct otg_instance *otg, u8 flag) +{ + otg_event(otg, HCD_OK, CORE, "HCD_OK"); +} + +/*! + * otg_ocd_init_func() - + * + * This is a default hcd_init_func. It will be used + * if no other is provided. + * + * @param otg pointer to the otg instance. + * @param flag set or reset + */ +void otg_ocd_init_func (struct otg_instance *otg, u8 flag) +{ + otg_event(otg, HCD_OK, CORE, "OCD_OK"); +} + +/*! + * otg_hcd_set_ops() - + * @param hcd_ops - host operations table to use + */ +struct hcd_instance * otg_set_hcd_ops(struct hcd_ops *hcd_ops) +{ + otg_hcd_ops = hcd_ops; + if (hcd_ops) { + otg_instance_private.otg_output_ops[HCD_INIT_OUT] = otg_hcd_ops->hcd_init_func; + otg_instance_private.otg_output_ops[HCD_EN_OUT] = otg_hcd_ops->hcd_en_func; + otg_instance_private.otg_output_ops[LOC_SOF_OUT] = otg_hcd_ops->loc_sof_func; + otg_instance_private.otg_output_ops[LOC_SUSPEND_OUT] = otg_hcd_ops->loc_suspend_func; + otg_instance_private.otg_output_ops[REMOTE_WAKEUP_EN_OUT] = otg_hcd_ops->remote_wakeup_en_func; + otg_instance_private.otg_output_ops[HNP_EN_OUT] = otg_hcd_ops->hnp_en_func; + } + else + otg_instance_private.otg_output_ops[HCD_INIT_OUT] = + otg_instance_private.otg_output_ops[HCD_EN_OUT] = + otg_instance_private.otg_output_ops[LOC_SOF_OUT] = + otg_instance_private.otg_output_ops[LOC_SUSPEND_OUT] = + otg_instance_private.otg_output_ops[REMOTE_WAKEUP_EN_OUT] = + otg_instance_private.otg_output_ops[HNP_EN_OUT] = NULL; + + + UNLESS (otg_instance_private.otg_output_ops[HCD_INIT_OUT]) + otg_instance_private.otg_output_ops[HCD_INIT_OUT] = otg_hcd_init_func; + + return &hcd_instance_private; +} + +/*! + * otg_ocd_set_ops() - + * + * @param ocd_ops - ocd operations table to use + */ +struct ocd_instance * otg_set_ocd_ops(struct ocd_ops *ocd_ops) +{ + otg_ocd_ops = ocd_ops; + if (ocd_ops) { + otg_instance_private.otg_output_ops[OCD_INIT_OUT] = otg_ocd_ops->ocd_init_func; + otg_instance_private.start_timer = ocd_ops->start_timer; + } + else { + otg_instance_private.otg_output_ops[OCD_INIT_OUT] = NULL; + otg_instance_private.start_timer = NULL; + } + + UNLESS (otg_instance_private.otg_output_ops[OCD_INIT_OUT]) + otg_instance_private.otg_output_ops[OCD_INIT_OUT] = otg_ocd_init_func; + + return &ocd_instance_private; +} + +/*! + * otg_pcd_set_ops() - + * @param pcd_ops - pcd operations table to use + */ +struct pcd_instance * otg_set_pcd_ops(struct pcd_ops *pcd_ops) +{ + otg_pcd_ops = pcd_ops; + if (pcd_ops) { + otg_instance_private.otg_output_ops[PCD_INIT_OUT] = otg_pcd_ops->pcd_init_func; + otg_instance_private.otg_output_ops[PCD_EN_OUT] = otg_pcd_ops->pcd_en_func; + otg_instance_private.otg_output_ops[REMOTE_WAKEUP_OUT] = otg_pcd_ops->remote_wakeup_func; + } + else + otg_instance_private.otg_output_ops[PCD_INIT_OUT] = + otg_instance_private.otg_output_ops[PCD_EN_OUT] = + otg_instance_private.otg_output_ops[REMOTE_WAKEUP_OUT] = NULL; + + UNLESS (otg_instance_private.otg_output_ops[PCD_INIT_OUT]) + otg_instance_private.otg_output_ops[PCD_INIT_OUT] = otg_pcd_init_func; + + return &pcd_instance_private; +} + +/*! + * otg_tcd_set_ops() - + * @param tcd_ops - tcd operations table to use + */ +struct tcd_instance * otg_set_tcd_ops(struct tcd_ops *tcd_ops) +{ + otg_tcd_ops = tcd_ops; + if (tcd_ops) { + otg_instance_private.otg_output_ops[TCD_INIT_OUT] = otg_tcd_ops->tcd_init_func; + otg_instance_private.otg_output_ops[TCD_EN_OUT] = otg_tcd_ops->tcd_en_func; + otg_instance_private.otg_output_ops[CHRG_VBUS_OUT] = otg_tcd_ops->chrg_vbus_func; + otg_instance_private.otg_output_ops[DRV_VBUS_OUT] = otg_tcd_ops->drv_vbus_func; + otg_instance_private.otg_output_ops[DISCHRG_VBUS_OUT] = otg_tcd_ops->dischrg_vbus_func; + otg_instance_private.otg_output_ops[DP_PULLUP_OUT] = otg_tcd_ops->dp_pullup_func; + otg_instance_private.otg_output_ops[DM_PULLUP_OUT] = otg_tcd_ops->dm_pullup_func; + otg_instance_private.otg_output_ops[DP_PULLDOWN_OUT] = otg_tcd_ops->dp_pulldown_func; + otg_instance_private.otg_output_ops[DM_PULLDOWN_OUT] = otg_tcd_ops->dm_pulldown_func; + //otg_instance_private.otg_output_ops[PERIPHERAL_HOST] = otg_tcd_ops->peripheral_host_func; + otg_instance_private.otg_output_ops[CLR_OVERCURRENT_OUT] = otg_tcd_ops->overcurrent_func; + otg_instance_private.otg_output_ops[DM_DET_OUT] = otg_tcd_ops->dm_det_func; + otg_instance_private.otg_output_ops[DP_DET_OUT] = otg_tcd_ops->dp_det_func; + otg_instance_private.otg_output_ops[CR_DET_OUT] = otg_tcd_ops->cr_det_func; + otg_instance_private.otg_output_ops[AUDIO_OUT] = otg_tcd_ops->audio_func; + otg_instance_private.otg_output_ops[CHARGE_PUMP_OUT] = otg_tcd_ops->charge_pump_func; + otg_instance_private.otg_output_ops[BDIS_ACON_OUT] = otg_tcd_ops->bdis_acon_func; + otg_instance_private.otg_output_ops[MX21_VBUS_DRAIN] = otg_tcd_ops->mx21_vbus_drain_func; + otg_instance_private.otg_output_ops[ID_PULLDOWN_OUT] = otg_tcd_ops->id_pulldown_func; + otg_instance_private.otg_output_ops[UART_OUT] = otg_tcd_ops->uart_func; + otg_instance_private.otg_output_ops[MONO_OUT] = otg_tcd_ops->mono_func; + } + else + otg_instance_private.otg_output_ops[TCD_INIT_OUT] = + otg_instance_private.otg_output_ops[TCD_EN_OUT] = + otg_instance_private.otg_output_ops[CHRG_VBUS_OUT] = + otg_instance_private.otg_output_ops[DRV_VBUS_OUT] = + otg_instance_private.otg_output_ops[DISCHRG_VBUS_OUT] = + otg_instance_private.otg_output_ops[DP_PULLUP_OUT] = + otg_instance_private.otg_output_ops[DM_PULLUP_OUT] = + otg_instance_private.otg_output_ops[DP_PULLDOWN_OUT] = + otg_instance_private.otg_output_ops[DM_PULLDOWN_OUT] = + //otg_instance_private.otg_output_ops[PERIPHERAL_HOST] = + otg_instance_private.otg_output_ops[CLR_OVERCURRENT_OUT] = + otg_instance_private.otg_output_ops[DM_DET_OUT] = + otg_instance_private.otg_output_ops[DP_DET_OUT] = + otg_instance_private.otg_output_ops[CR_DET_OUT] = + otg_instance_private.otg_output_ops[AUDIO_OUT] = + otg_instance_private.otg_output_ops[CHARGE_PUMP_OUT] = + otg_instance_private.otg_output_ops[BDIS_ACON_OUT] = + otg_instance_private.otg_output_ops[MX21_VBUS_DRAIN] = + otg_instance_private.otg_output_ops[ID_PULLDOWN_OUT] = + otg_instance_private.otg_output_ops[UART_OUT] = + otg_instance_private.otg_output_ops[MONO_OUT] = NULL; + + UNLESS (otg_instance_private.otg_output_ops[TCD_INIT_OUT]) + otg_instance_private.otg_output_ops[TCD_INIT_OUT] = otg_tcd_init_func; + + return &tcd_instance_private; +} + +/*! + * otg_set_usbd_ops() - + * @param usbd_ops + */ +int otg_set_usbd_ops(struct usbd_ops *usbd_ops) +{ + otg_usbd_ops = usbd_ops; + return 0; +} + + +/*! + * otg_get_trace_info() + */ +void otg_get_trace_info(otg_trace_t *p) +{ + p->id_gnd = otg_instance_private.current_inputs & ID_GND ? 1 : 0; + p->va.s.ticks = (otg_ocd_ops && otg_ocd_ops->ticks) ? otg_ocd_ops->ticks () : 0; + p->va.s.interrupts = (otg_ocd_ops && otg_ocd_ops->interrupts) ? otg_ocd_ops->interrupts : 0; + p->va.s.framenum = (otg_pcd_ops && otg_pcd_ops->framenum) ? otg_pcd_ops->framenum() : 0; + p->in_interrupt = in_interrupt(); +} + +/*! + * otg_tmr_ticks() - + * @return number of ticks. + */ +u64 otg_tmr_ticks(void) +{ + return (otg_ocd_ops && otg_ocd_ops->ticks) ? otg_ocd_ops->ticks () : 0; +} + +/*! + * otg_tmr_elapsed() - + * @param t1 + * @param t2 + * @return number of uSecs between t1 and t2 ticks. + */ +u64 otg_tmr_elapsed(u64 *t1, u64 *t2) +{ + return (otg_ocd_ops && otg_ocd_ops->elapsed) ? otg_ocd_ops->elapsed (t1, t2) : 0; +} + +/*! + * otg_tmr_framenum() - + * @return the current USB frame number. + */ +u16 otg_tmr_framenum(void) +{ + return (otg_pcd_ops && otg_pcd_ops->framenum) ? otg_pcd_ops->framenum() : 0; +} + +/*! + * otg_tmr_interrupts() - + * @return the current number of interrupts. + */ +u32 otg_tmr_interrupts(void) +{ + return (otg_ocd_ops && otg_ocd_ops->interrupts) ? otg_ocd_ops->interrupts : 0; +} + + +OTG_EXPORT_SYMBOL(otg_hcd_ops); +OTG_EXPORT_SYMBOL(otg_ocd_ops); +OTG_EXPORT_SYMBOL(otg_pcd_ops); +OTG_EXPORT_SYMBOL(otg_tcd_ops); +OTG_EXPORT_SYMBOL(otg_set_hcd_ops); +OTG_EXPORT_SYMBOL(otg_set_ocd_ops); +OTG_EXPORT_SYMBOL(otg_set_pcd_ops); +OTG_EXPORT_SYMBOL(otg_set_tcd_ops); +OTG_EXPORT_SYMBOL(otg_set_usbd_ops); +OTG_EXPORT_SYMBOL(otg_tmr_ticks); +OTG_EXPORT_SYMBOL(otg_tmr_elapsed); +OTG_EXPORT_SYMBOL(otg_write_input_message_irq); + + +#if defined(OTG_WINCE) +#else /* defined(OTG_WINCE) */ +#endif /* defined(OTG_WINCE) */ + diff -uNr linux/drivers/no-otg/otgcore/sources linux/drivers/otg/otgcore/sources --- linux/drivers/no-otg/otgcore/sources 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/sources 2006-09-01 21:41:34.000000000 +0200 @@ -0,0 +1,41 @@ +!if 0 +Copyright (c) Microsoft Corporation. All rights reserved. +!endif +!if 0 +Use of this source code is subject to the terms of the Microsoft end-user +license agreement (EULA) under which you licensed this SOFTWARE PRODUCT. +If you did not accept the terms of the EULA, you are not authorized to use +this source code. For a copy of the EULA, please see the LICENSE.RTF on your +install media. +!endif + +SYNCHRONIZE_DRAIN=1 + +INCLUDES= \ + $(_PUBLICROOT)\common\ddk\inc; $(_PUBLICROOT)\common\oak\inc; $(_PUBLICROOT)\common\sdk\inc; \ + $(_WINCEROOT)\public\common\oak\DRIVERS\otg; + + +TARGETNAME=otgcore +TARGETTYPE=LIBRARY + + +SOURCES= \ + core-init-w42.c \ + otg.c \ + otg-mesg.c \ + otg-mesg-w42.c \ + otg-ops.c \ + usbp-bops.c \ + usbp-fops.c \ + otg-fw.c \ + otg-fw-df.c \ + otg-fw-mn.c \ + usbp.c \ + otg-trace.c \ + otg-trace-w42.c \ + ep0.c \ + +# usbp-procfs.c \ + + diff -uNr linux/drivers/no-otg/otgcore/usbp-bops.c linux/drivers/otg/otgcore/usbp-bops.c --- linux/drivers/no-otg/otgcore/usbp-bops.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/usbp-bops.c 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,1857 @@ +/* + * otg/otgcore/usbp-bops.c - USB Device Prototype + * + * Copyright (c) 2004-2005 Belcarra + * + * By: + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @file otg/otgcore/usbp-bops.c + * @brief Bus Interface related functions. + * + * This implements the functions used to implement Peripheral Controller Drivers (PCD.) + * + * @ingroup USBP + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* ********************************************************************************************* */ +/*! + * Note that all of the list functions overlapped operation using the usbd_bus_sem; + * + * usbd_device_bh + * + * usbd_register_bus + * usbd_deregister_bus + * usbd_enable_function_irq + * usbd_disable_function + */ +DECLARE_MUTEX(usbd_bus_sem); +void usbd_device_bh (void *data); + +/* ********************************************************************************************* */ +/*! + * Function driver enable/disable + * + * Called by usbd_enable_function/usbd_disable_function to call the selected + * function drivers function_enable or function_disable function. + */ +int usbd_strings_init (void); +void usbd_strings_exit(void); + +extern struct usbd_function_instance *usbd_find_function (char *name); + +struct usbd_bus_instance *usbd_bus_instance; +OTG_EXPORT_SYMBOL(usbd_bus_instance); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Bus Interface Management: + * usbd_register_bus() + * usbd_deregister_bus() + */ + +/*! urb_link_init() - initialize urb linke + * + * Initialize an urb_link to be a single element list. + * If the urb_link is being used as a distinguished list head + * the list is empty when the head is the only link in the list. + * + * @param ul link to urb + */ +static INLINE void urb_link_init (urb_link * ul) +{ + ul->prev = ul->next = ul; +} + +static struct usbd_function_operations ep0_ops; +struct usbd_function_driver ep0_driver; + + +/*! + * usbd_register_bus() - called by a USB BUS INTERFACE driver to register a bus driver + * + * Used by a USB Bus interface driver to register itself with the usb device layer. + * + * @param driver + * @param ep0_wMaxPacketSize + * @return non-zero if error + */ +struct usbd_bus_instance *usbd_register_bus (struct usbd_bus_driver *driver, int ep0_wMaxPacketSize) +{ + int i; + struct usbd_bus_instance *bus = NULL; + + DOWN(&usbd_bus_sem); + + THROW_IF(usbd_bus_instance, error); + THROW_IF((bus = CKMALLOC (sizeof (struct usbd_bus_instance), GFP_ATOMIC)) == NULL, error); + + bus->driver = driver; + bus->endpoints = bus->driver->max_endpoints; + bus->otg_bmAttributes = bus->driver->otg_bmAttributes; + + THROW_IF(!(bus->endpoint_array = CKMALLOC(sizeof (struct usbd_endpoint_instance) * bus->endpoints, GFP_ATOMIC)), error); + + for (i = 0; i < bus->endpoints; i++) { + struct usbd_endpoint_instance *endpoint = bus->endpoint_array + i; + endpoint->physical_endpoint = i; + urb_link_init (&endpoint->rdy); + urb_link_init (&endpoint->tx); + } + + urb_link_init (&bus->finished); + + ZERO(ep0_ops); + ZERO(ep0_driver); + ep0_driver.name = "EP0"; + ep0_driver.fops = &ep0_ops; + + + THROW_IF((bus->ep0 = CKMALLOC (sizeof (struct usbd_function_instance), GFP_ATOMIC)) == NULL, error); + bus->ep0->function_driver = &ep0_driver; + THROW_IF(!(bus->ep0->endpoint_map_array = CKMALLOC(sizeof(struct usbd_endpoint_map) * 2, GFP_KERNEL)), error); + bus->ep0->endpoint_map_array[0].wMaxPacketSize[0] = ep0_wMaxPacketSize; + bus->ep0->endpoint_map_array[0].wMaxPacketSize[1] = ep0_wMaxPacketSize; + + bus->device_state = STATE_CREATED; + bus->status = USBD_OPENING; + + usbd_bus_instance = bus; + + CATCH(error) { + if (bus) { + if (bus->endpoint_array) + LKFREE(bus->endpoint_array); + LKFREE(bus); + } + bus->endpoints = 0; + #if defined(OTG_LINUX) + printk(KERN_INFO"%s: FAILED\n", __FUNCTION__); + #endif /* defined(OTG_LINUX) */ + #if defined(OTG_WINCE) + DEBUGMSG(ZONE_INIT, (_T("usbd_register_bus: FAILED\n"))); + #endif /* defined(OTG_LINUX) */ + bus = NULL; + } + + UP(&usbd_bus_sem); + return bus; +} + +/*! + * usbd_deregister_bus() - called by a USB BUS INTERFACE driver to deregister a bus driver + * + * Used by a USB Bus interface driver to de-register itself with the usb device + * layer. + * + * @param bus + */ +void usbd_deregister_bus (struct usbd_bus_instance *bus) +{ + DOWN(&usbd_bus_sem); + + usbd_bus_instance = NULL; + + if (bus->ep0) { + if (bus->ep0->endpoint_map_array) + LKFREE(bus->ep0->endpoint_map_array); + LKFREE(bus->ep0); + } + + LKFREE (bus->arg); + LKFREE (bus->endpoint_array); + LKFREE (bus); + bus->endpoints = 0; + UP(&usbd_bus_sem); +} + +OTG_EXPORT_SYMBOL(usbd_register_bus); +OTG_EXPORT_SYMBOL(usbd_deregister_bus); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Function Management: + * usbd_enable_function() + * usbd_disable_function() + * + */ + +/*! + * alloc_function_alternates() - allocate alternate instance array + * + * Return a pointer to an array of alternate instances. Each instance contains a pointer + * to a filled in alternate descriptor and a pointer to the endpoint descriptor array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + * @param bInterfaceNumber + * @param interface_description + * @param alternates + * @param alternate_description_array + * @return pointer to alternate instance array + */ +static struct usbd_alternate_instance * +alloc_function_alternates (int bInterfaceNumber, + struct usbd_interface_description *interface_description, + int alternates, struct usbd_alternate_description *alternate_description_array) +{ + int alternate_num; + struct usbd_alternate_instance *alternate_instance_array = NULL; + + // allocate array of alternate instances + RETURN_NULL_IF(!(alternate_instance_array = (struct usbd_alternate_instance *) + CKMALLOC (sizeof (struct usbd_alternate_instance) * alternates, GFP_KERNEL))); + + // iterate across the alternate descriptions + for (alternate_num = 0; alternate_num < alternates; alternate_num++) { + + struct usbd_alternate_description *alternate_description = alternate_description_array + alternate_num; + struct usbd_alternate_instance *alternate_instance = alternate_instance_array + alternate_num; + struct usbd_interface_descriptor *interface_descriptor; + + interface_descriptor = alternate_description->interface_descriptor; + if (interface_descriptor->bInterfaceNumber != bInterfaceNumber) { + //TRACE_MSG2(USBD, "bInterfaceNumber mis-match: %d should be %d", + // interface_descriptor->bInterfaceNumber, bInterfaceNumber); + LKFREE(alternate_instance_array); + return NULL; + } + + interface_descriptor->bNumEndpoints = alternate_description->endpoints; + interface_descriptor->iInterface = usbd_alloc_string (alternate_description->iInterface ); + + alternate_instance->class_list = alternate_description->class_list; + alternate_instance->classes = alternate_description->classes; + alternate_instance->endpoint_list = alternate_description->endpoint_list; + alternate_instance->endpoint_indexes = alternate_description->endpoint_indexes; + + // save number of alternates, classes and endpoints for this alternate + alternate_instance->endpoints = alternate_description->endpoints; + alternate_instance->interface_descriptor = interface_descriptor; + } + return alternate_instance_array; +} + +/*! + * alloc_function_interfaces() - allocate interface instance array + * + * Return a pointer to an array of interface instances. Each instance contains a pointer + * to a filled in interface descriptor and a pointer to the endpoint descriptor array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + * + * @param bNumInterfaces + * @param interface_description_array + * @return interface instance + */ +static struct usbd_interfaces_instance * +alloc_function_interfaces (int bNumInterfaces, struct usbd_interface_description *interface_description_array) +{ + int interface_num; + struct usbd_interfaces_instance *interfaces_instance_array = NULL; + + // allocate array of interface instances + RETURN_NULL_IF(!(interfaces_instance_array = (struct usbd_interfaces_instance *) + CKMALLOC (sizeof (struct usbd_interfaces_instance) * bNumInterfaces, GFP_KERNEL))); + + // iterate across the interface descriptions + for (interface_num = 0; interface_num < bNumInterfaces; interface_num++) { + + struct usbd_interface_description *interface_description = interface_description_array + interface_num; + struct usbd_interfaces_instance *interfaces_instance = interfaces_instance_array + interface_num; + + interfaces_instance->alternates_instance_array = + alloc_function_alternates (interface_num, interface_description, + interface_description->alternates, interface_description->alternate_list); + interfaces_instance->alternates = interface_description->alternates; + } + return interfaces_instance_array; +} + + +/*! + * alloc_function_configurations() - allocate configuration instance array + * + * Return a pointer to an array of configuration instances. Each instance contains a pointer + * to a filled in configuration descriptor and a pointer to the interface instances array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + * + * @param bNumConfigurations + * @param configuration_description_array + * @param otg_descriptor + * @param remote_wakeup_supported + * @param bMaxPower + * @return configuration instance + */ +static struct usbd_configuration_instance * +alloc_function_configurations (struct usbd_function_driver *function_driver, + int remote_wakeup_supported, + int bMaxPower) +{ + + int bNumConfigurations = function_driver->bNumConfigurations; + struct usbd_configuration_description *configuration_description_array = function_driver->configuration_description; + struct usbd_otg_descriptor *otg_descriptor = function_driver->otg_descriptor; + int config_num; + struct usbd_configuration_instance *configuration_instance_array = NULL; + + //TRACE_MSG2(USBD, "remote: %x bMaxPower: %x", remote_wakeup_supported, bMaxPower); + + // allocate array + RETURN_NULL_IF(!(configuration_instance_array = (struct usbd_configuration_instance *) + CKMALLOC (sizeof (struct usbd_configuration_instance) * bNumConfigurations, GFP_KERNEL))); + // fill in array + for (config_num = 0; config_num < bNumConfigurations; config_num++) { + int interface_num; + int length; + + struct usbd_configuration_description *configuration_description = configuration_description_array + config_num; + struct usbd_configuration_descriptor *configuration_descriptor; + + configuration_descriptor = configuration_description->configuration_descriptor; + + // setup fields in configuration descriptor + // XXX c.f. 9.4.7 zero is default, so config MUST BE 1 to n, not 0 to n-1 + // XXX N.B. the configuration itself is fetched 0 to n-1. + + configuration_descriptor->bConfigurationValue = config_num + 1; + configuration_descriptor->wTotalLength = 0; + configuration_descriptor->bNumInterfaces = function_driver->bNumInterfaces; + configuration_descriptor->iConfiguration = usbd_alloc_string (configuration_description->iConfiguration); + configuration_descriptor->bMaxPower = bMaxPower; + configuration_descriptor->bmAttributes = BMATTRIBUTE_RESERVED | + bMaxPower ? 0 : BMATTRIBUTE_SELF_POWERED | + remote_wakeup_supported ? BMATTRIBUTE_REMOTE_WAKEUP : 0; + + // save the configuration descriptor in the configuration instance array + configuration_instance_array[config_num].configuration_descriptor = configuration_descriptor; + + RETURN_NULL_IF (!(configuration_instance_array[config_num].interfaces_instance_array = + alloc_function_interfaces (function_driver->bNumInterfaces, + function_driver->interface_list))); + + length = sizeof(struct usbd_configuration_descriptor); + + for (interface_num = 0; interface_num < configuration_descriptor->bNumInterfaces; interface_num++) { + + int alternate_num; + struct usbd_interfaces_instance *interfaces_instance = + configuration_instance_array[config_num].interfaces_instance_array + interface_num; + + TRACE_MSG2(USBD, "len: %02x:%02d configuration", length, length); + for (alternate_num = 0; alternate_num < interfaces_instance->alternates; alternate_num++) { + int class_num; + int endpoint; + struct usbd_alternate_instance *alternate_instance = + interfaces_instance->alternates_instance_array + alternate_num; + + length += sizeof (struct usbd_interface_descriptor); + + TRACE_MSG2(USBD, "len: %02x:%02d interface", length, length); + for (class_num = 0; class_num < alternate_instance->classes; class_num++) { + struct usbd_generic_class_descriptor * class_descriptor = + *(alternate_instance->class_list + class_num); + length += class_descriptor->bLength; + TRACE_MSG2(USBD, "len: %02x:%02d class_num", length, length); + } + + for (endpoint = 0; endpoint < alternate_instance->endpoints; endpoint++) { + struct usbd_endpoint_descriptor * endpoint_descriptor = + *(alternate_instance->endpoint_list + endpoint); + length += endpoint_descriptor->bLength; + TRACE_MSG2(USBD, "len: %02x:%02d endpoint", length, length); + } + } + } + length += otg_descriptor ? sizeof(struct usbd_otg_descriptor) : 0; + configuration_descriptor->wTotalLength = cpu_to_le16 ((u16)length); + // XXX only support a single configuration currently + configuration_instance_array[config_num].bNumInterfaces = function_driver->bNumInterfaces; + TRACE_MSG3(USBD, "configuration_instance_array[%d]: %p wTotalLength: %d", config_num, + &configuration_instance_array[config_num], length); + } + return configuration_instance_array; +} + + +/*! + * bus_function_enable() - enable a usbd function driver for use + * + * This will copy descriptors from the function driver and get them ready + * for use. + * @param bus + * @param function + * @param serial_number + * @return non-zero if error + */ +static int +bus_function_enable (struct usbd_bus_instance *bus, struct usbd_function_instance *function, char *serial_number) +{ + struct usbd_function_driver *function_driver = function->function_driver; + struct usbd_device_description *device_description; + struct usbd_device_descriptor *device_descriptor; +#ifdef CONFIG_OTG_HIGH_SPEED + struct usbd_device_qualifier_descriptor *device_qualifier_descriptor; +#endif /* CONFIG_OTG_HIGH_SPEED */ + struct usbd_otg_descriptor *otg_descriptor; + int rc = 0; + + function->bus = bus; + + RETURN_EINVAL_IF(function_driver->fops->function_enable && function_driver->fops->function_enable (function)); + RETURN_ZERO_IF(!(device_description = function_driver->device_description)); + RETURN_ZERO_IF(!(device_descriptor = device_description->device_descriptor)); + + device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize; + device_descriptor->iManufacturer = usbd_alloc_string (device_description->iManufacturer); + device_descriptor->iProduct = usbd_alloc_string (device_description->iProduct); + + if (strlen (serial_number)) + device_descriptor->iSerialNumber = usbd_alloc_string (serial_number); + else + device_descriptor->iSerialNumber = usbd_alloc_string (device_description->iSerialNumber); + + device_descriptor->bNumConfigurations = function_driver->bNumConfigurations; + device_descriptor->idVendor = function_driver->idVendor; + device_descriptor->idProduct = function_driver->idProduct; + device_descriptor->bcdDevice = function_driver->bcdDevice; + + if ((otg_descriptor = device_description->otg_descriptor)) { + otg_descriptor->bmAttributes = usbd_otg_bmattributes(function); + function_driver->otg_descriptor = otg_descriptor; + } + + /* call alloc_function_configurations() to allocate the configuration descriptor array + */ + RETURN_EINVAL_IF (!(function_driver->configuration_instance_array = + alloc_function_configurations (function_driver, + bus->driver->capabilities & REMOTE_WAKEUP_SUPPORTED, + bus->driver->bMaxPower + ))); + + function_driver->device_descriptor = device_descriptor; +#ifdef CONFIG_OTG_HIGH_SPEED + TRACE_MSG0(USBD, "HIGH SPEED"); + if ((device_qualifier_descriptor = device_description->device_qualifier_descriptor)) { + device_qualifier_descriptor->bNumConfigurations = function_driver->bNumConfigurations; + function_driver->device_qualifier_descriptor = device_qualifier_descriptor; + } +#endif /* CONFIG_OTG_HIGH_SPEED */ + //TRACE_MSG0(USBD, "finished"); + return rc; +} + +/*! + * usbd_function_disable() - disable a usbd function driver + * @param function + */ +void usbd_function_disable (struct usbd_function_instance *function) +{ + int configuration; + struct usbd_function_driver *function_driver = function->function_driver; + struct usbd_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array; + + if (function->function_driver->fops->function_disable) + function->function_driver->fops->function_disable (function); + + RETURN_IF(!function_driver->configuration_instance_array); + + // iterate across the descriptors list and de-allocate all structures + if (function_driver->configuration_instance_array) { + for (configuration = 0; configuration < function_driver->bNumConfigurations; configuration++) { + int interface_num; + struct usbd_configuration_instance *configuration_instance = configuration_instance_array + configuration; + + for (interface_num = 0; interface_num < configuration_instance->bNumInterfaces; interface_num++) { + + //int alternate_num; + struct usbd_interfaces_instance *interfaces_instance = + configuration_instance->interfaces_instance_array + interface_num; + + LKFREE (interfaces_instance->alternates_instance_array); + } + LKFREE (configuration_instance->interfaces_instance_array); + } + } + LKFREE (configuration_instance_array); + function->bus = NULL; +} + + +/*! + * usbd_enable_function() - called to enable the desired function + * + * Used by a USB Bus interface driver to create a virtual device. + * + * @param bus + * @param arg + * @param serial_number + * @return non-zero if error + */ +int usbd_enable_function(struct usbd_bus_instance *bus, char *arg, char *serial_number) +{ + struct usbd_function_instance *function = NULL; + struct usbd_function_driver *function_driver = NULL; + LIST_NODE *lhd = NULL; + int len = 0; + int i; + int rc = -EINVAL; + int epn, endpointsRequested; + int endpoints; + + DOWN(&usbd_bus_sem); + + if (arg && bus->arg) + LKFREE(bus->arg); + + if (arg) { + bus->arg = LSTRDUP(arg); + len = strlen(arg); + } + + /* initialize the strings pool, and zero endpoint array urbs + */ + THROW_IF(usbd_strings_init (), error); + + for (i = 1; i < bus->endpoints; i++) { + struct usbd_endpoint_instance *endpoint = bus->endpoint_array + i; + endpoint->rcv_urb = endpoint->tx_urb = NULL; + } + + /* find requested function + */ + if ((function = usbd_find_function(arg))) { + + function_driver = function->function_driver; + + TRACE_MSG1(USBD, "request endpoints: %x", function_driver); + + switch(function->function_type) { + case function_simple: + THROW_IF(bus->driver->bops->request_endpoints( bus, function->endpoint_map_array, + function->function_driver->endpointsRequested, + function->function_driver->requestedEndpoints), error); + break; + case function_interface: + break; + case function_composite: + THROW_IF(bus->driver->bops->request_endpoints( bus, function->endpoint_map_array, + function->endpoints, + function->requestedEndpoints), error); + break; + } + + + bus_function_enable (bus, function, serial_number); + } + + THROW_IF (!(bus->function_instance = function), error); + + + /* if composite we need to compose the interface list and endpoint map + */ + if(function_composite == function->function_type) { + char *name; + int i; + } + + /* we need to de-compose the endpoint request array into the individual + * interface endpoint map array. + */ + for (endpoints = 0, i = 0; i < function->interfaces; i++) { + struct usbd_function_instance *interface_function = function->interfaces_array[i]; + interface_function->endpoint_map_array = function->endpoint_map_array; + endpoints += interface_function->endpoints; + } + + + switch(bus->function_instance->function_type) { + case function_simple: + case function_composite: + THROW_IF(bus->driver->bops->set_endpoints( bus, + function->function_driver->endpointsRequested, + function->endpoint_map_array), error); + break; + case function_interface: + break; + } + + // device bottom half + PREPARE_WORK_ITEM(bus->device_bh, usbd_device_bh, bus); + bus->status = USBD_OK; + bus->bus_state = usbd_bus_state_enabled; + bus->ep0->endpoint_map_array->endpoint = bus->endpoint_array; + THROW_IF (bus_function_enable (bus, bus->ep0, NULL), error); + + // iterate across the logical endpoint map to copy appropriate information + // into the physical endpoint instance array + + endpointsRequested = bus->function_instance->function_driver->endpointsRequested; + + for (epn = 0; epn < /*bus->function_instance->*/endpointsRequested; epn++) { + + struct usbd_endpoint_map *endpoint_map = bus->function_instance->endpoint_map_array + epn; + int physicalEndpoint = endpoint_map->physicalEndpoint[0]; + struct usbd_endpoint_instance *endpoint = bus->endpoint_array + physicalEndpoint; + + endpoint_map->endpoint = endpoint; + endpoint->bEndpointAddress = endpoint_map->bEndpointAddress[0]; + endpoint->bmAttributes = endpoint_map->bmAttributes[0]; + + switch(endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + case USB_DIR_IN: + endpoint->tx_transferSize = endpoint_map->transferSize[0]; + endpoint->wMaxPacketSize = endpoint_map->wMaxPacketSize[0]; + endpoint->last = 0; + endpoint->tx_urb = NULL; + break; + + case USB_DIR_OUT: + endpoint->rcv_transferSize = endpoint_map->transferSize[0]; + endpoint->wMaxPacketSize = endpoint_map->wMaxPacketSize[0]; + endpoint->rcv_urb = NULL; + break; + } + } + + /* if composite we need to de-compose the endpoint map into the individual + * interface endpoint maps. + */ + if(function_composite == function->function_type) { + } + + rc = 0; + + CATCH(error) { + usbd_strings_exit(); + } + UP(&usbd_bus_sem); + return rc; +} + +int usbd_enable_function_irq (struct usbd_bus_instance *bus, char *arg, char *serial_number) +{ + return usbd_enable_function(bus, arg, serial_number); +} + +/*! + * usbd_disable_function() - called to disable the current function + * + * Used by a USB Bus interface driver to destroy a virtual device. + * + * @param bus + */ +void usbd_disable_function (struct usbd_bus_instance *bus) +{ + DOWN(&usbd_bus_sem); + + // prevent any more bottom half scheduling + bus->status = USBD_CLOSING; + UP(&usbd_bus_sem); + + /* XXX + * if composite ??? + */ + switch(bus->function_instance->function_type) { + case function_simple: + case function_interface: + break; + case function_composite: + break; + } + + // wait for pending device bottom half to finish +//XXX FIXME + //while (bus->device_bh.data /*|| device->function_bh.data */) { + while (PENDING_WORK_ITEM(bus->device_bh)){ + + //printk(KERN_INFO"%s: waiting for usbd_device_bh %ld %p interrupt: %d\n", __FUNCTION__, + // bus->device_bh.sync, bus->device_bh.data, in_interrupt()); + + // This can probably be either, but for consistency's sake... +#if 1 + //I.e. run this task immediately and then delay up to 2000 secdons ... +#ifdef LINUX24 + queue_task(&bus->device_bh, &tq_immediate); + mark_bh (IMMEDIATE_BH); +#else + SCHEDULE_WORK(bus->device_bh); +#endif + // schedule_task(&device->device_bh); + + SCHEDULE_TIMEOUT (2000 * HZ); +#else + //Generic version ... + SCHEDULE_IMMEDIATE_WORK(bus->device_bh); + SCHEDULE_TIMEOUT(2000); //Seconds +#endif + } + DOWN(&usbd_bus_sem); + + // tell the function driver to close + usbd_function_disable (bus->ep0); + usbd_function_disable (bus->function_instance); + + // free alternates memory + if (bus->alternates) { + bus->bNumInterfaces = 0; + LKFREE(bus->alternates); + bus->alternates = NULL; + } + if (bus->function_instance) { +#if 0 + if (bus->function_instance->endpoint_map_array) + LKFREE(bus->function_instance->endpoint_map_array); + LKFREE(bus->function_instance); +#endif + bus->function_instance = NULL; + } + usbd_strings_exit(); + bus->status = USBD_CLOSED; + UP(&usbd_bus_sem); +} + +OTG_EXPORT_SYMBOL(usbd_enable_function); +OTG_EXPORT_SYMBOL(usbd_enable_function_irq); +OTG_EXPORT_SYMBOL(usbd_disable_function); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Bus Event Handling: + * usbd_bus_event_handler() + * + */ +static usbd_device_state_t event_states[DEVICE_CLOSE] = { + STATE_UNKNOWN, STATE_INIT, STATE_ATTACHED, STATE_POWERED, + STATE_DEFAULT, STATE_ADDRESSED, STATE_CONFIGURED, STATE_UNKNOWN, + STATE_UNKNOWN, STATE_UNKNOWN, STATE_ADDRESSED, STATE_SUSPENDED, + STATE_UNKNOWN, STATE_POWERED, STATE_ATTACHED, +}; + +static usbd_device_status_t event_status[DEVICE_CLOSE+1] = { + USBD_UNKNOWN, // DEVICE_UNKNOWN + USBD_OPENING, // DEVICE_INIT + USBD_OPENING, // DEVICE_CREATE + USBD_OPENING, // DEVICE_HUB_CONFIGURED + USBD_RESETING, // DEVICE_RESET + USBD_OK, // DEVICE_ADDRESS_ASSIGNED + USBD_OK, // DEVICE_CONFIGURED + USBD_OK, // DEVICE_SET_INTERFACE + USBD_OK, // DEVICE_SET_FEATURE + USBD_OK, // DEVICE_CLEAR_FEATURE + USBD_OK, // DEVICE_DE_CONFIGURED + USBD_SUSPENDED, // DEVICE_BUS_INACTIVE + USBD_OK, // DEVICE_BUS_ACTIVITY + USBD_RESETING, // DEVICE_POWER_INTERRUPTION + USBD_RESETING, // DEVICE_HUB_RESET + USBD_CLOSING, // DEVICE_DESTROY + USBD_CLOSED, // DEVICE_CLOSE +}; + +/*! + * usbd_bus_event_handler_irq() - called to respond to various usb events + * + * This is called from an interrupt context. + * + * Used by a Bus driver to indicate an event. + * @param bus + * @param event + * @param data + */ +void usbd_bus_event_handler_irq (struct usbd_bus_instance *bus, usbd_device_event_t event, int data) +{ + RETURN_UNLESS(bus); + + /* get the next bus device state by table lookup of event, we need to save the + * current state IFF we are suspending so that it can be restored on resume. + */ + switch (event) { + case DEVICE_BUS_INACTIVE: + bus->suspended_state = bus->device_state; + /* FALL THROUGH */ + default: + bus->device_state = event_states[event]; + //TRACE_MSG0(USBD, "default"); + break; + case DEVICE_UNKNOWN: + //TRACE_MSG0(USBD, "UNKNOWN"); + break; + case DEVICE_BUS_ACTIVITY: + bus->device_state = bus->suspended_state; + //TRACE_MSG0(USBD, "ACTIVITY"); + break; + + case DEVICE_SET_INTERFACE: + case DEVICE_SET_FEATURE: + case DEVICE_CLEAR_FEATURE: + //TRACE_MSG0(USBD, "SET"); + break; + } + + /* set the bus status by table lookup of event. + */ + switch (event) { + case DEVICE_BUS_ACTIVITY: + case DEVICE_BUS_INACTIVE: + BREAK_IF(USBD_CLOSING != bus->status); + /* FALL THROUGH */ + default: + bus->status = event_status[event]; + //TRACE_MSG0(USBD, "DEFAULT"); + break; + } + + /* we need to reset things on some transitions + */ + switch (bus->device_state) { + default: + // if we lost configuration then get rid of alternate settings + if (bus->alternates) { + LKFREE(bus->alternates); + bus->alternates = NULL; + } + bus->bNumInterfaces = 0; + bus->device_feature_settings = 0; + bus->ConfigurationValue = 0; + case STATE_CONFIGURED: + break; + case STATE_SUSPENDED: + // XXX Checkme - is suspend equivalent to end of session? + // C.f. OTG 6.5.1-6.5.3 imply that these must be reset at the end of a session or reset + //bus->device_feature_settings &= + // ~(FEATURE(USB_OTG_A_HNP_ENABLE) | FEATURE(USB_OTG_B_HNP_ENABLE) | FEATURE(USB_OTG_A_ALT_HNP_ENABLE)); + break; + } + /* Pass the event to the bus interface driver and then the function driver and + * any interface function drivers. + */ + bus->driver->bops->event_handler (bus, event, data); + bus->function_instance->function_driver->fops->event_handler(bus->function_instance, event, data); + #if 0 + // XXX pass to interface function drivers + #endif +} + +/*! + * usbd_bus_event_handler() - process bus event + * @param bus + * @param event + * @param data + */ +void usbd_bus_event_handler (struct usbd_bus_instance *bus, usbd_device_event_t event, int data) +{ + unsigned long flags; + local_irq_save (flags); + usbd_bus_event_handler_irq(bus, event, data); + local_irq_restore (flags); +} + +OTG_EXPORT_SYMBOL(usbd_bus_event_handler); +OTG_EXPORT_SYMBOL(usbd_bus_event_handler_irq); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Device Request Processing: + * usbd_device_request() + * + */ + +/*! + * C.f. 9.4 Standard Requests and table 9.3 + * + * Encode valid requests into a bitmap for each recipient type for + * both directions. This is needed because there are some requests + * that appear to be a standard request but are not described in + * Chapter nine. For example the mouse driver hid request. + * + * So we only process the actual set of standard requests that are + * defined in chapter nine and even if it appears to be standard but + * is not in chapter nine then we pass it to the function driver. + * + * @{ + */ + +#define STD(x) (1<<(x+1)) + +/*! @{ */ +/*! h2d_standard_requests and d2h_standard_requests + * + * These tables list all of the valid Chapter Nine requests. Any request + * not listed in these tables will NOT be processed by the EP0 function and + * will instead be passed to the appropriate function driver. + */ +u32 h2d_standard_requests[4] = { + // 0 - Device + STD(USB_REQ_CLEAR_FEATURE) | + STD(USB_REQ_SET_FEATURE) | + STD(USB_REQ_SET_ADDRESS) | + STD(USB_REQ_SET_DESCRIPTOR) | + STD(USB_REQ_SET_CONFIGURATION) , + // 1 - Interface + STD(USB_REQ_CLEAR_FEATURE) | + STD(USB_REQ_SET_FEATURE) | + STD(USB_REQ_SET_INTERFACE) , + // 2 - Endpoint + STD(USB_REQ_CLEAR_FEATURE) | + STD(USB_REQ_SET_FEATURE) , + // 3 - Other + 0, +}; + +u32 d2h_standard_requests[4] = { + // 0 - Device + STD(USB_REQ_GET_STATUS) | + STD(USB_REQ_GET_DESCRIPTOR) | + STD(USB_REQ_GET_CONFIGURATION), + // 1 - Interface + STD(USB_REQ_GET_STATUS) | + STD(USB_REQ_GET_INTERFACE) , + // 2 - Endpoint + STD(USB_REQ_GET_STATUS) | + STD(USB_REQ_SYNCH_FRAME) , + // 3 - Other + 0, +}; + +/* @} */ + + +/*! + * devreq_set_configuration() - process a received urb + * + * Used by a USB Bus interface driver to pass received device request to + * the appropriate USB Function driver. + * + * @param function + * @param request + * @return non-zero if errror + */ +static int devreq_set_configuration(struct usbd_function_instance *function, int wValue) +{ + struct usbd_bus_instance *bus = function->bus; + struct usbd_function_driver *function_driver = bus->function_instance->function_driver; + int bNumConfigurations = function_driver->bNumConfigurations; + int bNumInterfaces; + u8 ConfigurationValue; + + struct usbd_configuration_instance *configuration_instance; + struct usbd_configuration_descriptor *configuration_descriptor; + + // get rid of previous interface and alternates + if (bus->bNumInterfaces && bus->alternates) { + bus->bNumInterfaces = 0; + LKFREE(bus->alternates); + bus->alternates = NULL; + } + + // c.f. 9.4.7 - the top half of wValue is reserved + // + // c.f. 9.4.7 - zero is the default or addressed state, in our case this + // is the same is configuration zero, but will be fixed in usbd.c when used. + + ConfigurationValue = wValue & 0x7f; + + RETURN_EINVAL_IF(ConfigurationValue > bNumConfigurations); + + /* save ConfigurationValue for GET_CONFIGURATION request + */ + bus->ConfigurationValue = ConfigurationValue; + + /* A ConfigurationValue of zero is for the default configuration (i.e.) the + * first one. A non-zero value needs to be decremented to allow it to be used + * as an index. + */ + ConfigurationValue = !ConfigurationValue ? 0 : ConfigurationValue -1; + + configuration_instance = + &function_driver->configuration_instance_array[ConfigurationValue]; + + configuration_descriptor = configuration_instance->configuration_descriptor; + + RETURN_EINVAL_IF(!configuration_descriptor); + + bNumInterfaces = configuration_instance->bNumInterfaces; + + /* reset interface and alternate settings + */ + RETURN_EINVAL_IF (!(bus->alternates = (u8 *) CKMALLOC(bNumInterfaces, GFP_ATOMIC))); + bus->bNumInterfaces = bNumInterfaces; + + /* Call function driver set_configuration() operation and if available any + * interface drivers set_configuration() operations. + */ + if (function_driver->fops->set_configuration) { + function_driver->fops->function_enable && function_driver->fops->set_configuration (function, wValue); + } + /* + * XXX + */ + + usbd_bus_event_handler (bus, DEVICE_CONFIGURED, 0); + return 0; +} + +/*! + * devreq_set_interface_altsetting() - + * + * Used by a USB Bus interface driver to pass received device request to + * the appropriate USB Function driver. + * + * @param function + * @param request + * @return non-zero if errror + */ +static int devreq_set_interface_altsetting(struct usbd_function_instance *function, int wIndex, int wValue) +{ + struct usbd_bus_instance *bus = function->bus; + struct usbd_function_driver *function_driver = function->function_driver; + + RETURN_EINVAL_IF(wIndex > bus->bNumInterfaces); // wIndex is interface + + bus->alternates[wIndex] = (u8) wValue; + + /* Call function driver set_interface() operation and if available any + * interface drivers set_interface() operations. + */ + if (function_driver->fops->set_interface) + function_driver->fops->function_enable && function_driver->fops->set_interface (function, wIndex, wValue); + /* + * XXX + */ + + usbd_bus_event_handler (bus, DEVICE_SET_INTERFACE, 0); + + return 0; +} + +/*! + * devreq_get_device_feature_settings() - process a received urb + * + * Used by a USB Bus interface driver to pass received device request to + * the appropriate USB Function driver. + * + * @param function + * @param request + * @return non-zero if errror + */ +int devreq_get_device_feature_settings(struct usbd_function_instance *function) +{ + struct usbd_bus_instance *bus = function->bus; + return bus->device_feature_settings; +} + + +/*! devreq_device_feature - handle set/clear feature requests for device + * Used by the USB Device Core to set/clear endpoint halt status. + * + * We assume that if the udc driver does not implement anything then + * we should just return zero for ok. + */ +int devreq_device_feature (struct usbd_bus_instance *bus, int features, int flag) +{ + struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; + //u32 features = bus->device_feature_settings; + + TRACE_MSG0(USBD, "BUS_DEVICE FEATURE"); + + /* if A-Device has enabled hnp + */ + if (features & FEATURE(USB_OTG_B_HNP_ENABLE) ) + otg_event(pcd->otg, HNP_ENABLED, USBD, "DEVICE FEATURE - B_HNP_ENABLE"); + + /* if A-Device does not support HNP on this port + */ + else if ( features & FEATURE(USB_OTG_A_ALT_HNP_ENABLE) ) + ; //otg_event(pcd->otg, not(a_hnp_support)); + + /* if A-Device does support HNP + */ + else if (features & FEATURE(USB_OTG_A_HNP_SUPPORT)) + ; //otg_event(pcd->otg, a_hnp_support); + + return 0; + //return usbd_pcd_ops.device_feature ? usbd_pcd_ops.device_feature() : 0; +} + +/*! + * copy_config() - copy data into buffer + */ +static int copy_config (u8 *cp, void *data, int actual_length, int max_buf) +{ + int available = max_buf - actual_length; + int length = MIN(*(u8 *)data, available); + RETURN_ZERO_UNLESS (data); + RETURN_ZERO_UNLESS (length); + memcpy (cp, data, length); + return length; +} + +/*! + * copy_endpoint() - copy data into buffer + */ +static int copy_endpoint (struct usbd_function_instance *function, u8 *cp, + struct usbd_endpoint_descriptor *endpoint, int endpoint_index, int actual_length, int max_buf, int hs) +{ + int available = max_buf - actual_length; + int length = MIN(endpoint->bLength, available); + struct usbd_endpoint_descriptor endpoint_copy; + + RETURN_ZERO_IF (!length); + memcpy (&endpoint_copy, endpoint, endpoint->bLength); + usbd_endpoint_update(function, endpoint_index, &endpoint_copy, hs); + memcpy (cp, &endpoint_copy, length); + return length; +} + + +/*! + * usbd_old_get_descriptor() - copy a descriptor into buffer + * + * Return non-zero for error. + */ +int usbd_old_get_descriptor (struct usbd_function_instance *function, u8 *buffer, int max, int descriptor_type, int index) +{ + struct usbd_bus_instance *bus = function->bus; + struct usbd_function_driver *function_driver = bus->function_instance->function_driver; + int actual_length = 0; + + switch (descriptor_type) { + case USB_DT_DEVICE: + { + struct usbd_device_descriptor *device_descriptor = function_driver->device_descriptor; + + // copy descriptor for this device + actual_length += copy_config (buffer + actual_length, device_descriptor, actual_length, max); + + // correct the correct control endpoint 0 max packet size into the descriptor + device_descriptor = (struct usbd_device_descriptor *) buffer; + device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize; + } + break; + + #ifdef CONFIG_OTG_HIGH_SPEED + case USB_DT_DEVICE_QUALIFIER: // c.f. 9.6.2 Device Qualifier + { + struct usbd_device_qualifier_descriptor *device_qualifier_descriptor = + function_driver->device_qualifier_descriptor; + + // copy descriptor for this device + actual_length += copy_config (buffer + actual_length, device_qualifier_descriptor, actual_length, max); + + } + break; + + case USB_DT_OTHER_SPEED_CONFIGURATION: + #endif /* CONFIG_OTG_HIGH_SPEED */ + + case USB_DT_CONFIGURATION: + { + #ifdef CONFIG_OTG_HIGH_SPEED + int hs = bus->HighSpeedFlag ? descriptor_type == USB_DT_CONFIGURATION: + descriptor_type == USB_DT_OTHER_SPEED_CONFIGURATION; + #else /* CONFIG_OTG_HIGH_SPEED */ + int hs = 0; + #endif /* CONFIG_OTG_HIGH_SPEED */ + int interface_num; + + struct usbd_configuration_instance *configuration_instance = + &function_driver->configuration_instance_array[index]; + + struct usbd_configuration_descriptor *configuration_descriptor = + configuration_instance->configuration_descriptor; + + struct usbd_otg_descriptor *otg_descriptor = function_driver->otg_descriptor; + + RETURN_EINVAL_IF (!configuration_descriptor); + RETURN_EINVAL_IF (index > function_driver->device_descriptor->bNumConfigurations); + + actual_length += copy_config (buffer + actual_length, configuration_descriptor, actual_length, max); + + // iterate across all bNumInterfaces for specified configuration + for (interface_num = 0; interface_num < configuration_descriptor->bNumInterfaces; interface_num++) { + + int alternate_num; + struct usbd_interfaces_instance *interfaces_instance = + configuration_instance->interfaces_instance_array + interface_num; + + // iterate across all interface alternates + for (alternate_num = 0; alternate_num < interfaces_instance->alternates; alternate_num++) { + + int class_num; + int endpoint; + + struct usbd_alternate_instance *alternate_instance = + interfaces_instance->alternates_instance_array + alternate_num; + + //struct usbd_interface_descriptor *usbd_interface_descriptor; + + // copy descriptor for this interface + actual_length += copy_config (buffer + actual_length, + alternate_instance->interface_descriptor, actual_length, max); + + // iterate across all classes for the alternate interface + for (class_num = 0; class_num < alternate_instance->classes; class_num++) + actual_length += copy_config (buffer + actual_length, + *(alternate_instance->class_list + class_num), + actual_length, max); + + // iterate across all endpoints for the alternate interface + //interface_descriptor = alternate_instance->interface_descriptor; + + for (endpoint = 0; endpoint < alternate_instance->endpoints ; endpoint++) { + + TRACE_MSG2(USBD, "endpoint: %d index: %d", + endpoint, alternate_instance->endpoint_indexes[endpoint]); + + actual_length += copy_endpoint ( + bus->function_instance, + buffer + actual_length, + *(( alternate_instance->endpoint_list) + endpoint), + alternate_instance->endpoint_indexes[endpoint], + actual_length, + max, + hs); + } + } + } + + /* copy the OTG descriptor if required + */ + actual_length += copy_config (buffer + actual_length, otg_descriptor, actual_length, max); + + /* set the configuration attributes and power fields in the configuration descriptor + */ + configuration_descriptor = (struct usbd_configuration_descriptor *)buffer; + + configuration_descriptor->bmAttributes = BMATTRIBUTE_RESERVED; + + if (bus->device_feature_settings & FEATURE(USB_DEVICE_REMOTE_WAKEUP)) + configuration_descriptor->bmAttributes |= BMATTRIBUTE_REMOTE_WAKEUP; + + if (bus->driver->bMaxPower == 0) { + configuration_descriptor->bmAttributes |= BMATTRIBUTE_SELF_POWERED; + configuration_descriptor->bMaxPower = 1; + } + else + configuration_descriptor->bMaxPower = bus->driver->bMaxPower; + + TRACE_MSG2(USBD, "bmAttributes: %02x bMaxPower: %02x", + configuration_descriptor->bmAttributes, configuration_descriptor->bMaxPower); + } + break; + + case USB_DT_STRING: + { + struct usbd_string_descriptor *string_descriptor = NULL; + RETURN_EINVAL_IF (!(string_descriptor = usbd_get_string_descriptor ((u8)index))); + actual_length += copy_config (buffer + actual_length, string_descriptor, actual_length, max); + } + break; + default: + return -EINVAL; + } + return actual_length; +} + + + + +/*! + * setclear() - set or reset a bit in a mask + * @param features + * @param flag + * @param value + */ +static void setclear(u32 *features, u32 flag, u32 value) +{ + if (value) *features |= flag; + else *features &= ~flag; +} + +/*! do_standard_device_request - process a device request + * + * Process a received device request. If not a Chapter nine request pass it + * to the other loaded function driver device_request() function. + * + * Return non-zero to indicate failure. + */ +static int do_standard_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) +{ + struct usbd_bus_instance *bus = function->bus; + u8 bRequest = request->bRequest; + u8 bmRequestType = request->bmRequestType; + u16 wValue = le16_to_cpu(request->wValue); + u16 wIndex = le16_to_cpu(request->wIndex); + u16 wLength = le16_to_cpu(request->wLength); + u16 endpoint_index; + + usbd_device_state_t device_state = usbd_get_device_state(function); + + switch (device_state) { + case STATE_CREATED: + case STATE_ATTACHED: + case STATE_POWERED: + TRACE_MSG1(USBD, "bad device_state: %d", device_state); + return -EINVAL; + + case STATE_INIT: + case STATE_DEFAULT: + switch (bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_GET_INTERFACE: + case USB_REQ_GET_STATUS: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_SET_INTERFACE: + case USB_REQ_SYNCH_FRAME: + TRACE_MSG2(USBD, "bad request: %d for this device_state: %d", bRequest, device_state); + return -EINVAL; + + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_SET_FEATURE: + break; + } + case STATE_ADDRESSED: + case STATE_CONFIGURED: + case STATE_SUSPENDED: + break; + case STATE_UNKNOWN: + TRACE_MSG1(USBD, "unknown device_state: %d", device_state); + return -EINVAL; + } + + /* Handle all requests that return data (direction bit set on bm RequestType). + * N.B. no Chapter 9 requests send additional data. + */ + if ((bmRequestType & USB_REQ_DIRECTION_MASK)) { + struct usbd_urb *urb; + int rc = 0; + switch (bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_SYNCH_FRAME: + TRACE_MSG0(USBD, "bad direction"); + return -EINVAL; + } + + RETURN_EINVAL_IF(!wLength); + + /* allocate urb, no callback, urb will be automatically de-allocated + */ + RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0 (function, wLength, NULL))); + + switch (bRequest) { + + case USB_REQ_GET_STATUS: + urb->actual_length = 2; + urb->buffer[0] = urb->buffer[1] = 0; + + rc = 0; + switch (bmRequestType & USB_REQ_RECIPIENT_MASK) { + case USB_REQ_RECIPIENT_DEVICE: + + UNLESS (usbd_get_bMaxPower(function)) urb->buffer[0] |= USB_STATUS_SELFPOWERED; + urb->buffer[0] |= + (devreq_get_device_feature_settings(function) & FEATURE(USB_DEVICE_REMOTE_WAKEUP)) ? + USB_STATUS_REMOTEWAKEUP : 0; + break; + case USB_REQ_RECIPIENT_ENDPOINT: + RETURN_EINVAL_UNLESS((endpoint_index = usbd_find_endpoint_index(bus, wIndex)) < bus->endpoints); + urb->buffer[0] = usbd_endpoint_halted (function, endpoint_index); + break; + case USB_REQ_RECIPIENT_INTERFACE: + break; + case USB_REQ_RECIPIENT_OTHER: + default: + TRACE_MSG0(USBD, "bad recipient"); + rc = -EINVAL; + } + break; + + case USB_REQ_GET_DESCRIPTOR: + rc = usbd_old_get_descriptor (function, urb->buffer, wLength, wValue >> 8, wValue & 0xff); + if (rc != -EINVAL) { + urb->actual_length = rc; + rc = 0; + } + else + TRACE_MSG0(USBD, "bad description"); + break; + + case USB_REQ_GET_CONFIGURATION: + TRACE_MSG1(USBD, "GET CONFIGURATION: %d", bus->ConfigurationValue); + urb->actual_length = 1; + urb->buffer[0] = bus->ConfigurationValue; + break; + + case USB_REQ_GET_INTERFACE: + RETURN_EINVAL_IF(wIndex > bus->bNumInterfaces); + urb->actual_length = 1; + urb->buffer[0] = bus->alternates[wIndex]; + break; + default: + TRACE_MSG0(USBD, "bad descriptor type"); + rc = 1; + } + + if (!(urb->actual_length % usbd_endpoint_zero_wMaxPacketSize(function, usbd_high_speed(function))) && + (urb->actual_length < wLength)) + urb->flags |= USBD_URB_SENDZLP; + + RETURN_ZERO_UNLESS(rc || usbd_start_in_urb(urb)); + /* only get here if error */ + usbd_free_urb(urb); + TRACE_MSG0(USBD, "get failed"); + return -EINVAL; + } + /* Handle the requests that do not return data. + */ + else { + int setclear_flag = USB_REQ_SET_FEATURE == bRequest; + switch (bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + TRACE_MSG2(USBD, "FEATURE: recipient: %d wValue: %d", + bmRequestType & USB_REQ_RECIPIENT_MASK, wValue); + switch (bmRequestType & USB_REQ_RECIPIENT_MASK) { + case USB_REQ_RECIPIENT_DEVICE: + //switch (wValue) { + // otg_event(pcd->otg, HNP_ENABLED, PCD, "DEVICE FEATURE - B_HNP_ENABLE"); + // break; + //} + switch (wValue) { + case USB_OTG_A_ALT_HNP_ENABLE: // C.f. OTG 6.5.3 + case USB_OTG_A_HNP_SUPPORT: // C.f. OTG 6.5.2 + case USB_OTG_B_HNP_ENABLE: // C.f. OTG 6.5.1 + RETURN_EINVAL_UNLESS(setclear_flag); // cleared by reset + case USB_DEVICE_REMOTE_WAKEUP: + break; + default: + return -EINVAL; + } + setclear(&bus->device_feature_settings, FEATURE(wValue), setclear_flag); + return devreq_device_feature(bus, FEATURE(wValue), setclear_flag); + + case USB_REQ_RECIPIENT_ENDPOINT: + RETURN_EINVAL_UNLESS(USB_ENDPOINT_HALT == wValue); + + setclear(&(bus->endpoint_array[wIndex].feature_setting), + FEATURE(wValue), bRequest == USB_REQ_SET_FEATURE); + + /* wIndex contains bEndpointAddress, also find and verify the endpoint map index as well. + * Then clear the endpoint using the bus interface driver halt_endpoint() operation, + * if that is ok, then inform the function driver using the endpoint_cleared() operation. + * + */ + RETURN_EINVAL_UNLESS((endpoint_index = usbd_find_endpoint_index(bus, wIndex)) < bus->endpoints); + RETURN_EINVAL_IF(function->bus->driver->bops->halt_endpoint(bus, wIndex, + endpoint_index, setclear_flag)); + + if (function->function_driver->fops->endpoint_cleared && !setclear_flag) + function->function_driver->fops->endpoint_cleared(function, wIndex, endpoint_index ); + + return 0; + + case USB_REQ_RECIPIENT_INTERFACE: + case USB_REQ_RECIPIENT_OTHER: + default: + TRACE_MSG0(USBD, "bad recipient"); + return -EINVAL; + } + + case USB_REQ_SET_ADDRESS: + if (bus->driver->bops->set_address) bus->driver->bops->set_address (bus, wValue); + usbd_bus_event_handler (bus, DEVICE_ADDRESS_ASSIGNED, wValue); + return 0; + + case USB_REQ_SET_DESCRIPTOR: + TRACE_MSG0(USBD, "set descriptor not supported"); + return -EINVAL; + + case USB_REQ_SET_CONFIGURATION: + return devreq_set_configuration(function, wValue); + + case USB_REQ_SET_INTERFACE: + return devreq_set_interface_altsetting(function, wIndex, wValue); + + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_INTERFACE: + case USB_REQ_GET_STATUS: + case USB_REQ_SYNCH_FRAME: + TRACE_MSG0(USBD, "unkown"); + return -EINVAL; + } + } + TRACE_MSG0(USBD, "not possible"); + return -EINVAL; +} + + +/*! + * usbd_device_request() - process a device request + * + * Used by a USB Bus interface driver to pass received device request to + * the appropriate USB Function driver. + * + * @param function + * @param request + * @return non-zero if errror + */ +int usbd_device_request(struct usbd_bus_instance *bus, struct usbd_device_request *request) +{ + struct usbd_function_instance *function = bus->function_instance; + u8 bRequest = request->bRequest; + u8 bmRequestType = request->bmRequestType; + u16 wValue = le16_to_cpu(request->wValue); + u16 wIndex = le16_to_cpu(request->wIndex); + u16 wLength = le16_to_cpu(request->wLength); + + TRACE_MSG5(USBD, "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x", + bmRequestType, bRequest, wValue, wIndex, wLength); + + /* Handle only USB Standard Requests (c.f. USB Spec table 9-2, D6..5 must be 0), + * otherwise call the currently enabled function specific receive setup. + */ + if (!(bmRequestType & USB_REQ_TYPE_MASK) && + ((bmRequestType & USB_DIR_IN ? d2h_standard_requests : h2d_standard_requests) + [bmRequestType & 0x3] & STD(bRequest))) + return do_standard_device_request(function, request); + + /* Standard Requests not in Chapter nine, Class and Vendor device requests + * must be handled by the function driver or interface driver as appropriate. + */ + switch(bmRequestType & USB_REQ_RECIPIENT_MASK) { + case USB_REQ_RECIPIENT_INTERFACE: + case USB_REQ_RECIPIENT_ENDPOINT: + /* XXX + * if function->interfaces[] is valid then we shuffle request off to + * interface driver for interface and endpoint recipients, otherwise + * fall through. + */ + + /* + * XXX for now just fall through for all. + */ + + case USB_REQ_RECIPIENT_DEVICE: + case USB_REQ_RECIPIENT_OTHER: + default: + return function->function_driver->fops->device_request(function, request); + } +} + +OTG_EXPORT_SYMBOL(usbd_device_request); +OTG_EXPORT_SYMBOL(usbd_old_get_descriptor); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Device I/O: + * usbd_urb_finished() + * usbd_first_urb_detached() + * usbd_find_endpoint_address() + */ + +/*! + * do_urb_callback() - tell function that an urb has been transmitted. + * + * Must be called from an interrupt or with interrupts disabled. + * + * Used by a USB Bus driver to pass a sent urb back to the function + * driver via the endpoints done queue. + * + * If there is no callback or if the URB call back returns non-zero then the + * urb must be de-allocated here. + * + * @param urb + * @param rc + */ +static void do_urb_callback (struct usbd_urb *urb, int rc) +{ + RETURN_UNLESS (urb); + if (!urb->callback || urb->callback(urb,rc)) + usbd_free_urb(urb); +} + +/*! + * urb_append_irq() - append a linked list of urbs to another linked list + * @param hd link to urb list + * @param urb to append to list + */ +static INLINE void urb_append_irq (urb_link * hd, struct usbd_urb *urb) +{ + urb_link *new; + urb_link *pul; + RETURN_UNLESS (hd && urb); + new = &urb->link; + pul = hd->prev; + new->prev->next = hd; + hd->prev = new->prev; + new->prev = pul; + pul->next = new; +} + +/*! + * urb_append() - irq safe version + * @param hd list of urbs + * @param urb urb to append + */ +void urb_append(urb_link *hd, struct usbd_urb *urb) +{ + unsigned long flags; + local_irq_save (flags); + urb_append_irq(hd, urb); + local_irq_restore (flags); +} + + +/*! + * usbd_urb_finished() - tell function that an urb has been finished. + * + * Used by a USB Bus driver to pass a sent urb back to the function + * driver via the endpoints finished queue. + * @param urb urb to process + * @param rc result code + * + */ +void usbd_urb_finished(struct usbd_urb *urb, int rc) +{ + urb->status = (usbd_urb_status_t)rc; + #if defined(OTG_LINUX) + urb->jiffies = jiffies; + #endif /* defined(OTG_LINUX) */ + if (USBD_OK != urb->bus->status) + do_urb_callback (urb, rc); + else { + urb_append_irq (&(urb->bus->finished), urb); + RETURN_IF (PENDING_WORK_ITEM(urb->bus->device_bh)); +#ifdef LINUX24 + //FIXME XXX + queue_task (&urb->bus->device_bh, &tq_immediate); + mark_bh (IMMEDIATE_BH); +#else + SCHEDULE_WORK(urb->bus->device_bh); +#endif + } +} +void usbd_urb_finished_irq (struct usbd_urb *urb, int rc) +{ + usbd_urb_finished(urb, rc); +} + +/*! + * @name STRUCT + * @brief + * Structure member address manipulation macros. + * + * These are used by client code (code using the urb_link routines), since + * the urb_link structure is embedded in the client data structures. + * + * Note: a macro offsetof equivalent to member_offset is defined in stddef.h + * but this is kept here for the sake of portability. + * + * p2surround returns a pointer to the surrounding structure given + * type of the surrounding structure, the name memb of the structure + * member pointed at by ptr. For example, if you have: + * + * struct foo { + * int x; + * float y; + * char z; + * } thingy; + * + * char *cp = &thingy.z; + * + * then + * &thingy == p2surround(struct foo, z, cp) + * + * @{ + */ + +#define _cv_(ptr) ((char*)(void*)(ptr)) +#define member_offset(type,memb) (_cv_(&(((type*)0)->memb))-(char*)0) +#define p2surround(type,memb,ptr) ((type*)(void*)(_cv_(ptr)-member_offset(type,memb))) + +/*! + * @name list + * @brief List support functions + * + * @{ + */ + +/*! + * urb_link() - return first urb_link in list + * + * Return the first urb_link in a list with a distinguished + * head "hd", or NULL if the list is empty. This will also + * work as a predicate, returning NULL if empty, and non-NULL + * otherwise. + * + * Called from interrupt. + * + * @return link to urb + */ +static INLINE urb_link *first_urb_link_irq (urb_link * hd) +{ + urb_link *nx; + return (!hd || !(nx = hd->next) || (nx == hd)) ? NULL : nx; +} + +/*! + * first_urb_irq() - return first urb in list + * Return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + * + * Called from interrupt. + * + * @param hd linked list of urbs + * @return pointer to urb + */ +static INLINE struct usbd_urb *first_urb_irq (urb_link * hd) +{ + urb_link *nx; + return (!(nx = first_urb_link_irq (hd))) ? NULL : p2surround (struct usbd_urb, link, nx); +} + + + +/*! + * usbd_first_urb_detached_irq() - detach an return first urb + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + * + * @param hd linked list of urbs + * @return pointer to urb or NULL + */ +struct usbd_urb *usbd_first_urb_detached_irq (urb_link * hd) +{ + struct usbd_urb *urb; + urb_link *ul; + RETURN_NULL_IF (!(urb = first_urb_irq (hd))); + ul = &urb->link; + ul->next->prev = ul->prev; + ul->prev->next = ul->next; + ul->prev = ul->next = ul; + return urb; +} +/* @} */ + +/*! + * usbd_first_urb_detached() - detach and return urb + * + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + * + * @param hd list of urbs + * @return pointer to urb or NULL + */ +struct usbd_urb *usbd_first_urb_detached (urb_link * hd) +{ + struct usbd_urb *urb; + unsigned long flags; + local_irq_save (flags); + urb = usbd_first_urb_detached_irq (hd); + local_irq_restore (flags); + return urb; +} + + +/*! + * usbd_find_endpoint_index() + * @param bus + * @param bEndpointAddress + * Find the endpoint map index for the specified bEndpointAddress. + */ +int usbd_find_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress) +{ + int i; + for (i = 0; i < bus->endpoints; i++) + BREAK_IF (bus->endpoint_array[i].bEndpointAddress == bEndpointAddress); + return i; +} + +OTG_EXPORT_SYMBOL(usbd_urb_finished); +OTG_EXPORT_SYMBOL(usbd_urb_finished_irq); +OTG_EXPORT_SYMBOL(usbd_first_urb_detached); +OTG_EXPORT_SYMBOL(usbd_first_urb_detached_irq); +OTG_EXPORT_SYMBOL(usbd_find_endpoint_index); + +/* ********************************************************************************************** */ +/* usbb bus bottom half ************************************************************************* */ + +/*! + * usbd_device_bh() - Bottom half handler to process sent or received urbs. + * @param data + */ +void usbd_device_bh (void *data) +{ + struct usbd_bus_instance *bus = data; + struct usbd_endpoint_instance *endpoint; + struct usbd_urb *urb; + + RETURN_IF (!bus || !(endpoint = bus->endpoint_array)); + DOWN(&usbd_bus_sem); + + for (; (urb = usbd_first_urb_detached (&bus->finished)); do_urb_callback (urb, urb->status)); + + #if defined(OTG_LINUX) + if (USBD_CLOSING == bus->status) + bus->device_bh.data = NULL; + #endif /* defined(OTG_LINUX) */ + UP(&usbd_bus_sem); +} + +/* ********************************************************************************************** */ +/* Module init ********************************************************************************** */ + +extern char * usbd_function_name(int n); +struct usbd_ops usbd_ops; +otg_tag_t USBD; + +/*! + * usbd_device_init() - initialize + */ +int usbd_device_init (void) +{ + USBD = otg_trace_obtain_tag(); + TRACE_MSG0(USBD,"--"); + usbd_ops.function_name = usbd_function_name; + otg_set_usbd_ops(&usbd_ops); + return 0; +} + +/*! + * usbd_device_exit() - de-initialize + */ +void usbd_device_exit (void) +{ + otg_set_usbd_ops(NULL); + otg_trace_invalidate_tag(USBD); +} + + diff -uNr linux/drivers/no-otg/otgcore/usbp-fops.c linux/drivers/otg/otgcore/usbp-fops.c --- linux/drivers/no-otg/otgcore/usbp-fops.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/usbp-fops.c 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,1123 @@ +/* + * otg/otgcore/usbp-fops.c - USB Function support + * + * Copyright (c) 2004-2005 Belcarra + * + * By: + * Stuart Lynne , + * Bruce Balden + * + */ +/*! + * @file otg/otgcore/usbp-fops.c + * @brief Function driver related functions. + * + * This implements the functions used to implement USB Function drivers. + * + * @ingroup USBP + */ + +#include +#include + +#include +#include +#include +#include + + +struct usbd_string_descriptor **usb_strings; +void urb_append(urb_link *hd, struct usbd_urb *urb); + +LIST_NODE_INIT(usbd_function_instances); // list of all registered configuration function modules +LIST_NODE_INIT(usbd_interface_instances); // list of all registered interface function modules + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Function Registration: + * usbd_register_function() + * usbd_register_interface() + * usbd_register_composite() + * usbd_deregister_function() + * usbd_find_function() + * usbd_find_interface() + * + */ + +/*! + * usbd_register_function_internal() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param function_driver + * @param privdata + * @return function instance + * + */ +static struct usbd_function_instance * +usbd_register_function_internal(struct usbd_function_driver *function_driver, char *name, + usbd_function_types_t function_type, char **interface_list, void *privdata) +{ + struct usbd_function_instance *function = NULL; + + TRACE_MSG3(USBD, "interface: %s driver: %s endpoints: %d", name, + function_driver->name, function_driver->endpointsRequested); + + if (!(function = (struct usbd_function_instance *) CKMALLOC (sizeof (struct usbd_function_instance), GFP_KERNEL))) { + #if defined(OTG_LINUX) + printk(KERN_ERR,"usbd_function_instance allocation failed.\n"); + #endif /* defined(OTG_LINUX) */ + #if defined(OTG_WINCE) + DEBUGMSG(ZONE_INIT, (_T("usbd_register_function: FAILED\n"))); + #endif /* defined(OTG_LINUX) */ + return(NULL); + } + function->name = LSTRDUP(name); + function->function_driver = function_driver; + function->privdata = privdata; + function->function_type = function_type; + function->interfaces_list = interface_list; + + //function->endpointsRequested = function->function_driver->endpointsRequested; + //function->requestedEndpoitns = function->function_driver->requestedEndpoints; + + switch(function_type) { + case function_simple: + case function_interface: + break; + case function_composite: + break; + } + + if (!(function->endpoint_map_array = (struct usbd_endpoint_map *) + CKMALLOC (sizeof(struct usbd_endpoint_map) * + function_driver->endpointsRequested, GFP_KERNEL))) { + #if defined(OTG_LINUX) + printk(KERN_ERR,"usbd_endpoint_map allocation failed.\n"); + #endif /* defined(OTG_LINUX) */ + #if defined(OTG_WINCE) + DEBUGMSG(ZONE_INIT, (_T("usbd_register_function: FAILED\n"))); + #endif /* defined(OTG_LINUX) */ + LKFREE(function); + return(NULL); + } + + switch(function_type) { + case function_simple: + case function_interface: + case function_composite: + break; + } + return function; +} + +/*! + * usbd_register_function() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param function_driver + * @param privdata + * @return function instance + * + */ +struct usbd_function_instance * +usbd_register_function (struct usbd_function_driver *function_driver, char * name, void *privdata) +{ + struct usbd_function_instance *function = + usbd_register_function_internal(function_driver, name, function_simple, NULL, privdata); + TRACE_MSG1(USBD, "%s", name); + + LIST_ADD_TAIL (&function->instances, &usbd_function_instances); + return function; +} + +/*! + * usbd_register_interface() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param function_driver + * @param privdata + * @return function instance + * + */ +struct usbd_function_instance * +usbd_register_interface(struct usbd_function_driver *function_driver, char * name, void *privdata) +{ + struct usbd_function_instance *function = + usbd_register_function_internal(function_driver, name, function_interface, NULL, privdata); + TRACE_MSG1(USBD, "%s", name); + LIST_ADD_TAIL (&function->instances, &usbd_interface_instances); + return function; +} + +/*! + * usbd_register_composite() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param function_driver + * @param privdata + * @return function instance + * + */ +struct usbd_function_instance * +usbd_register_composite (struct usbd_function_driver *function_driver, char * name, char **interfaces_list, void *privdata) +{ + struct usbd_function_instance *function = NULL; + struct usbd_function_instance **interfaces_array = NULL; + struct usbd_endpoint_request *requestedEndpoints = NULL; + struct usbd_endpoint_map *endpoint_map_array = NULL; + + int i, j, k, l; + char *interface_name; + int endpoints = 0; + int cfg_length = 0; + + TRACE_MSG1(USBD, "%s", name); + THROW_UNLESS((function = usbd_register_function_internal(function_driver, + name, function_composite, interfaces_list, privdata)), error); + + /* list and count interfaces, then allocate interfaces_array. + */ + for (i = 0; (interface_name = function->interfaces_list[i]); i++) { + TRACE_MSG2(USBD, "INTERFACE[%02d] %20s", i, interface_name); + } + function->interfaces = i; + + TRACE_MSG1(USBD, "interfaces: %d", function->interfaces); + + THROW_UNLESS((interfaces_array = (struct usbd_function_instance **) + CKMALLOC (sizeof(struct usbd_function_instance) * function->interfaces, GFP_KERNEL)), error); + function->interfaces_array = interfaces_array; + +#if 0 + /* Find all interfaces and compose the interface list, count the endpoints + * and allocte the endpoint request array. Also find and total descriptor sizes. + */ + for (i = 0; i < function->interfaces; i++) { + struct usbd_function_instance *interface_function; + struct usbd_interface_description *interface_description = interface_function->interface_list; + THROW_UNLESS(interface_function = usbd_find_interface(interfaces_list[i]), error); + + TRACE_MSG4(USBD, "interface_function: %x name: %s driver: %s %d", interface_function, + interface_function->name, + interface_function->function_driver->name, + interface_function->function_driver->endpointsRequested); + + interfaces_array[i] = interface_function; + endpoints += interface_function->function_driver->endpointsRequested; + TRACE_MSG4(USBD, "ENDPOINT [%02d] %20s %02d %02d", + i, interfaces_list[i], interface_function->function_driver->endpointsRequested, endpoints); + + /* iterate across all interface descriptions */ + for (j = 0; j < bNumInterfaces; j++) { + struct usbd_alternate_instance *alternate_instance = interface_description[j]; + + /* iterate across all alternates for this interface */ + for (k = 0; k < interface_description[j].alternates; k++) { + + + + } + } + + } +#endif + function->endpoints = endpoints; + TRACE_MSG2(USBD, "endpoints: %d %d", endpoints, function->endpoints); + + + /* requeset the endpoints from the bus interface driver + */ + THROW_UNLESS((requestedEndpoints = (struct usbd_endpoint_request *) + CKMALLOC (sizeof(struct usbd_endpoint_request) * function->endpoints, GFP_KERNEL)), error); + + function->requestedEndpoints = requestedEndpoints; +#if 0 + /* we need to compose the endpoint request array + */ + for (endpoints = 0, i = 0; i < function->interfaces; i++) { + struct usbd_function_instance *interface_function = interfaces_array[i]; + struct usbd_interface_description *interface_description = interface_function->interface_list; + + for (j = 0; j < interface_function->endpoints; j++) { + memcpy(&requestedEndpoints[endpoints], &interface_function->requestedEndpoints[j], + sizeof(struct usbd_endpoint_request)); + requestedEndpoints[endpoints].interface_num = i; + } + } +#endif + + + /* Insert into function list + */ + LIST_ADD_TAIL (&function->instances, &usbd_function_instances); + return function; + + CATCH(error) { + if (requestedEndpoints) LKFREE(requestedEndpoints); + if (interfaces_array) LKFREE(interfaces_array); + if (function) usbd_deregister_function(function); + return NULL; + } +} + +/*! + * usbd_deregister_function() - de-register a usbd function driver instance + * + * USBD Function drivers call this to de-register with the USBD Core layer. + * @param function_instance + */ +void usbd_deregister_function (struct usbd_function_instance *function_instance) +{ + RETURN_UNLESS (function_instance); + LIST_DEL (function_instance->instances); + if (function_instance->name) LKFREE(function_instance->name); + if (function_instance->endpoint_map_array) LKFREE(function_instance->endpoint_map_array); + LKFREE(function_instance); +} + +/*! + * usbd_find_function_internal() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param name + * @return function instance + * + */ +static struct usbd_function_instance * +usbd_find_function_internal(LIST_NODE *usbd_function_instances, char *name) +{ + int len = name ? strlen(name) : 0; + LIST_NODE *lhd = NULL; + TRACE_MSG1(USBD, "%s", name); + LIST_FOR_EACH (lhd, usbd_function_instances) { + + struct usbd_function_instance *function = LIST_ENTRY (lhd, struct usbd_function_instance, instances); + struct usbd_function_driver *function_driver; + function_driver = function->function_driver; + printk(KERN_ERR"%s: name: %s len: %d function: %s\n", __FUNCTION__, name ? name : "", len, function_driver->name); + CONTINUE_IF(len || strncmp(function_driver->name, name, len)); + + TRACE_MSG5(USBD, "%s %s %d %s %d", name, function->name, len, + function->function_driver->name, + function->function_driver->endpointsRequested); + CONTINUE_IF(len && strncmp(function->name, name, len)); + + TRACE_MSG4(USBD, "FOUND %s %x %s %d", name, function, + function->function_driver->name, + function->function_driver->endpointsRequested); + + return function; + + //struct usbd_function_driver *function_driver = function->function_driver; + //printk(KERN_ERR"%s: name: %s len: %d function: %s\n", __FUNCTION__,name?name : "", len, function_driver->name); + //CONTINUE_IF(len || strncmp(function_driver->name, name, len)); + } + return NULL; +} + + +/*! + * usbd_find_function() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param name + * @return function instance + * + */ +struct usbd_function_instance * +usbd_find_function (char *name) +{ + return usbd_find_function_internal(&usbd_function_instances, name); +} + +/*! + * usbd_default_function() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param name + * @return function instance + * + */ +struct usbd_function_instance * +usbd_default_function (char *name) +{ + return usbd_find_function_internal(&usbd_function_instances, ""); +} + +/*! + * usbd_find_interface() - register a usbd function driver + * + * USBD Function drivers call this to register with the USBD Core layer. + * Return NULL on failure. + * + * @param name + * @return function instance + * + */ +struct usbd_function_instance *usbd_find_interface(char *name) +{ + TRACE_MSG1(USBD, "%s", name); + return usbd_find_function_internal(&usbd_interface_instances, name); +} + + +/*! + * usbd_function_name() - return name of nth function driver + * @param n index to function name + */ +char * usbd_function_name(int n) +{ + struct usbd_function_instance *function = NULL; + LIST_NODE *lhd = NULL; + + LIST_FOR_EACH (lhd, &usbd_function_instances) { + struct usbd_function_driver *function_driver; + function = LIST_ENTRY (lhd, struct usbd_function_instance, instances); + function_driver = function->function_driver; + + TRACE_MSG2(USBD, "name: [%d] %s", n, function_driver->name); + + CONTINUE_IF(n--); + return (char *)function_driver->name; + }; + return NULL; +} + +OTG_EXPORT_SYMBOL(usbd_register_function); +OTG_EXPORT_SYMBOL(usbd_register_interface); +OTG_EXPORT_SYMBOL(usbd_register_composite); +OTG_EXPORT_SYMBOL(usbd_deregister_function); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * String Descriptors: + * usbd_alloc_string_descriptor() + * usbd_free_string_descriptor() + * usbd_get_string_descriptor() + */ + +#define LANGID_ENGLISH "\011" +#define LANGID_US_ENGLISH "\004" +#define LANGIDs LANGID_US_ENGLISH LANGID_ENGLISH + +/*! + * usbd_alloc_string_zero() - allocate a string descriptor and return index number + * + * Find an empty slot in index string array, create a corresponding descriptor + * and return the slot number. + * + * @param str string to allocate + * @return the slot number + */ +static u8 usbd_alloc_string_zero (char *str) +{ + u8 bLength; + u16 *wData; + struct usbd_string_descriptor *string = NULL; + + RETURN_ZERO_IF(usb_strings[0] != NULL); + + bLength = sizeof (struct usbd_string_descriptor) + strlen (str); + + RETURN_ZERO_IF(!(string = (struct usbd_string_descriptor *) CKMALLOC (bLength, GFP_KERNEL))); + + string->bLength = bLength; // set descriptor length + string->bDescriptorType = USB_DT_STRING; // set descriptor type + + for (wData = string->wData; *str; str += 2, wData++) // copy + *wData = (u16) ((str[0] << 8 | str[1])); + + usb_strings[0] = string; // save in string index array + return 0; +} + +int usbd_maxstrings = 40; + +/*! + * usbd_strings_init() - initialize usb strings pool + */ +int usbd_strings_init (void) +{ + //TRACE_MSG0(USBD, "entered"); + RETURN_ZERO_IF(usb_strings); + usbd_maxstrings = MIN(usbd_maxstrings, 254); + + RETURN_EINVAL_IF (!(usb_strings = CKMALLOC (sizeof (struct usbd_string_descriptor *) * usbd_maxstrings, GFP_KERNEL))); + if (usbd_alloc_string_zero (LANGIDs) != 0) { + LKFREE (usb_strings); + return -1; + } + return 0; +} + +/*! + * usbd_strings_exit() - de-initialize usb strings pool + */ +void usbd_strings_exit(void) +{ + int i; + //TRACE_MSG0(USBD, "entered"); + RETURN_IF (!usb_strings); + for (i = 0; i < usbd_maxstrings; i++) + usbd_free_string_descriptor((u8)i); + LKFREE (usb_strings); + usb_strings = NULL; +} + + +/*! + * usbd_get_string_descriptor() - find and return a string descriptor + * + * Find an indexed string and return a pointer to a it. + * @param index of string + * @return pointer to string descriptor or NULL + */ +struct usbd_string_descriptor *usbd_get_string_descriptor (u8 index) +{ + RETURN_NULL_IF (index >= usbd_maxstrings); + return usb_strings[index]; +} + +/*! + * usbd_realloc_string() - allocate a string descriptor and return index number + * + * Find an empty slot in index string array, create a corresponding descriptor + * and return the slot number. + * @param index + * @param str + * @return new index + */ +u8 usbd_realloc_string (u8 index, char *str) +{ + struct usbd_string_descriptor *string; + u8 bLength; + u16 *wData; + + RETURN_ZERO_IF(!str || !strlen (str)); + //TRACE_MSG2(USBD, "index: %d str: %s", index, str); + + // XXX should have semaphores... + + if ((index < usbd_maxstrings) && (string = usb_strings[index])) { + usb_strings[index] = NULL; + LKFREE (string); + } + + bLength = sizeof (struct usbd_string_descriptor) + 2 * strlen (str); + + RETURN_ZERO_IF(!(string = (struct usbd_string_descriptor *)CKMALLOC (bLength, GFP_KERNEL))); + + string->bLength = bLength; + string->bDescriptorType = USB_DT_STRING; + + for (wData = string->wData; *str;) + *wData++ = (u16) (*str++); + + // store in string index array + usb_strings[index] = string; + return index; +} + +/*! + * usbd_alloc_string() - allocate a string descriptor and return index number + * + * Find an empty slot in index string array, create a corresponding descriptor + * and return the slot number. + * @param str + * @return index + * + * XXX rename to usbd_alloc_string_descriptor + */ +u8 usbd_alloc_string (char *str) +{ + int i; + struct usbd_string_descriptor *string = NULL; + u8 bLength; + u16 *wData; + + RETURN_ZERO_IF(!str || !strlen (str)); + + // find an empty string descriptor slot + for (i = 1; i < usbd_maxstrings; i++) { + + CONTINUE_IF (usb_strings[i] != NULL); + + bLength = sizeof (struct usbd_string_descriptor) + 2 * strlen (str); + + RETURN_ZERO_IF(!(string = (struct usbd_string_descriptor *)CKMALLOC (bLength, GFP_KERNEL))); + + string->bLength = bLength; + string->bDescriptorType = USB_DT_STRING; + + for (wData = string->wData; *str;) + *wData++ = (u16) (*str++); + + // store in string index array + usb_strings[i] = string; + return i; + } + return 0; +} + + +/*! + * usbd_free_string_descriptor() - deallocate a string descriptor + * + * Find and remove an allocated string. + * @param index + * + */ +void usbd_free_string_descriptor (u8 index) +{ + struct usbd_string_descriptor *string; + + if ((index < usbd_maxstrings) && (string = usb_strings[index])) { + usb_strings[index] = NULL; + LKFREE (string); + } +} + +OTG_EXPORT_SYMBOL(usbd_realloc_string); +OTG_EXPORT_SYMBOL(usbd_alloc_string); +OTG_EXPORT_SYMBOL(usbd_free_string_descriptor); +OTG_EXPORT_SYMBOL(usbd_get_string_descriptor); +OTG_EXPORT_SYMBOL(usbd_maxstrings); + + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Device Information: + * usbd_high_speed() + * usbd_get_bMaxPower() + * usbd_endpoint_wMaxPacketsize() + * usbd_endpoint_wMaxPacketsize_ep0() + * usbd_endpoint_bEndpointAddress() + * + */ + + +/*! + * usbd_high_speed() - return high speed status + * @return true if operating at high speed + */ +int usbd_high_speed(struct usbd_function_instance *function) +{ + // XXX TODO - per function modifications for composite devices + return function->bus->HighSpeedFlag ? 1 : 0; +} + +/*! + * usbd_get_bMaxPower() - process a received urb + * + * Used by a USB Bus interface driver to pass received device request to + * the appropriate USB Function driver. + * + * @param function + * @param request + * @return non-zero if errror + */ +int usbd_get_bMaxPower(struct usbd_function_instance *function) +{ + struct usbd_bus_instance *bus = function->bus; + return bus->driver->bMaxPower; +} + + +/*! + * usbd_endpoint_wMaxPacketSize() - get maximum packet size for endpoint + * @param function + * @param endpoint_index + * @param hs highspeed flag + * @return endpoint size + */ +int usbd_endpoint_wMaxPacketSize(struct usbd_function_instance *function, int endpoint_index, int hs) +{ + struct usbd_endpoint_map *endpoint_map; + RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); + return le16_to_cpu(endpoint_map[endpoint_index].wMaxPacketSize[hs]); +} + +/*! + * usbd_endpoint_zero_wMaxPacketSize() - get maximum packet size for endpoint zero + * @param function + * @param hs highspeed flag + * @return endpoint size + */ +int usbd_endpoint_zero_wMaxPacketSize(struct usbd_function_instance *function, int hs) +{ + struct usbd_endpoint_map *endpoint_map; + RETURN_ZERO_IF(!(endpoint_map = function->bus->ep0->endpoint_map_array)); + return le16_to_cpu(endpoint_map[0].wMaxPacketSize[hs]); +} + +/*! + * usbd_endpoint_bEndpointAddress() - get endpoint addrsess + * @param function + * @param endpoint_index + * @param hs high speed flag + * @return endpoint address + */ +int usbd_endpoint_bEndpointAddress(struct usbd_function_instance *function, int endpoint_index, int hs) +{ + struct usbd_endpoint_map *endpoint_map; + RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); + return endpoint_map[endpoint_index].bEndpointAddress[hs]; +} + +OTG_EXPORT_SYMBOL(usbd_endpoint_wMaxPacketSize); +OTG_EXPORT_SYMBOL(usbd_endpoint_zero_wMaxPacketSize); +OTG_EXPORT_SYMBOL(usbd_endpoint_bEndpointAddress); +OTG_EXPORT_SYMBOL(usbd_high_speed); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Device Control: + * + * XXX usbd_request_endpoints() + * XXX usbd_configure_endpoints() + * + * usbd_get_device_state() + * usbd_get_device_status() + * usbd_framenum() + * usbd_ticks() + * usbd_elapsed() + */ + +/*! + * usbd_flush_endpoint_irq() - flush urbs from endpoint + * + * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs. + * + * @param endpoint flush urbs on this endpoint + */ +void usbd_flush_endpoint_irq (struct usbd_endpoint_instance *endpoint) +{ + struct usbd_urb *urb; + + if (endpoint->bEndpointAddress) + TRACE_MSG1(USBD, "bEndpointAddress: %02x", endpoint->bEndpointAddress); + + if ((urb = endpoint->tx_urb)) + usbd_cancel_urb(urb); + + for (; (urb = usbd_first_urb_detached_irq (&endpoint->tx)); usbd_cancel_urb(urb)); + + if ((urb = endpoint->rcv_urb)) + usbd_cancel_urb(urb); + + for (; (urb = usbd_first_urb_detached_irq (&endpoint->rdy)); usbd_cancel_urb(urb)); +} + +/*! + * usbd_flush_endpoint() - flush urbs from endpoint + * + * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs. + * + * @param endpoint flush urbs on this endpoint + */ +void usbd_flush_endpoint (struct usbd_endpoint_instance *endpoint) +{ + unsigned long flags; + local_irq_save (flags); + usbd_flush_endpoint_irq(endpoint); + local_irq_restore (flags); +} + +/*! + * usbd_flush_endpoint_address() - flush endpoint + * @param function + * @param endpoint_index + */ +void usbd_flush_endpoint_index (struct usbd_function_instance *function, int endpoint_index) +{ + struct usbd_endpoint_map *endpoint_map; + struct usbd_endpoint_instance *endpoint; + + RETURN_IF(!function); + RETURN_IF(!(endpoint_map = function->endpoint_map_array)); + RETURN_IF(!(endpoint = endpoint_map[endpoint_index].endpoint)); + usbd_flush_endpoint (endpoint); +} + +/*! + * usbd_get_device_state() - return device state + * @param function + * @return current device state + */ +usbd_device_state_t usbd_get_device_state(struct usbd_function_instance *function) +{ + //TRACE_MSG1(USBD, "%d", function->bus->device_state); + return (function && function->bus) ? function->bus->device_state : STATE_UNKNOWN; +} + +/*! + * usbd_get_device_status() - return device status + * @param function + * @return current device status + */ +usbd_device_status_t usbd_get_device_status(struct usbd_function_instance *function) +{ + //TRACE_MSG1(USBD, "%d", function->bus->status); + return (function && function->bus) ? function->bus->status : USBD_UNKNOWN; +} + +/*! + * usbd_framenum() - return framenum + * @param function + * @return current framenum + */ +int usbd_framenum(struct usbd_function_instance * function) +{ + RETURN_ZERO_UNLESS(function); + RETURN_ZERO_UNLESS(function->bus->driver->bops->framenum); + return function->bus->driver->bops->framenum (); +} + +/*! + * usbd_ticks() - return ticks + * @param function + * @return current ticks + */ +u64 usbd_ticks(struct usbd_function_instance *function) +{ + RETURN_ZERO_UNLESS(function); + RETURN_ZERO_UNLESS(function->bus->driver->bops->ticks); + return function->bus->driver->bops->ticks (); +} + +/*! + * usbd_elapsed() - return elapsed + * @param function + * @param t1 + * @param t2 + * @return elapsed uSecs between t1 and t2 + */ +u64 usbd_elapsed(struct usbd_function_instance *function, u64 *t1, u64 *t2) +{ + RETURN_ZERO_UNLESS(function); + RETURN_ZERO_UNLESS(function->bus->driver->bops->elapsed); + return function->bus->driver->bops->elapsed (t1, t2); +} + + +//OTG_EXPORT_SYMBOL(usbd_set_endpoint_halt); +//OTG_EXPORT_SYMBOL(usbd_endpoint_halted); +OTG_EXPORT_SYMBOL(usbd_get_device_state); +OTG_EXPORT_SYMBOL(usbd_get_device_status); +OTG_EXPORT_SYMBOL(usbd_framenum); +OTG_EXPORT_SYMBOL(usbd_ticks); +OTG_EXPORT_SYMBOL(usbd_elapsed); +OTG_EXPORT_SYMBOL(usbd_flush_endpoint); +OTG_EXPORT_SYMBOL(usbd_flush_endpoint_index); + +/* ********************************************************************************************* */ +/* ********************************************************************************************* */ +/*! + * Endpoint I/O: + * usbd_alloc_urb() + * usbd_start_in_urb() + * usbd_start_out_urb() + * usbd_cancel_urb() + * + */ + +/*! + * usbd_alloc_urb() - allocate an URB appropriate for specified endpoint + * + * Allocate an urb structure. The usb device urb structure is used to + * contain all data associated with a transfer, including a setup packet for + * control transfers. + * + * @param function + * @param endpoint_index + * @param length + * @param callback + * @return urb + */ +struct usbd_urb *usbd_alloc_urb (struct usbd_function_instance *function, int endpoint_index, + int length, int (*callback) (struct usbd_urb *, int)) +{ + struct usbd_urb *urb = NULL; + struct usbd_endpoint_map *endpoint_map; + struct usbd_endpoint_instance *endpoint; + unsigned long flags; + + RETURN_NULL_IF(!(endpoint_map = function->endpoint_map_array)); + RETURN_NULL_IF(!(endpoint = endpoint_map[endpoint_index].endpoint)); + + local_irq_save(flags); + THROW_IF (!(urb = (struct usbd_urb *)CKMALLOC (sizeof (struct usbd_urb), GFP_ATOMIC)), error); + urb->endpoint = endpoint; + urb->bus = function->bus; + urb->function_instance = function; + urb->link.prev = urb->link.next = &urb->link; + urb->callback = callback; + urb->buffer_length = urb->actual_length = 0; + + if (length) { + urb->request_length = length; + + /* For receive we always overallocate to ensure that receiving another + * full sized packet when we are only expecting a short packet will + * not overflow the buffer + */ + if (!endpoint->bEndpointAddress || endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + length = ((length / endpoint->wMaxPacketSize) + 1) * endpoint->wMaxPacketSize; + } + urb->buffer_length = length; + + #if 0 + if (urb->endpoint && urb->endpoint->bEndpointAddress && urb->function_instance && + urb->function_instance->function_driver->fops->alloc_urb_data) + { + THROW_IF(urb->function_instance->function_driver->fops->alloc_urb_data (urb, length), error); + } + else + #endif + THROW_IF(!(urb->buffer = (u8 *)CKMALLOC (length, GFP_ATOMIC)), error); + + } + + CATCH(error) { + #if defined(OTG_LINUX) + printk(KERN_ERR"%s: dealloc %p\n", __FUNCTION__, urb); + #endif /* defined(OTG_LINUX) */ + #if defined(OTG_WINCE) + DEBUGMSG(ZONE_INIT, (_T("usbd_alloc_urb: FAILED\n"))); + #endif /* defined(OTG_LINUX) */ + usbd_free_urb(urb); + urb = NULL; + } + local_irq_restore(flags); + return urb; +} + +/*! + * usbd_halt_endpoint() - + * + * @param function function that owns endpoint + * @param endpoint endpoint number + * @return non-zero if endpoint is halted. + */ +int usbd_halt_endpoint (struct usbd_function_instance *function, int endpoint_index) +{ + struct usbd_endpoint_map *endpoint_map; + struct usbd_endpoint_instance *endpoint; + + RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); + RETURN_ZERO_IF(!(endpoint = endpoint_map[endpoint_index].endpoint)); + + return function->bus->driver->bops->halt_endpoint (function->bus, endpoint->bEndpointAddress, endpoint_index, 1); +} + + +/*! + * usbd_alloc_urb_ep0() - allocate an urb for endpoint zero + * @param function + * @param length + * @param callback + * @return urb + */ +struct usbd_urb *usbd_alloc_urb_ep0 (struct usbd_function_instance *function, int length, + int (*callback) (struct usbd_urb *, int)) +{ + return usbd_alloc_urb(function->bus->ep0, 0, length, callback); +} + +/*! + * usbd_free_urb() - deallocate an URB and associated buffer + * + * Deallocate an urb structure and associated data. + * @param urb + */ +void usbd_free_urb (struct usbd_urb *urb) +{ + RETURN_IF (!urb); + if (urb->buffer) { + #if 0 + if (urb->function_instance && urb->function_instance->function_driver->fops->dealloc_urb_data) + urb->function_instance->function_driver->fops->dealloc_urb_data ((void *) urb->buffer); + else + #endif + LKFREE ((void *) urb->buffer); + } + LKFREE (urb); +} + + +/*! + * usbd_start_in_urb() - submit a urb to send + * + * Used by a USB Function driver to submit data to be sent in an urb to the + * appropriate USB Bus driver via the endpoints transmit queue. + * + * @param urb to send + * @return non-zero if error + */ +int usbd_start_in_urb (struct usbd_urb *urb) +{ + RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status)); + if (urb->endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)) { + usbd_urb_finished(urb, SEND_STALLED); + return 0; + } + urb->status = SEND_IN_QUEUE; + #if defined(OTG_LINUX) + urb->jiffies = jiffies; + #endif /* defined(OTG_LINUX) */ + urb_append (&(urb->endpoint->tx), urb); + return urb->bus->driver->bops->start_endpoint_in(urb->bus, urb->endpoint); +} + +/*! + * usbd_start_out_urb() - recycle a received urb + * + * Used by a USB Function interface driver to recycle an urb. + * + * @param urb to process + * @return non-zero if error + */ +int usbd_start_out_urb (struct usbd_urb *urb) +{ + RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status)); + if (urb->endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)) { + usbd_urb_finished(urb, RECV_STALLED); + return 0; + } + urb->actual_length = 0; + urb->status = RECV_IN_QUEUE; + #if defined(OTG_LINUX) + urb->jiffies = jiffies; + #endif /* defined(OTG_LINUX) */ + urb_append (&(urb->endpoint->rdy), urb); + return urb->bus->driver->bops->start_endpoint_out(urb->bus, urb->endpoint); +} + +/*! + * usbd_cancel_urb() - cancel an urb being sent + * + * @param urb to process + * @return non-zero if error + */ +int usbd_cancel_urb (struct usbd_urb *urb) +{ + //TRACE_MSG0(USBD, "entered"); + return urb->bus->driver->bops->cancel_urb_irq (urb); +} + + +/*! + * usbd_endpoint_halted() - return halt status + * + * @param function function that owns endpoint + * @param endpoint endpoint number + * @return non-zero if endpoint is halted. + */ +int usbd_endpoint_halted (struct usbd_function_instance *function, int endpoint_index) +{ + struct usbd_endpoint_instance *endpoint; + struct usbd_endpoint_map *endpoint_map; + //TRACE_MSG0(USBD, "entered"); + // + RETURN_ZERO_UNLESS((endpoint_map = function->endpoint_map_array)); + RETURN_ZERO_UNLESS((endpoint = endpoint_map[endpoint_index].endpoint)); + return function->bus->driver->bops->endpoint_halted (function->bus, endpoint->bEndpointAddress, endpoint_index); +} + + +OTG_EXPORT_SYMBOL(usbd_alloc_urb); +OTG_EXPORT_SYMBOL(usbd_alloc_urb_ep0); +OTG_EXPORT_SYMBOL(usbd_free_urb); + +OTG_EXPORT_SYMBOL(usbd_start_in_urb); +OTG_EXPORT_SYMBOL(usbd_start_out_urb); +OTG_EXPORT_SYMBOL(usbd_cancel_urb); +OTG_EXPORT_SYMBOL(usbd_halt_endpoint); + +/* ********************************************************************************************* */ + +/*! + * usbd_function_get_privdata() - get private data pointer + * @param function + * @return void * pointer to private data + */ +void *usbd_function_get_privdata(struct usbd_function_instance *function) +{ + return(function->privdata); +} + +/*! + * usbd_function_set_privdata() - set private data structure in function + * @param function + * @param privdata + */ +void usbd_function_set_privdata(struct usbd_function_instance *function, void *privdata) +{ + function->privdata = privdata; +} + + + + +/*! + * usbd_endpoint_transferSize() - get transferSize for endpoint + * @param function + * @param endpoint_index + * @param hs highspeed flag + * @return transfer size + */ +int usbd_endpoint_transferSize(struct usbd_function_instance *function, int endpoint_index, int hs) +{ + struct usbd_endpoint_map *endpoint_map; + RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); + return endpoint_map[endpoint_index].transferSize[hs]; +} + +/*! + * usbd_endpoint_update() - update endpoint address and size + * @param function + * @param endpoint_index + * @param endpoint descriptor + * @param hs high speed flag + */ +void usbd_endpoint_update(struct usbd_function_instance *function, int endpoint_index, + struct usbd_endpoint_descriptor *endpoint, int hs) +{ + endpoint->bEndpointAddress = usbd_endpoint_bEndpointAddress(function, endpoint_index, hs); + endpoint->wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, endpoint_index, hs); +} + +/*! + * usbd_otg_bmattributes() - return attributes + * @param function + * @return endpoint attributes + */ +int usbd_otg_bmattributes(struct usbd_function_instance *function) +{ + // XXX TODO - per function modifications for composite devices + return function->bus->otg_bmAttributes; +} + +OTG_EXPORT_SYMBOL(usbd_function_get_privdata); +OTG_EXPORT_SYMBOL(usbd_function_set_privdata); +OTG_EXPORT_SYMBOL(usbd_endpoint_transferSize); +OTG_EXPORT_SYMBOL(usbd_otg_bmattributes); +OTG_EXPORT_SYMBOL(usbd_endpoint_update); diff -uNr linux/drivers/no-otg/otgcore/usbp-procfs.c linux/drivers/otg/otgcore/usbp-procfs.c --- linux/drivers/no-otg/otgcore/usbp-procfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otgcore/usbp-procfs.c 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,302 @@ +/* + * otg/otgcore/usbp-procfs.c - USB Device Core Layer + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otgcore/usbp-procfs.c + * @brief Implements /proc/usbd-functions, which displays descriptors for the current selected function. + * + * + * @ingroup USBP + */ + +#include +#include + +#ifdef LINUX24 +EXPORT_NO_SYMBOLS; +#endif + + +MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MOD_DESCRIPTION ("USB Device Core Support Procfs"); +EMBED_LICENSE(); + +#include +#include +#include + +EMBED_USBD_INFO ("usbdprocfs 2.0-beta"); + +#define MAX_INTERFACES 2 + +extern struct usbd_bus_instance *usbd_bus_instance; + +/* Proc Filesystem *************************************************************************** */ +/* * + * dohex + * + */ +static void dohexdigit (char *cp, unsigned char val) +{ + if (val < 0xa) { + *cp = val + '0'; + } else if ((val >= 0x0a) && (val <= 0x0f)) { + *cp = val - 0x0a + 'a'; + } +} + +/* * + * dohex + * + */ +static void dohexval (char *cp, unsigned char val) +{ + dohexdigit (cp++, val >> 4); + dohexdigit (cp++, val & 0xf); +} + +/* * + * dump_descriptor + */ +static int dump_descriptor (char *buf, char *sp) +{ + int num; + int len = 0; + + RETURN_ZERO_UNLESS(sp); + + num = *sp; + + //printk(KERN_INFO"%s: %p %d %d %d\n", __FUNCTION__, buf, *buf, buf[0], num); + + while (sp && num--) { + dohexval (buf, *sp++); + buf += 2; + *buf++ = ' '; + len += 3; + } + len++; + *buf = '\n'; + return len; +} + +/* dump_descriptors + */ +static int dump_config_descriptor(char *buf, char *sp) +{ + struct usbd_configuration_descriptor *config = (struct usbd_configuration_descriptor *) sp; + + int wTotalLength = le16_to_cpu(config->wTotalLength); + int bConfigurationValue = config->bConfigurationValue; + int interface_num; + int class_num; + int endpoint_num; + int total; + + interface_num = class_num = endpoint_num = 0; + + for (total = 0; wTotalLength > 0; ) { + //printk(KERN_INFO"%s: wTotalLength: %d total: %d bLength: %d type: %d\n", + // __FUNCTION__, wTotalLength, total, sp[0], sp[1]); + BREAK_UNLESS(sp[0]); + switch (sp[1]) { + case USB_DT_CONFIGURATION: + interface_num = class_num = endpoint_num = 0; + total += sprintf(buf + total, "\nConfiguration descriptor [%d ] ", + bConfigurationValue); + break; + case USB_DT_INTERFACE: + class_num = 0; + total += sprintf(buf + total, "\nInterface descriptor [%d:%d:%d ] ", + bConfigurationValue, ++interface_num, class_num); + break; + case USB_DT_ENDPOINT: + class_num = endpoint_num = 0; + total += sprintf(buf + total, "Endpoint descriptor [%d:%d:%d:%d] ", + bConfigurationValue, interface_num, class_num, ++endpoint_num); + break; + case USB_DT_OTG: + class_num = endpoint_num = 0; + total += sprintf(buf + total, "OTG descriptor [%d ] ", bConfigurationValue); + break; + default: + endpoint_num = 0; + total += sprintf(buf + total, "Class descriptor [%d:%d:%d ] ", + bConfigurationValue, interface_num, ++class_num); + break; + } + total += dump_descriptor(buf + total, sp); + wTotalLength -= sp[0]; + sp += sp[0]; + } + total += sprintf(buf + total, "\n"); + return total; +} + +/*! * + * usbd_device_proc_read - implement proc file system read. + * @param file + * @param buf + * @param count + * @param pos + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return + * statistics for. + */ +static ssize_t usbd_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + u8 config_descriptor[512]; + int config_size; + + //struct list_head *lhd; + + // get a page, max 4095 bytes of data... +// if (!(page = GET_FREE_PAGE(GFP_KERNEL))) { + + if (!(page = GET_KERNEL_PAGE())) { + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + if (index == 0) { + len += sprintf ((char *) page + len, "usb-device list\n"); + } + + //printk(KERN_INFO"%s: index: %d len: %d\n", __FUNCTION__, index, len); + + if (usbd_bus_instance && usbd_bus_instance->function_instance) { + int configuration = index; + struct usbd_function_instance *function_instance = usbd_bus_instance->function_instance; + struct usbd_function_driver *function_driver = function_instance->function_driver; + struct usbd_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array; + if (configuration_instance_array) { + + if (index == 0) { + len += sprintf ((char *) page + len, "\nDevice descriptor "); + len += dump_descriptor ((char *) page + len, (char *) function_driver->device_descriptor); +#ifdef CONFIG_OTG_HIGH_SPEED + len += sprintf ((char *) page + len, "\nDevice Qualifier descriptor "); + len += dump_descriptor ((char *) page + len, + (char *) function_driver->device_qualifier_descriptor); +#endif + } + if (configuration < function_driver->bNumConfigurations) { + + if ((config_size = usbd_old_get_descriptor(usbd_bus_instance->ep0, config_descriptor, + sizeof(config_descriptor), + USB_DT_CONFIGURATION, 0)) > index) { + len += dump_config_descriptor((char *)page + len, config_descriptor ); + } +#ifdef CONFIG_OTG_HIGH_SPEED + if ((config_size = usbd_old_get_descriptor(usbd_bus_instance->ep0, config_descriptor, + sizeof(config_descriptor), + USB_DT_OTHER_SPEED_CONFIGURATION, index)) > 0) { + len += dump_config_descriptor((char *)page + len, config_descriptor ); + } + +#endif + } + else if (configuration == function_driver->bNumConfigurations) { + int i; + int k; + struct usbd_string_descriptor *string_descriptor; + + //len += sprintf ((char *) page + len, "\n\n"); + + if ((string_descriptor = usbd_get_string_descriptor (0)) != NULL) { + len += sprintf ((char *) page + len, "String [%2d] ", 0); + + for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { + len += sprintf ((char *) page + len, "%02x %02x ", + string_descriptor->wData[k] >> 8, + string_descriptor->wData[k] & 0xff); + len++; + } + len += sprintf ((char *) page + len, "\n"); + } + + for (i = 1; i < usbd_maxstrings; i++) { + + if ((string_descriptor = usbd_get_string_descriptor (i)) != NULL) { + + len += sprintf((char *)page+len, "String [%2d:%2d] ", + i, string_descriptor->bLength); + + // bLength = sizeof(struct usbd_string_descriptor) + 2*strlen(str)-2; + + for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { + *(char *) (page + len) = (char) string_descriptor->wData[k]; + len++; + } + len += sprintf ((char *) page + len, "\n"); + } + } + len += sprintf((char *)page + len, "\n--\n"); + } + } + } + + //printk(KERN_INFO"%s: len: %d count: %d\n", __FUNCTION__, len, count); + + if (len > count) { + //printk(KERN_INFO"%s: len > count\n", __FUNCTION__); + //printk(KERN_INFO"%s", page); + len = -EINVAL; + } + else if ((len > 0) && copy_to_user (buf, (char *) page, len)) { + //printk(KERN_INFO"%s: EFAULT\n", __FUNCTION__); + len = -EFAULT; + } + else { + //printk(KERN_INFO"%s: OK\n", __FUNCTION__); + } + free_page (page); + return len; +} + +/* Module init ******************************************************************************* */ +#if defined(OTG_WINCE) + +#else /* defined(OTG_WINCE) */ + +static struct file_operations usbd_device_proc_operations_functions = { + read:usbd_device_proc_read_functions, +}; + +static int usbd_procfs_init (void) +{ + struct proc_dir_entry *p; + // create proc filesystem entries + if ((p = create_proc_entry ("usb-functions", 0, 0)) == NULL) + return -ENOMEM; + p->proc_fops = &usbd_device_proc_operations_functions; + return 0; +} + +static void usbd_procfs_exit (void) +{ + // remove proc filesystem entry + remove_proc_entry ("usb-functions", NULL); +} + +module_init (usbd_procfs_init); +module_exit (usbd_procfs_exit); + +#endif /* defined(OTG_WINCE) */ + diff -uNr linux/drivers/no-otg/otghw/atlas-hardware.h linux/drivers/otg/otghw/atlas-hardware.h --- linux/drivers/no-otg/otghw/atlas-hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/atlas-hardware.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,59 @@ +/* + * otg/include/atlas-hardware.h -- Atlas Transceiver hardware specific defines + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @defgroup ATLAS + * @ingroup tcdgroup + */ +/*! + * @file otghw/atlas-hardware.h + * @brief Hardware defines for Freescale USBOTG Hardware + * + * @ingroup ATLAS + */ + +/* References are to: + * + * Atlas DTS 0.7 - 04/02/27 + */ + +/* + * SPI Register Summary + */ + +#define ATLAS_FSENB (1 << 0) +#define ATLAS_USB_SUSPEND (1 << 1) +#define ATLAS_USB_PU (1 << 2) +#define ATLAS_UDP_PD (1 << 3) +#define ATLAS_UDM_PD (1 << 4) +#define ATLAS_PULLOVR (1 << 5) +#define ATLAS_VUSB_EN (1 << 6) +#define ATLAS_USB_PS (1 << 7) +#define ATLAS_VBUS_REG_EN (1 << 8) +#define ATLAS_VBUS_PD_ENB (1 << 9) +#define ATLAS_CURRLIM (1 << 10) +#define ATLAS_SE0_CONN (1 << 11) +#define ATLAS_DLP_SRP (1 << 12) +#define ATLAS_USB_XCVR_EN (1 << 13) +#define ATLAS_VBUS_REG_LVL (1 << 14) + +#define ATLAS_DATA_MODE_MASK (3 << 15) +#define ATLAS_DATA_MODE_USE (0 << 15) +#define ATLAS_DATA_MODE_BSE (1 << 15) +#define ATLAS_DATA_MODE_DM (2 << 15) +#define ATLAS_DATA_MODE_NU (3 << 15) + +#define ATLAS_RS232ENB (1 << 17) +#define ATLAS_ID_INTERRUPT (1 << 20) +#define ATLAS_AUDIO_EN (1 << 21) +#define ATLAS_STEREO_EN (1 << 22) + + diff -uNr linux/drivers/no-otg/otghw/bvd-hardware.h linux/drivers/otg/otghw/bvd-hardware.h --- linux/drivers/no-otg/otghw/bvd-hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/bvd-hardware.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,601 @@ +/* + * otg/otghw/bvd-hardware.h + * + * Copyright (c) 2004 Belcarra + * + * + * TODO + * + * 1. see if using UDCCSR0_ACM/AREN will help + * + */ +/*! + * @file otghw/bvd-hardware.h + * @brief Hardware defines for Intel PXA-270 Hardware + * + * @ingroup BVD + */ + + +#ifndef UDCOTGICR +#define UDCOTGICR __REG(0x40600018) /* UDC On-The-Go interrupt control */ +#endif + +#ifndef UDCOTGISR +#define UDCOTGISR __REG(0x4060001c) + +#define UDCOTGISR_IRSF (1 << 24) /* OTG SET_FEATURE command recvd */ +#define UDCOTGISR_IRXR (1 << 17) /* Extra Transciever Interrupt Rising Edge Interrupt Enable */ +#define UDCOTGISR_IRXF (1 << 16) /* Extra Transciever Interrupt Falling Edge Interrupt Enable */ +#define UDCOTGISR_IRVV40R (1 << 9) /* OTG Vbus Valid 4.0V Rising Edge Interrupt Enable */ +#define UDCOTGISR_IRVV40F (1 << 8) /* OTG Vbus Valid 4.0V Falling Edge Interrupt Enable */ +#define UDCOTGISR_IRVV44R (1 << 7) /* OTG Vbus Valid 4.4V Rising Edge Interrupt Enable */ +#define UDCOTGISR_IRVV44F (1 << 6) /* OTG Vbus Valid 4.4V Falling Edge Interrupt Enable */ +#define UDCOTGISR_IRSVR (1 << 5) /* OTG Session Valid Rising Edge Interrupt Enable */ +#define UDCOTGISR_IRSVF (1 << 4) /* OTG Session Valid Falling Edge Interrupt Enable */ +#define UDCOTGISR_IRSDR (1 << 3) /* OTG A-Device SRP Detect Rising Edge Interrupt Enable */ +#define UDCOTGISR_IRSDF (1 << 2) /* OTG A-Device SRP Detect Falling Edge Interrupt Enable */ +#define UDCOTGISR_IRIDR (1 << 1) /* OTG ID Change Rising Edge Interrupt Enable */ +#define UDCOTGISR_IRIDF (1 << 0) /* OTG ID Change Falling Edge */ + + +#endif + + +#ifndef UP2OCR +#define UP2OCR __REG(0x40600020) + +#define UP2OCR_SEOS_OFF (0x00 << 24) +#define UP2OCR_SEOS_DSEO (0x02 << 24) +#define UP2OCR_SEOS_HSEO (0x03 << 24) +#define UP2OCR_SEOS_DEXT (0x04 << 24) +#define UP2OCR_SEOS_HEXT (0x05 << 24) +#define UP2OCR_SEOS_DINT (0x06 << 24) +#define UP2OCR_SEOS_HINT (0x07 << 24) + +#define UP2OCR_HXOE (0x1 << 17) +#define UP2OCR_HXS (0x1 << 16) + +#define UP2OCR_IDON (0x1 << 10) +#define UP2OCR_EXSUS (0x1 << 9) +#define UP2OCR_EXSP (0x1 << 8) +#define UP2OCR_DMPUBE (0x1 << 7) +#define UP2OCR_DPPUBE (0x1 << 6) +#define UP2OCR_DMPUE (0x1 << 5) +#define UP2OCR_DPPUE (0x1 << 4) +#define UP2OCR_DMPDE (0x1 << 3) +#define UP2OCR_DPPDE (0x1 << 2) +#define UP2OCR_CPVPE (0x1 << 1) +#define UP2OCR_CPVEN (0x1 << 0) + +#endif + +#ifndef UP3OCR +#define UP3OCR __REG(0x40600024) +#endif + +/* + * UDCCSR0 Bit Definitions C.f. Sheet 1 or 3 + */ +#define UDCCSR0_ACM (1 << 9) +#define UDCCSR0_AREN (1 << 8) + + +/* + * Mainstone Miscellanous Write Register (MSCWR2) C.f. 3.2.2.6 (DVK) + * + * USB_OTG_RST enable external usb otg transceiver + * USB_OTG_SEL enable usb otg (disables ffuart) + * nUSBC_SC enable usb client detection + */ +#ifndef MSCWR2_USB_OTG_SEL +#define MSCWR2_USB_OTG_SEL MST_MSCWR2_USB_OTG_SEL +#endif +#ifndef MSCWR2_USB_OTG_RST +#define MSCWR2_USB_OTG_RST MST_MSCWR2_USB_OTG_RST +#endif +#ifndef MSCRD1_USB_CBL +#define MSCRD1_USB_CBL MST_MSCRD_USB_CBL +#endif +#ifndef MSCWR2_nUSBC_SC +#define MSCWR2_nUSBC_SC MST_MSCWR2_nUSBC_SC +#endif + +/* + * USB_P2_N GPIO configuration - C.f. 24.2 + * + * These should be in + */ +#define GPIO34_USB_P2_2 (34 | GPIO_ALT_FN_1_OUT) +#define GPIO35_USB_P2_1 (35 | GPIO_ALT_FN_2_IN) +#define GPIO36_USB_P2_4 (36 | GPIO_ALT_FN_1_OUT) +#define GPIO37_USB_P2_8 (37 | GPIO_ALT_FN_1_OUT) +#define GPIO38_USB_P2_3 (38 | GPIO_ALT_FN_3_IN) +#define GPIO39_USB_P2_6 (39 | GPIO_ALT_FN_1_OUT) +#define GPIO40_USB_P2_5 (40 | GPIO_ALT_FN_3_IN) +#define GPIO41_USB_P2_7 (41 | GPIO_ALT_FN_2_IN) + + +#if 0 +/* UDC endpiont0 states */ +#define EP0_WAIT_FOR_SETUP 0 +#define EP0_DATA_STATE_XMIT 1 +#define EP0_DATA_STATE_NEED_ZLP 2 +#define EP0_WAIT_FOR_OUT_STATUS 3 + +/* UDC register definitions */ +#define UDCCR __REG(0x40600000) /* UDC Control Register */ +#define UDCICR0 __REG(0x40600004) /* UDC Interrupt Control Register0 */ +#define UDCICR1 __REG(0x40600008) /* UDC Interrupt Control Register1 */ +#define UDCISR0 __REG(0x4060000C) /* UDC Interrupt Status Register 0 */ +#define UDCISR1 __REG(0x40600010) /* UDC Interrupt Status Register 1 */ +#define UDCFNR __REG(0x40600014) /* UDC Frame Number Register */ + +#define UDCCSR0 __REG(0x40600100) /* UDC Control/Status register - Endpoint 0 */ +#define UDCCSRA __REG(0x40600104) /* UDC Control/Status register - Endpoint A */ +#define UDCCSRB __REG(0x40600108) /* UDC Control/Status register - Endpoint B */ +#define UDCCSRC __REG(0x4060010C) /* UDC Control/Status register - Endpoint C */ +#define UDCCSRD __REG(0x40600110) /* UDC Control/Status register - Endpoint D */ +#define UDCCSRE __REG(0x40600114) /* UDC Control/Status register - Endpoint E */ +#define UDCCSRF __REG(0x40600118) /* UDC Control/Status register - Endpoint F */ +#define UDCCSRG __REG(0x4060011C) /* UDC Control/Status register - Endpoint G */ +#define UDCCSRH __REG(0x40600120) /* UDC Control/Status register - Endpoint H */ +#define UDCCSRI __REG(0x40600124) /* UDC Control/Status register - Endpoint I */ +#define UDCCSRJ __REG(0x40600128) /* UDC Control/Status register - Endpoint J */ +#define UDCCSRK __REG(0x4060012C) /* UDC Control/Status register - Endpoint K */ +#define UDCCSRL __REG(0x40600130) /* UDC Control/Status register - Endpoint L */ +#define UDCCSRM __REG(0x40600134) /* UDC Control/Status register - Endpoint M */ +#define UDCCSRN __REG(0x40600138) /* UDC Control/Status register - Endpoint N */ +#define UDCCSRP __REG(0x4060013C) /* UDC Control/Status register - Endpoint P */ +#define UDCCSRQ __REG(0x40600140) /* UDC Control/Status register - Endpoint Q */ +#define UDCCSRR __REG(0x40600144) /* UDC Control/Status register - Endpoint R */ +#define UDCCSRS __REG(0x40600148) /* UDC Control/Status register - Endpoint S */ +#define UDCCSRT __REG(0x4060014C) /* UDC Control/Status register - Endpoint T */ +#define UDCCSRU __REG(0x40600150) /* UDC Control/Status register - Endpoint U */ +#define UDCCSRV __REG(0x40600154) /* UDC Control/Status register - Endpoint V */ +#define UDCCSRW __REG(0x40600158) /* UDC Control/Status register - Endpoint W */ +#define UDCCSRX __REG(0x4060015C) /* UDC Control/Status register - Endpoint X */ + +#define UDCBCR0 __REG(0x40600200) /* Byte Count Register - EP0 */ +#define UDCBCRA __REG(0x40600204) /* Byte Count Register - EPA */ +#define UDCBCRB __REG(0x40600208) /* Byte Count Register - EPB */ +#define UDCBCRC __REG(0x4060020C) /* Byte Count Register - EPC */ +#define UDCBCRD __REG(0x40600210) /* Byte Count Register - EPD */ +#define UDCBCRE __REG(0x40600214) /* Byte Count Register - EPE */ +#define UDCBCRF __REG(0x40600218) /* Byte Count Register - EPF */ +#define UDCBCRG __REG(0x4060021C) /* Byte Count Register - EPG */ +#define UDCBCRH __REG(0x40600220) /* Byte Count Register - EPH */ +#define UDCBCRI __REG(0x40600224) /* Byte Count Register - EPI */ +#define UDCBCRJ __REG(0x40600228) /* Byte Count Register - EPJ */ +#define UDCBCRK __REG(0x4060022C) /* Byte Count Register - EPK */ +#define UDCBCRL __REG(0x40600230) /* Byte Count Register - EPL */ +#define UDCBCRM __REG(0x40600234) /* Byte Count Register - EPM */ +#define UDCBCRN __REG(0x40600238) /* Byte Count Register - EPN */ +#define UDCBCRP __REG(0x4060023C) /* Byte Count Register - EPP */ +#define UDCBCRQ __REG(0x40600240) /* Byte Count Register - EPQ */ +#define UDCBCRR __REG(0x40600244) /* Byte Count Register - EPR */ +#define UDCBCRS __REG(0x40600248) /* Byte Count Register - EPS */ +#define UDCBCRT __REG(0x4060024C) /* Byte Count Register - EPT */ +#define UDCBCRU __REG(0x40600250) /* Byte Count Register - EPU */ +#define UDCBCRV __REG(0x40600254) /* Byte Count Register - EPV */ +#define UDCBCRW __REG(0x40600258) /* Byte Count Register - EPW */ +#define UDCBCRX __REG(0x4060025C) /* Byte Count Register - EPX */ + +#define UDCDR0 __REG(0x40600300) /* Data Register - EP0 */ +#define UDCDRA __REG(0x40600304) /* Data Register - EPA */ +#define UDCDRB __REG(0x40600308) /* Data Register - EPB */ +#define UDCDRC __REG(0x4060030C) /* Data Register - EPC */ +#define UDCDRD __REG(0x40600310) /* Data Register - EPD */ +#define UDCDRE __REG(0x40600314) /* Data Register - EPE */ +#define UDCDRF __REG(0x40600318) /* Data Register - EPF */ +#define UDCDRG __REG(0x4060031C) /* Data Register - EPG */ +#define UDCDRH __REG(0x40600320) /* Data Register - EPH */ +#define UDCDRI __REG(0x40600324) /* Data Register - EPI */ +#define UDCDRJ __REG(0x40600328) /* Data Register - EPJ */ +#define UDCDRK __REG(0x4060032C) /* Data Register - EPK */ +#define UDCDRL __REG(0x40600330) /* Data Register - EPL */ +#define UDCDRM __REG(0x40600334) /* Data Register - EPM */ +#define UDCDRN __REG(0x40600338) /* Data Register - EPN */ +#define UDCDRP __REG(0x4060033C) /* Data Register - EPP */ +#define UDCDRQ __REG(0x40600340) /* Data Register - EPQ */ +#define UDCDRR __REG(0x40600344) /* Data Register - EPR */ +#define UDCDRS __REG(0x40600348) /* Data Register - EPS */ +#define UDCDRT __REG(0x4060034C) /* Data Register - EPT */ +#define UDCDRU __REG(0x40600350) /* Data Register - EPU */ +#define UDCDRV __REG(0x40600354) /* Data Register - EPV */ +#define UDCDRW __REG(0x40600358) /* Data Register - EPW */ +#define UDCDRX __REG(0x4060035C) /* Data Register - EPX */ + +#define UDCCRA __REG(0x40600404) /* Configuration register EPA */ +#define UDCCRB __REG(0x40600408) /* Configuration register EPB */ +#define UDCCRC __REG(0x4060040C) /* Configuration register EPC */ +#define UDCCRD __REG(0x40600410) /* Configuration register EPD */ +#define UDCCRE __REG(0x40600414) /* Configuration register EPE */ +#define UDCCRF __REG(0x40600418) /* Configuration register EPF */ +#define UDCCRG __REG(0x4060041C) /* Configuration register EPG */ +#define UDCCRH __REG(0x40600420) /* Configuration register EPH */ +#define UDCCRI __REG(0x40600424) /* Configuration register EPI */ +#define UDCCRJ __REG(0x40600428) /* Configuration register EPJ */ +#define UDCCRK __REG(0x4060042C) /* Configuration register EPK */ +#define UDCCRL __REG(0x40600430) /* Configuration register EPL */ +#define UDCCRM __REG(0x40600434) /* Configuration register EPM */ +#define UDCCRN __REG(0x40600438) /* Configuration register EPN */ +#define UDCCRP __REG(0x4060043C) /* Configuration register EPP */ +#define UDCCRQ __REG(0x40600440) /* Configuration register EPQ */ +#define UDCCRR __REG(0x40600444) /* Configuration register EPR */ +#define UDCCRS __REG(0x40600448) /* Configuration register EPS */ +#define UDCCRT __REG(0x4060044C) /* Configuration register EPT */ +#define UDCCRU __REG(0x40600450) /* Configuration register EPU */ +#define UDCCRV __REG(0x40600454) /* Configuration register EPV */ +#define UDCCRW __REG(0x40600458) /* Configuration register EPW */ +#define UDCCRX __REG(0x4060045C) /* Configuration register EPX */ + +/* UDC Control Register (UDCCR) */ +#define UDCCR_DWRE (1 <<16) /* Device Remote Wake-up Feature */ +#define UDCCR_ACN (1 <<11) /* Active UDC Configuration Number */ +#define UDCCR_AIN (1 << 8) /* Active UDC Interface Number */ +#define UDCCR_AAISN (1 << 5) /* Active UDC Alternative Interface Setting Number */ +#define UDCCR_SMAC (1 << 4) /* Switch Endpoint Memory to Active Configuration */ +#define UDCCR_EMCE (1 << 3) /* Endpoint Meory Configuration Error */ +#define UDCCR_UDR (1 << 2) /* UDC Resume */ +#define UDCCR_UDA (1 << 1) /* UDC Active */ +#define UDCCR_UDE (1 << 0) /* UDC Enable */ + +#define UDCCR_ACN_MASK 0x3 +#define UDCCR_AIN_MASK 0x7 +#define UDCCR_AAISN_MASK 0x7 + +#define UDCICR0_IE0 (1<<0) /* EP0 Interrupt Enable */ +#define UDCICR0_IEA (1<<2) /* EPA Interrupt Enable */ +#define UDCICR0_IEB (1<<4) /* EPB Interrupt Enable */ +#define UDCICR0_IEC (1<<6) /* EPC Interrupt Enable */ +#define UDCICR0_IED (1<<8) /* EPD Interrupt Enable */ +#define UDCICR0_IEE (1<<10) /* EPE Interrupt Enable */ +#define UDCICR0_IEF (1<<12) /* EPF Interrupt Enable */ +#define UDCICR0_IEG (1<<14) /* EPG Interrupt Enable */ +#define UDCICR0_IEH (1<<16) /* EPH Interrupt Enable */ +#define UDCICR0_IEI (1<<18) /* EPI Interrupt Enable */ +#define UDCICR0_IEJ (1<<20) /* EPJ Interrupt Enable */ +#define UDCICR0_IEK (1<<22) /* EPK Interrupt Enable */ +#define UDCICR0_IEL (1<<24) /* EPL Interrupt Enable */ +#define UDCICR0_IEM (1<<26) /* EPM Interrupt Enable */ +#define UDCICR0_IEN (1<<28) /* EPN Interrupt Enable */ +#define UDCICR0_IEP (1<<30) /* EPP Interrupt Enable */ +#define UDCICR1_IEQ (1<<0) /* EPQ Interrupt Enable */ +#define UDCICR1_IER (1<<2) /* EPR Interrupt Enable */ +#define UDCICR1_IES (1<<4) /* EPS Interrupt Enable */ +#define UDCICR1_IET (1<<6) /* EPT Interrupt Enable */ +#define UDCICR1_IEU (1<<8) /* EPU Interrupt Enable */ +#define UDCICR1_IEV (1<<10) /* EPV Interrupt Enable */ +#define UDCICR1_IEW (1<<12) /* EPW Interrupt Enable */ +#define UDCICR1_IEX (1<<14) /* EPX Interrupt Enable */ +#define UDCICR1_IERS (1<<27) /* Reset */ +#define UDCICR1_IESU (1<<28) /* Suspend */ +#define UDCICR1_IERU (1<<29) /* Resume */ +#define UDCICR1_IESOF (1<<30) /* Start Of Frame */ +#define UDCICR1_IECC (1<<31) /* Configuration Changed */ + +#define UDCISR0_IE0 (1<<0) /* EP0 Interrupt Enable */ +#define UDCISR0_IEA (1<<2) /* EPA Interrupt Enable */ +#define UDCISR0_IEB (1<<4) /* EPB Interrupt Enable */ +#define UDCISR0_IEC (1<<6) /* EPC Interrupt Enable */ +#define UDCISR0_IED (1<<8) /* EPD Interrupt Enable */ +#define UDCISR0_IEE (1<<10) /* EPE Interrupt Enable */ +#define UDCISR0_IEF (1<<12) /* EPF Interrupt Enable */ +#define UDCISR0_IEG (1<<14) /* EPG Interrupt Enable */ +#define UDCISR0_IEH (1<<16) /* EPH Interrupt Enable */ +#define UDCISR0_IEI (1<<18) /* EPI Interrupt Enable */ +#define UDCISR0_IEJ (1<<20) /* EPJ Interrupt Enable */ +#define UDCISR0_IEK (1<<22) /* EPK Interrupt Enable */ +#define UDCISR0_IEL (1<<24) /* EPL Interrupt Enable */ +#define UDCISR0_IEM (1<<26) /* EPM Interrupt Enable */ +#define UDCISR0_IEN (1<<28) /* EPN Interrupt Enable */ +#define UDCISR0_IEP (1<<30) /* EPP Interrupt Enable */ +#define UDCISR1_IEQ (1<<0) /* EPQ Interrupt Enable */ +#define UDCISR1_IER (1<<2) /* EPR Interrupt Enable */ +#define UDCISR1_IES (1<<4) /* EPS Interrupt Enable */ +#define UDCISR1_IET (1<<6) /* EPT Interrupt Enable */ +#define UDCISR1_IEU (1<<8) /* EPU Interrupt Enable */ +#define UDCISR1_IEV (1<<10) /* EPV Interrupt Enable */ +#define UDCISR1_IEW (1<<12) /* EPW Interrupt Enable */ +#define UDCISR1_IEX (1<<14) /* EPX Interrupt Enable */ +#define UDCISR1_IERS (1<<27) /* Reset */ +#define UDCISR1_IESU (1<<28) /* Suspend */ +#define UDCISR1_IERU (1<<29) /* Resume */ +#define UDCISR1_IESOF (1<<30) /* Start Of Frame */ +#define UDCISR1_IECC (1<<31) /* Configuration Changed */ + +#define UDC_INT_FIFOERROR (0x2) +#define UDC_INT_PACKETCMP (0x1) + +#define UDC_FNR_MASK (0x7ff) + +/* UDC Endpoint 0 Control Status Register (UDCCS0) */ +#define UDCCSR0_OPR (1 << 0) /* OUT packet ready */ +#define UDCCSR0_IPR (1 << 1) /* IN packet ready */ +#define UDCCSR0_FTF (1 << 2) /* Flush Tx FIFO */ +#define UDCCSR0_DME (1 << 3) /* DMA Enable */ +#define UDCCSR0_SST (1 << 4) /* Sent stall */ +#define UDCCSR0_FST (1 << 5) /* Force stall */ +#define UDCCSR0_RNE (1 << 6) /* Receive FIFO not empty */ +#define UDCCSR0_SA (1 << 7) /* Setup Active */ + +/* UDC Endpoint A-X */ +#define UDCCSR_FS (1 << 0) /* FIFO service */ +#define UDCCSR_PC (1 << 1) /* Packet complete */ +#define UDCCSR_TRN (1 << 2) /* Tx/Rx NAK */ +#define UDCCSR_DME (1 << 3) /* DMA Enable */ +#define UDCCSR_SST (1 << 4) /* Sent STALL */ +#define UDCCSR_FST (1 << 5) /* Force STALL */ +#define UDCCSR_BNE (1 << 6) /* Buffer Not Empty */ +#define UDCCSR_BNF (1 << 6) /* Buffer Not Full */ +#define UDCCSR_SP (1 << 7) /* Short packet */ +#define UDCCSR_FEF (1 << 8) /* Flush Endpoint FIFO */ +#define UDCCSR_DPE (1 << 9) /* Data Packet Error */ + +#define UDCCSR_WR_MASK (UDCCSR_DME|UDCCSR_FST) +#define UDC_BCR_MASK (0x3ff) + +#define UDCCONR_EE (1 << 0) /* Endpoint Enable */ +#define UDCCONR_DE (1 << 1) /* Double-buffering Enable */ +#define UDCCONR_MPS (1 << 2) /* Maximum Packet Size */ +#define UDCCONR_ED (1 <<12) /* USB Endpoint Direction */ +#define UDCCONR_ET (1 <<13) /* Endpoint Type */ +#define UDCCONR_EN (1 <<15) /* Endpoint Number */ +#define UDCCONR_AISN (1 <<19) /* Alternate Interface Number */ +#define UDCCONR_IN (1 <<22) /* Interface Number */ +#define UDCCONR_CN (1 <<25) /* Configuration Number */ + +#define UDC_CON_CN_MASK (0x3) +#define UDC_CON_IN_MASK (0x7) +#define UDC_CON_AISN_MASK (0x7) +#define UDC_CON_EN_MASK (0xf) +#define UDC_CON_ET_MASK (0x3) +#define UDC_CON_MPS_MASK (0x3ff) + +/* DMA Request to Channel Map Register */ + +/* Endpoint 0 state definitions */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_RECV 2 +#define DATA_STATE_PENDING_XMIT 3 +#define DATA_STATE_NEED_ZLP 4 +#define WAIT_FOR_OUT_STATUS 5 + +#endif + + +#define UDCCR __REG(0x40600000) /* UDC Control Register */ +#define UDCCR_OEN (1 << 31) /* On-the-Go Enable */ +#define UDCCR_AALTHNP (1 << 30) /* A-device Alternate Host Negotiation + Protocol Port Support */ +#define UDCCR_AHNP (1 << 29) /* A-device Host Negotiation Protocol + Support */ +#define UDCCR_BHNP (1 << 28) /* B-device Host Negotiation Protocol + Enable */ +#define UDCCR_DWRE (1 << 16) /* Device Remote Wake-up Enable */ +#define UDCCR_ACN (0x03 << 11) /* Active UDC configuration Number */ +#define UDCCR_ACN_S 11 +#define UDCCR_AIN (0x07 << 8) /* Active UDC interface Number */ +#define UDCCR_AIN_S 8 +#define UDCCR_AAISN (0x07 << 5) /* Active UDC Alternate Interface + Setting Number */ +#define UDCCR_AAISN_S 5 +#define UDCCR_SMAC (1 << 4) /* Switch Endpoint Memory to Active + Configuration */ +#define UDCCR_EMCE (1 << 3) /* Endpoint Memory Configuration + Error */ +#define UDCCR_UDR (1 << 2) /* UDC Resume */ +#define UDCCR_UDA (1 << 1) /* UDC Active */ +#define UDCCR_UDE (1 << 0) /* UDC Enable */ + +#define UDCICR0 __REG(0x40600004) /* UDC Interrupt Control Register0 */ +#define UDCICR1 __REG(0x40600008) /* UDC Interrupt Control Register1 */ +#define UDCICR_FIFOERR (1 << 1) /* FIFO Error interrupt for EP */ +#define UDCICR_PKTCOMPL (1 << 0) /* Packet Complete interrupt for EP */ + +#define UDC_INT_FIFOERROR (0x2) +#define UDC_INT_PACKETCMP (0x1) + +#define UDCICR_INT(n,intr) (((intr) & 0x03) << (((n) & 0x0F) * 2)) +#define UDCICR1_IECC (1 << 31) /* IntEn - Configuration Change */ +#define UDCICR1_IESOF (1 << 30) /* IntEn - Start of Frame */ +#define UDCICR1_IERU (1 << 29) /* IntEn - Resume */ +#define UDCICR1_IESU (1 << 28) /* IntEn - Suspend */ +#define UDCICR1_IERS (1 << 27) /* IntEn - Reset */ + +#define UDCISR0 __REG(0x4060000C) /* UDC Interrupt Status Register 0 */ +#define UDCISR1 __REG(0x40600010) /* UDC Interrupt Status Register 1 */ +#define UDCISR_INT(n,intr) (((intr) & 0x03) << (((n) & 0x0F) * 2)) +#define UDCISR1_IECC (1 << 31) /* IntEn - Configuration Change */ +#define UDCISR1_IESOF (1 << 30) /* IntEn - Start of Frame */ +#define UDCISR1_IERU (1 << 29) /* IntEn - Resume */ +#define UDCISR1_IESU (1 << 28) /* IntEn - Suspend */ +#define UDCISR1_IERS (1 << 27) /* IntEn - Reset */ + + +#define UDCFNR __REG(0x40600014) /* UDC Frame Number Register */ +#define UDCOTGICR __REG(0x40600018) /* UDC On-The-Go interrupt control */ +#define UDCOTGICR_IESF (1 << 24) /* OTG SET_FEATURE command recvd */ +#define UDCOTGICR_IEXR (1 << 17) /* Extra Transciever Interrupt + Rising Edge Interrupt Enable */ +#define UDCOTGICR_IEXF (1 << 16) /* Extra Transciever Interrupt + Falling Edge Interrupt Enable */ +#define UDCOTGICR_IEVV40R (1 << 9) /* OTG Vbus Valid 4.0V Rising Edge + Interrupt Enable */ +#define UDCOTGICR_IEVV40F (1 << 8) /* OTG Vbus Valid 4.0V Falling Edge + Interrupt Enable */ +#define UDCOTGICR_IEVV44R (1 << 7) /* OTG Vbus Valid 4.4V Rising Edge + Interrupt Enable */ +#define UDCOTGICR_IEVV44F (1 << 6) /* OTG Vbus Valid 4.4V Falling Edge + Interrupt Enable */ +#define UDCOTGICR_IESVR (1 << 5) /* OTG Session Valid Rising Edge + Interrupt Enable */ +#define UDCOTGICR_IESVF (1 << 4) /* OTG Session Valid Falling Edge + Interrupt Enable */ +#define UDCOTGICR_IESDR (1 << 3) /* OTG A-Device SRP Detect Rising + Edge Interrupt Enable */ +#define UDCOTGICR_IESDF (1 << 2) /* OTG A-Device SRP Detect Falling + Edge Interrupt Enable */ +#define UDCOTGICR_IEIDR (1 << 1) /* OTG ID Change Rising Edge + Interrupt Enable */ +#define UDCOTGICR_IEIDF (1 << 0) /* OTG ID Change Falling Edge + Interrupt Enable */ + +#define UDCCSN(x) __REG2(0x40600100, (x) << 2) +#define UDCCSR0 __REG(0x40600100) /* UDC Control/Status register - Endpoint 0 */ +#define UDCCSR0_SA (1 << 7) /* Setup Active */ +#define UDCCSR0_RNE (1 << 6) /* Receive FIFO Not Empty */ +#define UDCCSR0_FST (1 << 5) /* Force Stall */ +#define UDCCSR0_SST (1 << 4) /* Sent Stall */ +#define UDCCSR0_DME (1 << 3) /* DMA Enable */ +#define UDCCSR0_FTF (1 << 2) /* Flush Transmit FIFO */ +#define UDCCSR0_IPR (1 << 1) /* IN Packet Ready */ +#define UDCCSR0_OPC (1 << 0) /* OUT Packet Complete */ + +#define UDCCSRA __REG(0x40600104) /* UDC Control/Status register - Endpoint A */ +#define UDCCSRB __REG(0x40600108) /* UDC Control/Status register - Endpoint B */ +#define UDCCSRC __REG(0x4060010C) /* UDC Control/Status register - Endpoint C */ +#define UDCCSRD __REG(0x40600110) /* UDC Control/Status register - Endpoint D */ +#define UDCCSRE __REG(0x40600114) /* UDC Control/Status register - Endpoint E */ +#define UDCCSRF __REG(0x40600118) /* UDC Control/Status register - Endpoint F */ +#define UDCCSRG __REG(0x4060011C) /* UDC Control/Status register - Endpoint G */ +#define UDCCSRH __REG(0x40600120) /* UDC Control/Status register - Endpoint H */ +#define UDCCSRI __REG(0x40600124) /* UDC Control/Status register - Endpoint I */ +#define UDCCSRJ __REG(0x40600128) /* UDC Control/Status register - Endpoint J */ +#define UDCCSRK __REG(0x4060012C) /* UDC Control/Status register - Endpoint K */ +#define UDCCSRL __REG(0x40600130) /* UDC Control/Status register - Endpoint L */ +#define UDCCSRM __REG(0x40600134) /* UDC Control/Status register - Endpoint M */ +#define UDCCSRN __REG(0x40600138) /* UDC Control/Status register - Endpoint N */ +#define UDCCSRP __REG(0x4060013C) /* UDC Control/Status register - Endpoint P */ +#define UDCCSRQ __REG(0x40600140) /* UDC Control/Status register - Endpoint Q */ +#define UDCCSRR __REG(0x40600144) /* UDC Control/Status register - Endpoint R */ +#define UDCCSRS __REG(0x40600148) /* UDC Control/Status register - Endpoint S */ +#define UDCCSRT __REG(0x4060014C) /* UDC Control/Status register - Endpoint T */ +#define UDCCSRU __REG(0x40600150) /* UDC Control/Status register - Endpoint U */ +#define UDCCSRV __REG(0x40600154) /* UDC Control/Status register - Endpoint V */ +#define UDCCSRW __REG(0x40600158) /* UDC Control/Status register - Endpoint W */ +#define UDCCSRX __REG(0x4060015C) /* UDC Control/Status register - Endpoint X */ + +#define UDCCSR_DPE (1 << 9) /* Data Packet Error */ +#define UDCCSR_FEF (1 << 8) /* Flush Endpoint FIFO */ +#define UDCCSR_SP (1 << 7) /* Short Packet Control/Status */ +#define UDCCSR_BNE (1 << 6) /* Buffer Not Empty (IN endpoints) */ +#define UDCCSR_BNF (1 << 6) /* Buffer Not Full (OUT endpoints) */ +#define UDCCSR_FST (1 << 5) /* Force STALL */ +#define UDCCSR_SST (1 << 4) /* Sent STALL */ +#define UDCCSR_DME (1 << 3) /* DMA Enable */ +#define UDCCSR_TRN (1 << 2) /* Tx/Rx NAK */ +#define UDCCSR_PC (1 << 1) /* Packet Complete */ +#define UDCCSR_FS (1 << 0) /* FIFO needs service */ + +#define UDCBCN(x) __REG2(0x40600200, (x)<<2) +#define UDCBCR0 __REG(0x40600200) /* Byte Count Register - EP0 */ +#define UDCBCRA __REG(0x40600204) /* Byte Count Register - EPA */ +#define UDCBCRB __REG(0x40600208) /* Byte Count Register - EPB */ +#define UDCBCRC __REG(0x4060020C) /* Byte Count Register - EPC */ +#define UDCBCRD __REG(0x40600210) /* Byte Count Register - EPD */ +#define UDCBCRE __REG(0x40600214) /* Byte Count Register - EPE */ +#define UDCBCRF __REG(0x40600218) /* Byte Count Register - EPF */ +#define UDCBCRG __REG(0x4060021C) /* Byte Count Register - EPG */ +#define UDCBCRH __REG(0x40600220) /* Byte Count Register - EPH */ +#define UDCBCRI __REG(0x40600224) /* Byte Count Register - EPI */ +#define UDCBCRJ __REG(0x40600228) /* Byte Count Register - EPJ */ +#define UDCBCRK __REG(0x4060022C) /* Byte Count Register - EPK */ +#define UDCBCRL __REG(0x40600230) /* Byte Count Register - EPL */ +#define UDCBCRM __REG(0x40600234) /* Byte Count Register - EPM */ +#define UDCBCRN __REG(0x40600238) /* Byte Count Register - EPN */ +#define UDCBCRP __REG(0x4060023C) /* Byte Count Register - EPP */ +#define UDCBCRQ __REG(0x40600240) /* Byte Count Register - EPQ */ +#define UDCBCRR __REG(0x40600244) /* Byte Count Register - EPR */ +#define UDCBCRS __REG(0x40600248) /* Byte Count Register - EPS */ +#define UDCBCRT __REG(0x4060024C) /* Byte Count Register - EPT */ +#define UDCBCRU __REG(0x40600250) /* Byte Count Register - EPU */ +#define UDCBCRV __REG(0x40600254) /* Byte Count Register - EPV */ +#define UDCBCRW __REG(0x40600258) /* Byte Count Register - EPW */ +#define UDCBCRX __REG(0x4060025C) /* Byte Count Register - EPX */ + +#define UDCDN(x) __REG2(0x40600300, (x)<<2) +#define PHYS_UDCDN(x) (0x40600300 + ((x)<<2)) +#define PUDCDN(x) (volatile u32 *)(io_p2v(PHYS_UDCDN((x)))) +#define UDCDR0 __REG(0x40600300) /* Data Register - EP0 */ +#define UDCDRA __REG(0x40600304) /* Data Register - EPA */ +#define UDCDRB __REG(0x40600308) /* Data Register - EPB */ +#define UDCDRC __REG(0x4060030C) /* Data Register - EPC */ +#define UDCDRD __REG(0x40600310) /* Data Register - EPD */ +#define UDCDRE __REG(0x40600314) /* Data Register - EPE */ +#define UDCDRF __REG(0x40600318) /* Data Register - EPF */ +#define UDCDRG __REG(0x4060031C) /* Data Register - EPG */ +#define UDCDRH __REG(0x40600320) /* Data Register - EPH */ +#define UDCDRI __REG(0x40600324) /* Data Register - EPI */ +#define UDCDRJ __REG(0x40600328) /* Data Register - EPJ */ +#define UDCDRK __REG(0x4060032C) /* Data Register - EPK */ +#define UDCDRL __REG(0x40600330) /* Data Register - EPL */ +#define UDCDRM __REG(0x40600334) /* Data Register - EPM */ +#define UDCDRN __REG(0x40600338) /* Data Register - EPN */ +#define UDCDRP __REG(0x4060033C) /* Data Register - EPP */ +#define UDCDRQ __REG(0x40600340) /* Data Register - EPQ */ +#define UDCDRR __REG(0x40600344) /* Data Register - EPR */ +#define UDCDRS __REG(0x40600348) /* Data Register - EPS */ +#define UDCDRT __REG(0x4060034C) /* Data Register - EPT */ +#define UDCDRU __REG(0x40600350) /* Data Register - EPU */ +#define UDCDRV __REG(0x40600354) /* Data Register - EPV */ +#define UDCDRW __REG(0x40600358) /* Data Register - EPW */ +#define UDCDRX __REG(0x4060035C) /* Data Register - EPX */ + +#define UDCCN(x) __REG2(0x40600400, (x)<<2) +#define UDCCRA __REG(0x40600404) /* Configuration register EPA */ +#define UDCCRB __REG(0x40600408) /* Configuration register EPB */ +#define UDCCRC __REG(0x4060040C) /* Configuration register EPC */ +#define UDCCRD __REG(0x40600410) /* Configuration register EPD */ +#define UDCCRE __REG(0x40600414) /* Configuration register EPE */ +#define UDCCRF __REG(0x40600418) /* Configuration register EPF */ +#define UDCCRG __REG(0x4060041C) /* Configuration register EPG */ +#define UDCCRH __REG(0x40600420) /* Configuration register EPH */ +#define UDCCRI __REG(0x40600424) /* Configuration register EPI */ +#define UDCCRJ __REG(0x40600428) /* Configuration register EPJ */ +#define UDCCRK __REG(0x4060042C) /* Configuration register EPK */ +#define UDCCRL __REG(0x40600430) /* Configuration register EPL */ +#define UDCCRM __REG(0x40600434) /* Configuration register EPM */ +#define UDCCRN __REG(0x40600438) /* Configuration register EPN */ +#define UDCCRP __REG(0x4060043C) /* Configuration register EPP */ +#define UDCCRQ __REG(0x40600440) /* Configuration register EPQ */ +#define UDCCRR __REG(0x40600444) /* Configuration register EPR */ +#define UDCCRS __REG(0x40600448) /* Configuration register EPS */ +#define UDCCRT __REG(0x4060044C) /* Configuration register EPT */ +#define UDCCRU __REG(0x40600450) /* Configuration register EPU */ +#define UDCCRV __REG(0x40600454) /* Configuration register EPV */ +#define UDCCRW __REG(0x40600458) /* Configuration register EPW */ +#define UDCCRX __REG(0x4060045C) /* Configuration register EPX */ + +#define UDCCONR_CN (0x03 << 25) /* Configuration Number */ +#define UDCCONR_CN_S (25) +#define UDCCONR_IN (0x07 << 22) /* Interface Number */ +#define UDCCONR_IN_S (22) +#define UDCCONR_AISN (0x07 << 19) /* Alternate Interface Number */ +#define UDCCONR_AISN_S (19) +#define UDCCONR_EN (0x0f << 15) /* Endpoint Number */ +#define UDCCONR_EN_S (15) +#define UDCCONR_ET (0x03 << 13) /* Endpoint Type: */ +#define UDCCONR_ET_S (13) +#define UDCCONR_ET_INT (0x03 << 13) /* Interrupt */ +#define UDCCONR_ET_BULK (0x02 << 13) /* Bulk */ +#define UDCCONR_ET_ISO (0x01 << 13) /* Isochronous */ +#define UDCCONR_ET_NU (0x00 << 13) /* Not used */ +#define UDCCONR_ED (1 << 12) /* Endpoint Direction */ +#define UDCCONR_MPS (0x3ff << 2) /* Maximum Packet Size */ +#define UDCCONR_MPS_S (2) +#define UDCCONR_DE (1 << 1) /* Double Buffering Enable */ +#define UDCCONR_EE (1 << 0) /* Endpoint Enable */ + + +#define UDC_INT_FIFOERROR (0x2) +#define UDC_INT_PACKETCMP (0x1) + +#define UDC_FNR_MASK (0x7ff) + +#define UDCCSR_WR_MASK (UDCCSR_DME|UDCCSR_FST) +#define UDC_BCR_MASK (0x3ff) + + diff -uNr linux/drivers/no-otg/otghw/cea-936.h linux/drivers/otg/otghw/cea-936.h --- linux/drivers/no-otg/otghw/cea-936.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/cea-936.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,148 @@ +/* + * otg/carkit.h + * + * Copyright (c) 2004 Belcarra + * + * + * Notes + * + * 1. Document references are to CEA-936a - Dec 12, 2003 + * + */ +/*! + * @defgroup CEA + * @ingroup tcdgroup + */ +/*! + * @file otghw/cea-936.h + * @brief Hardware defines for CEA-936 Hardware + * + * @ingroup CEA + */ + + +/* + * Command Descriptions - C.f. 10.3 + */ +#define CARKIT_GET_DEVICE 0x01 +#define CARKIT_GET_CARKIT 0x02 +#define CARKIT_AUDIO_EN 0x03 +#define CARKIT_SET_CRNT 0x04 +#define CARKIT_SET_UART 0x05 +#define CARKIT_CARKIT_MASTER 0x06 + +/* + * generic get command + */ +struct carkit_get { + u8 strt; + u8 command_type; + u8 crc; + u8 terminator; +}; + +struct carkit_set { + u8 strt; + u8 command_type; + u8 command_value; + u8 crc; + u8 terminator; +}; + +struct carkit_get_carkit_ack { + u8 ack; + u8 terminator; +}; + +/* + * Get Device - C.f 10.3.1 + */ + +struct carkit_get_device_carkit { + u8 ack; + u8 device_id; + u16 vendor_id; + u16 product_id; + u8 crc; + u8 terminator; +}; + +#define CARKIT_DEVICE_ID_GENERIC 0x01 +#define CARKIT_DEVICE_ID_ENHANCED 0x02 +#define CARKIT_DEVICE_ID_PROPRIETARY 0x03 + + +/* + * Get CarKit - C.f. 10.3.2 + */ + +struct carkit_get_carkit_carkit { + u8 ack; + u16 cr_ftrs; + u8 cr_sts; + u8 crc; + u8 terminator; +}; + +#define CARKIT_CR_FTRS_0_STERO (1 << 0) +#define CARKIT_CR_FTRS_0_CRNT_ADJ (1 << 1) +#define CARKIT_CR_FTRS_0_UART_MAX (7 << 2) +#define CARKIT_CR_FTRS_0_UART_MSTR (1 << 5) + +#define CARKIT_CR_FTRS_1_PH_REMOVE_FTR (1 << 0) +#define CARKIT_CR_FTRS_1_CAR_OFF_FTR (1 << 1) +#define CARKIT_CR_FTRS_1_CRDL_BTN_FTR (1 << 2) + +#define CARKIT_CR_PH_REMOVE_STS (1 << 0) +#define CARKIT_CR_CAR_OFF_STS (1 << 1) +#define CARKIT_CR_CRDL_BTN_STS (1 << 2) +#define CARKIT_CR_PH_RMV_CHG (1 << 4) +#define CARKIT_CR_CAR_OFF_CHG (1 << 5) +#define CARKIT_CR_CRDL_BTN_CHG (1 << 6) + + +/* + * Set Carkit - C.f. 10.3.3 + */ +#define CARKIT_CR_CTL_PH_RMV_IEN (1 << 0) +#define CARKIT_CR_CAR_OFF_IEN (1 << 1) +#define CARKIT_CR_CRDL_BTN_IEN (1 << 2) +#define CARKIT_CR_CARKIT_MSTR (1 << 5) +#define CARKIT_CR_PULSE_ID (1 << 6) +#define CARKIT_CR_ID_INT_EN (1 << 7) + + +/* + * Audio En - C.f. 10.3.4 + */ +#define CARKIT_AUDIO_MODE_MONO (1 << 0) +#define CARKIT_AUDIO_MODE_STEREO (1 << 1) + +/* + * Set Crnt - C.f. 10.3.5 + */ +#define CARKIT_CRNT_LMT (0xf) + + +/* + * Set UART - C.f. 10.3.6 + */ +#define CARKIT_UART_SPEED_9600 (1 << 0) +#define CARKIT_UART_SPEED_19200 (1 << 1) +#define CARKIT_UART_SPEED_38400 (1 << 3) +#define CARKIT_UART_SPEED_57600 (1 << 4) +#define CARKIT_UART_SPEED_115200 (1 << 4) + + +/* + * Special chars - C.f. 10.4 + */ +#define CARKIT_STRT 0x41 +#define CARKIT_ESC 0x18 +#define CARKIT_ACK 0x06 +#define CARKIT_NYET 0x30 +#define CARKIT_NAK 0x15 +#define CARKIT_NSUP 0x1f +#define CARKIT_CR 0x0d + + diff -uNr linux/drivers/no-otg/otghw/fsotg-hardware.h linux/drivers/otg/otghw/fsotg-hardware.h --- linux/drivers/no-otg/otghw/fsotg-hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/fsotg-hardware.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,1054 @@ +/* + * otghw/fsotg-hardware.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ + +/*! + * @defgroup FSOTG Freescale USBOTG Support + * @ingroup libgroup + */ +/*! + * @file otghw/fsotg-hardware.h + * @brief Hardware defines for Freescale USBOTG Hardware + * + * This supports the Freescale implementation of the Trans Dimension + * USBOTG. + * + * This is used on: + * + * - i.MX21 + * - SCM-A11 + * - Argon+ + * - Zeus + * + * @ingroup FSOTG + */ + +#if defined(CONFIG_ARCH_MX2ADS) +#include +#include +#endif /* defined(CONFIG_ARCH_MX2ADS) */ + +#if defined(CONFIG_ARCH_SCMA11EVB) +#endif /* defined(CONFIG_ARCH_SCMA11EVB) */ + + + +/*! + * @name OTG_SYS_CTRL + * C.f. 23.8 USB Control Register + */ + /*! @{ */ + +//#define OTG_SYS_CTRL (OTG_SYS_BASE+0x00) + +#define SYS_CTRL_I2C_WU_INT_STAT (1 << 27) +#define SYS_CTRL_OTG_WU_INT_STAT (1 << 26) +#define SYS_CTRL_HOST_WU_INT_STAT (1 << 25) +#define SYS_CTRL_FNT_WU_INT_STAT (1 << 24) + +#define SYS_CTRL_I2C_WU_INT_EN (1 << 19) +#define SYS_CTRL_OTG_WU_INT_EN (1 << 18) +#define SYS_CTRL_HOST_WU_INT_EN (1 << 17) +#define SYS_CTRL_FNT_WU_INT_EN (1 << 16) + +#define SYS_CTRL_OTG_BYP_VAL (0x3 << 11) +#define SYS_CTRL_HOST1_BYP_VAL (0x3 << 9) + +#define SYS_CTRL_OTG_PWR_MASK (1 << 6) +#define SYS_CTRL_HOST1_PWR_MASK (1 << 6) +#define SYS_CTRL_HOST2_PWR_MASK (1 << 4) +#define SYS_CTRL_USB_BYP (1 << 2) +#define SYS_CTRL_HOST1_TXEN_OE (1 << 1) +/*! @} */ + +/*! + * @name C.f. 23.9 USBOTG Module Registers + */ + /*! @{ */ + +#define OTG_CORE_HWMODE (OTG_CORE_BASE+0x00) // 32bit core hardware mode reg + +#define XCVR_D_D 0x00 +#define XCVR_SE_D 0x01 +#define XCVR_D_SE 0x02 +#define XCVR_SE_SE 0x03 + +#define MODULE_ANASDBEN (1 << 14) +#define MODULE_OTGXCVR (0x3 << 6) +#define MODULE_HOSTXCVR (0x3 << 4) +#define MODULE_CRECFG (0x3) +#define MODULE_CRECFG_HHNP (0x0) +#define MODULE_CRECFG_HOST (0x1) +#define MODULE_CRECFG_FUNC (0x2) +#define MODULE_CRECFG_SHNP (0x3) + +#define OTG_CORE_CINT_STAT (OTG_CORE_BASE+0x04) // 32bit core int status reg + +#define MODULE_FCINTDSPEN (1 << 6) + +#define MODULE_ASHNPINT (1 << 5) +#define MODULE_ASFCINT (1 << 4) +#define MODULE_ASHCINT (1 << 3) +#define MODULE_HNPINT (1 << 2) +#define MODULE_FCINT (1 << 1) +#define MODULE_HCINT (1) + +#define OTG_CORE_CINT_STEN (OTG_CORE_BASE+0x08) // 32bit core int enable reg + +#define MODULE_ASHNPINT_EN (1 << 5) +#define MODULE_ASFCINT_EN (1 << 4) +#define MODULE_ASHCINT_EN (1 << 3) +#define MODULE_HNPINT_EN (1 << 2) +#define MODULE_FCINT_EN (1 << 1) +#define MODULE_HCINT_EN (1) + +#define OTG_CORE_CLK_CTRL (OTG_CORE_BASE+0x0C) // 32bit core clock control reg + +#define MODULE_FUNC_CLK (1 << 2) +#define MODULE_HOST_CLK (1 << 1) +#define MODULE_MAIN_CLK (1) + +#define OTG_CORE_RST_CTRL (OTG_CORE_BASE+0x10) // 32bit core reset control reg + +#define MODULE_RSTI2C (1 << 15) +#define MODULE_RSTCTRL (1 << 5) +#define MODULE_RSTFC (1 << 4) +#define MODULE_RSTFSIE (1 << 3) +#define MODULE_RSTRH (1 << 2) +#define MODULE_RSTHSIE (1 << 1) +#define MODULE_RSTHC (1) + +#define OTG_CORE_FRM_INTVL (OTG_CORE_BASE+0x14) // 32bit core frame interval reg + +#define MODULE_RESET_FRAME (1 << 15) + +#define OTG_CORE_FRM_REMAIN (OTG_CORE_BASE+0x18) // 32bit core frame remaining reg + +#define OTG_CORE_HNP_CSTAT (OTG_CORE_BASE+0x1C) // 32bit core HNP current state reg + +#define MODULE_HNPDAT (1 << 30) +#define MODULE_VBUSBSE (1 << 29) +#define MODULE_VBUSABSV (1 << 28) +#define MODULE_VBUSGTAVV (1 << 27) + +#define MODULE_ARMTHNPE (1 << 25) +#define MODULE_BHNPEN (1 << 24) + +#define MODULE_SLAVE (1 << 22) +#define MODULE_MASTER (1 << 21) +#define MODULE_BGEN (1 << 20) +#define MODULE_CMPEN (1 << 19) +#define MODULE_ISBDEV (1 << 18) +#define MODULE_ISADEV (1 << 17) + +#define MODULE_SWVBUSPUL (1 << 15) + +#define MODULE_SWAUTORST (1 << 12) +#define MODULE_SWPUDP (1 << 11) + +#define MODULE_SWPDDM (1 << 9) +#define MODULE_HNPSTATE (0x1f << 8) +#define MODULE_CLRERROR (1 << 3) +#define MODULE_ADROPBUS (1 << 2) +#define MODULE_ABBUSREQ (1 << 1) + +#define HNP_A_IDLE (0x10 << 8) +#define HNP_A_MASTER (0x11 << 8) +#define HNP_A_SLAVE (0x12 << 8) +#define HNP_A_WAIT_VPULSE (0x13 << 8) + +#define HNP_A_WAIT_DPULSE (0x14 << 8) +#define HNP_A_WAIT_CONN_A (0x15 << 8) +#define HNP_A_WAIT_CONN_B (0x16 << 8) +#define HNP_A_WAIT_VRISE (0x17 << 8) +#define HNP_A_SUSPEND (0x18 << 8) +#define HNP_A_WAIT_VFALL (0x19 << 8) +#define HNP_A_VBUS_ERR (0x1a << 8) +#define HNP_CONN_DEBOUNCE (0x1b << 8) +#define HNP_A_WAIT_ABREQ (0x1c << 8) +#define HNP_B_IDLE (0x00 << 8) +#define HNP_B_MASTER (0x01 << 8) +#define HNP_B_SLAVE (0x02 << 8) +#define HNP_B_SRP_INIT_V (0x03 << 8) +#define HNP_B_SRP_INIT_D (0x04 << 8) +#define HNP_B_PRE_WAIT_CONN_AB_SHORT_DB (0x05 << 8) +#define HNP_B_WAIT_CONN_A (0x06 << 8) +#define HNP_B_PRE_SLAVE (0x0a << 8) + + +#define OTG_CORE_HNP_TIMER1 (OTG_CORE_BASE+0x20) // 32bit core HNP timer 1 reg +#define OTG_CORE_HNP_TIMER2 (OTG_CORE_BASE+0x24) // 32bit core HNP timer 2 reg +#define OTG_CORE_HNP_T3PCR (OTG_CORE_BASE+0x28) // 32bit core HNP timer 3 pulse ctrl + +#define HNP_DATAPULSE (1 << 5) +#define HNP_VBUSPULSE (1 << 4) + +#define OTG_CORE_HINT_STAT (OTG_CORE_BASE+0x2C) // 32bit core HNP int status reg + +#define HNP_I2COTGINT (1 << 15) +#define HNP_AWAITBTO (1 << 8) // Hardware HNP Only +#define HNP_AIDLEBDTO (1 << 7) // Hardware HNP Only +#define HNP_SRPSUCFAIL (1 << 6) // Hardware HNP Only +#define HNP_SRPINT (1 << 5) +#define HNP_VBUSERROR (1 << 4) // Hardware HNP Only +#define HNP_ABSEVAILD (1 << 3) +#define HNP_ABUSVALID (1 << 2) +#define HNP_MASSLVCHG (1 << 1) // Hardware HNP Only +#define HNP_IDCHANGE (1) + +#define OTG_CORE_HINT_STEN (OTG_CORE_BASE+0x30) // 32bit core HNP int enable reg + +#define HNP_I2COTGINT_EN (1 << 15) +#define HNP_AWAITBTO_EN (1 << 8) +#define HNP_AIDLEBDTO_EN (1 << 7) +#define HNP_SRPSUCFAIL_EN (1 << 6) +#define HNP_SRPINT_EN (1 << 5) +#define HNP_VBUSERROR_EN (1 << 4) +#define HNP_ABSEVAILD_EN (1 << 3) +#define HNP_ABUSVALID_EN (1 << 2) +#define HNP_MASSLVCHG_EN (1 << 1) +#define HNP_IDCHANGE_EN (1) + + +#define OTG_CORE_CPUEPSEL_STAT (OTG_CORE_BASE+0x34) +#define OTG_CORE_INTERRUPT_STEN (OTG_CORE_BASE+0x3c) +/*! @} */ + + +/*! + * @name C.f. 23.11.11 Host Registers + */ + /*! @{ */ + + +#define OTG_HOST_CONTROL (OTG_HOST_BASE+0x00) // 32bit host controller config reg + +#define HOST_CONTROL_HCRESET (1 << 31) +#define HOST_CONTROL_RMTWUEN (1 << 4) + +#define HOST_CONTROL_HCUSBSTE_RESET (0 << 2) +#define HOST_CONTROL_HCUSBSTE_RESUME (1 << 2) +#define HOST_CONTROL_HCUSBSTE_OPERATIONAL (2 << 2) +#define HOST_CONTROL_HCUSBSTE_SUSPEND (3 << 2) + +#define HOST_CONTROL_CTLBLKSR_11 (0) +#define HOST_CONTROL_CTLBLKSR_21 (1) +#define HOST_CONTROL_CTLBLKSR_41 (2) +#define HOST_CONTROL_CTLBLKSR_81 (3) + + +#define OTG_HOST_SINT_STAT (OTG_HOST_BASE+0x08) // 32bit host system int status reg + +#define HOST_PSCINT (1 << 6) // Port Status Change +#define HOST_FMOFINT (1 << 5) // Frame Number Overflow +#define HOST_HERRINT (1 << 4) // Host Error +#define HOST_RESDETINT (1 << 3) // Resume Detected +#define HOST_SOFINT (1 << 2) // Start of Frame +#define HOST_DONEINT (1 << 1) // Done Register +#define HOST_SORINT (1) // Schedule Overrun + +#define OTG_HOST_SINT_STEN (OTG_HOST_BASE+0x0C) // 32bit host system int enable reg + +#define HOST_PSCINT_EN (1 << 6) // Port Status Change +#define HOST_FMOFINT_EN (1 << 5) // Frame Number Overflow +#define HOST_HERRINT_EN (1 << 4) // Host Error +#define HOST_RESDETINT_EN (1 << 3) // Resume Detected +#define HOST_SOFINT_EN (1 << 2) // Start of Frame +#define HOST_DONEINT_EN (1 << 1) // Done Register +#define HOST_SORINT_EN (1) // Schedule Overrun + +#define OTG_HOST_XINT_STAT (OTG_HOST_BASE+0x18) // 32bit host X buf int status reg +#define OTG_HOST_YINT_STAT (OTG_HOST_BASE+0x1C) // 32bit host Y buf int status reg + +#define OTG_HOST_XYINT_STEN (OTG_HOST_BASE+0x20) // 32bit host XY buf int enable reg +#define OTG_HOST_XFILL_STAT (OTG_HOST_BASE+0x28) // 32bit host X filled status reg +#define OTG_HOST_YFILL_STAT (OTG_HOST_BASE+0x2C) // 32bit host Y filled status reg + +#define OTG_HOST_ETD_EN (OTG_HOST_BASE+0x40) // 32bit host ETD enables reg +#define OTG_HOST_DIR_ROUTE (OTG_HOST_BASE+0x48) // 32bit host direct routing reg +#define OTG_HOST_IINT (OTG_HOST_BASE+0x4C) // 32bit host immediate interrupt reg + +#define OTG_HOST_EP_DSTAT (OTG_HOST_BASE+0x50) // 32bit host endpoints done status +#define OTG_HOST_ETD_DONE (OTG_HOST_BASE+0x54) // 32bit host ETD done reg + +#define OTG_HOST_FRM_NUM (OTG_HOST_BASE+0x60) // 32bit host frame number reg +#define OTG_HOST_LSP_THRESH (OTG_HOST_BASE+0x64) // 32bit host low speed threshold reg + +#define OTG_HOST_ROOTHUB_DESCA (OTG_HOST_BASE+0x68) // 32bit host root hub descriptor A +#define OTG_HOST_ROOTHUB_DESCB (OTG_HOST_BASE+0x6C) // 32bit host root hub descriptor B +/* @} */ + +/*! + * @name C.f. 23.11.29 + */ +/*! @{ */ +#define OTG_HOST_ROOTHUB_STATUS (OTG_HOST_BASE+0x70) // 32bit host root hub status reg + +#define ROOTHUB_STATUS_CLRMTWUE (1 << 31) // Clear Remote Wakeup Enable +#define ROOTHUB_STATUS_OVRCURCHG (1 << 17) // Over Current Indicator Change +#define ROOTHUB_STATUS_LOCPWRSC (1 << 16) // Local Power Status Change +#define ROOTHUB_STATUS_DEVCONWUE (1 << 15) // Device Connect Wakeup Enable +#define ROOTHUB_STATUS_OVRCURI (1 << 1) // Over Current Indicator +#define ROOTHUB_STATUS_LOCPWRS (1) // Local Power Status +/*! @} */ + + +/*! + * @name C.f. 23.11.30 + */ +/*! @{ */ +#define OTG_HOST_PORT_STATUS_1 (OTG_HOST_BASE+0x74) // 32bit host port 1 status bits +#define OTG_HOST_PORT_STATUS_2 (OTG_HOST_BASE+0x78) // 32bit host port 2 status bits +#define OTG_HOST_PORT_STATUS_3 (OTG_HOST_BASE+0x7c) // 32bit host port 3 status bits + +#define PORT_STATUS_PRTRSTSC (1 << 20) // Port Reset Status Change +#define PORT_STATUS_OVRCURIC (1 << 19) // Port Over Current Indicator Change +#define PORT_STATUS_PRTSTATSC (1 << 18) // Port Suspend Status Change +#define PORT_STATUS_PRTENBLSC (1 << 17) // Port Enable Status Change +#define PORT_STATUS_CONNECTSC (1 << 16) // Connect Status Change + +#define PORT_STATUS_LSDEVCON (1 << 9) // Low Speed Device Attached +#define PORT_STATUS_PRTPWRST (1 << 8) // Port Power Status + +#define PORT_STATUS_PRTRSTST (1 << 4) // Port Reset Status +#define PORT_STATUS_PRTOVRCURI (1 << 3) // Port Over Current Indicator +#define PORT_STATUS_PRTSUSPST (1 << 2) // Port Suspend Status +#define PORT_STATUS_PRTENABST (1 << 1) // Port Enable Status +#define PORT_STATUS_CURCONST (1) // Current Connect Status + + +/* flags are the same for the interrupt, control and bulk transfer descriptors */ +// R1: sec 23.11.10.2,3 pg 23-44,47 +#define ETD_GET_COMPCODE(flags) ((flags >> 12) & 0xf) +// R1: sec 23.11.10 Table 23-23 pg 23-40 +#define ETD_CC_NOERROR 0x0 +#define ETD_CC_CRCERR 0x1 +#define ETD_CC_BITSTUFFERR 0x2 +#define ETD_CC_DATATOGERR 0x3 +#define ETD_CC_STALL 0x4 +#define ETD_CC_DEVNOTRESPONDING 0x5 +#define ETD_CC_PIDFAILURE 0x6 +// Reserved 0x7 +#define ETD_CC_DATAOVERRUN 0x8 +#define ETD_CC_DATAUNDERRUN 0x9 +#define ETD_CC_ACK 0xA +#define ETD_CC_NAK 0xB +#define ETD_CC_BUFFEROVERRUN 0xC +#define ETD_CC_BUFFERUNDERRUN 0xD +#define ETD_CC_SCHEDOVERRUN 0xE +#define ETD_CC_NOTACCESSED 0xF + +// R1: sec 23.11.10.2,3 pg 23-44,47 (contd) +#define ETD_GET_ERRORCNT(flags) (((flags) >> 8) & 0xf) +#define ETD_GET_DATATOGL(flags) (((flags) >> 6) & 0x3) +#define ETD_SET_DATATOGL(v) (((v) & 0x3) << 6) +#define ETD_GET_DELAYINT(flags) (((flags) >> 3) & 0x7) +#define ETD_SET_DELAYINT(v) (((v) & 0x7) << 3) +#define ETD_GET_BUFROUND(flags) (((flags) >> 2) & 0x1) +#define ETD_SET_BUFROUND(v) (((v) & 0x1) << 2) +#define ETD_GET_DIRPID(flags) ((flags) & 0x3) +#define ETD_SET_DIRPID(v) ((v) & 0x3) +#define ETD_FLIP_DIRPID(flags) ((flags) ^ 0x3) /* xor with 0x3 flips 2<->1 (IN<->OUT) */ +#define ETD_DIRPID_SETUP 0x0 // OUT +#define ETD_DIRPID_OUT 0x1 +#define ETD_DIRPID_IN 0x2 +#define ETD_DIRPID_RESERVED 0x3 + +// For non-isoc td.w3.val - +#define ETD_GET_BUFSIZE(w3) (((w3) >> 21) & 0xfff) +#define ETD_SET_BUFSIZE(v) (((v) & 0xfff) << 21) +#define ETD_GET_TOTBYECNT(w3) ((w3) & 0xfffff) +#define ETD_SET_TOTBYECNT(v) ((v) & 0xfffff) + +/* isoc flags shares the COMPCODE and DELAYINT bitfields with the other TDs, + but the remaining bits are different. */ +#define ETD_GET_AUTOISO(flags) (((flags) >> 11) & 0x1) +#define ETD_SET_AUTOISO(v) (((v) & 0x1) << 11) +#define ETD_GET_FRAMECNT(flags) (((flags) >> 8) & 0x1) +#define ETD_SET_FRAMECNT(v) (((v) & 0x1) << 8) +// For isoc td.w3.val - +/* pkt1 and pkt0 use the COMPCODE bitfield, and add a PKTLEN field. */ +#define ETD_GET_PKTLEN(pkt) ((pkt) & 0x3ff) +#define ETD_SET_PKTLEN(v) ((v) & 0x3ff) + + +// ETD endpoint descriptor (etd->epd) bitfield access +// R1: sec 23.11.10.1 pg 23-41 (word0) +#define ETD_GET_SNDNAK(epd) (((epd) >> 30) & 0x1) +#define ETD_SET_SNDNAK(v) (((v) & 0x1) << 30) +#define ETD_GET_TOGCRY(epd) (((epd) >> 28) & 0x1) +#define ETD_SET_TOGCRY(v) (((v) & 0x1) << 28) +#define ETD_GET_HALTED(epd) (((epd) >> 27) & 0x1) +#define ETD_SET_HALTED(v) (((v) & 0x1) << 27) +#define ETD_GET_MAXPKTSIZ(epd) (((epd) >> 16) & 0x3ff) +#define ETD_SET_MAXPKTSIZ(v) (((v) & 0x3ff) << 16) +#define ETD_GET_FORMAT(epd) (((epd) >> 14) & 0x3) +#define ETD_SET_FORMAT(v) (((v) & 0x3) << 14) +#define ETD_FORMAT_CONTROL 0x0 +#define ETD_FORMAT_ISOC 0x1 +#define ETD_FORMAT_BULK 0x2 +#define ETD_FORMAT_INTERRUPT 0x3 +#define ETD_GET_SPEED(epd) (((epd) >> 13) & 0x1) +#define ETD_SET_SPEED(v) (((v) & 0x1) << 13) +#define ETD_SPEED_FULL 0x0 +#define ETD_SPEED_LOW 0x1 +#define ETD_GET_DIRECT(epd) (((epd) >> 11) & 0x3) +#define ETD_SET_DIRECT(v) (((v) & 0x3) << 11) +/* xor with 3 flips 2<->1 */ +#define ETD_FLIP_DIRECT(epd) ((epd) ^ (0x3 << 11)) +#define ETD_DIRECT_TD00 0x0 +#define ETD_DIRECT_OUT 0x1 +#define ETD_DIRECT_IN 0x2 +#define ETD_DIRECT_TD11 0x3 +#define ETD_GET_ENDPNT(epd) (((epd) >> 7) & 0xf) +#define ETD_SET_ENDPNT(v) (((v) & 0xf) << 7) +#define ETD_GET_ADDRESS(epd) ((epd) & 0x7f) +#define ETD_SET_ADDRESS(v) ((v) & 0x7f) + +// etd_urb_state[] values +#define ETD_URB_COMPLETED 0 +#define ETD_URB_SETUP_STATUS 1 +#define ETD_URB_SETUP_DATA 2 +#define ETD_URB_SETUP_START 3 +#define ETD_URB_BULK_START 4 +#define ETD_URB_BULKWZLP 5 +#define ETD_URB_BULKWZLP_START 6 +#define ETD_URB_INTERRUPT_START 7 +#define ETD_URB_ISOC_START 8 +/*! @} */ + + +/*! + * @name C.f. 23.12.8 Function Registers + */ +/*! @{ */ + +#define OTG_FUNC_CMD_STAT (OTG_FUNC_BASE+0x00) // 32bit func command status reg + +#define COMMAND_SOFTRESET (1 << 7) +#define COMMAND_BADISOAP (1 << 3) +#define COMMAND_SUPDET (1 << 2) +#define COMMAND_RSMINPROG (1 << 1) +#define COMMAND_RESETDET (1) + + +#define OTG_FUNC_DEV_ADDR (OTG_FUNC_BASE+0x04) // 32bit func device address reg + +#define OTG_FUNC_SINT_STAT (OTG_FUNC_BASE+0x08) // 32bit func system int status reg + +#define SYSTEM_DONEREGINTDS (1 << 5) +#define SYSTEM_SOFDETINT (1 << 4) +#define SYSTEM_DONEREGINT (1 << 3) +#define SYSTEM_SUSPDETINT (1 << 2) +#define SYSTEM_RSMFININT (1 << 1) +#define SYSTEM_RESETINT (1) + +#define OTG_FUNC_SINT_STEN (OTG_FUNC_BASE+0x0C) // 32bit func system int enable reg + +#define SYSTEM_DONEREGINTDS_EN (1 << 5) +#define SYSTEM_SOFDETINT_EN (1 << 4) +#define SYSTEM_DONEREGINT_EN (1 << 3) +#define SYSTEM_SUSPDETINT_EN (1 << 2) +#define SYSTEM_RSMFININT_EN (1 << 1) +#define SYSTEM_RESETINT_EN (1) + + +#define OTG_FUNC_XINT_STAT (OTG_FUNC_BASE+0x10) // 32bit func X buf int status reg +#define OTG_FUNC_YINT_STAT (OTG_FUNC_BASE+0x14) // 32bit func Y buf int status reg +#define OTG_FUNC_XYINT_STEN (OTG_FUNC_BASE+0x18) // 32bit func XY buf int enable reg +#define OTG_FUNC_XFILL_STAT (OTG_FUNC_BASE+0x1C) // 32bit func X filled status reg + +#define OTG_FUNC_YFILL_STAT (OTG_FUNC_BASE+0x20) // 32bit func Y filled status reg +#define OTG_FUNC_EP_EN (OTG_FUNC_BASE+0x24) // 32bit func endpoints enable reg +#define OTG_FUNC_EP_RDY (OTG_FUNC_BASE+0x28) // 32bit func endpoints ready reg +#define OTG_FUNC_IINT (OTG_FUNC_BASE+0x2C) // 32bit func immediate interrupt reg + +#define OTG_FUNC_EP_DSTAT (OTG_FUNC_BASE+0x30) // 32bit func endpoints done status +#define OTG_FUNC_EP_DEN (OTG_FUNC_BASE+0x34) // 32bit func endpoints done enable +#define OTG_FUNC_EP_TOGGLE (OTG_FUNC_BASE+0x38) // 32bit func endpoints toggle bits +#define OTG_FUNC_FRM_NUM (OTG_FUNC_BASE+0x3C) // 32bit func frame number reg + + +#define EP0_STALL (1 << 31) +#define EP0_SETUP (1 << 30) +#define EP0_OVERRUN (1 << 29) +#define EP0_AUTOISO (1 << 27) + + +/* generic endpoint register manipulations + */ +#define EP_OUT 0x1 +#define EP_IN 0x2 +#define EP_BOTH 0x3 + +#define ETD_MASK(n) (1 << n) +#define ep_num_both(n) (EP_BOTH << n) +#define ep_num_dir(n, dir) ((dir ? EP_IN : EP_OUT) << (n*2)) +#define ep_num_out(n) ep_num_dir(n, USB_DIR_OUT) +#define ep_num_in(n) ep_num_dir(n, USB_DIR_IN) + + +/* ep descriptor access + */ +static __inline__ u32 etd_word(int n, int word) +{ + u32 offset = n * 16; + offset += word * 4; + return OTG_ETD_BASE + offset; +} + +/* ep descriptor access + */ +static __inline__ u32 ep_word(int n, int dir, int word) +{ + u32 offset = n * 2; + offset += dir ? 1 : 0; + offset *= 16; + offset += word * 4; + return OTG_EP_BASE + offset; +} + +/* endpoint data buffer access + * + * This is a simplistic allocator, will do until we want to support ISO or host mode. + * + * This works because we are assuming a maximum of 16 allocate endpoints, and no + * overlapped endpoints (both in and out are allocated). + */ + +static volatile __inline__ u16 data_x_buf(int n, int dir) +{ + return 0x40 * (n * 4 + 2 * (dir ? 1 : 0)); +} +static volatile __inline__ u16 data_y_buf(int n, int dir) +{ + return 0x40 * (n * 4 + 2 * (dir ? 1 : 0) + 1); +} + +static volatile __inline__ u8 * data_x_address(int n, int dir) +{ + return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_x_buf(n, dir)); +} +static volatile __inline__ u8 * data_y_address(int n, int dir) +{ + return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_y_buf(n, dir)); +} +/*! @} */ + +/*! + * @name C.f. 23.13.3 DMA Registers + */ +/*! @{ */ +#define OTG_DMA_REV_NUM (OTG_DMA_BASE+0x000) // 32bit dma revision number reg +#define OTG_DMA_DINT_STAT (OTG_DMA_BASE+0x004) // 32bit dma int status reg +#define OTG_DMA_DINT_STEN (OTG_DMA_BASE+0x008) // 32bit dma int enable reg +#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg +#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg +#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg +#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg +#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req +#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req +#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req +#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req +#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg +#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg +#define OTG_DMA_MISC_CTRL (OTG_DMA_BASE+0x040) // 32bit dma EP misc control reg +#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg +#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg + + +#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg +#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg +#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg +#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req +#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req +#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg + + + +#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg +#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg +#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg +#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req +#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req +#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg + + +#define dma_num_dir(n, dir) (n * 2 + (dir ? 1 : 0)) +#define dma_num_out(n) dma_num_dir(n, USB_DIR_OUT) +#define dma_num_in(n) dma_num_dir(n, USB_DIR_IN) + +#define OTG_DMA_ETD_MSA(x) (OTG_DMA_BASE+0x100+x*4) +#define OTG_DMA_EPN_MSA(x) (OTG_DMA_BASE+0x180+x*4) +#define OTG_DMA_ETDN_BPTR(x) (OTG_DMA_BASE+0x280+x*4) +#define OTG_DMA_EPN_BPTR(x) (OTG_DMA_BASE+0x284+x*4) + +/*! @} */ + +/* ********************************************************************************************** */ +/*! + * Warning: the MX21 memory mapped region appears to require 32-bit access only. + * As of this writing, only the ETD region has been tested, but it does show the + * following behavior: + * + * Step 1: *(volatile u32 *)(void*)0xe40243f8 == 00000000 + * Step 2: *(volatile u16 *)(void*)0xe40243f8 = 0x0084; + * Step 3: *(volatile u32 *)(void*)0xe40243f8 == 00840084 + * Step 4: *(volatile u8 *)(void*)(0xe40243f8+3) = 0x01; + * Step 5: *(volatile u32 *)(void*)0xe40243f8 == 01010101 + * + * Any access to less than 32 bits is replicated as many times as required + * to make 32 bits, and the surrounding 32-bit region is changed. + * + * Just to add insult to injury, gcc 3.2.3 wants to store constants + * that are small enough using byte instructions. E.g. + * + * mm->host.System_Interrupt_Enables = (SIEN_PSCIEN | SIEN_FMOFIEN | SIEN_HERRIEN | SIEN_RESDETIEN | + * SIEN_DONEIEN | SIEN_SORIEN); + * + * becomes: + * + * ldrb r2, [r3, #140] + * mov r2, #123 + * strb r2, [r3, #140] + * ldrb r2, [r3, #141] + * strb r1, [r3, #141] + * ldrb r2, [r3, #142] + * strb r1, [r3, #142] + * ldrb r2, [r3, #143] + * strb r1, [r3, #143] + * + * which, given the 32-bit access restrictions above, + * is not going to work very well. Changing the original assignment to: + * + * *&(mm->host.System_Interrupt_Enables) = (SIEN_PSCIEN | SIEN_FMOFIEN | SIEN_HERRIEN | SIEN_RESDETIEN | + * SIEN_DONEIEN | SIEN_SORIEN); + * + * which should be identical, from a language definition point of view, + * produces: + * + * mov r2, #123 + * str r2, [r3, #140] + * + */ + + +/*! + * @name TransferDescriptors + * + * Note that the MX21 needs to run in little-endian mode for OTG, but + * since the memory can only be accessed in 32-bit quantities, anything + * smaller will be built in regular memory, then transfered one word at + * a time to the memory-mapped registers, and that word access will flip + * the order of bytes. u16 quantities survive this flipping because they + * get "pre-flipped" in regular memory. + * + * + * An Endpoint Transfer Descriptor (ETD) consists of 4x32bit words. + * The first word is the Endpoint Descriptor, the last three are + * the transfer descriptor. Transfer descriptors come in 3 formats, + * CONTROL/BULK, INTERRUPT, and ISOCRONOUS. Becasue of the memory + * access restrictions to less than 32 bits described above, only + * the words are mapped directly, The smaller fields are assembled + * into 32-bit words before being placed into the live ETD. + * + */ + /*! @{ */ + +/*! + * @struct transfer_descriptor_w1 + * All three types of TD have the same format for w1. + */ +typedef struct transfer_descriptor_w1 { +#if defined(LITTLE_ENDIAN) + u16 x; + u16 y; +#else + u16 y; + u16 x; +#endif +} __attribute__ ((packed)) volatile transfer_descriptor_w1; + +/*! + * @struct control_bulk_transfer_descriptor_w2 + * There are 3 different formats for w2. + * C.f. R1: sec 23.11.10.2 pg 23-43 + */ +typedef struct control_bulk_transfer_descriptor_w2 { +#if defined(LITTLE_ENDIAN) + u8 rtrydelay; + u8 reserved; + u16 flags; +#else + u16 flags; + u8 reserved; + u8 rtrydelay; +#endif +} __attribute__ ((packed)) volatile control_bulk_transfer_descriptor_w2; + +/*! + * @struct interrupt_transfer_descriptor_w2 + * C.f. R1: sec 23.11.10.3 pg 23-46 + */ +typedef struct interrupt_transfer_descriptor_w2 { +#if defined(LITTLE_ENDIAN) + u8 polinterv; + u8 relpolpos; + u16 flags; +#else + u16 flags; + u8 relpolpos; + u8 polinterv; +#endif +} __attribute__ ((packed)) volatile interrupt_transfer_descriptor_w2; + +/*! + * @struct isoc_transfer_descriptor_w2 + * C.f. R1: sec 23.11.10.4 pg 23-48 + */ +typedef struct isoc_transfer_descriptor_w2 { +#if defined(LITTLE_ENDIAN) + u16 startfrm; + u16 flags; +#else + u16 flags; + u16 startfrm; +#endif +} __attribute__ ((packed)) volatile isoc_transfer_descriptor_w2; + + + +/*! + * @struct isoc_transfer_descriptor_w3 + * There are 2 different formats for w3. + * All but isoc use a packet size and 20-bit bytecount, accessed through w3.val. + */ +typedef struct isoc_transfer_descriptor_w3 { +#if defined(LITTLE_ENDIAN) + u16 pkt0; + u16 pkt1; +#else + u16 pkt1; + u16 pkt0; +#endif +} __attribute__ ((packed)) volatile isoc_transfer_descriptor_w3; + +/*! + * @name transfer_descriptor + */ +/*! @{ */ +typedef struct transfer_descriptor { + union { + u32 val; + transfer_descriptor_w1 bufsrtad; + } volatile w1; + union { + u32 val; + control_bulk_transfer_descriptor_w2 cb; + interrupt_transfer_descriptor_w2 intr; + isoc_transfer_descriptor_w2 isoc; + } volatile w2; + union { + u32 val; + isoc_transfer_descriptor_w3 isoc; + } volatile w3; +} __attribute__ ((packed)) volatile transfer_descriptor; + +/*! @} */ + +/*! + * @name endpoint_transfer_descriptor + * C.f. R1: sec 23.11.10 pg 23-41 + */ + /*! @{ */ +typedef struct endpoint_transfer_descriptor { + u32 epd; + transfer_descriptor td; +} __attribute__ ((packed)) volatile endpoint_transfer_descriptor; // ETD + +/*! @} */ + + +/*! + * @name DataBuffs + * + * There is no predetermined size for data buffers, so + * this structure was chosen as an arbitrary, but generally + * adequate size. + */ +/*! @{ */ + +/*! + * @struct fs_data_buff + */ +typedef struct fs_data_buff { + u32 data[DATA_BUFF_SIZE/sizeof(u32)]; // Cannot safely write less than 32-bit quantities +} volatile fs_data_buff; + + +/*! + * @struct fs_hcpriv + */ +typedef struct fs_hcpriv { + struct bus_hcpriv *bus_hcpriv; + u32 sof_count; + u32 free_etds; + endpoint_transfer_descriptor sdp_etd[NUM_ETDS]; // Setup data phase save area + void *sdp_data[NUM_ETDS]; + u8 etd_urb_state[NUM_ETDS]; + u16 free_buffs; + u16 buff_list[NUM_DATA_BUFFS]; +} fs_hcpriv; + + +/*! + * get_data_buff() - get data buff + */ +static __inline__ fs_data_buff *get_data_buff(fs_hcpriv *fs_hcpriv) +{ + // Get the next available free data_buff, return NULL if none available. + unsigned long flags; + fs_data_buff *db; + u16 ndx; + local_irq_save(flags); + if (0xffff != (ndx = fs_hcpriv->free_buffs)) { + fs_hcpriv->free_buffs = fs_hcpriv->buff_list[ndx]; + db = ndx + (fs_data_buff *)OTG_DATA_BASE; + } + else + db = NULL; + local_irq_restore(flags); + return(db); +} + +/*! + * rel_data_buff() - release data buff + */ +static __inline__ fs_data_buff *rel_data_buff(fs_hcpriv *fs_hcpriv, fs_data_buff *db) +{ + // Release db to the pool of available data_buffs. + unsigned long flags; + u16 ndx; + if (NULL != db) { + local_irq_save(flags); + ndx = db - (fs_data_buff *)OTG_DATA_BASE; + fs_hcpriv->buff_list[ndx] = fs_hcpriv->free_buffs; + fs_hcpriv->free_buffs = ndx; +#if 0 + db->next = fs_hcpriv->free_buffs; + fs_hcpriv->free_buffs = db; +#endif + local_irq_restore(flags); + } + return(NULL); +} + +/*! + * data_buff_boff() - get data buffer offset given address + */ +static __inline__ u16 data_buff_boff(fs_data_buff *db) +{ + return((u16)(((void *) db) - ((void *) OTG_DATA_BASE))); +} + +/*! + * data_buff_addr() - get data buffer address given offset + */ +static __inline__ fs_data_buff *data_buff_addr(u16 boff) +{ + // Return the address of the fs_data_buffer that is boff bytes from the start of data buffer memory. + return(boff + ((void *) OTG_DATA_BASE)); +} +/*! @} */ + +/* ********************************************************************************************** */ + +/*! + * @name FSUSBOTGIO + * @brief Freescale USBOTG I/O support. + */ + /*! @{ */ + +/*! + * fs_rb() - read a byte + * @param port + * @return byte read + */ +static u8 __inline__ fs_rb(u32 port) +{ + return *(volatile u8 *) (IO_ADDRESS(port)); +} + +/*! + * fs_rl() - read a long + * @param port + * @return word read + */ +static u32 __inline__ fs_rl(u32 port) +{ + return *(volatile u32 *) (IO_ADDRESS(port)); +} + +/*! + * fs_wb() - write a byte + * @param port + * @param val + */ +static void __inline__ fs_wb(u32 port, u8 val) +{ + *(volatile u8 *)(IO_ADDRESS(port)) = val; +} + +/*! + * fs_orb() - or a byte + * @param port + * @param val + */ +static void __inline__ fs_orb(u32 port, u8 val) +{ + u8 set = fs_rb(port) | val; + *(volatile u8 *)(IO_ADDRESS(port)) = set; +} + +/*! + * fs_andb() - and a byte + * @param port + * @param val + */ +static void __inline__ fs_andb(u32 port, u8 val) +{ + u8 set = fs_rb(port) & val; + *(volatile u8 *)(IO_ADDRESS(port)) = set; +} + +/*! + * fs_wl() - write a long + * @param port + * @param val + */ +static void __inline__ fs_wl(u32 port, u32 val) +{ + u32 set; + *(volatile u32 *)(IO_ADDRESS(port)) = val; + set = fs_rl(port); +} + +/*! + * fs_orl() - or a long + * @param port + * @param val + */ +static void __inline__ fs_orl(u32 port, u32 val) +{ + u32 set = fs_rl(port); + *(volatile u32 *)(IO_ADDRESS(port)) = (set | val); +} + +/*! + * fs_andl() - and a long + * @param port + * @param val + */ +static void __inline__ fs_andl(u32 port, u32 val) +{ + u32 set = fs_rl(port); + *(volatile u32 *)(IO_ADDRESS(port)) = (set & val); +} + +/*! + * fs_host_port_stat() - get host start port address for ports 1-3 + * @param n + * @returns port address + */ +static u32 inline fs_host_port_stat(int n) +{ + switch (n) { + default: + case 1: + return OTG_HOST_PORT_STATUS_1; + case 2: + return OTG_HOST_PORT_STATUS_2; + case 3: + return OTG_HOST_PORT_STATUS_3; + } +} + + +/*! + * fs_wl_set() - set a word and verify + * @param tag + * @param port + * @param val + */ +static void inline fs_wl_set(otg_tag_t tag, u32 port, u32 val) +{ + u32 set; + *(volatile u32 *)(IO_ADDRESS(port)) = val; + set = fs_rl(port); + if ((set & val) != val) { + TRACE_MSG1(tag, "SET FAILED: %08x", set); + } +} + +/*! + * fs_wl_clr() - clr a word and verify + * @param tag + * @param port + * @param clr + */ +static void inline fs_wl_clr(otg_tag_t tag, u32 port, u32 clr) +{ + u32 set; + *(volatile u32 *)(IO_ADDRESS(port)) = clr; + set = fs_rl(port); + if (set & clr) { + TRACE_MSG1(tag, "CLEAR FAILED 1: %08x", set); + *(volatile u32 *)(IO_ADDRESS(port)) = clr; + set = fs_rl(port); + if (set & clr) + TRACE_MSG1(tag, "CLEAR FAILED 2: %08x", set); + } +} + + +/*! + * fs_memcpy32() - emulage memcpy using long copy + * @param dp destination pointer + * @param sp source pointer + * @param words number of 32bit words to copy + * + */ +static void inline fs_memcpy32(u32 *dp, u32 *sp, volatile int words) +{ + while (words--) *dp++ = *sp++; +} +/*! + * fs_memcpy() - emulage memcpy using byte copy + * @param dp destination pointer + * @param sp source pointer + * @param bytes number of 8bit bytes to copy + */ +static void inline fs_memcpy(u8 *dp, u8 *sp, volatile int bytes) +{ + while (bytes--) *dp++ = *sp++; +} +/*! + * fs_clear_words() - clear memory + * @param addr address to clear from + * @param words number of 32bit words to clear. + */ +static void inline fs_clear_words(volatile u32 *addr, int words) +{ + while (words--) *addr++ = 0; +} + +/*! @} */ + diff -uNr linux/drivers/no-otg/otghw/isp1301-hardware.h linux/drivers/otg/otghw/isp1301-hardware.h --- linux/drivers/no-otg/otghw/isp1301-hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/isp1301-hardware.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,218 @@ +/* + * otg/otghw/isp1301-hardware.h -- ISP1301 hardware specific defines + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otghw/isp1301-hardware.h + * @brief Public Structures And Defines For ISP1301 Hardware. + * + * The Phillips ISP1301 is an OTG Transceiver. There a additional + * compatible parts such as the Maxim MAX3301E + * + * @ingroup ISP1301TCD + */ + + +/*! + * @name ADR/PSW - C.f. 8.9.1 + * + * The i2c address is either 0x2c or 0x2d depending on the level of the + * ADR/PSW pin after device reset. Note that this pin can be programmed as + * an output via the Mode Control 2 register, bit PSW_OE to enable an + * @{ + */ + +#define ISP1301_I2C_ADDR_LOW 0x2c +#define ISP1301_I2C_ADDR_HIGH 0x2d +/*! @} */ + +/*! + * @name ISP1301 Registers C.f. 11.1 + * @{ + */ + +#define ISP1301_VENDOR_ID 0x00 +#define ISP1301_PRODUCT_ID 0x02 +#define ISP1301_VERSION_ID 0x14 + +#define ISP1301_VENDOR_ID_LOW 0x00 +#define ISP1301_PRODUCT_ID_LOW 0x02 +#define ISP1301_VERSION_ID_LOW 0x14 +#define ISP1301_VENDOR_ID_HIGH 0x01 +#define ISP1301_PRODUCT_ID_HIGH 0x03 +#define ISP1301_VERSION_ID_HIGH 0x15 + +/* these are all single byte registers + */ +#define ISP1301_MODE_CONTROL_1 0x04 +#define ISP1301_MODE_CONTROL_1_SET 0x04 +#define ISP1301_MODE_CONTROL_1_CLR 0x05 + +#define ISP1301_MODE_CONTROL_2 0x12 +#define ISP1301_MODE_CONTROL_2_SET 0x12 +#define ISP1301_MODE_CONTROL_2_CLR 0x13 + +#define ISP1301_OTG_CONTROL 0x06 +#define ISP1301_OTG_CONTROL_SET 0x06 +#define ISP1301_OTG_CONTROL_CLR 0x07 + +#define ISP1301_OTG_STATUS 0x10 + +#define ISP1301_INTERRUPT_SOURCE 0x08 + +#define ISP1301_INTERRUPT_LATCH 0x0a +#define ISP1301_INTERRUPT_LATCH_SET 0x0a +#define ISP1301_INTERRUPT_LATCH_CLR 0x0b + +#define ISP1301_INTERRUPT_ENABLE_LOW 0x0c +#define ISP1301_INTERRUPT_ENABLE_LOW_SET 0x0c +#define ISP1301_INTERRUPT_ENABLE_LOW_CLR 0x0d + +#define ISP1301_INTERRUPT_ENABLE_HIGH 0x0e +#define ISP1301_INTERRUPT_ENABLE_HIGH_SET 0x0e +#define ISP1301_INTERRUPT_ENABLE_HIGH_CLR 0x0f +/*! @} */ + + +/*! + * @name Mode Control 1 register - C.f. Table 17 and Table 18 + * @{ + */ +#define ISP1301_UART_EN (1 << 6) +#define ISP1301_OE_INT_EN (1 << 5) +#define ISP1301_BDIS_ACON_EN (1 << 4) +#define ISP1301_TRANSP_EN (1 << 3) +#define ISP1301_DAT_SE0 (1 << 2) +#define ISP1301_SUSPEND_REG (1 << 1) +#define ISP1301_SPEED_REG (1 << 0) +/*! @} */ + + +/*! + * @name Mode Control 2 register - C.f. Table 19 and Table 20 + * @{ + */ +#define ISP1301_EN2V7 (1 << 7) +#define ISP1301_PSW_OE (1 << 6) +#define ISP1301_AUDIO_EN (1 << 5) +#define ISP1301_TRANSP_BDIR (3 << 3) +#define ISP1301_BI_DI (1 << 2) +#define ISP1301_SPD_SUSP_CTRL (1 << 1) +#define ISP1301_GLOBAL_PWR_ON (1 << 0) +/*! @} */ + + +/*! + * @name OTG Control register - C.f. Table 21 and Table 22 + * @{ + */ +#define ISP1301_VBUS_CHRG (1 << 7) +#define ISP1301_VBUS_DISCHRG (1 << 6) +#define ISP1301_VBUS_DRV (1 << 5) +#define ISP1301_ID_PULLDOWN (1 << 4) +#define ISP1301_DM_PULLDOWN (1 << 3) +#define ISP1301_DP_PULLDOWN (1 << 2) +#define ISP1301_DM_PULLUP (1 << 1) +#define ISP1301_DP_PULLUP (1 << 0) + +#define ISP1301_VBUS_RESET (ISP1301_VBUS_CHRG | ISP1301_VBUS_DISCHRG | ISP1301_VBUS_DRV) + +/*! @} */ + + +/*! + * @name OTG Status register - C.f. Table 23 and Table 24 + * @{ + */ +#define ISP1301_B_SESS_VLD (1 << 7) +#define ISP1301_B_SESS_END (1 << 6) +/*! @} */ + + +/*! + * @name Interrupt Source Register + * Interrupt Source register - C.f. Table 25 and Table 26 + * Interrupt Latch register - C.f. Table 27 and Table 28 + * Interrupt Enable Low register - C.f. Table 29 and Table 30 + * Interrupt Enable High register - C.f. Table 31 and Table 32 + * @{ + */ +#define ISP1301_CR_INT (1 << 7) +#define ISP1301_BDIS_ACON (1 << 6) +#define ISP1301_ID_FLOAT (1 << 5) +#define ISP1301_DM_HI (1 << 4) +#define ISP1301_ID_GND (1 << 3) +#define ISP1301_DP_HI (1 << 2) +#define ISP1301_SESS_VLD (1 << 1) +#define ISP1301_VBUS_VLD (1 << 0) + +//#define ISP1301_SE1 (ISP1301_DM_HI | ISP1301_DP_HI) + +/*! @} */ + +/*! + * @name Maxim MAX3301E + * + * This part is compatible with the ISP1301 with minor differences: + * + * Mode Control 1 register is called Control Register 1. It is almost + * identical except for: + * + * bit isp1301 max3301e + * 3 transp_en not used + * @{ + */ +#define MAX3301E_CONTROL_REGISTER_1 0x04 +#define MAX3301E_CONTROL_REGISTER_1_SET 0x04 +#define MAX3301E_CONTROL_REGISTER_1_CLR 0x05 +/*! @} */ + +/*! + * @name Mode Control 2 + * Mode Control 2 is called Special Function Register 1. Mostly the same + * except for: + * + * bit isp1301 max3301e + * 7 en2v7 gp_en + * 6 psw_oe sess_end + * 5 audio_en int_source + * @{ + */ +#define MAX3301E_SPECIAL_FUNCTION_1 0x12 +#define MAX3301E_SPECIAL_FUNCTION_1_SET 0x12 +#define MAX3301E_SPECIAL_FUNCTION_1_CLR 0x13 +#define MAX3301E_GP_EN (1 << 7) +#define MAX3301E_SESS_END (1 << 6) +#define MAX3301E_INT_SOURCE (1 << 5) +/*! @} */ + +/*! + * @name OTG Status + * The OTG Status is not present, but B_SESS_END seems to be available + * in the Mode Control 2 register bit 6. + * + * An additional register is available for MAX3301E specific features. + * + * Special Function Register 2 + * @{ + */ + +#define MAX3301E_SPECIAL_FUNCTION_2 0x16 +#define MAX3301E_SPECIAL_FUNCTION_2_SET 0x16 +#define MAX3301E_SPECIAL_FUNCTION_2_CLR 0x17 +#define MAX3301E_SDWN (1 << 0) +#define MAX3301E_IRQ_MODE (1 << 1) +#define MAX3301E_XCVR_INPUT_DISC (1 << 2) +#define MAX3301E_REQ_SET (1 << 3) +/*! @} */ + + + + diff -uNr linux/drivers/no-otg/otghw/isp1301.h linux/drivers/otg/otghw/isp1301.h --- linux/drivers/no-otg/otghw/isp1301.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/isp1301.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,158 @@ +/* + * otg/otghw/isp1301/isp1301.h -- USB Transceiver Controller driver + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @defgroup ISP1301TCD Phillips ISP1301 + * @ingroup tcdgroup + */ +/*! + * @file otg/otghw/isp1301.h + * @brief Private structures and defines for ISP1301 Transciever Controller Driver. + * + * This file contains the private defines and structures for the ISP1301 Transceiver + * Driver. + * + * @ingroup ISP1301TCD + */ + +/*! + * @name ISP1301 ISP1301 configuration + * @{ + */ + +#ifdef CONFIG_OMAP_H2 +#define ISP1301_GPIO (2) +#define TCD_ISP1301_I2C_ADDR ISP1301_I2C_ADDR_HIGH; +#endif + +#define ISP1301_NAME "isp1301" + +#define ISP1301_LOC_CONN 0x01 + +#define ISP1301_INT_SRC 0xeb + +/*! + * @enum otg_transceiver + * Define ISP1301 compatible transceivers + */ +typedef enum otg_transceiver { + unknown_transceiver, /*!< unknown */ + isp1301, /*!< Phillips ISP1301 */ + max3301e /*!< Maxim MAX3301e */ +} otg_transceiver_t; + +/*! + * @struct otg_transceiver_map + * How to determine transceiver model and manufacturer + */ +struct otg_transceiver_map { + otg_transceiver_t transceiver_type; /*!< transceiver type */ + u16 vendor; /*!< vendor number */ + u16 product; /*!< product number */ + u16 revision; /*!< revision number */ + char *name; /*!< name */ +}; + +/*! + * @struct isp1301_private + * Information required for operation + */ +struct isp1301_private { + WORK_STRUCT bh; /*!< work structure for bottom half handler */ + u16 vendor; /*!< vendor number found */ + u16 product; /*!< product number found */ + u16 revision; /*!< revision number found */ + u32 flags; /*!< flags */ + u8 int_src; /*!< current interrupt source */ + struct otg_transceiver_map *transceiver_map; /*! pointer to transceiver map for this transceiver */ +}; + +/*! + * @enum tx_mode + * This defines the various ways that the ISP1301 can be + * wired into the host USB. + */ +typedef enum tx_mode { + dat_se0_bidirectional, /*!< 3-Wire */ + vp_vm_bidirectional, /*!< 4-Wire */ + dat_se0_unidirectional /*!< 6-Wire */ +} tx_mode_t; + +/*! + * @enum spd_ctrl + * This defines how the speed and suspend pins are controlled + * for this host. + */ +typedef enum spd_ctrl { + spd_susp_pins, /*!< controlled by SPEED and SUSPEND pins */ + spd_susp_reg, /*!< controled by SPEED_REG and SUSPEND_REG in Mode Control 1 register */ +} spd_ctrl_t; + + +extern struct tcd_ops tcd_ops; +extern struct isp1301_private isp1301_private; +extern struct tcd_instance *tcd_instance; + +/* isp1301.c */ +extern int isp1301_mod_init(WORK_PROC ); +extern int isp1301_mod_init__(void ); +extern void isp1301_mod_exit(void); +extern void isp1301_tcd_en(struct otg_instance *, u8 ); +extern void isp1301_chrg_vbus(struct otg_instance *, u8 ); +extern void isp1301_drv_vbus(struct otg_instance *, u8 ); +extern void isp1301_dischrg_vbus(struct otg_instance *, u8 ); +extern void isp1301_dp_pullup_func(struct otg_instance *, u8 ); +extern void isp1301_dm_pullup_func(struct otg_instance *, u8 ); +extern void isp1301_dp_pulldown_func(struct otg_instance *, u8 ); +extern void isp1301_dm_pulldown_func(struct otg_instance *, u8 ); +extern void isp1301_peripheral_host_func(struct otg_instance *, u8 ); +extern void isp1301_dm_det_func(struct otg_instance *, u8 ); +extern void isp1301_dp_det_func(struct otg_instance *, u8 ); +extern void isp1301_cr_det_func(struct otg_instance *, u8 ); +extern void isp1301_bdis_acon_func(struct otg_instance *, u8 ); +extern void isp1301_mx21_vbus_drain_func(struct otg_instance *, u8 ); +extern void isp1301_id_pulldown_func(struct otg_instance *, u8 ); +extern void isp1301_audio_func(struct otg_instance *, u8 ); +extern void isp1301_uart_func(struct otg_instance *, u8 ); +extern void isp1301_mono_func(struct otg_instance *, u8 ); + +extern void isp1301_otg_timer(struct otg_instance *, u8 ); +extern void isp1301_bh(void *); +extern int isp1301_id (struct otg_instance *); +extern int isp1301_vbus (struct otg_instance *); +extern void isp1301_configure(tx_mode_t, spd_ctrl_t ); + +extern void isp1301_bh_wakeup(int); + +/* thread */ +extern int isp1301_thread_init (void (bh_proc)(void *)); +extern void isp1301_thread_exit (wait_queue_head_t *); +extern void isp1301_thread_wakeup(int enabled, int first); + + + +/* i2c-xxx.c */ +int i2c_configure(char *name, int addr); +extern void i2c_close(void); +extern u8 i2c_readb(u8 ); +extern u16 i2c_readw(u8 ); +extern u32 i2c_readl(u8 ); +extern void i2c_writeb(u8 , u8 ); + + +/* ********************************************************************************************* */ +#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) +#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) +#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) +#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) + +/* @} */ + diff -uNr linux/drivers/no-otg/otghw/max3353e-hardware.h linux/drivers/otg/otghw/max3353e-hardware.h --- linux/drivers/no-otg/otghw/max3353e-hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/max3353e-hardware.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,141 @@ +/* + * otg/otg/max3353e-hardware.h -- MAX3353E hardware specific defines + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otg/otg/max3353e-hardware.h + * @brief Public structures and defines for MAX3353E Hardware. + * + * The Maxim MAX3353E is an OTG Charge Pump with controllable + * pullup resistors. + * + * @ingroup MAX3353ETCD + */ + + +/*! + * @name ADR/PSW - C.f. Slave Address + * + * The high/low address is selected with the ADD pin. + * + * @{ + */ + +#define MAX3353E_I2C_ADDR_LOW 0x16 +#define MAX3353E_I2C_ADDR_HIGH 0x2C + +/*! @} */ + + + +/*! + * @name MAX3353E Registers C.f. Table 2 - Register Address Map + * @{ + */ + +#define MAX3353E_MANUFACTURER_REGISTER_0 0x00 +#define MAX3353E_MANUFACTURER_REGISTER_1 0x01 +#define MAX3353E_MANUFACTURER_REGISTER_2 0x02 +#define MAX3353E_MANUFACTURER_REGISTER_3 0x03 + +#define MAX3353E_PRODUCT_ID_REGISTER_0 0x04 +#define MAX3353E_PRODUCT_ID_REGISTER_1 0x05 +#define MAX3353E_PRODUCT_ID_REGISTER_2 0x06 +#define MAX3353E_PRODUCT_ID_REGISTER_3 0x07 + +#define MAX3353E_CONTROL_REGISTER_1 0x10 +#define MAX3353E_CONTROL_REGISTER_2 0x11 + +#define MAX3353E_STATUS_REGISTER 0x13 + +#define MAX3353E_INTERRUPT_MASK 0x14 +#define MAX3353E_INTERRUPT_EDGE 0x15 +#define MAX3353E_INTERRUPT_LATCH 0x16 + +/*! @} */ + + +/*! + * @name Control Register 1 + * @{ + */ +#define MAX3353E_DM_PULLDOWN (1 << 7) +#define MAX3353E_DP_PULLDOWN (1 << 6) +#define MAX3353E_DM_PULLUP (1 << 5) +#define MAX3353E_DP_PULLUP (1 << 4) + +#define MAX3353E_BDISC_ACONN (1 << 2) +#define MAX3353E_IRQ_MODE (1 << 1) + +/*! @} */ + +/*! + * @name Control Register 2 + * @{ + */ +#define MAX3353E_VBUS_DISCHG (1 << 4) +#define MAX3353E_VBUS_DRV (1 << 3) +#define MAX3353E_VBUS_CHG2 (1 << 2) +#define MAX3353E_VBUS_CHG1 (1 << 1) +#define MAX3353E_DSWIN (1 << 0) + +/*! @} */ + +/*! + * @name Status Register + * @{ + */ +#define MAX3353E_B_HNP (1 << 6) +#define MAX3353E_A_HNP (1 << 5) +#define MAX3353E_ID_FLOAT (1 << 4) +#define MAX3353E_ID_GND (1 << 3) +#define MAX3353E_SESSION_END (1 << 2) +#define MAX3353E_SESSION_VALID_EN (1 << 1) +#define MAX3353E_VBUS_VALID (1 << 0) + +/*! @} */ + +/*! + * @name Interrupt Mask + * @{ + */ +#define MAX3353E_A_HNP_EN (1 << 5) +#define MAX3353E_ID_FLOAT_EN (1 << 4) +#define MAX3353E_ID_GND_EN (1 << 3) +#define MAX3353E_SESSION_END_EN (1 << 2) +#define MAX3353E_SESSION_VALID_EN (1 << 1) +#define MAX3353E_VBUS_VALID_EN (1 << 0) + +/*! @} */ + +/*! + * @name Interrupt Edge + * @{ + */ +#define MAX3353E_SESSION_VALID_ED (1 << 1) +#define MAX3353E_VBUS_VALID_ED (1 << 0) + +/*! @} */ + +/*! + * @name Interrupt Latch + * @{ + */ +#define MAX3353E_A_HNP_RQ (1 << 7) +#define MAX3353E_ID_FLOAT_RQ (1 << 6) +#define MAX3353E_ID_GND_RQ (1 << 5) +#define MAX3353E_SESSION_END_RQ (1 << 4) +#define MAX3353E_SESSION_VALID_RN (1 << 3) +#define MAX3353E_VBUS_VALID_RN (1 << 2) +#define MAX3353E_SESSION_VALID_RP (1 << 1) +#define MAX3353E_VBUS_VALID_RP (1 << 0) + +/*! @} */ + diff -uNr linux/drivers/no-otg/otghw/mx2ads-hardware.h linux/drivers/otg/otghw/mx2ads-hardware.h --- linux/drivers/no-otg/otghw/mx2ads-hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/mx2ads-hardware.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,111 @@ +/* + * otghw/mx2ads-hardware.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @file otghw/mx2ads-hardware.h + * @brief MX2ADS Hardware constants + * + * @ingroup MX2ADS + */ + + +/*! + * @name MX2ETDS + * R1 = i.MX21 Application Processor Reference Manual Rev 0.6 2003/11/01 TO 1.0 + * (aka "IMX21RM_TO1.pdf") + * R2 = "USBOTG_L3_Specification_v.C1.0.pdf" + * @{ + */ + +#define NUM_ETDS 32 +#define DATA_BUFF_SIZE 64 +#define DATA_BUFFER_TOTAL 4096 +#define NUM_DATA_BUFFS (4096/DATA_BUFF_SIZE) +//#define LITTLE_ENDIAN 1 + +/* @} */ + + +/*! + * @name MX2Interrupts + * @{ + */ +#define OTG_USBWKUP 53 +#define OTG_USBDMA 54 +#define OTG_USBHOST 55 +#define OTG_USBFUNC 56 +#define OTG_USBHNP 57 +#define OTG_USBCTRL 58 +/* @} */ + +/*! + * @name AHBMemoryMap + * C.F. Table 23-5 AHB Memory Map + * @{ + */ +#ifndef OTG_BASE_ADDR +#define OTG_BASE_ADDR 0x10024000 + +#define OTG_CORE_BASE (OTG_BASE_ADDR+0x000) // base location for core +#define OTG_FUNC_BASE (OTG_BASE_ADDR+0x040) // base location for function +#define OTG_HOST_BASE (OTG_BASE_ADDR+0x080) // base location for host +#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) // base location for I2C + +#define OTG_ETD_BASE (OTG_BASE_ADDR+0x200) // base location for etd memory +#define OTG_EP_BASE (OTG_BASE_ADDR+0x400) // base location for ep memory +#define OTG_SYS_BASE (OTG_BASE_ADDR+0x600) // base location for system + +#define OTG_DMA_BASE (OTG_BASE_ADDR+0x800) // base location for dma + +#define OTG_DATA_BASE (OTG_BASE_ADDR+0x1000) // base location for data memory + +#define OTG_SYS_CTRL (OTG_SYS_BASE+0x000) // base location for system +#endif +/* @} */ + +/*! + * @name I2CTransceiver + * C.f. 23.14.10 I2C OTG Transceiver Controller Registers + * These are the I2C controller and access registers. + * + * N.B. I2C_ERROR is not documented. + * @{ + */ + + +#define I2C_BUSY (1 << 7) +#define I2C_ERROR (1 << 2) +#define I2C_HWSMODE (1 << 1) +#define I2C_I2COE (1 << 0) + +#define I2C_SCLK_TO_SCL_DIVISION (OTG_I2C_BASE+0x1E) + +#define I2C_INTERRUPT_AND_CONTROL (OTG_I2C_BASE+0x1F) + +#define I2C_STATUS_MASK (0x7) + +#define I2C_NOACK_EN (1 << 6) +#define I2C_RWREADY_EN (1 << 5) +#define I2C_OTGXCVRINT_EN (1 << 4) + +#define I2C_NOACK (1 << 2) +#define I2C_RWREADY (1 << 1) +#define I2C_OTGXCVRINT (1 << 0) + +#define MX2_OTG_XCVR_DEVAD OTG_I2C_BASE+0x18 +#define MX2_SEQ_OP_REG OTG_I2C_BASE+0x19 +#define MX2_SEQ_RD_STARTAD OTG_I2C_BASE+0x1a +#define MX2_I2C_OP_CTRL_REG OTG_I2C_BASE+0x1b +#define MX2_SCLK_TO_SCL_HPER OTG_I2C_BASE+0x1e +#define MX2_I2C_INTERRUPT_AND_CTRL OTG_I2C_BASE+0x1f + +/* @} */ + diff -uNr linux/drivers/no-otg/otghw/mx2ads.h linux/drivers/otg/otghw/mx2ads.h --- linux/drivers/no-otg/otghw/mx2ads.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/mx2ads.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,46 @@ +/* + * otghw/mx2ads.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + */ + +/*! + * @defgroup MX2ADS Freescale MX2ADS + * @ingroup platformgroup + */ +/*! + * @file otghw/mx2ads.h + * @brief MX2ADS Platform constants + * + * Notes + * + * 1. Interrupts - we seem to get all interrupts on the ctrl interrupt. + * + * 2. Clearing - it appears that clearing the status bits sometimes fails, This + * has been observed in SINT_STAT and XFILL_STAT. + * + * 3. Double buffer - without DMA it does not appear to be possible to use + * both X and Y buffers for OUT transfers because we can not apparantely guarantee + * the order that the X and Y buffers will be used. If we get an interrupt with + * both X and Y active we cannot determine which order to use them. As long as + * we get each interrupt fast enough this is not a problem as we can then just + * use whichever is active. + * + * 4. Remote Wakeup - cannot set RSMINPROG bit + * + * @ingroup MX2ADS + */ + + +#define UDC_NAME "MX21" +#define UDC_MAX_ENDPOINTS 32 +#define EP0_PACKETSIZE 64 + + +extern otg_tag_t MX2; + diff -uNr linux/drivers/no-otg/otghw/td243-hardware.h linux/drivers/otg/otghw/td243-hardware.h --- linux/drivers/no-otg/otghw/td243-hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ linux/drivers/otg/otghw/td243-hardware.h 2006-09-01 21:41:35.000000000 +0200 @@ -0,0 +1,709 @@ +/* + * otg/pcd/mx2/td243-hardware.h + * + * Copyright (c) 2004 Belcarra + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + */ +/*! + * @defgroup TD243 Transdimension USBOTG + * @ingroup libgroup + */ +/*! + * @file /otg/otghw/td243-hardware.h + * @brief Hardware defines for Transimension USBOTG Hardware + * + * @ingroup TD243 + */ + + +/* + * C.f. 23.8 USB Control Register + */ + +#define SYS_CTRL_I2C_WU_INT_STAT (1 << 27) +#define SYS_CTRL_OTG_WU_INT_STAT (1 << 26) +#define SYS_CTRL_HOST_WU_INT_STAT (1 << 25) +#define SYS_CTRL_FNT_WU_INT_STAT (1 << 24) + +#define SYS_CTRL_I2C_WU_INT_EN (1 << 19) +#define SYS_CTRL_OTG_WU_INT_EN (1 << 18) +#define SYS_CTRL_HOST_WU_INT_EN (1 << 17) +#define SYS_CTRL_FNT_WU_INT_EN (1 << 16) + +#define SYS_CTRL_OTG_BYP_VAL (0x3 << 11) +#define SYS_CTRL_HOST1_BYP_VAL (0x3 << 9) + + + +#define SYS_CTRL_OTG_PWR_MASK (1 << 6) +#define SYS_CTRL_HOST1_PWR_MASK (1 << 6) +#define SYS_CTRL_HOST2_PWR_MASK (1 << 4) +#define SYS_CTRL_USB_BYP (1 << 2) +#define SYS_CTRL_HOST1_TXEN_OE (1 << 1) + + +/* + * C.f. 23.9 USBOTG Module Registers + */ + +#define OTG_CORE_HWMODE (OTG_CORE_BASE+0x00) // 32bit core hardware mode reg + +#define XCVR_D_D 0x00 +#define XCVR_SE_D 0x01 +#define XCVR_D_SE 0x02 +#define XCVR_SE_SE 0x03 + +#define MODULE_ANASDBEN (1 << 14) +#define MODULE_OTGXCVR (0x3 << 6) +#define MODULE_HOSTXCVR (0x3 << 4) +#define MODULE_CRECFG (0x3) +#define MODULE_CRECFG_HHNP (0x0) +#define MODULE_CRECFG_HOST (0x1) +#define MODULE_CRECFG_FUNC (0x2) +#define MODULE_CRECFG_SHNP (0x3) + +#define OTG_CORE_CINT_STAT (OTG_CORE_BASE+0x04) // 32bit core int status reg + +#define MODULE_FCINTDSPEN (1 << 6) + +#define MODULE_ASHNPINT (1 << 5) +#define MODULE_ASFCINT (1 << 4) +#define MODULE_ASHCINT (1 << 3) +#define MODULE_HNPINT (1 << 2) +#define MODULE_FCINT (1 << 1) +#define MODULE_HCINT (1) + +#define OTG_CORE_CINT_STEN (OTG_CORE_BASE+0x08) // 32bit core int enable reg + +#define MODULE_ASHNPINT_EN (1 << 5) +#define MODULE_ASFCINT_EN (1 << 4) +#define MODULE_ASHCINT_EN (1 << 3) +#define MODULE_HNPINT_EN (1 << 2) +#define MODULE_FCINT_EN (1 << 1) +#define MODULE_HCINT_EN (1) + +#define OTG_CORE_CLK_CTRL (OTG_CORE_BASE+0x0C) // 32bit core clock control reg + +#define MODULE_FUNC_CLK (1 << 2) +#define MODULE_HOST_CLK (1 << 1) +#define MODULE_MAIN_CLK (1) + +#define OTG_CORE_RST_CTRL (OTG_CORE_BASE+0x10) // 32bit core reset control reg + +#define MODULE_RSTI2C (1 << 15) +#define MODULE_RSTCTRL (1 << 5) +#define MODULE_RSTFC (1 << 4) +#define MODULE_RSTFSIE (1 << 3) +#define MODULE_RSTRH (1 << 2) +#define MODULE_RSTHSIE (1 << 1) +#define MODULE_RSTHC (1) + +#define OTG_CORE_FRM_INTVL (OTG_CORE_BASE+0x14) // 32bit core frame interval reg + +#define MODULE_RESET_FRAME (1 << 15) + +#define OTG_CORE_FRM_REMAIN (OTG_CORE_BASE+0x18) // 32bit core frame remaining reg + +#define OTG_CORE_HNP_CSTAT (OTG_CORE_BASE+0x1C) // 32bit core HNP current state reg + +#define MODULE_HNPDAT (1 << 30) +#define MODULE_VBUSBSE (1 << 29) +#define MODULE_VBUSABSV (1 << 28) +#define MODULE_VBUSGTAVV (1 << 27) + +#define MODULE_ARMTHNPE (1 << 25) +#define MODULE_BHNPEN (1 << 24) + +#define MODULE_SLAVE (1 << 22) +#define MODULE_MASTER (1 << 21) +#define MODULE_BGEN (1 << 20) +#define MODULE_CMPEN (1 << 19) +#define MODULE_ISBDEV (1 << 18) +#define MODULE_ISADEV (1 << 17) + +#define MODULE_SWVBUSPUL (1 << 15) + +#define MODULE_SWAUTORST (1 << 12) +#define MODULE_SWPUDP (1 << 11) + +#define MODULE_SWPDDM (1 << 9) +#define MODULE_HNPSTATE (0x1f << 8) +#define MODULE_CLRERROR (1 << 3) +#define MODULE_ADROPBUS (1 << 2) +#define MODULE_ABBUSREQ (1 << 1) + +#define HNP_A_IDLE (0x10 << 8) +#define HNP_A_MASTER (0x11 << 8) +#define HNP_A_SLAVE (0x12 << 8) +#define HNP_A_WAIT_VPULSE (0x13 << 8) + +#define HNP_A_WAIT_DPULSE (0x14 << 8) +#define HNP_A_WAIT_CONN_A (0x15 << 8) +#define HNP_A_WAIT_CONN_B (0x16 << 8) +#define HNP_A_WAIT_VRISE (0x17 << 8) +#define HNP_A_SUSPEND (0x18 << 8) +#define HNP_A_WAIT_VFALL (0x19 << 8) +#define HNP_A_VBUS_ERR (0x1a << 8) +#define HNP_CONN_DEBOUNCE (0x1b << 8) +#define HNP_A_WAIT_ABREQ (0x1c << 8) +#define HNP_B_IDLE (0x00 << 8) +#define HNP_B_MASTER (0x01 << 8) +#define HNP_B_SLAVE (0x02 << 8) +#define HNP_B_SRP_INIT_V (0x03 << 8) +#define HNP_B_SRP_INIT_D (0x04 << 8) +#define HNP_B_PRE_WAIT_CONN_AB_SHORT_DB (0x05 << 8) +#define HNP_B_WAIT_CONN_A (0x06 << 8) +#define HNP_B_PRE_SLAVE (0x0a << 8) + + +#define OTG_CORE_HNP_TIMER1 (OTG_CORE_BASE+0x20) // 32bit core HNP timer 1 reg +#define OTG_CORE_HNP_TIMER2 (OTG_CORE_BASE+0x24) // 32bit core HNP timer 2 reg +#define OTG_CORE_HNP_T3PCR (OTG_CORE_BASE+0x28) // 32bit core HNP timer 3 pulse ctrl + +#define HNP_DATAPULSE (1 << 5) +#define HNP_VBUSPULSE (1 << 4) + +#define OTG_CORE_HINT_STAT (OTG_CORE_BASE+0x2C) // 32bit core HNP int status reg + +#define HNP_I2COTGINT (1 << 15) +#define HNP_AWAITBTO (1 << 8) // Hardware HNP Only +#define HNP_AIDLEBDTO (1 << 7) // Hardware HNP Only +#define HNP_SRPSUCFAIL (1 << 6) // Hardware HNP Only +#define HNP_SRPINT (1 << 5) +#define HNP_VBUSERROR (1 << 4) // Hardware HNP Only +#define HNP_ABSEVAILD (1 << 3) +#define HNP_ABUSVALID (1 << 2) +#define HNP_MASSLVCHG (1 << 1) // Hardware HNP Only +#define HNP_IDCHANGE (1) + +#define OTG_CORE_HINT_STEN (OTG_CORE_BASE+0x30) // 32bit core HNP int enable reg + +#define HNP_I2COTGINT_EN (1 << 15) +#define HNP_AWAITBTO_EN (1 << 8) +#define HNP_AIDLEBDTO_EN (1 << 7) +#define HNP_SRPSUCFAIL_EN (1 << 6) +#define HNP_SRPINT_EN (1 << 5) +#define HNP_VBUSERROR_EN (1 << 4) +#define HNP_ABSEVAILD_EN (1 << 3) +#define HNP_ABUSVALID_EN (1 << 2) +#define HNP_MASSLVCHG_EN (1 << 1) +#define HNP_IDCHANGE_EN (1) + + +#define OTG_CORE_CPUEPSEL_STAT (OTG_CORE_BASE+0x34) +#define OTG_CORE_INTERRUPT_STEN (OTG_CORE_BASE+0x3c) + + +/* + * C.f. 23.11.11 Host Registers + */ + + +#define OTG_HOST_CONTROL (OTG_HOST_BASE+0x00) // 32bit host controller config reg + +#define HOST_CONTROL_HCRESET (1 << 31) +#define HOST_CONTROL_RMTWUEN (1 << 4) + +#define HOST_CONTROL_HCUSBSTE_RESET (0 << 2) +#define HOST_CONTROL_HCUSBSTE_RESUME (1 << 2) +#define HOST_CONTROL_HCUSBSTE_OPERATIONAL (2 << 2) +#define HOST_CONTROL_HCUSBSTE_SUSPEND (3 << 2) + +#define HOST_CONTROL_CTLBLKSR_11 (0) +#define HOST_CONTROL_CTLBLKSR_21 (1) +#define HOST_CONTROL_CTLBLKSR_41 (2) +#define HOST_CONTROL_CTLBLKSR_81 (3) + + +#define OTG_HOST_SINT_STAT (OTG_HOST_BASE+0x08) // 32bit host system int status reg + +#define HOST_PSCINT (1 << 6) // Port Status Change +#define HOST_FMOFINT (1 << 5) // Frame Number Overflow +#define HOST_HERRINT (1 << 4) // Host Error +#define HOST_RESDETINT (1 << 3) // Resume Detected +#define HOST_SOFINT (1 << 2) // Start of Frame +#define HOST_DONEINT (1 << 1) // Done Register +#define HOST_SORINT (1) // Schedule Overrun + +#define OTG_HOST_SINT_STEN (OTG_HOST_BASE+0x0C) // 32bit host system int enable reg + +#define HOST_PSCINT_EN (1 << 6) // Port Status Change +#define HOST_FMOFINT_EN (1 << 5) // Frame Number Overflow +#define HOST_HERRINT_EN (1 << 4) // Host Error +#define HOST_RESDETINT_EN (1 << 3) // Resume Detected +#define HOST_SOFINT_EN (1 << 2) // Start of Frame +#define HOST_DONEINT_EN (1 << 1) // Done Register +#define HOST_SORINT_EN (1) // Schedule Overrun + +#define OTG_HOST_XINT_STAT (OTG_HOST_BASE+0x18) // 32bit host X buf int status reg +#define OTG_HOST_YINT_STAT (OTG_HOST_BASE+0x1C) // 32bit host Y buf int status reg + +#define OTG_HOST_XYINT_STEN (OTG_HOST_BASE+0x20) // 32bit host XY buf int enable reg +#define OTG_HOST_XFILL_STAT (OTG_HOST_BASE+0x28) // 32bit host X filled status reg +#define OTG_HOST_YFILL_STAT (OTG_HOST_BASE+0x2C) // 32bit host Y filled status reg + +#define OTG_HOST_ETD_EN (OTG_HOST_BASE+0x40) // 32bit host ETD enables reg +#define OTG_HOST_DIR_ROUTE (OTG_HOST_BASE+0x48) // 32bit host direct routing reg +#define OTG_HOST_IINT (OTG_HOST_BASE+0x4C) // 32bit host immediate interrupt reg + +#define OTG_HOST_EP_DSTAT (OTG_HOST_BASE+0x50) // 32bit host endpoints done status +#define OTG_HOST_ETD_DONE (OTG_HOST_BASE+0x54) // 32bit host ETD done reg + +#define OTG_HOST_FRM_NUM (OTG_HOST_BASE+0x60) // 32bit host frame number reg +#define OTG_HOST_LSP_THRESH (OTG_HOST_BASE+0x64) // 32bit host low speed threshold reg + +#define OTG_HOST_ROOTHUB_DESCA (OTG_HOST_BASE+0x68) // 32bit host root hub descriptor A +#define OTG_HOST_ROOTHUB_DESCB (OTG_HOST_BASE+0x6C) // 32bit host root hub descriptor B + +/* + * C.f. 23.11.29 + */ +#define OTG_HOST_ROOTHUB_STATUS (OTG_HOST_BASE+0x70) // 32bit host root hub status reg + +#define ROOTHUB_STATUS_CLRMTWUE (1 << 31) // Clear Remote Wakeup Enable +#define ROOTHUB_STATUS_OVRCURCHG (1 << 17) // Over Current Indicator Change +#define ROOTHUB_STATUS_LOCPWRSC (1 << 16) // Local Power Status Change +#define ROOTHUB_STATUS_DEVCONWUE (1 << 15) // Device Connect Wakeup Enable +#define ROOTHUB_STATUS_OVRCURI (1 << 1) // Over Current Indicator +#define ROOTHUB_STATUS_LOCPWRS (1) // Local Power Status + + +/* + * C.f. 23.11.30 + */ +#define OTG_HOST_PORT_STATUS_1 (OTG_HOST_BASE+0x74) // 32bit host port 1 status bits +#define OTG_HOST_PORT_STATUS_2 (OTG_HOST_BASE+0x78) // 32bit host port 2 status bits +#define OTG_HOST_PORT_STATUS_3 (OTG_HOST_BASE+0x7c) // 32bit host port 3 status bits + +#define PORT_STATUS_PRTRSTSC (1 << 20) // Port Reset Status Change +#define PORT_STATUS_OVRCURIC (1 << 19) // Port Over Current Indicator Change +#define PORT_STATUS_PRTSTATSC (1 << 18) // Port Suspend Status Change +#define PORT_STATUS_PRTENBLSC (1 << 17) // Port Enable Status Change +#define PORT_STATUS_CONNECTSC (1 << 16) // Connect Status Change + +#define PORT_STATUS_LSDEVCON (1 << 9) // Low Speed Device Attached +#define PORT_STATUS_PRTPWRST (1 << 8) // Port Power Status + +#define PORT_STATUS_PRTRSTST (1 << 4) // Port Reset Status +#define PORT_STATUS_PRTOVRCURI (1 << 3) // Port Over Current Indicator +#define PORT_STATUS_PRTSUSPST (1 << 2) // Port Suspend Status +#define PORT_STATUS_PRTENABST (1 << 1) // Port Enable Status +#define PORT_STATUS_CURCONST (1) // Current Connect Status + + +/* flags are the same for the interrupt, control and bulk transfer descriptors */ +// R1: sec 23.11.10.2,3 pg 23-44,47 +#define ETD_GET_COMPCODE(flags) ((flags >> 12) & 0xf) +// R1: sec 23.11.10 Table 23-23 pg 23-40 +#define ETD_CC_NOERROR 0x0 +#define ETD_CC_CRCERR 0x1 +#define ETD_CC_BITSTUFFERR 0x2 +#define ETD_CC_DATATOGERR 0x3 +#define ETD_CC_STALL 0x4 +#define ETD_CC_DEVNOTRESPONDING 0x5 +#define ETD_CC_PIDFAILURE 0x6 +// Reserved 0x7 +#define ETD_CC_DATAOVERRUN 0x8 +#define ETD_CC_DATAUNDERRUN 0x9 +#define ETD_CC_ACK 0xA +#define ETD_CC_NAK 0xB +#define ETD_CC_BUFFEROVERRUN 0xC +#define ETD_CC_BUFFERUNDERRUN 0xD +#define ETD_CC_SCHEDOVERRUN 0xE +#define ETD_CC_NOTACCESSED 0xF + +// R1: sec 23.11.10.2,3 pg 23-44,47 (contd) +#define ETD_GET_ERRORCNT(flags) (((flags) >> 8) & 0xf) +#define ETD_GET_DATATOGL(flags) (((flags) >> 6) & 0x3) +#define ETD_SET_DATATOGL(v) (((v) & 0x3) << 6) +#define ETD_GET_DELAYINT(flags) (((flags) >> 3) & 0x7) +#define ETD_SET_DELAYINT(v) (((v) & 0x7) << 3) +#define ETD_GET_BUFROUND(flags) (((flags) >> 2) & 0x1) +#define ETD_SET_BUFROUND(v) (((v) & 0x1) << 2) +#define ETD_GET_DIRPID(flags) ((flags) & 0x3) +#define ETD_SET_DIRPID(v) ((v) & 0x3) +#define ETD_FLIP_DIRPID(flags) ((flags) ^ 0x3) /* xor with 0x3 flips 2<->1 (IN<->OUT) */ +#define ETD_DIRPID_SETUP 0x0 // OUT +#define ETD_DIRPID_OUT 0x1 +#define ETD_DIRPID_IN 0x2 +#define ETD_DIRPID_RESERVED 0x3 + +// For non-isoc td.w3.val - +#define ETD_GET_BUFSIZE(w3) (((w3) >> 21) & 0xfff) +#define ETD_SET_BUFSIZE(v) (((v) & 0xfff) << 21) +#define ETD_GET_TOTBYECNT(w3) ((w3) & 0xfffff) +#define ETD_SET_TOTBYECNT(v) ((v) & 0xfffff) + +/* isoc flags shares the COMPCODE and DELAYINT bitfields with the other TDs, + but the remaining bits are different. */ +#define ETD_GET_AUTOISO(flags) (((flags) >> 11) & 0x1) +#define ETD_SET_AUTOISO(v) (((v) & 0x1) << 11) +#define ETD_GET_FRAMECNT(flags) (((flags) >> 8) & 0x1) +#define ETD_SET_FRAMECNT(v) (((v) & 0x1) << 8) +// For isoc td.w3.val - +/* pkt1 and pkt0 use the COMPCODE bitfield, and add a PKTLEN field. */ +#define ETD_GET_PKTLEN(pkt) ((pkt) & 0x3ff) +#define ETD_SET_PKTLEN(v) ((v) & 0x3ff) + + +// ETD endpoint descriptor (etd->epd) bitfield access +// R1: sec 23.11.10.1 pg 23-41 (word0) +#define ETD_GET_SNDNAK(epd) (((epd) >> 30) & 0x1) +#define ETD_SET_SNDNAK(v) (((v) & 0x1) << 30) +#define ETD_GET_TOGCRY(epd) (((epd) >> 28) & 0x1) +#define ETD_SET_TOGCRY(v) (((v) & 0x1) << 28) +#define ETD_GET_HALTED(epd) (((epd) >> 27) & 0x1) +#define ETD_SET_HALTED(v) (((v) & 0x1) << 27) +#define ETD_GET_MAXPKTSIZ(epd) (((epd) >> 16) & 0x3ff) +#define ETD_SET_MAXPKTSIZ(v) (((v) & 0x3ff) << 16) +#define ETD_GET_FORMAT(epd) (((epd) >> 14) & 0x3) +#define ETD_SET_FORMAT(v) (((v) & 0x3) << 14) +#define ETD_FORMAT_CONTROL 0x0 +#define ETD_FORMAT_ISOC 0x1 +#define ETD_FORMAT_BULK 0x2 +#define ETD_FORMAT_INTERRUPT 0x3 +#define ETD_GET_SPEED(epd) (((epd) >> 13) & 0x1) +#define ETD_SET_SPEED(v) (((v) & 0x1) << 13) +#define ETD_SPEED_FULL 0x0 +#define ETD_SPEED_LOW 0x1 +#define ETD_GET_DIRECT(epd) (((epd) >> 11) & 0x3) +#define ETD_SET_DIRECT(v) (((v) & 0x3) << 11) +/* xor with 3 flips 2<->1 */ +#define ETD_FLIP_DIRECT(epd) ((epd) ^ (0x3 << 11)) +#define ETD_DIRECT_TD00 0x0 +#define ETD_DIRECT_OUT 0x1 +#define ETD_DIRECT_IN 0x2 +#define ETD_DIRECT_TD11 0x3 +#define ETD_GET_ENDPNT(epd) (((epd) >> 7) & 0xf) +#define ETD_SET_ENDPNT(v) (((v) & 0xf) << 7) +#define ETD_GET_ADDRESS(epd) ((epd) & 0x7f) +#define ETD_SET_ADDRESS(v) ((v) & 0x7f) + +// etd_urb_state[] values +#define ETD_URB_COMPLETED 0 +#define ETD_URB_SETUP_STATUS 1 +#define ETD_URB_SETUP_DATA 2 +#define ETD_URB_SETUP_START 3 +#define ETD_URB_BULK_START 4 +#define ETD_URB_BULKWZLP 5 +#define ETD_URB_BULKWZLP_START 6 +#define ETD_URB_INTERRUPT_START 7 +#define ETD_URB_ISOC_START 8 + + +/* + * C.f. 23.12.8 Function Registers + */ + +#define OTG_FUNC_CMD_STAT (OTG_FUNC_BASE+0x00) // 32bit func command status reg + +#define COMMAND_SOFTRESET (1 << 7) +#define COMMAND_BADISOAP (1 << 3) +#define COMMAND_SUPDET (1 << 2) +#define COMMAND_RSMINPROG (1 << 1) +#define COMMAND_RESETDET (1) + + +#define OTG_FUNC_DEV_ADDR (OTG_FUNC_BASE+0x04) // 32bit func device address reg + +#define OTG_FUNC_SINT_STAT (OTG_FUNC_BASE+0x08) // 32bit func system int status reg + +#define SYSTEM_DONEREGINTDS (1 << 5) +#define SYSTEM_SOFDETINT (1 << 4) +#define SYSTEM_DONEREGINT (1 << 3) +#define SYSTEM_SUSPDETINT (1 << 2) +#define SYSTEM_RSMFININT (1 << 1) +#define SYSTEM_RESETINT (1) + +#define OTG_FUNC_SINT_STEN (OTG_FUNC_BASE+0x0C) // 32bit func system int enable reg + +#define SYSTEM_DONEREGINTDS_EN (1 << 5) +#define SYSTEM_SOFDETINT_EN (1 << 4) +#define SYSTEM_DONEREGINT_EN (1 << 3) +#define SYSTEM_SUSPDETINT_EN (1 << 2) +#define SYSTEM_RSMFININT_EN (1 << 1) +#define SYSTEM_RESETINT_EN (1) + + +#define OTG_FUNC_XINT_STAT (OTG_FUNC_BASE+0x10) // 32bit func X buf int status reg +#define OTG_FUNC_YINT_STAT (OTG_FUNC_BASE+0x14) // 32bit func Y buf int status reg +#define OTG_FUNC_XYINT_STEN (OTG_FUNC_BASE+0x18) // 32bit func XY buf int enable reg +#define OTG_FUNC_XFILL_STAT (OTG_FUNC_BASE+0x1C) // 32bit func X filled status reg + +#define OTG_FUNC_YFILL_STAT (OTG_FUNC_BASE+0x20) // 32bit func Y filled status reg +#define OTG_FUNC_EP_EN (OTG_FUNC_BASE+0x24) // 32bit func endpoints enable reg +#define OTG_FUNC_EP_RDY (OTG_FUNC_BASE+0x28) // 32bit func endpoints ready reg +#define OTG_FUNC_IINT (OTG_FUNC_BASE+0x2C) // 32bit func immediate interrupt reg + +#define OTG_FUNC_EP_DSTAT (OTG_FUNC_BASE+0x30) // 32bit func endpoints done status +#define OTG_FUNC_EP_DEN (OTG_FUNC_BASE+0x34) // 32bit func endpoints done enable +#define OTG_FUNC_EP_TOGGLE (OTG_FUNC_BASE+0x38) // 32bit func endpoints toggle bits +#define OTG_FUNC_FRM_NUM (OTG_FUNC_BASE+0x3C) // 32bit func frame number reg + + +#define EP0_STALL (1 << 31) +#define EP0_SETUP (1 << 30) +#define EP0_OVERRUN (1 << 29) +#define EP0_AUTOISO (1 << 27) + + +/* generic endpoint register manipulations + */ +#define EP_OUT 0x1 +#define EP_IN 0x2 +#define EP_BOTH 0x3 + +#define ETD_MASK(n) (1 << n) +#define ep_num_both(n) (EP_BOTH << n) +#define ep_num_dir(n, dir) ((dir ? EP_IN : EP_OUT) << (n*2)) +#define ep_num_out(n) ep_num_dir(n, USB_DIR_OUT) +#define ep_num_in(n) ep_num_dir(n, USB_DIR_IN) + + +/* ep descriptor access + */ +static __inline__ u32 etd_word(int n, int word) +{ + u32 offset = n * 16; + offset += word * 4; + return OTG_ETD_BASE + offset; +} + +/* ep descriptor access + */ +static __inline__ u32 ep_word(int n, int dir, int word) +{ + u32 offset = n * 2; + offset += dir ? 1 : 0; + offset *= 16; + offset += word * 4; + return OTG_EP_BASE + offset; +} + +/* endpoint data buffer access + * + * This is a simplistic allocator, will do until we want to support ISO or host mode. + * + * This works because we are assuming a maximum of 16 allocate endpoints, and no + * overlapped endpoints (both in and out are allocated). + */ + +static volatile __inline__ u16 data_x_buf(int n, int dir) +{ + return 0x40 * (n * 4 + 2 * (dir ? 1 : 0)); +} +static volatile __inline__ u16 data_y_buf(int n, int dir) +{ + return 0x40 * (n * 4 + 2 * (dir ? 1 : 0) + 1); +} + +static volatile __inline__ u8 * data_x_address(int n, int dir) +{ + return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_x_buf(n, dir)); +} +static volatile __inline__ u8 * data_y_address(int n, int dir) +{ + return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_y_buf(n, dir)); +} + +/* + * C.f. 23.13.3 DMA Registers + */ +#define OTG_DMA_REV_NUM (OTG_DMA_BASE+0x000) // 32bit dma revision number reg +#define OTG_DMA_DINT_STAT (OTG_DMA_BASE+0x004) // 32bit dma int status reg +#define OTG_DMA_DINT_STEN (OTG_DMA_BASE+0x008) // 32bit dma int enable reg +#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg +#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg +#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg +#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg +#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req +#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req +#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req +#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req +#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg +#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg +#define OTG_DMA_MISC_CTRL (OTG_DMA_BASE+0x040) // 32bit dma EP misc control reg +#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg +#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg + + +#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg +#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg +#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg +#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req +#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req +#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg + + + +#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg +#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg +#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg +#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req +#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req +#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg + + +#define dma_num_dir(n, dir) (n * 2 + (dir ? 1 : 0)) +#define dma_num_out(n) dma_num_dir(n, USB_DIR_OUT) +#define dma_num_in(n) dma_num_dir(n, USB_DIR_IN) + +#define OTG_DMA_ETD_MSA(x) (OTG_DMA_BASE+0x100+x*4) +#define OTG_DMA_EPN_MSA(x) (OTG_DMA_BASE+0x180+x*4) +#define OTG_DMA_ETDN_BPTR(x) (OTG_DMA_BASE+0x280+x*4) +#define OTG_DMA_EPN_BPTR(x) (OTG_DMA_BASE+0x284+x*4) + + +/* + * C.f. 23.14.10 I2C OTG Transceiver Controller Registers + * These are the ISP1301 shadow registers. + * + * See isp1301-hardware.h for register bit defines. + */ +#if 1 +#define MX2_VENDOR_ID_LOW OTG_I2C_BASE+0x00 +#define MX2_VENDOR_ID_HIGH OTG_I2C_BASE+0x01 +#define MX2_PRODUCT_ID_LOW OTG_I2C_BASE+0x02 +#define MX2_PRODUCT_ID_HIGH OTG_I2C_BASE+0x03 + +#define MX2_MODE_CONTROL_1_SET OTG_I2C_BASE+0x04 +#define MX2_MODE_CONTROL_1_CLR OTG_I2C_BASE+0x05 +#define MX2_OTG_CONTROL_SET OTG_I2C_BASE+0x06 +#define MX2_OTG_CONTROL_CLR OTG_I2C_BASE+0x07 + +#define MX2_INTERRUPT_SOURCE OTG_I2C_BASE+0x08 + +#define MX2_INT_LAT_REG_SET OTG_I2C_BASE+0x0a +#define MX2_INT_LAT_REG_CLR OTG_I2C_BASE+0x0b + +#define MX2_INT_FALSE_REG_SET OTG_I2C_BASE+0x0c +#define MX2_INT_FALSE_REG_CLR OTG_I2C_BASE+0x0d +#define MX2_INT_TRUE_REG_SET OTG_I2C_BASE+0x0e +#define MX2_INT_TRUE_REG_CLR OTG_I2C_BASE+0x0f + +#define MX2_OTG_STATUS_REG OTG_I2C_BASE+0x10 + +#define MX2_MODE_CONTROL_2_SET OTG_I2C_BASE+0x12 +#define MX2_MODE_CONTROL_2_CLR OTG_I2C_BASE+0x13 + +#define MX2_BCD_DEV_LOW OTG_I2C_BASE+0x14 +#define MX2_BCD_DEV_HIGH OTG_I2C_BASE+0x15 +#endif + +/* + * C.f. 23.14.10 I2C OTG Transceiver Controller Registers + * These are the I2C controller and access registers. + * + * N.B. I2C_ERROR is not documented. + */ + + +#define I2C_BUSY (1 << 7) +#define I2C_ERROR (1 << 2) +#define I2C_HWSMODE (1 << 1) +#define I2C_I2COE (1 << 0) + +#define I2C_SCLK_TO_SCL_DIVISION (OTG_I2C_BASE+0x1E) + +#define I2C_INTERRUPT_AND_CONTROL (OTG_I2C_BASE+0x1F) + +#define I2C_STATUS_MASK (0x7) + +#define I2C_NOACK_EN (1 << 6) +#define I2C_RWREADY_EN (1 << 5) +#define I2C_OTGXCVRINT_EN (1 << 4) + +#define I2C_NOACK (1 << 2) +#define I2C_RWREADY (1 << 1) +#define I2C_OTGXCVRINT (1 << 0) + +#define MX2_OTG_XCVR_DEVAD OTG_I2C_BASE+0x18 +#define MX2_SEQ_OP_REG OTG_I2C_BASE+0x19 +#define MX2_SEQ_RD_STARTAD OTG_I2C_BASE+0x1a +#define MX2_I2C_OP_CTRL_REG OTG_I2C_BASE+0x1b +#define MX2_SCLK_TO_SCL_HPER OTG_I2C_BASE+0x1e +#define MX2_I2C_INTERRUPT_AND_CTRL OTG_I2C_BASE+0x1f + + +/* ********************************************************************************************** */ + +/*! mx2_rb + */ +static u8 __inline__ mx2_rb(u32 port) +{ + return *(volatile u8 *) (IO_ADDRESS(port)); +} + +/*! mx2_rl + */ +static u32 __inline__ mx2_rl(u32 port) +{ + return *(volatile u32 *) (IO_ADDRESS(port)); +} + +/*! mx2_wb + */ +static void __inline__ mx2_wb(u32 port, u8 val) +{ + *(volatile u8 *)(IO_ADDRESS(port)) = val; +} + +/*! mx2_orb + */ +static void __inline__ mx2_orb(u32 port, u8 val) +{ + u8 set = mx2_rb(port) | val; + *(volatile u8 *)(IO_ADDRESS(port)) = set; +} + +/*! mx2_andb + */ +static void __inline__ mx2_andb(u32 port, u8 val) +{ + u8 set = mx2_rb(port) & val; + *(volatile u8 *)(IO_ADDRESS(port)) = set; +} + +/*! mx2_wl + */ +static void __inline__ mx2_wl(u32 port, u32 val) +{ + u32 set; + *(volatile u32 *)(IO_ADDRESS(port)) = val; + set = mx2_rl(port); +} + +/*! mx2_orl + */ +static void __inline__ mx2_orl(u32 port, u32 val) +{ + u32 set = mx2_rl(port); + *(volatile u32 *)(IO_ADDRESS(port)) = (set | val); +} + +/*! mx2_andl + */ +static void __inline__ mx2_andl(u32 port, u32 val) +{ + u32 set = mx2_rl(port); + *(volatile u32 *)(IO_ADDRESS(port)) = (set & val); +} + +static u32 inline mx2_host_port_stat(int n) +{ + switch (n) { + default: + case 1: + return OTG_HOST_PORT_STATUS_1; + case 2: + return OTG_HOST_PORT_STATUS_2; + case 3: + return OTG_HOST_PORT_STATUS_3; + } +} +