diff options
author | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
---|---|---|
committer | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
commit | 709c4d66e0b107ca606941b988bad717c0b45d9b (patch) | |
tree | 37ee08b1eb308f3b2b6426d5793545c38396b838 /recipes/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff | |
parent | fa6cd5a3b993f16c27de4ff82b42684516d433ba (diff) |
rename packages/ to recipes/ per earlier agreement
See links below for more details:
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816
Signed-off-by: Denys Dmytriyenko <denis@denix.org>
Acked-by: Mike Westerhof <mwester@dls.net>
Acked-by: Philip Balister <philip@balister.org>
Acked-by: Khem Raj <raj.khem@gmail.com>
Acked-by: Marcin Juszkiewicz <hrw@openembedded.org>
Acked-by: Koen Kooi <koen@openembedded.org>
Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'recipes/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff')
-rw-r--r-- | recipes/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff | 15816 |
1 files changed, 15816 insertions, 0 deletions
diff --git a/recipes/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff b/recipes/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff new file mode 100644 index 0000000000..7766f71846 --- /dev/null +++ b/recipes/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff @@ -0,0 +1,15816 @@ +/home/sl/USB/PB1500/work/usbd-gpl/kits/amd-pb1x00-kit/amd-pb1x00-linux-2.4.21 +diff -Nru a/arch/mips/config-shared.in b/arch/mips/config-shared.in +--- a/arch/mips/config-shared.in Fri Feb 27 14:22:51 2004 ++++ b/arch/mips/config-shared.in Fri Feb 27 14:22:51 2004 +@@ -985,6 +985,7 @@ + endmenu + + source drivers/usb/Config.in ++source drivers/usbd/Config.in + + source net/bluetooth/Config.in + +--- a/drivers/Makefile 2005-08-15 18:51:50.332030952 +0200 ++++ b/drivers/Makefile 2005-08-15 18:53:08.938081016 +0200 +@@ -41,6 +41,7 @@ + subdir-$(CONFIG_ISDN_BOOL) += isdn + subdir-$(CONFIG_ATM) += atm + subdir-$(CONFIG_FC4) += fc4 ++subdir-$(CONFIG_USBD) += usbd + + # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set -- ch + subdir-$(CONFIG_HAMRADIO) += net/hamradio +diff -Nru a/drivers/usbd/Config.in b/drivers/usbd/Config.in +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/Config.in Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,58 @@ ++# ++# USB device configuration from the device viewpoint (e.g. Linux running inside a USB device, not as host) ++# ++# Copyright (c) 2002-2003 Belcarra ++# ++# ++ ++mainmenu_option next_comment ++ ++comment 'USB clients (devices, not hosts)' ++ ++tristate 'Support for USB Clients (USB Device, not USB Host)' CONFIG_USBD ++ ++if [ "$CONFIG_USBD" = "y" -o "$CONFIG_USBD" = "m" ]; then ++ comment '' ++ bool ' Enable High Speed Descriptors' CONFIG_USBD_HIGH_SPEED ++ ++ bool ' Do Not Use Serial Number in Device Descriptor (Default is no' CONFIG_USBD_NO_SERIAL_NUMBER ++ if [ "$CONFIG_USBD_NO_SERIAL_NUMBER" != "y" ]; then ++ string ' Default Serial number (string)' CONFIG_USBD_SERIAL_NUMBER_STR "" ++ fi ++ ++ int ' Max Power (mA) (Default is zero, whick is self powered)' CONFIG_USBD_MAXPOWER "0" ++ ++ comment '' ++ ++ bool ' USBD Proc FS' CONFIG_USBD_PROCFS ++ ++ tristate ' USBD Proc FS Module' CONFIG_USBD_PROCFSM $CONFIG_USBD ++ ++ comment 'Function Drivers' ++ ++ source drivers/usbd/network_fd/Config.in ++ source drivers/usbd/acm_fd/Config.in ++ ++ source drivers/usbd/mouse_fd/Config.in ++ ++ comment 'Bus Interface' ++ ++ source drivers/usbd/au1x00_bi/Config.in ++ ++ bool ' USB Device Register Tracing' CONFIG_USBD_BI_REGISTER_TRACE ++ if [ "$CONFIG_USBD" = "y" ]; then ++ bool ' USB Device Manual Enable' CONFIG_USBD_BI_DELAY_ENABLE ++ fi ++ ++ ++ #source drivers/usbd/l7205_bi/Config.in ++ #source drivers/usbd/sl11_bi/Config.in ++ ++ #source drivers/usbd/cdc_fd/Config.in ++ #source drivers/usbd/serialnumber/Config.in ++ #source drivers/usbd/audio_fd/Config.in ++ #source drivers/usbd/eg_fd/Config.in ++ #source drivers/usbd/keyboard_fd/Config.in ++fi ++ ++endmenu +diff -Nru a/drivers/usbd/Makefile b/drivers/usbd/Makefile +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/Makefile Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,157 @@ ++# ++# Makefile for the kernel USBD (device not host) drivers. ++# ++# Copyright (c) 2002 Belcarra ++# Copyright (C) 2001 Lineo, Inc. ++# Copyright (C) 2001 Hewlett-Packard Co. ++ ++# Subdirs. ++# This is a bit complex, because some subdirs are for ++# proprietary code, and are simply not present in a ++# general distribution. ++ ++TOPDIR ?= ../../.. ++ ++# 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- := ++ ++# Function Drivers ++subdir-$(CONFIG_USBD_ACM) += acm_fd ++subdir-$(CONFIG_USBD_MOUSE) += mouse_fd ++subdir-$(CONFIG_USBD_NETWORK) += network_fd ++ ++#subdir-$(CONFIG_USBD_AUDIO) += audio_fd ++#subdir-$(CONFIG_USBD_EG) += eg_fd ++#subdir-$(CONFIG_USBD_KEYBOARD) += keyboard_fd ++#subdir-$(CONFIG_USBD_CDC) += cdc_fd ++ ++# Bus Interface Drivers ++subdir-$(CONFIG_USBD_AU1X00_BUS) += au1x00_bi ++ ++#subdir-$(CONFIG_USBD_OMAP_BUS) += omap_bi ++#subdir-$(CONFIG_USBD_TC86C001_BUS) += tc86c001_bi ++ ++#subdir-$(CONFIG_USBD_SL11_BUS) += sl11_bi ++ ++# The target object and module list name. ++ ++O_TARGET := usbdev.o ++ ++# Objects that export symbols. ++ ++export-objs := usbd.o usbd-bops.o usbd-fops.o usbd-bi.o ep0.o ++ ++# Multipart objects. ++ ++list-multi := usbdcore.o ++usbdcore-objs := usbd.o ep0.o usbd-fops.o usbd-bops.o ++usbdprocfs-objs := usbd-procfs.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_USBD) += usbdcore.o ++obj-$(CONFIG_USBD_PROCFSM) += usbdprocfs.o ++ ++# Object files in subdirectories ++ ++# ++obj-$(CONFIG_USBD_MONITOR) += monitor/monitor.o ++ ++obj-$(CONFIG_USBD_ACM) += acm_fd/acm_fd.o ++obj-$(CONFIG_USBD_MOUSE) += mouse_fd/mouse_fd.o ++obj-$(CONFIG_USBD_NETWORK) += network_fd/network_fd.o ++ ++#obj-$(CONFIG_USBD_AUDIO) += audio_fd/audio_fd.o ++#obj-$(CONFIG_USBD_EG) += eg_fd/eg_fd.o ++#obj-$(CONFIG_USBD_KEYBOARD) += keyboard_fd/keyboard_fd.o ++ ++# Bus Interface Drivers ++obj-$(CONFIG_USBD_AU1X00_BUS) += au1x00_bi/au1x00_bi.o ++ ++#obj-$(CONFIG_USBD_OMAP_BUS) += omap_bi/omap_bi.o ++#obj-$(CONFIG_USBD_TC86C001_BUS) += tc86c001_bi/tc86c001_bi.o ++ ++#obj-$(CONFIG_USBD_SL11_BUS) += sl11_bi/sl11_bi.o ++ ++ ++# Yech. This isn't the best way to do this, but there isn't a config flag ++# common to all the possible bus interfaces ++ ++ ++# 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 ++ ++# Link rules for multi-part drivers. ++ ++usbdprocfs.o: $(usbdprocfs-objs) ++ $(LD) -r -o $@ $(usbdprocfs-objs) ++ ++usbdcore.o: $(usbdcore-objs) ++ $(LD) -r -o $@ $(usbdcore-objs) ++ ++# dependencies: ++ ++usbd.o: usbd-build.h ++ ++usbd-build.h: ++ echo "#define USBD_BUILD \"000\"" > $@ ++ ++# local ++ ++%.h:%.p ++ release inc build < $< > $@ ++ cp $@ $< ++ ++release.h: release.p ++ ++inc-build: ++ release inc build < release.p > release.h ++ cp release.h release.p ++ ++ ++menuconfig: ++ cd $(TOPDIR); make menuconfig ++ ++xconfig: ++ cd $(TOPDIR); make xconfig ++ ++ ++ +diff -Nru a/drivers/usbd/acm_fd/Config.in b/drivers/usbd/acm_fd/Config.in +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/acm_fd/Config.in Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,27 @@ ++# ++# CDC ACM Function Driver ++# ++# Copyright (C) 2003,2004 Belcarra ++# ++ ++mainmenu_option next_comment ++comment "CDC ACM Function" ++ ++dep_tristate ' CDC ACM Function' CONFIG_USBD_ACM $CONFIG_USBD ++if [ "$CONFIG_USBD_ACM" = "y" -o "$CONFIG_USBD_ACM" = "m" ]; then ++ hex 'VendorID (hex value)' CONFIG_USBD_ACM_VENDORID "12b9" ++ hex 'ProductID (hex value)' CONFIG_USBD_ACM_PRODUCTID "f002" ++ hex 'bcdDevice (binary-coded decimal)' CONFIG_USBD_ACM_BCDDEVICE "0100" ++ ++ string 'iManufacturer (string)' CONFIG_USBD_ACM_MANUFACTURER "Belcarra" ++ string 'iProduct (string)' CONFIG_USBD_ACM_PRODUCT_NAME "Belcarra ACM Device" ++ ++ string 'iConfiguration (string)' CONFIG_USBD_ACM_DESC "Acm Cfg" ++ string 'Comm Interface iInterface (string)' CONFIG_USBD_ACM_COMM_INTF "Comm Intf" ++ string 'Data Interface iInterface (string)' CONFIG_USBD_ACM_DATA_INTF "Data Intf" ++ ++ bool ' ACM Tracing' CONFIG_USBD_ACM_TRACE ++ comment '' ++fi ++ ++endmenu +diff -Nru a/drivers/usbd/acm_fd/Makefile b/drivers/usbd/acm_fd/Makefile +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/acm_fd/Makefile Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,65 @@ ++# ++# Function driver for a CDC ACM USB Device ++# ++# Copyright (c) 2003 Belcarra ++ ++# Multipart objects. ++ ++O_TARGET := acm_fd.o ++list-multi := acm_fd.o ++ ++acm_fd-objs := acm.o trace.o ++ ++# Objects that export symbols. ++export-objs := acm.o ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_USBD_ACM) += acm_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. ++ ++USBD=$(TOPDIR)/drivers/usbd ++ACMD=$(USBD)/acm_fd ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -I$(ACMD) -I$(USBD) -Wno-unused -Wno-format ++EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(USBD) -Wno-unused -Wno-format ++ ++# Link rules for multi-part drivers. ++ ++acm_fd.o: $(acm_fd-objs) ++ $(LD) -r -o $@ $(acm_fd-objs) ++ ++# dependencies: ++ ++acm.o: $(USBD)/usbd.h $(USBD)/usbd-bus.h $(USBD)/usbd-func.h ++ +diff -Nru a/drivers/usbd/acm_fd/acm.c b/drivers/usbd/acm_fd/acm.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/acm_fd/acm.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,1662 @@ ++/* ++ * usbd/acm_fd/acm.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ * 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 and interrupt endpoint. ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++ ++MODULE_DESCRIPTION ("Belcarra CDC-ACM Function"); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++MODULE_LICENSE("GPL"); ++#endif ++ ++#define ACM_TRACE_NAME "acm_trace" ++ ++#undef USE_TICKER ++#undef MCEL ++//#define MCEL 1 ++//#define CONFIG_USBD_ACM_DATALOG 1 ++#undef CONFIG_USBD_ACM_DATALOG ++#undef PST_FD_AVAILABLE ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/tty.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++#include "trace.h" ++// 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) ++int acm_interrupts; ++ ++USBD_MODULE_INFO ("acm_fd 2.1-beta"); ++ ++#define MAX_QUEUED_BYTES 256 ++#define MAX_QUEUED_URBS 10 // Max for write ++ ++#define MAX_RECV_URBS 2 // Max for receiving data ++#define RECV_RING_SIZE (MAX_RECV_URBS+1) ++ ++// Endpoint indexes in acm_endpoint_requests[] and the endpoint map. ++#define BULK_OUT 0x00 ++#define BULK_IN 0x01 ++#define INT_IN 0x02 ++#if !defined(CONFIG_USBD_ACM_DATALOG) ++#define ENDPOINTS 0x03 ++#else ++#define DATALOG_BULK_IN 0x03 ++#define ENDPOINTS 0x04 ++#endif ++ ++#define COMM_INTF 0x00 ++#define DATA_INTF 0x01 ++#if defined(CONFIG_USBD_ACM_DATALOG) ++#define DATALOG_INTF 0x02 ++#define TESTCMD_INTF 0x03 ++#endif ++ ++#if defined(CONFIG_USBD_ACM_DATALOG) && defined(PST_FD_AVAILABLE) ++extern int pst_dev_create(struct usb_device_instance *device); ++extern void pst_dev_destroy(void); ++extern void ep0_process_vendor_request( struct urb *urb ); ++extern int pst_urb_sent (struct urb *urb, int rc); ++#endif ++ ++/* 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; ++ ++MODULE_PARM (vendor_id, "i"); ++MODULE_PARM (product_id, "i"); ++MODULE_PARM (max_queued_urbs, "i"); ++MODULE_PARM (max_queued_bytes, "i"); ++ ++MODULE_PARM_DESC (vendor_id, "Device Vendor ID"); ++MODULE_PARM_DESC (product_id, "Device Product ID"); ++MODULE_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs"); ++MODULE_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes"); ++ ++/* ++ * CDC ACM Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++/* Endpoints */ ++static __u8 acm_alt_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK, 0, 0x00, 0x00, }; ++static __u8 acm_alt_2[] = { 0x07, USB_DT_ENDPOINT, IN, BULK, 0, 0x00, 0x00, }; ++static struct usb_endpoint_descriptor *acm_alt_endpoints[] = { ++ (struct usb_endpoint_descriptor *) acm_alt_1, ++ (struct usb_endpoint_descriptor *) acm_alt_2, }; ++u8 acm_alt_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++static __u8 acm_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN, INTERRUPT, 0, 0x00, 0x0a, }; ++static struct usb_endpoint_descriptor *acm_comm_endpoints[] = { (struct usb_endpoint_descriptor *) acm_comm_1 }; ++u8 acm_comm_indexes[] = { INT_IN, }; ++ ++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 - 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 usb_generic_class_descriptor *cdc_comm_class_descriptors[] = ++ { (struct usb_generic_class_descriptor *) cdc_class_1, ++ (struct usb_generic_class_descriptor *) cdc_class_2, ++ (struct usb_generic_class_descriptor *) cdc_class_3, ++ (struct usb_generic_class_descriptor *) cdc_class_4, }; ++ ++ ++#if defined(CONFIG_USBD_ACM_DATALOG) ++ ++#if 0 ++/* This is used to get a specific INTERFACE number, by padding the list of interfaces. */ ++static struct usb_alternate_description dummy_alternate_descriptions[] = { ++ { iInterface:"Dummy", }, ++}; ++#endif ++ ++/* There is no way to request a specific ENDPOINT address, since that depends on ++ the bus interface hardware. */ ++//#define DATA_LOG_IN_ENDPOINT 11 ++ ++static __u8 datalog_endpoint[] = { ++ 0x07, // bLength ++ USB_DT_ENDPOINT, // bDescriptorType // 0x5 ++ /*DATA_LOG_IN_ENDPOINT |*/ IN, // bEndpointAddress ++ BULK, // bmAttributes ++ 0, 0x00, // wMaxPacketSize ++ 0x00, // bInterval ++}; ++static struct usb_endpoint_descriptor *datalog_endpoints[] = { datalog_endpoint }; ++u8 datalog_indexes[] = { DATALOG_BULK_IN, }; ++ ++ ++// No endpoints needed for TestCmd interface. ++#endif ++ ++/* Alternate Descriptors */ ++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04) ++static __u8 cdc_comm_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, }; ++ ++static __u8 cdc_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, }; ++ ++ ++#if defined(CONFIG_USBD_ACM_DATALOG) ++ ++static __u8 datalog_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, DATALOG_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ // bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol, iInterface ++ 0xFF, 0x02, 0xFF, 0x00, }; ++ ++static __u8 testcmd_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, TESTCMD_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ // bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol, iInterface ++ 0xFF, 0x03, 0xFF, 0x00, }; ++ ++#endif ++ ++/* Alternate Descriptions */ ++static struct usb_alternate_description cdc_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_ACM_COMM_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&cdc_comm_alternate_descriptor, ++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *), ++ class_list: cdc_comm_class_descriptors, ++ endpoint_list: acm_comm_endpoints, ++ endpoints:sizeof (acm_comm_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_indexes: acm_comm_indexes, ++ }, }; ++ ++static struct usb_alternate_description cdc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_ACM_DATA_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&cdc_data_alternate_descriptor, ++ endpoint_list: acm_alt_endpoints, ++ endpoints:sizeof (acm_alt_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_indexes: acm_alt_indexes, ++ }, }; ++ ++#if defined(CONFIG_USBD_ACM_DATALOG) ++ ++static struct usb_alternate_description datalog_alternate_descriptions[] = { ++ { iInterface:"Motorola MCU Data Logger", ++ interface_descriptor: (struct usb_interface_descriptor *)&datalog_alternate_descriptor, ++ endpoint_list: datalog_endpoints, ++ endpoints:sizeof (datalog_endpoints) / sizeof (struct usb_endpoint_descriptor *), ++ endpoint_indexes: datalog_indexes, ++ }, ++}; ++ ++static struct usb_alternate_description testcmd_alternate_descriptions[] = { ++ { iInterface:"Motorola Test Command", ++ interface_descriptor: (struct usb_interface_descriptor *)&testcmd_alternate_descriptor, ++ }, ++}; ++#endif ++ ++/* Interface Descriptions */ ++static struct usb_interface_description cdc_interfaces[] = { ++ { ++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:cdc_comm_alternate_descriptions, ++ }, ++ ++ { ++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:cdc_data_alternate_descriptions, ++ }, ++ ++#if defined(CONFIG_USBD_ACM_DATALOG) ++#if 0 ++ // Space the INTERFACEs out by adding as many dummies as required (for now, none). ++ { alternate_list: dummy_alternate_descriptions, }, /* 0xN */ ++#endif ++ ++ { alternates:sizeof (datalog_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:datalog_alternate_descriptions, ++ }, ++ ++ { alternates:sizeof (testcmd_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:testcmd_alternate_descriptions, ++ }, ++ ++#endif ++ ++}; ++ ++ ++/* Configuration Descriptor and Description */ ++static __u8 cdc_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIG, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usb_interface_description), ++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, }; ++ ++struct usb_configuration_description acm_description[] = { ++ { iConfiguration: CONFIG_USBD_ACM_DESC, ++ configuration_descriptor: (struct usb_configuration_descriptor *)cdc_configuration_descriptor, ++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usb_interface_description), ++ interface_list:cdc_interfaces,}, }; ++ ++static struct usb_device_descriptor acm_device_descriptor = { ++ bLength: sizeof(struct usb_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_USBD_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_ACM_BCDDEVICE), ++}; ++ ++ ++static struct usb_endpoint_request acm_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, }, ++#if defined(CONFIG_USBD_ACM_DATALOG) ++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64, 512, }, ++#endif ++ { 0, }, ++}; ++ ++ ++struct usb_device_description acm_device_description = { ++ device_descriptor: &acm_device_descriptor, ++ iManufacturer: CONFIG_USBD_ACM_MANUFACTURER, ++ iProduct: CONFIG_USBD_ACM_PRODUCT_NAME, ++#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_USBD_SERIAL_NUMBER_STR, ++#endif ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: acm_endpoint_requests, ++}; ++ ++/* Missing atomic functions ******************************************************************** */ ++ ++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); ++} ++ ++/* ACM ***************************************************************************************** */ ++/* ++ * Output and Input control lines and line errors. ++ */ ++#define LINE_OUT_DTR 0x01 ++#define LINE_OUT_RTS 0x02 ++#define LINE_IN_DCD 0x01 ++#define LINE_IN_DSR 0x02 ++#define LINE_IN_BRK 0x04 ++#define LINE_IN_RI 0x08 ++#define LINE_IN_FRAMING 0x10 ++#define LINE_IN_PARITY 0x20 ++#define LINE_IN_OVERRUN 0x40 ++ ++/* ******************************************************************************************* */ ++ ++#define ACM_TTY_MAJOR 166 ++#define ACM_TTY_MINOR 0 ++#define ACM_TTY_MINORS 1 ++ ++struct acm_private { ++ struct usb_function_instance *function; ++ struct tty_driver *tty_driver; ++ 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 ++ wait_queue_head_t open_wait; // wait queue for blocking open ++ int open_wait_count; // count of (possible) blocked ++ int exiting; // True if module exiting ++ ++ unsigned char throttle; // non-zero if we are throttled ++ unsigned char clocal; // non-zero if clocal set ++ unsigned char connected; // non-zero if connected to host (configured) ++ ++ unsigned int writesize; // packetsize * 4 ++ unsigned int ctrlin; // line state device sends to host ++ unsigned int ctrlout; // line state device received from host ++ ++ int exclusive; ++ atomic_t used; ++ atomic_t queued_bytes; ++ atomic_t queued_urbs; ++ ++ /*TBR debug receive flow control */ ++ unsigned long bytes_received; ++ unsigned long bytes_forwarded; ++ /*TBR end debug */ ++ ++ struct urb *recv_ring[RECV_RING_SIZE]; ++ unsigned int rr_in_ndx; ++ unsigned int rr_out_ndx; ++ ++ struct urb *int_urb; // pending interrupt urb ++}; ++ ++static struct acm_private acm_private; ++ ++static int acm_send_int_notification(struct usb_function_instance *, int , int ); ++static int acm_urb_sent_bulk (struct urb *urb, int rc); ++static int acm_urb_sent_int (struct urb *urb, int rc); ++static int acm_recv_urb (struct urb *urb, int rc); ++ ++/* Serial Functions **************************************************************************** */ ++ ++static void acm_schedule(struct tq_struct *queue) ++{ ++ TRACE_MSG1("task %p",queue); ++ RETURN_IF(!queue->data || queue->sync); ++ MOD_INC_USE_COUNT; ++ queue_task(queue, &tq_immediate); ++ mark_bh(IMMEDIATE_BH); ++ TRACE_MSG1("task %p scheduled and marked",queue); ++} ++ ++ ++static int block_until_ready( ++ struct tty_struct *tty, ++ struct file *filp, ++ struct acm_private *acm) ++{ ++ unsigned long flags; ++ int rc = 0; ++ ++ // FUTURE: check tty for non-blocking open... ++ ++ local_irq_save(flags); ++ TRACE_MSG1("owc=%d --> at entry",acm->open_wait_count); ++ acm->open_wait_count += 1; ++ for (;;) { ++ if (tty_hung_up_p(filp)) { ++ TRACE_MSG("tty_hung_up_p()"); ++ rc = -ERESTARTSYS; ++ break; ++ } ++ if (signal_pending(current)) { ++ TRACE_MSG("signal_pending()"); ++ rc = -ERESTARTSYS; ++ break; ++ } ++ if (acm->exiting) { ++ TRACE_MSG("module exiting()"); ++ rc = -ENODEV; ++ break; ++ } ++ if (acm->ctrlout & LINE_OUT_DTR) { ++ // OK, there's somebody on the other end, let's go... ++ TRACE_MSG("found DTR"); ++ break; ++ } ++ TRACE_MSG1("owc=%d sleeping...",acm->open_wait_count); ++ interruptible_sleep_on(&acm->open_wait); ++ TRACE_MSG1("owc=%d got WAKEUP",acm->open_wait_count); ++ } ++ acm->open_wait_count -= 1; ++ TRACE_MSG1("owc=%d <-- at exit",acm->open_wait_count); ++ local_irq_restore(flags); ++ return(rc); ++} ++ ++static void acm_hangup(void *private) ++{ ++ //struct acm_private *acm = &acm_private; ++ struct acm_private *acm = (struct acm_private *) private; ++ struct tty_struct *tty = acm->tty; ++ TRACE_MSG("entered"); ++ ++ if (tty && !acm->clocal) ++ tty_hangup(tty); ++ ++ wake_up_interruptible(&acm->open_wait); ++ ++ MOD_DEC_USE_COUNT; ++ //printk (KERN_INFO"%s: MOD: %d connected: %d tty: %p clocal: %x\n", ++ // __FUNCTION__, MOD_IN_USE, acm->connected, tty, acm->clocal); ++ TRACE_MSG("exited"); ++} ++ ++static int acm_tty_open(struct tty_struct *tty, struct file *filp) ++{ ++ struct acm_private *acm = &acm_private; ++ //struct usb_function_instance *function = acm->function; ++ int used; ++ int nonblocking; ++ int rc = 0; ++ unsigned long flags; ++ ++ TRACE_MSG2("used=%d MOD=%d",atomic_read(&acm->used),MOD_IN_USE); ++ //printk (KERN_INFO"%s: bus: %p used: %d MOD: %d\n", ++ // __FUNCTION__, acm->function, atomic_read(&acm->used), MOD_IN_USE); ++ 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 (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 { ++ acm->exclusive = 1; ++ set_bit(TTY_EXCLUSIVE, &tty->flags); ++ } ++ } else if (acm->exclusive != 0 && !suser()) { ++ // Only the superuser can do a normal open of an O_EXCL tty ++ rc = -EBUSY; ++ } ++ local_irq_restore(flags); ++ if (0 == used) { ++ // The value before incrementing was 0, this is the first open. ++ MOD_INC_USE_COUNT; ++ } ++ // OK, now it's safe to make an early return. ++ if (0 != 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); ++ } ++ /* 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. */ ++ if (0 == used) { ++ // First open. ++ TRACE_MSG2("FIRST OPEN nb=%x xo=%x",nonblocking,acm->exclusive); ++ //printk (KERN_INFO"%s: FIRST OPEN used_%sblock: s=%d, a=%d x=%d\n", ++ // __FUNCTION__,(nonblocking?"non":""), used, atomic_read(&acm->used), ++ // acm->exclusive); ++ tty->driver_data = acm; ++ acm->tty = tty; ++ tty->low_latency = 1; ++ acm->bytes_received = 0; ++ acm->bytes_forwarded = 0; ++ acm->ctrlin = LINE_IN_DCD | LINE_IN_DSR; ++ if (NULL != acm->function) { ++ TRACE_MSG1("sending notification ctrlin=%x",acm->ctrlin); ++ //printk (KERN_INFO"%s: sending ctrlin notification\n",__FUNCTION__); ++ rc = acm_send_int_notification(acm->function, CDC_NOTIFICATION_SERIAL_STATE, ++ acm->ctrlin); ++ } ++ } ++ ++ if (0 == rc && !nonblocking) { ++ // Blocking open - all callers block until DTR shows up. ++ rc = block_until_ready(tty,filp,acm); ++ } ++ ++ /* The tty layer calls acm_tty_close() even if this open fails, ++ so any cleanup (rc != 0) will be done there. */ ++ TRACE_MSG2("used=%d rc=%d",atomic_read(&acm->used),rc); ++ return(rc); ++} ++ ++static void acm_tty_close(struct tty_struct *tty, struct file *filp) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct usb_function_instance *function = acm->function; ++ int used; ++ ++ TRACE_MSG2("used=%d MOD=%d",atomic_read(&acm->used),MOD_IN_USE); ++ //printk (KERN_INFO"%s: function: %p used: %d MOD: %d\n", ++ // __FUNCTION__, function, atomic_read(&acm->used), MOD_IN_USE); ++ ++ // lock and decrement used counter, save result ++ used = atomic_pre_dec(&acm->used); ++ ++ // finished unless this is the last close ++ if (used <= 0) { ++ // This is the last close, clean up ++ ++ acm->tty = NULL; ++ acm->ctrlin = 0x0; ++ ++ if (acm->function) { ++ acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin); ++ } ++ /* This should never happen if this is the last close, ++ but it can't hurt to check. */ ++ if (acm->open_wait_count) { ++ wake_up_interruptible(&acm->open_wait); ++ } ++ if (acm->exclusive) { ++ acm->exclusive = 0; ++ clear_bit(TTY_EXCLUSIVE, &tty->flags); ++ } ++ MOD_DEC_USE_COUNT; ++ TRACE_MSG1("LAST CLOSE r-f=%d",(acm->bytes_received-acm->bytes_forwarded)); ++ //printk (KERN_INFO"%s: LAST CLOSE used: %d MOD: %d bytes_received: %lu forwarded: %lu\n", ++ // __FUNCTION__, atomic_read(&acm->used), MOD_IN_USE, acm->bytes_received, ++ // acm->bytes_forwarded); ++ } ++ TRACE_MSG("exited"); ++} ++ ++/* Transmit Function - called by serproto ****************************************************** */ ++ ++static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct usb_function_instance *function = acm->function; ++ struct urb *urb; ++ ++ TRACE_MSG1("count=%d",count); ++ //printk (KERN_INFO"%s: used: %d count: %d\n", __FUNCTION__, atomic_read(&acm->used), count); ++ ++ RETURN_EINVAL_IF(!function); ++ TRACE_MSG("function OK"); ++ ++ // sanity check and are we connect ++ RETURN_EINVAL_IF(!atomic_read(&acm->used)); ++ TRACE_MSG("used OK"); ++ RETURN_ZERO_IF (!count || !acm->connected); ++ TRACE_MSG("connected OK"); ++ RETURN_ZERO_IF(max_queued_urbs <= atomic_read(&acm->queued_urbs)); ++ TRACE_MSG("max_queued_urbs OK"); ++ ++ // allocate a write urb ++ count = MIN(count, acm->writesize); ++ ++ // XXX verify that we don't attempt to send wMaxPacketSize packets... ++ if (!(count % usbd_endpoint_wMaxPacketSize(function, BULK_OUT, 0))) //QQSV ++ count--; ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ RETURN_ENOMEM_IF (!(urb = usbd_alloc_urb (function, BULK_IN, count, acm_urb_sent_bulk))); ++ TRACE_MSG("alloc_urb OK"); ++ ++ if (from_user) ++ copy_from_user(urb->buffer, buf, count); ++ else ++ memcpy(urb->buffer, buf, count); ++ urb->privdata = acm; ++ urb->actual_length = count; ++ atomic_add(count, &acm->queued_bytes); ++ atomic_inc(&acm->queued_urbs); ++ usbd_send_urb(urb); ++ TRACE_MSG2("urbID#%04x --> %d",urbID(urb),count); ++ return count; ++} ++ ++static int acm_tty_write_room(struct tty_struct *tty) ++{ ++ struct acm_private *acm = &acm_private; ++ int rc; ++ ++ TRACE_MSG("entered"); ++ //printk (KERN_INFO"%s: used: %d queued: %d %d room: %d\n", __FUNCTION__, atomic_read(&acm->used), ++ // atomic_read(&acm->queued_urbs), atomic_read(&acm->queued_bytes), ++ // (max_queued_urbs > atomic_read(&acm->queued_urbs)) && ++ // (max_queued_bytes > atomic_read(&acm->queued_bytes)) ? acm->writesize : 0); ++ ++ RETURN_EINVAL_IF(!atomic_read(&acm->used)); ++ TRACE_MSG("used OK"); ++ rc = (!acm->function || max_queued_urbs <= atomic_read(&acm->queued_urbs) || ++ max_queued_bytes <= atomic_read(&acm->queued_bytes) ) ? 0 : acm->writesize ; ++ TRACE_MSG1("--> %d",rc); ++ return(rc); ++} ++ ++static int acm_tty_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct acm_private *acm = &acm_private; ++ int rc; ++ TRACE_MSG("entered"); ++ RETURN_EINVAL_IF(!atomic_read(&acm->used)); ++ TRACE_MSG("used OK"); ++ RETURN_ZERO_IF(!acm->function); ++ TRACE_MSG("function OK"); ++ rc = atomic_read(&acm->queued_bytes); ++ TRACE_MSG1("--> %d",rc); ++ return(rc); ++} ++ ++static struct urb *acm_frwd_recv_urbs(struct tty_struct *tty, struct acm_private *acm) ++{ ++ unsigned long flags; ++ struct urb *urb; ++ ++ TRACE_MSG("entered"); ++ //struct usb_function_instance *function = acm->function; ++ ++ //if (acm->throttle) ++ // printk(KERN_INFO"%s: THROTTLED at 0\n", __FUNCTION__); ++ ++ while (acm->rr_in_ndx != acm->rr_out_ndx && !acm->throttle) { ++ // Forward one URB from the receive ring ++ local_irq_save(flags); ++ urb = acm->recv_ring[acm->rr_out_ndx]; ++ acm->recv_ring[acm->rr_out_ndx] = NULL; ++ acm->rr_out_ndx = (acm->rr_out_ndx + 1) % RECV_RING_SIZE; ++ local_irq_restore(flags); ++ ++ if (urb->status == RECV_OK && tty) { ++ unsigned char *cp = urb->buffer; ++ int i; ++ int f = 0; ++ // Future: think about throttle part way through... ++ //printk(KERN_INFO"%s: ", __FUNCTION__); ++#define ACM_WORD_COPY ++#ifndef ACM_WORD_COPY ++ for (i = 0; i < urb->actual_length; i++) { ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ tty_flip_buffer_push(tty); ++ acm->bytes_forwarded += f; ++ f = 0; ++ } ++ ++ //printk("%02x ", *cp); ++ tty_insert_flip_char(tty, *cp++, 0); ++ f += 1; ++ ++ if (acm->throttle) { ++ //printk(KERN_INFO"%s: throttled at %d/%d\n", __FUNCTION__,i,urb->actual_length); ++ break; ++ } ++ } ++ //printk("\n"); ++ tty_flip_buffer_push(tty); ++ acm->bytes_forwarded += f; ++#else ++ for (i = urb->actual_length; i > 0; i -= f) { ++ f = i; ++ if (tty->flip.count + f > TTY_FLIPBUF_SIZE) { ++ tty_flip_buffer_push(tty); ++ f = MIN(f, TTY_FLIPBUF_SIZE - tty->flip.count); ++ } ++ if (f <= 0) { ++ // Shouldn't happen... ++ printk(KERN_ERR"%s: %d bytes dropped\n", __FUNCTION__, i); ++ break; ++ } ++ memcpy(tty->flip.char_buf_ptr, cp, f); // see: arch/arm/lib/memcpy.S ++ cp += f; ++ tty->flip.count += f; ++ tty->flip.flag_buf_ptr += f; ++ tty->flip.char_buf_ptr += f; // w20146 - Jul 31 ++ tty_flip_buffer_push(tty); ++ acm->bytes_forwarded += f; ++ } ++ ++#endif ++ if (acm->throttle) { ++ printk(KERN_INFO"%s: THROTTLED at 1\n", __FUNCTION__); ++ } ++ } ++ else if (urb->status != RECV_OK) ++ printk(KERN_INFO"%s: !RECV_OK, dumping %d bytes\n", __FUNCTION__,urb->actual_length); ++ else ++ printk(KERN_INFO"%s: tty NULL\n", __FUNCTION__); ++ ++ // Put urb back in line for a refill. ++ if (usbd_start_recv (urb)) { ++ // Bail if it can't be queued for refill. ++ printk(KERN_INFO"%s: usbd_start_recv() failed\n", __FUNCTION__); ++ return(urb); ++ } ++ } ++ TRACE_MSG("exited"); ++ return(NULL); ++} ++ ++static void acm_tty_throttle(struct tty_struct *tty) ++{ ++ struct acm_private *acm = &acm_private; ++ TRACE_MSG("entered"); ++ //uuu printk (KERN_INFO"%s:\n", __FUNCTION__); ++ if (acm && atomic_read(&acm->used)) ++ acm->throttle = 1; ++ TRACE_MSG("exited"); ++} ++ ++static void acm_tty_unthrottle(struct tty_struct *tty) ++{ ++ struct acm_private *acm = &acm_private; ++ struct urb *urb; ++ TRACE_MSG("entered"); ++ //uuu printk (KERN_INFO"%s:\n", __FUNCTION__); ++ if (acm && atomic_read(&acm->used)) { ++ acm->throttle = 0; ++ // Forward any queued URBS. ++ if (NULL != (urb = acm_frwd_recv_urbs(tty,acm))) { ++ /* usbd_start_recv(urb) failed, so we need to dispose of this urb. ++ Count on someone else to replace it. */ ++ usbd_dealloc_urb(urb); ++ } ++ } ++ TRACE_MSG("exited"); ++} ++ ++static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct acm_private *acm = &acm_private; ++ struct usb_function_instance *function = acm->function; ++ unsigned int mask; ++ unsigned int newctrl; ++ ++ TRACE_MSG("entered"); ++ //printk (KERN_INFO"%s: used: %d\n", __FUNCTION__, atomic_read(&acm->used)); ++ RETURN_EINVAL_IF(!atomic_read(&acm->used)); ++ ++#if 1 // temporary solution for IOCTL ++ if(cmd == TIOCMGET) ++ return put_user( acm->ctrlout & LINE_OUT_DTR ? TIOCM_DTR : 0 | TIOCM_CTS, (unsigned long *) arg); ++#endif ++ ++ TRACE_MSG("--> -ENOIOCTLCMD"); ++ return -ENOIOCTLCMD; ++ ++#if 0 ++ switch(cmd) { ++ case TCGETS: ++ case TCFLSH: ++ case TCSETS: ++ printk (KERN_INFO"%s: ignored cmd: %x\n", __FUNCTION__, cmd); ++ return 0; ++ ++ case TCSETSW: ++ ++ //user_termios_to_kernel_termios(&priv->termios, (struct termios *)arg); ++ printk (KERN_INFO"%s: ignored cmd: %x\n", __FUNCTION__, cmd); ++ return 0; ++ ++ case TIOCMGET: ++ printk (KERN_INFO"%s: TIOCMGET: %04x\n", __FUNCTION__, acm->ctrlin & LINE_OUT_DTR ? TIOCM_DTR : 0 | TIOCM_CTS); ++ return put_user( acm->ctrlin & LINE_OUT_DTR ? TIOCM_DTR : 0 | TIOCM_CTS, (unsigned long *) arg); ++ ++ case TIOCMSET: ++ case TIOCMBIS: ++ case TIOCMBIC: ++ ++ printk (KERN_INFO"%s: TIOCM{SET,BIS,BIC}:\n", __FUNCTION__); ++ RETURN_EFAULT_IF (get_user(mask, (unsigned long *) arg)); ++ ++ printk (KERN_INFO"%s: TIOCM{SET,BIS,BIC}: %04x\n", __FUNCTION__, mask); ++ ++ newctrl = acm->ctrlin; ++ ++ mask = (mask & TIOCM_DTR ? LINE_IN_DCD|LINE_IN_DSR : 0)/* | (mask & TIOCM_RTS ? LINE_OUT_RTS : 0)*/; ++ ++ switch(cmd) { ++ case TIOCMSET: newctrl = mask; break; ++ case TIOCMBIS: newctrl |= mask; break; ++ case TIOCMBIC: newctrl &= mask; break; ++ } ++ RETURN_ZERO_IF(acm->ctrlin == newctrl); ++ ++ printk (KERN_INFO"%s: newctrl: %04x\n", __FUNCTION__, newctrl); ++ return acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin); ++ ++ case 3: ++ return 0; ++ default: ++ printk (KERN_INFO"%s: unknown: %04x\n", __FUNCTION__, cmd); ++ break; ++ } ++ return -ENOIOCTLCMD; ++#endif ++} ++ ++static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old) ++{ ++ struct acm_private *acm = &acm_private; ++ struct termios *termios; ++ ++ TRACE_MSG("entered"); ++ //printk (KERN_INFO"%s: tty: %p\n", __FUNCTION__, tty); ++ ++ RETURN_IF(!atomic_read(&acm->used) || !tty || !tty->termios); ++ ++ termios = tty->termios; ++ acm->clocal = termios->c_cflag & CLOCAL; ++ ++ //printk (KERN_INFO"%s: clocal: %d\n", __FUNCTION__, acm->clocal); ++ TRACE_MSG("exited"); ++} ++ ++/* ********************************************************************************************* */ ++ ++static void acm_wakeup_writers(void *private) ++{ ++ struct acm_private *acm = &acm_private; ++ struct tty_struct *tty = acm->tty; ++ ++ TRACE_MSG("entered"); ++ MOD_DEC_USE_COUNT; ++ ++ //printk (KERN_INFO"%s: MOD: %d connected: %d tty: %p\n", __FUNCTION__, MOD_IN_USE, acm->connected, tty); ++ ++ RETURN_IF(!acm->connected || !atomic_read(&acm->used) || !tty); ++ TRACE_MSG("connected and used OK"); ++ ++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { ++ TRACE_MSG("ldisc wakeup"); ++ (tty->ldisc.write_wakeup)(tty); ++ } ++ ++ // printk (KERN_INFO"%s: write_wait wakeup\n", __FUNCTION__); ++ wake_up_interruptible(&tty->write_wait); ++ TRACE_MSG("exited"); ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++static int acm_tty_refcount; ++static struct tty_struct *acm_tty_table[ACM_TTY_MINORS]; ++static struct termios *acm_tty_termios[ACM_TTY_MINORS]; ++static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS]; ++ ++static struct tty_driver acm_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: acm_tty_open, ++ close: acm_tty_close, ++ write: acm_tty_write, ++ write_room: acm_tty_write_room, ++ ioctl: acm_tty_ioctl, ++ throttle: acm_tty_throttle, ++ unthrottle: acm_tty_unthrottle, ++ chars_in_buffer: acm_tty_chars_in_buffer, ++ set_termios: acm_tty_set_termios, ++ ++ refcount: &acm_tty_refcount, ++ table: acm_tty_table, ++ termios: acm_tty_termios, ++ termios_locked: acm_tty_termios_locked, ++}; ++ ++ ++/* Transmit INTERRUPT ************************************************************************** */ ++ ++/** ++ * Generates a response urb on the notification (INTERRUPT) endpoint. ++ * Return a non-zero result code to STALL the transaction. ++ * CALLED from interrupt context. ++ */ ++static int acm_send_int_notification(struct usb_function_instance *function, int bnotification, int data) ++{ ++ struct acm_private *acm = &acm_private; ++ struct urb *urb = NULL; ++ struct cdc_notification_descriptor *notification; ++ unsigned long flags; ++ int rc; ++ ++ //printk(KERN_INFO"%s: function: %p connected: %d DTR: %d\n", __FUNCTION__, ++ // function, acm->connected, acm->ctrlout & LINE_OUT_DTR); ++ ++ local_irq_save(flags); ++ TRACE_MSG("entered"); ++ ++ do { ++ BREAK_IF(!function); ++ //BREAK_IF(!acm->connected || !acm->ctrlout & LINE_OUT_DTR); ++ BREAK_IF(!acm->connected); ++ ++ //printk (KERN_INFO"%s: bnotification: %x data: %d int_urb: %p\n", ++ // __FUNCTION__, bnotification, data, acm->int_urb); ++ ++ if (acm->int_urb) { ++ //uuu printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, acm->int_urb); ++ usbd_cancel_urb_irq(acm->int_urb); ++ acm->int_urb = NULL; ++ } ++ ++ //printk(KERN_INFO"%s: AAA\n", __FUNCTION__); ++ ++ //uuu printk(KERN_INFO"%s:\n", __FUNCTION__); ++ BREAK_IF(!(urb = usbd_alloc_urb (function, INT_IN, ++ sizeof(struct cdc_notification_descriptor), acm_urb_sent_int))); ++ ++ //printk(KERN_INFO"%s: BBB\n", __FUNCTION__); ++ ++ memset(urb->buffer, 0, urb->buffer_length); ++ urb->actual_length = sizeof(struct cdc_notification_descriptor); ++ urb->privdata = &acm_private; ++ ++ // 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; ++ } ++ ++ acm->int_urb = urb; ++ //printk(KERN_INFO"%s: CCC\n", __FUNCTION__); ++ BREAK_IF(!(rc = usbd_send_urb (urb))); ++ ++ //uuu printk(KERN_ERR"%s: usbd_send_urb failed err: %x\n", __FUNCTION__, rc); ++ acm->int_urb = urb; ++ urb->privdata = NULL; ++ usbd_dealloc_urb (urb); ++ ++ } while(0); ++ ++ TRACE_MSG1("urbID#%04x --> 0",urbID(urb)); ++ local_irq_restore(flags); ++ //printk(KERN_INFO"%s: DDD\n", __FUNCTION__); ++ return 0; ++} ++ ++/* Callback functions for TX urb completion (function chosen when urb is allocated) ***********/ ++ ++/* acm_urb_sent_bulk - called to indicate URB transmit finished ++ * @urb: pointer to struct urb ++ * @rc: result ++ */ ++static int acm_urb_sent_bulk (struct urb *urb, int rc) ++{ ++ struct acm_private *acm = &acm_private; ++ struct usb_function_instance *function; ++ ++ acm_interrupts++; ++ TRACE_MSG2("entered urbID#%04x rc=%d",urbID(urb),rc); ++ ++ if (!urb || !(function = urb->function_instance)) { ++ TRACE_MSG1("urbID#%04x --> -EINVAL",urbID(urb)); ++ return(-EINVAL); ++ } ++ TRACE_MSG1("IN length=%d",urb->actual_length); ++ ++ atomic_sub(urb->actual_length, &acm->queued_bytes); ++ atomic_dec(&acm->queued_urbs); ++ urb->privdata = NULL; ++ usbd_dealloc_urb (urb); ++ acm_schedule(&acm->wqueue); ++ TRACE_MSG1("urbID#%04x --> 0",urbID(urb)); ++ return 0; ++} ++ ++/* acm_urb_sent_int - called to indicate URB transmit finished ++ * @urb: pointer to struct urb ++ * @rc: result ++ */ ++static int acm_urb_sent_int (struct urb *urb, int rc) ++{ ++ struct acm_private *acm = &acm_private; ++ struct usb_function_instance *function; ++ ++ acm_interrupts++; ++ TRACE_MSG2("entered urbID#%04x rc=%d",urbID(urb),rc); ++ //printk(KERN_INFO"%s: urb: %p function: %p rc: %d\n", __FUNCTION__, urb, urb->function_instance, rc); ++ if (!urb || !(function = urb->function_instance)) { ++ TRACE_MSG1("urbID#%04x --> -EINVAL",urbID(urb)); ++ return(-EINVAL); ++ } ++ TRACE_MSG1("INT length=%d",urb->actual_length); ++ ++ acm->int_urb = NULL; ++ usbd_dealloc_urb(urb); ++ TRACE_MSG1("urbID#%04x --> 0",urbID(urb)); ++ return 0; ++} ++ ++#if defined(CONFIG_USBD_ACM_DATALOG) ++/* acm_urb_sent_pst - called to indicate URB transmit finished ++ * @urb: pointer to struct urb ++ * @rc: result ++ */ ++int acm_urb_sent_pst (struct urb *urb, int rc) ++{ ++ struct acm_private *acm = &acm_private; ++ struct usb_function_instance *function; ++ ++ acm_interrupts++; ++ TRACE_MSG2("entered urbID#%04x rc=%d",urbID(urb),rc); ++ //uuu printk(KERN_INFO"urb: %p function: %p rc: %d\n", urb, urb->function_instance, rc); ++ if (!urb || !(function = urb->function_instance)) { ++ TRACE_MSG1("urbID#%04x --> -EINVAL",urbID(urb)); ++ return(-EINVAL); ++ } ++ TRACE_MSG1("PST length=%d",urb->actual_length); ++ ++#if defined(PST_FD_AVAILABLE) ++ pst_urb_sent(urb, rc); ++#endif ++ urb->privdata = NULL; ++ usbd_dealloc_urb(urb); ++ TRACE_MSG1("urbID#%04x --> 0",urbID(urb)); ++ return 0; ++} ++#endif ++ ++/* USB Device Functions ************************************************************************ */ ++ ++/* acm_event_irq - process a device event ++ * ++ */ ++void acm_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data) ++{ ++ struct acm_private *acm = &acm_private; ++ int i; ++ ++ acm_interrupts++; ++ TRACE_MSG1("entered ev=%d",event); ++ switch (event) { ++ ++#if defined(CONFIG_USBD_ACM_DATALOG) ++ case DEVICE_CREATE: ++#if defined(PST_FD_AVAILABLE) ++ if (0 != (ret = pst_dev_create(device))) ++ return; ++#endif ++ break; ++ ++ case DEVICE_DESTROY: ++#if defined(PST_FD_AVAILABLE) ++ pst_dev_destroy(); ++#endif ++ break; ++#endif ++ ++ case DEVICE_CONFIGURED: ++ TRACE_MSG("CONFIGURED"); ++ acm->connected = 1; ++ acm_schedule(&acm->wqueue); ++ for (i = 0; i < MAX_RECV_URBS; i++) { ++ struct urb *urb; ++ // printk(KERN_INFO"%s:\n", __FUNCTION__); ++ BREAK_IF(!(urb = usbd_alloc_urb(function, BULK_OUT, ++ usbd_endpoint_transferSize(function, BULK_OUT,usbd_high_speed(function)), ++ acm_recv_urb))); ++ if (usbd_start_recv(urb)) ++ usbd_dealloc_urb(urb); ++ } ++ break; ++ ++ case DEVICE_RESET: ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG("RESET"); ++ BREAK_IF(!acm->connected); ++ acm->connected = 0; ++ acm->int_urb = NULL; ++ acm_schedule(&acm->hqueue); ++ // XXX flush ++ // Release any queued urbs ++ break; ++ ++ default: break; ++ } ++ TRACE_MSG("exited"); ++} ++ ++static int acm_recv_urb (struct 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 = &acm_private; ++ struct tty_struct *tty = acm->tty; ++ struct urb *furb; ++ ++ acm_interrupts++; ++ TRACE_MSG1("entered rc=%d",rc); ++ //printk(KERN_INFO"%s: urb: %p rc=%d\n", __FUNCTION__, urb, rc); ++ if (RECV_CANCELLED == rc) { ++ TRACE_MSG1("cancelled URB=%p",urb); ++ usbd_dealloc_urb(urb); ++ return(0); ++ } ++ if (RECV_OK != rc) { ++ // Shouldn't happen, but... ++ // Reject it. ++ TRACE_MSG1("rejected URB=%p",urb); ++ //printk(KERN_INFO"%s: rejecting URB %p rc=%d\n", __FUNCTION__, urb, rc); ++ return(1); ++ } ++ // Queue it for forwarding, then kick off forwarding routine. ++ acm->bytes_received += urb->actual_length; ++ acm->recv_ring[acm->rr_in_ndx] = urb; ++ acm->rr_in_ndx = (acm->rr_in_ndx + 1) % RECV_RING_SIZE; ++ if (urb == (furb = acm_frwd_recv_urbs(tty,acm))) { ++ // Couldn't restart urb, tell the caller to dispose of it. ++ TRACE_MSG1("caller to release URB=%p",urb); ++ //printk(KERN_INFO"%s: caller to release URB %p rc=%d\n", __FUNCTION__, urb, rc); ++ return(1); ++ } ++ if (NULL != furb) { ++ /* Couldn't restart some other urb, but we can't tell the caller ++ about it, so we need to dispose of it ourselves. This should ++ only happen if the bi layer is shutting down, so we let someone ++ else allocate the replacement if one is needed. */ ++ TRACE_MSG1("releasing URB=%p",urb); ++ usbd_dealloc_urb(urb); ++ } ++ TRACE_MSG("--> 0"); ++ return(0); ++} ++ ++/* acm_line_coding_urb_received - callback for sent URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * Returns non-zero for failure. ++ */ ++static int acm_line_coding_urb_received (struct urb *urb, int urb_rc) ++{ ++ TRACE_MSG2("urbID#%04x rc=%d",urbID(urb),urb_rc); ++ ++ RETURN_EINVAL_IF (RECV_OK != urb_rc); ++ ++ return -EINVAL; // caller will de-allocate ++} ++ ++/* acm_recv_setup_irq - called to indicate urb has been received ++ */ ++int acm_recv_setup_irq (struct usb_device_request *request) ++{ ++ struct acm_private *acm = &acm_private; ++ struct usb_function_instance *function = acm->function; ++ ++ acm_interrupts++; ++ TRACE_SETUP(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_MSG("--> 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 urb *urb; ++ int len = le16_to_cpu(request->wLength); ++ TRACE_MSG1("SET_LINE_CODING wLength=%d",len); ++ if (len <= 0) { ++ TRACE_MSG("(len<=0)--> 0"); ++ return(0); ++ } ++ // Set up an ep0 recv urb for the rest of it. ++ urb = usbd_alloc_urb_ep0(function, len, acm_line_coding_urb_received); ++ if (NULL == urb) { ++ TRACE_MSG("no mem for ep0 recv urb"); ++ return(-EINVAL); ++ } ++ if (usbd_start_recv(urb)) { ++ TRACE_MSG("usbd_start_recv() failed"); ++ usbd_dealloc_urb(urb); // de-alloc if error ++ TRACE_MSG("--> -EINVAL"); ++ return(-EINVAL); ++ } ++ } ++ break; ++ case CDC_CLASS_REQUEST_SET_CONTROL_STATE: ++ { ++ struct acm_private *acm = &acm_private; ++ unsigned int prev_ctrlout = acm->ctrlout; ++ acm->ctrlout = le16_to_cpu(request->wValue); ++ ++ TRACE_MSG1("set control state, tty=%p",acm->tty); ++ //uuu printk(KERN_INFO"%s: tty: %p clocal: %x ctrlout: %02x DTR: %x\n", ++ //uuu __FUNCTION__, acm->tty, acm->clocal, acm->ctrlout, acm->ctrlout & LINE_OUT_DTR); ++ ++ // schedule writers or hangup IFF open ++ BREAK_IF(!acm->tty); ++ TRACE_MSG1("set control state, ctrlout#%04x",acm->ctrlout); ++ // make sure there really is a state change ++ if ((acm->ctrlout ^ prev_ctrlout) & LINE_OUT_DTR) { ++ TRACE_MSG1("DTR state changed -> %x",(acm->ctrlout&LINE_OUT_DTR)); ++ //printk(KERN_INFO"%s: tty: %p DTR state changed -> %u\n", ++ // __FUNCTION__, acm->tty, (acm->ctrlout&LINE_OUT_DTR)); ++ acm_schedule(((acm->ctrlout & LINE_OUT_DTR) ? &acm->wqueue : &acm->hqueue)); ++ // wake up blocked opens ++ if (acm->open_wait_count > 0) { ++ wake_up_interruptible(&acm->open_wait); ++ } ++ } // end of state change operation ++ ++ // send notification if we have DCD ++ TRACE_MSG1("checking DCD ctrlin#%04x",acm->ctrlin); ++ BREAK_IF(!(acm->ctrlin & (LINE_IN_DCD | LINE_IN_DSR))); ++ TRACE_MSG1("tty=%p sending (DCD|DSR) notification",acm->tty); ++ acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin); ++ } ++ break; ++ ++ case CDC_CLASS_REQUEST_SEND_BREAK: break; ++ default: break; ++ } ++ TRACE_MSG("--> 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 urb *urb; ++ struct cdc_acm_line_coding *results; ++ int len = le16_to_cpu(request->wLength); ++ if (len != sizeof(struct cdc_acm_line_coding)) { ++ TRACE_MSG2("(len=%d!=sz=%d--> -EINVAL",len,sizeof(struct cdc_acm_line_coding)); ++ return -EINVAL; ++ } ++ ++ if (!(urb = usbd_alloc_urb_ep0(function, len, NULL))) { ++ TRACE_MSG("(nomem)--> -EINVAL"); ++ return -EINVAL; ++ } ++ results = (struct cdc_acm_line_coding *)urb->buffer; ++ results->dwDTERate = cpu_to_le16(0x1c200); // 115200 ++ results ->bDataBits = 0x08; ++ urb->actual_length = len; ++ TRACE_MSG1("sending line coding urb=%p",(u32)(void*)urb); ++ if (!usbd_send_urb(urb)) { ++ TRACE_MSG("--> 0"); ++ return(0); ++ } ++ usbd_dealloc_urb(urb); ++ TRACE_MSG("(send failed)--> -EINVAL"); ++ return -EINVAL; ++ } ++ default: break; ++ } ++ TRACE_MSG("--> 0"); ++ return 0; ++ ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR: break; ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR: break; ++#if defined(PST_FD_AVAILABLE) ++ ep0_process_vendor_request( urb ); ++ TRACE_MSG("--> 0"); ++ return 0; ++#endif ++ ++ default: break; ++ } ++ TRACE_MSG("--> 0"); ++ return 0; ++} ++ ++ ++static int acm_function_enable (struct usb_function_instance *function) ++{ ++ struct acm_private *acm = &acm_private; ++ acm_interrupts++; ++ TRACE_MSG("entered"); ++ MOD_INC_USE_COUNT; ++ acm->function = function; ++ acm->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, 0) * 4; // QQSV ++ TRACE_MSG("-> 0"); ++ return 0; ++} ++ ++static void acm_function_disable (struct usb_function_instance *function) ++{ ++ struct acm_private *acm = &acm_private; ++ acm_interrupts++; ++ TRACE_MSG("entered"); ++ acm->writesize = 0; ++ acm->function = NULL; ++ MOD_DEC_USE_COUNT; ++ TRACE_MSG("exited"); ++} ++ ++ ++static struct usb_function_operations function_ops = { ++ event_irq: acm_event_irq, ++ recv_setup_irq: acm_recv_setup_irq, ++ function_enable: acm_function_enable, ++ function_disable: acm_function_disable, ++}; ++ ++static struct usb_function_driver function_driver = { ++ name: "acm-CDC", ++ fops:&function_ops, ++ device_description:&acm_device_description, ++ bNumConfigurations:sizeof (acm_description) / sizeof (struct usb_configuration_description), ++ configuration_description:acm_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_USBD_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_ACM_BCDDEVICE), ++}; ++ ++#if defined(USE_TICKER) ++/* usb ticker ********************************************************************************** */ ++ ++#define retrytime 10 ++ ++int ticker_terminating; ++int ticker_timer_set; ++ ++static DECLARE_MUTEX_LOCKED (ticker_sem_start); ++static DECLARE_MUTEX_LOCKED (ticker_sem_work); ++ ++void ticker_tick (unsigned long data) ++{ ++ ticker_timer_set = 0; ++ up (&ticker_sem_work); ++} ++ ++void udc_ticker_poke (void) ++{ ++ up (&ticker_sem_work); ++} ++ ++int ticker_thread (void *data) ++{ ++ struct timer_list ticker; ++ // detach ++ lock_kernel (); ++ exit_mm (current); ++ exit_files (current); ++ exit_fs (current); ++ ++ // setsid equivalent, used at start of kernel thread, no error checks needed, or at least none made :). ++ current->leader = 1; ++ current->session = current->pgrp = current->pid; ++ current->tty = NULL; ++ current->tty_old_pgrp = 0; ++ sprintf (current->comm, "acm_fd"); ++ ++ // setup signal handler ++ current->exit_signal = SIGCHLD; ++ spin_lock (¤t->sigmask_lock); ++ flush_signals (current); ++ spin_unlock (¤t->sigmask_lock); ++ ++ // run at a high priority, ahead of sync and friends ++ current->policy = SCHED_OTHER; ++ unlock_kernel (); ++ ++ // setup timer ++ init_timer (&ticker); ++ ticker.data = 0; ++ ticker.function = ticker_tick; ++ ++ // let startup continue ++ up (&ticker_sem_start); ++ ++ // process loop ++ for (ticker_timer_set = ticker_terminating = 0; !ticker_terminating;) { ++ ++ struct acm_private * acm = &acm_private; ++ ++ if (!ticker_timer_set) { ++ mod_timer (&ticker, jiffies + HZ * retrytime); ++ } ++ ++ // wait for someone to tell us to do something ++ down (&ticker_sem_work); ++ ++ // sanity checks before proceeding ++ BREAK_IF(ticker_terminating); ++ CONTINUE_IF(!(function = acm->function)); ++ CONTINUE_IF(USBD_OK != bus->status); ++ ++ // do what we need to do ++ acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin); ++ } ++ ++ // remove timer, let the process stopping us know we are done and return ++ del_timer (&ticker); ++ up (&ticker_sem_start); ++ return 0; ++} ++#endif ++ ++ ++/* 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\n"); ++ ++ if (0 != acm_trace_init(ACM_TRACE_NAME)) { ++ printk(KERN_ERR"%s: ERROR tracing configured, but init failed.\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ TRACE_MSG("entered"); ++ ++ if (vendor_id) ++ function_driver.idVendor = cpu_to_le16(vendor_id); ++ if (product_id) ++ function_driver.idProduct = cpu_to_le16(product_id); ++ printk (KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", __FUNCTION__, ++ __usbd_module_info, function_driver.idVendor, function_driver.idProduct); ++ ++ // 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; ++ ++ init_waitqueue_head(&acm_private.open_wait); ++ ++ for (i = 0; i < RECV_RING_SIZE; i++) { ++ acm_private.recv_ring[i] = NULL; ++ } ++ acm_private.rr_in_ndx = acm_private.rr_out_ndx = 0; ++ ++ // register as tty driver ++ acm_tty_driver.init_termios = tty_std_termios; ++ acm_tty_driver.init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; ++ acm_tty_driver.init_termios.c_lflag &= ~(ECHO | ICANON); ++ THROW_IF(tty_register_driver(&acm_tty_driver), error); ++ ++ tty_register_devfs(&acm_tty_driver, 0, ACM_TTY_MINOR); ++ acm_private.tty_driver_registered++; ++ ++ // register as usb function driver ++ THROW_IF (usbd_register_function (&function_driver), error); ++ acm_private.usb_driver_registered++; ++ ++#if defined(USE_TICKER) ++ // kickoff_thread - start management thread ++ //ticker_terminating = 0; ++ //kernel_thread (&ticker_thread, NULL, 0); ++ //down (&ticker_sem_start); ++#endif ++ ++ CATCH(error) { ++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__); ++ ++ if (acm_private.tty_driver_registered) { ++ tty_unregister_driver(&acm_tty_driver); ++ acm_private.tty_driver_registered = 0; ++ } ++ if (acm_private.usb_driver_registered) { ++ usbd_deregister_function (&function_driver); ++ acm_private.usb_driver_registered = 0; ++ } ++ TRACE_MSG("--> -EINVAL"); ++ return -EINVAL; ++ } ++ TRACE_MSG("--> 0"); ++ return 0; ++} ++ ++void acm_wait_task(struct tq_struct *queue) ++{ ++ TRACE_MSG1("entered data=%p",queue->data); ++ RETURN_IF(!queue->data); ++ queue->data = NULL; ++ while (queue->sync) { ++ //uuu printk(KERN_INFO"%s: waiting for queue: %p\n", __FUNCTION__, queue); ++ TRACE_MSG1("waiting for queue: %p",queue); ++ schedule_timeout(HZ); ++ } ++ TRACE_MSG("exited"); ++} ++ ++/* acm_modexit - module cleanup ++ */ ++static void acm_modexit (void) ++{ ++ unsigned long flags; ++ struct urb *urb; ++ TRACE_MSG("entered"); ++ ++#if defined(USE_TICKER) ++ // killoff_thread - stop management thread ++ //if (!ticker_terminating) { ++ // ticker_terminating = 1; ++ // up (&ticker_sem_work); ++ // down (&ticker_sem_start); ++ //} ++#endif ++ ++ // 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); ++ ++ // de-register as tty and usb drivers ++ if (acm_private.tty_driver_registered) { ++ tty_unregister_driver(&acm_tty_driver); ++ } ++ if (acm_private.usb_driver_registered) { ++ usbd_deregister_function (&function_driver); ++ } ++ ++ // Flush any urbs in the recv_ring. ++ while (acm_private.rr_in_ndx != acm_private.rr_out_ndx) { ++ // Remove and dealloc one URB from the receive ring ++ local_irq_save(flags); ++ urb = acm_private.recv_ring[acm_private.rr_out_ndx]; ++ acm_private.recv_ring[acm_private.rr_out_ndx] = NULL; ++ acm_private.rr_out_ndx = (acm_private.rr_out_ndx + 1) % RECV_RING_SIZE; ++ local_irq_restore(flags); ++ // printk(KERN_INFO"%s: releasing URB %p\n", __FUNCTION__, urb); ++ usbd_dealloc_urb(urb); ++ } ++ acm_trace_exit(ACM_TRACE_NAME); ++} ++ ++ ++module_init (acm_modinit); ++module_exit (acm_modexit); +diff -Nru a/drivers/usbd/acm_fd/trace.c b/drivers/usbd/acm_fd/trace.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/acm_fd/trace.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,530 @@ ++/* ++ * usbd/acm_fd/trace.c ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/version.h> ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/pci.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++ ++#include <linux/proc_fs.h> ++#include <linux/vmalloc.h> ++ ++#include <asm/atomic.h> ++#include <asm/io.h> ++ ++#include <linux/proc_fs.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) ++#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1 ++#include <linux/timer.h> ++#else ++#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK ++#include <linux/tqueue.h> ++#endif ++ ++#include <linux/netdevice.h> ++#include <linux/pci.h> ++#include <linux/cache.h> ++ ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++#include <asm/dma.h> ++#include <asm/mach/dma.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/hardware.h> ++#include <asm/types.h> ++#endif ++ ++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++#include <asm/au1000.h> ++#include <asm/au1000_dma.h> ++#include <asm/mipsregs.h> ++#endif ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#include <asm/arch/timers.h> ++#include <asm/arch/hardware.h> ++#endif ++ ++#if defined(CONFIG_ARCH_MX1ADS) ++#include "dbmx1_bi/dbmx1.h" ++#endif ++ ++#include <asm/uaccess.h> ++#include <asm/io.h> ++#include <asm/pgtable.h> ++#include <asm/pgalloc.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <trace.h> ++ ++ ++int acm_trace_first; ++int acm_trace_last_read; ++int acm_trace_next; ++int acm_trace_total; ++acm_trace_t *acm_traces; ++ ++extern int acm_interrupts; ++ ++ ++#if defined(CONFIG_USBD_ACM_TRACE) && defined(CONFIG_PROC_FS) ++ ++acm_trace_t *ACM_TRACE_NEXT(char *fn, acm_trace_types_t acm_trace_type) ++{ ++ acm_trace_t *p; ++ unsigned long flags; ++ ++ // Get the next trace slot - this needs to be atomic. ++ local_irq_save (flags); ++ p = acm_traces + acm_trace_next; ++#if defined(TRACE_MAX_IS_2N) ++ acm_trace_next = (acm_trace_next + 1) & TRACE_MASK; ++ if (acm_trace_next == acm_trace_first) { ++ // We have wraparound, bump acm_trace_first ++ if (acm_trace_first == acm_trace_last_read) { ++ // We have to bump last read too. ++ acm_trace_last_read = (acm_trace_last_read + 1) & TRACE_MASK; ++ } ++ acm_trace_first = (acm_trace_first + 1) & TRACE_MASK; ++ } ++#else ++ if (TRACE_MAX <= ++acm_trace_next) ++ acm_trace_next = 0; ++ ++ if (acm_trace_next == acm_trace_first) { ++ // We have wrap around, bump acm_trace_first ++ if (acm_trace_first == acm_trace_last_read) { ++ // We have to bump last read too. ++ if (TRACE_MAX <= ++acm_trace_last_read) ++ acm_trace_last_read = 0; ++ } ++ if (TRACE_MAX <= ++acm_trace_first) ++ acm_trace_first = 0; ++ } ++#endif ++ acm_trace_total++; ++ // End of next trace slot. ++ local_irq_restore (flags); ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ p->oscr = OSCR; ++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) ++ p->cp0_count = __read_32bit_c0_register(CP0_COUNT); ++#else ++ p->cp0_count = read_c0_count(); ++#endif ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ //p->jiffies = jiffies; ++ //p->tcnt0 = *(volatile u32 *)TCNT0; ++ p->tcnt1 = *(volatile u32 *)TCNT1; ++#else ++ p->jiffies = jiffies; ++#endif ++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++ //p->sofs = au_readl(USBD_FRAMENUM); ++#endif ++#if defined(CONFIG_ARCH_MX1ADS) ++ p->sofs = USBD_FRAME & 0x3ff; ++#endif ++ ++ p->interrupts = acm_interrupts; ++ p->acm_trace_type = acm_trace_type; ++ p->function = fn; ++ ++ //printk(KERN_INFO"first: %d next: %d interrupts: %d oscr: %d\n", acm_trace_first, acm_trace_next, acm_interrupts, p->oscr); ++ ++ return p; ++} ++ ++/* Proc Filesystem *************************************************************************** */ ++ ++/* * ++ * acm_trace_proc_read - implement proc file system read. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Standard proc file system read function. ++ */ ++static ssize_t acm_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; ++ unsigned long flags; ++ ++ acm_trace_t px; ++ acm_trace_t ox; ++ acm_trace_t *o, *p; ++ ++ ++ MOD_INC_USE_COUNT; ++ ++ /* Get the index of the entry to read and update last_read - this needs to be atomic. */ ++ p = &px; ++ o = NULL; ++ oindex = index = (*pos)++; ++ local_irq_save (flags); ++ ++#if defined(TRACE_MAX_IS_2N) ++ index = (acm_trace_first + index) & TRACE_MASK; ++#else ++ index += acm_trace_first; ++ if (index >= TRACE_MAX) { ++ index -= TRACE_MAX; ++ } ++#endif ++ // Are we at the end of the data? ++ if (((acm_trace_first < acm_trace_next) && ++ (index >= acm_trace_first) && (index < acm_trace_next)) || ++ ((acm_trace_first > acm_trace_next) && ++ ((index < acm_trace_next) || (index >= acm_trace_first)))) { ++ // Nope, there's data to show. ++ memcpy(p,(acm_traces+index),sizeof(acm_trace_t)); ++ // Is there a previous event? ++ previous = (index) ? (index - 1) : (TRACE_MAX - 1); ++ if (previous != acm_trace_next && acm_trace_total > 1) { ++ // There is a valid previous event. ++ o = &ox; ++ memcpy(o,(acm_traces+previous),sizeof(acm_trace_t)); ++ } ++ acm_trace_last_read = index; ++ } else { ++ index = -1; ++ } ++ local_irq_restore (flags); ++ if (index < 0) { ++ // End of data. ++ return(0); ++ } ++ ++ // get a page, max 4095 bytes of data... ++ if (!(page = get_free_page (GFP_KERNEL))) { ++ MOD_DEC_USE_COUNT; ++ return -ENOMEM; ++ } ++ ++ len = 0; ++ ++ if (oindex == 0) { ++#if defined(CONFIG_ARCH_SAMSUNG) ++ len += sprintf ((char *) page + len, " Index Ints Ticks [%d]\n", CONFIG_USBD_SMDK2500_BCLOCK ); ++#else ++ len += sprintf ((char *) page + len, " Index Ints Ticks\n"); ++#endif ++ } ++ ++ //printk(KERN_INFO"first: %d next: %d index: %d %d prev: %d\n", acm_trace_first, acm_trace_next, oindex, index, previous); ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++ u32 ticks = 0; ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ u32 ticks = 0; ++#else ++ u64 jifs = 0; ++#endif ++ unsigned char *cp; ++ unsigned int *ip; ++ int skip = 0; ++ ++ /* 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 and wraparound. */ ++ if (o != NULL) { ++ ++#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->oscr) { ++ ticks = (p->oscr > o->oscr) ? (p->oscr - o->oscr) : (o->oscr - p->oscr) ; ++ ticks = (ticks * 60) / 221; ++ } ++ ++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++ /* ++ * 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 = ticks / CONFIG_USBD_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_USBD_SMDK2500_BCLOCK; ++ } ++#else ++ if (o->jiffies) { ++ jifs = p->jiffies - acm_traces[previous].jiffies; ++ } ++#endif ++ ++ 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 defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++ if (ticks > 1024*1024) { ++ len += sprintf ((char *) page + len, "%8dM ", ticks>>20); ++ } ++ else { ++ len += sprintf ((char *) page + len, "%8d ", 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>>10); ++ } ++ else { ++ len += sprintf ((char *) page + len, "%4d ", (int)jifs); ++ } ++#endif ++#if defined(CONFIG_ARCH_MX1ADS) ++ len += sprintf ((char *) page + len, "%6d ", (int)p->sofs); ++#endif ++ ++ switch (p->acm_trace_type) { ++ case acm_trace_msg_n: ++ len += sprintf ((char *) page + len, " -- %s: ",p->function); ++ len += sprintf ((char *) page + len, p->trace.msg.msg); ++ break; ++ ++ case acm_trace_msg32_n: ++ len += sprintf ((char *) page + len, " -- %s: ",p->function); ++ len += sprintf ((char *) page + len, p->trace.msg32.msg, p->trace.msg32.val); ++ break; ++ ++ case acm_trace_msg16_n: ++ len += sprintf ((char *) page + len, " -- %s: ",p->function); ++ len += sprintf ((char *) page + len, p->trace.msg16.msg, p->trace.msg16.val0, p->trace.msg16.val1); ++ break; ++ ++ case acm_trace_msg8_n: ++ len += sprintf ((char *) page + len, " -- %s: ",p->function); ++ 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 acm_trace_setup_n: ++ cp = (unsigned char *)&p->trace.setup; ++ len += sprintf ((char *) page + len, ++ " -- %s: request [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ p->function, cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++ break; ++ } ++ len += sprintf ((char *) page + len, "\n"); ++ ++ // len == 0 is valid, it just means we've reached the end of the data. ++ if (len > count) { ++ len = -EINVAL; ++ } ++ else if (len > 0 && copy_to_user (buf, (char *) page, len)) { ++ len = -EFAULT; ++ } ++ free_page (page); ++ MOD_DEC_USE_COUNT; ++ return len; ++} ++ ++/* * ++ * acm_trace_proc_write - implement proc file system write. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Proc file system write function. ++ */ ++static ssize_t acm_trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * 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 { ++ // flush remainder, if any ++ n -= l; ++ while (n > 0) { ++ // Not too efficient, but it shouldn't matter ++ 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; ++ } ++ ++ if (!strncmp("flush", command, 5)) { ++ /* Move pointers so that next read continues from last read point, ++ instead of all available messages - this needs to be atomic. */ ++ unsigned long flags; ++ ++ local_irq_save (flags); ++#if defined(TRACE_MAX_IS_2N) ++ acm_trace_first = (acm_trace_last_read + 1) & TRACE_MASK; ++#else ++ if (TRACE_MAX <= (acm_trace_first = acm_trace_last_read + 1)) { ++ acm_trace_first = 0; ++ } ++#endif ++ local_irq_restore (flags); ++ } ++ ++ return count; ++} ++ ++static struct file_operations acm_trace_proc_operations_functions = { ++ read:acm_trace_proc_read, ++ write:acm_trace_proc_write, ++}; ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#endif ++ ++/** ++ * acm_trace_init ++ * ++ * Return non-zero if not successful. ++ */ ++int acm_trace_init (char *name) ++{ ++ printk(KERN_INFO"%s: creating /proc/%s with %u entries\n", __FUNCTION__,name,TRACE_MAX); ++ if (!(acm_traces = vmalloc(sizeof(acm_trace_t) * TRACE_MAX))) { ++ printk(KERN_ERR"%s: malloc failed %p %d\n", __FUNCTION__, acm_traces, sizeof(acm_trace_t) * TRACE_MAX); ++ return -EINVAL; ++ } ++ memset(acm_traces, 0, sizeof(acm_trace_t) * TRACE_MAX); ++ acm_trace_last_read = TRACE_MAX - 1; ++ ++ { ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry (name, 0, 0)) == NULL) { ++ printk(KERN_INFO"%s PROC FS failed\n",name); ++ } ++ else { ++ p->proc_fops = &acm_trace_proc_operations_functions; ++ } ++ } ++#if defined(CONFIG_ARCH_SAMSUNG) ++ *(volatile u32 *)TMOD |= 0x3 << 3; ++#endif ++ printk(KERN_INFO"%s: OK\n", __FUNCTION__); ++ return 0; ++} ++ ++/** ++ * acm_trace_exit - remove procfs entry, free trace data space. ++ */ ++void acm_trace_exit (char *name) ++{ ++ { ++ unsigned long flags; ++ local_irq_save (flags); ++ remove_proc_entry (name, NULL); ++ if (acm_traces) { ++ acm_trace_t *p = acm_traces; ++ acm_traces = NULL; ++ vfree(p); ++ } ++ local_irq_restore (flags); ++ } ++} ++ ++ ++#else ++int acm_trace_init (char *name) ++{ ++ return 0; ++} ++ ++void acm_trace_exit (char *name) ++{ ++ return; ++} ++#endif ++ ++/* End of FILE */ ++ +diff -Nru a/drivers/usbd/acm_fd/trace.h b/drivers/usbd/acm_fd/trace.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/acm_fd/trace.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,268 @@ ++/* ++ * usbd/acm_fd/trace.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ * 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. ++ * ++ */ ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#ifndef CONFIG_USBD_SMDK2500_BCLOCK ++#define CONFIG_USBD_SMDK2500_BCLOCK 66 ++#endif ++#endif ++ ++/* 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 style macros were avoided because there seems to be no way to avoid ++ a run-time check on the number of arguments if they are used, and the time penalty ++ doesn't seem to be worth the gain in utility. ++ */ ++ ++typedef enum acm_trace_types { ++ acm_trace_setup_n, acm_trace_msg_n, acm_trace_msg32_n, acm_trace_msg16_n, acm_trace_msg8_n ++} acm_trace_types_t; ++ ++typedef struct acm_trace_msg { ++ char *msg; ++} acm_trace_msg_t; ++ ++typedef struct acm_trace_msg32 { ++ u32 val; ++ char *msg; ++} acm_trace_msg32_t; ++ ++typedef struct acm_trace_msg16 { ++ u16 val0; ++ u16 val1; ++ char *msg; ++} acm_trace_msg16_t; ++ ++typedef struct acm_trace_msg8 { ++ u8 val0; ++ u8 val1; ++ u8 val2; ++ u8 val3; ++ char *msg; ++} acm_trace_msg8_t; ++ ++ ++typedef struct trace { ++ acm_trace_types_t acm_trace_type; ++ char *function; ++ u32 interrupts; ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ u32 oscr; ++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++ u32 cp0_count; ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ //u32 tcnt0; ++ u32 tcnt1; ++ //u64 jiffies; ++#else ++ u64 jiffies; ++#endif ++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_ARCH_MX1ADS) ++ u64 sofs; ++#endif ++ union { ++ acm_trace_msg_t msg; ++ acm_trace_msg8_t msg8; ++ acm_trace_msg16_t msg16; ++ acm_trace_msg32_t msg32; ++ ++ struct usb_device_request setup; ++ ++ } trace; ++ ++} acm_trace_t; ++ ++#define TRACE_MAX_IS_2N 1 ++#if defined(TRACE_MAX_IS_2N) ++#define TRACE_MAX 0x00008000 ++#define TRACE_MASK 0x00007FFF ++#else ++#define TRACE_MAX 30000 ++#endif ++ ++extern int acm_trace_first; ++extern int acm_trace_last_read; ++extern int acm_trace_next; ++ ++extern acm_trace_t *acm_traces; ++ ++#ifdef CONFIG_USBD_ACM_TRACE ++ ++acm_trace_t *ACM_TRACE_NEXT(char *fn, acm_trace_types_t acm_trace_type); ++ ++#if 0 ++static __inline__ acm_trace_t *ACM_TRACE_NEXT(char *fn, acm_trace_types_t acm_trace_type) ++{ ++ acm_trace_t *p; ++ unsigned long flags; ++ ++ // Get the next trace slot - this needs to be atomic. ++ local_irq_save (flags); ++ p = acm_traces + acm_trace_next; ++#if defined(TRACE_MAX_IS_2N) ++ acm_trace_next = (acm_trace_next + 1) & TRACE_MASK; ++ if (acm_trace_next == acm_trace_first) { ++ // We have wraparound, bump acm_trace_first ++ if (acm_trace_first == acm_trace_last_read) { ++ // We have to bump last read too. ++ acm_trace_last_read = (acm_trace_last_read + 1) & TRACE_MASK; ++ } ++ acm_trace_first = (acm_trace_first + 1) & TRACE_MASK; ++ } ++#else ++ if (TRACE_MAX <= ++acm_trace_next) ++ acm_trace_next = 0; ++ ++ if (acm_trace_next == acm_trace_first) { ++ // We have wrap around, bump acm_trace_first ++ if (acm_trace_first == acm_trace_last_read) { ++ // We have to bump last read too. ++ if (TRACE_MAX <= ++acm_trace_last_read) ++ acm_trace_last_read = 0; ++ } ++ if (TRACE_MAX <= ++acm_trace_first) ++ acm_trace_first = 0; ++ } ++#endif ++ // End of next trace slot. ++ local_irq_restore (flags); ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ p->oscr = OSCR; ++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++ //p->cp0_count = __read_32bit_c0_register(CP0_COUNT); ++ p->cp0_count = read_c0_count(); ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ //p->jiffies = jiffies; ++ //p->tcnt0 = *(volatile u32 *)TCNT0; ++ p->tcnt1 = *(volatile u32 *)TCNT1; ++#else ++ p->jiffies = jiffies; ++#endif ++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) ++ //p->sofs = au_readl(USBD_FRAMENUM); ++#endif ++ ++ p->interrupts = udc_interrupts; ++ p->acm_trace_type = acm_trace_type; ++ p->function = fn; ++ ++ acm_trace_next++; ++ acm_trace_next = (acm_trace_next == TRACE_MAX) ? 0 : acm_trace_next; ++ ++ if (acm_trace_next == acm_trace_first) { ++ acm_trace_first++; ++ acm_trace_first = (acm_trace_first == TRACE_MAX) ? 0 : acm_trace_first; ++ } ++ //printk(KERN_INFO"first: %d next: %d interrupts: %d\n", acm_trace_first, acm_trace_next, udc_interrupts); ++ ++ return p; ++} ++#endif ++ ++static __inline__ void acm_trace_setup(char *fn, struct usb_device_request *setup) ++{ ++ if (acm_traces) { ++ acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_setup_n); ++ memcpy(&p->trace.setup, setup, sizeof(struct usb_device_request)); ++ } ++} ++#define TRACE_SETUP(setup) acm_trace_setup(__FUNCTION__,setup) ++ ++static __inline__ void acm_trace_msg(char *fn, char *msg) ++{ ++ if (acm_traces) { ++ acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg_n); ++ p->trace.msg.msg = msg; ++ } ++} ++#define TRACE_MSG(msg) acm_trace_msg(__FUNCTION__,msg) ++ ++ ++static __inline__ void acm_trace_msg_1xU32(char *fn, char *fmt, u32 val) ++{ ++ if (acm_traces) { ++ acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg32_n); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = fmt; ++ } ++} ++#define TRACE_MSG1(fmt,val) acm_trace_msg_1xU32(__FUNCTION__,fmt,(u32)val) ++ ++static __inline__ void acm_trace_msg_2xU16(char *fn, char *fmt, u16 val0, u16 val1) ++{ ++ if (acm_traces) { ++ acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg16_n); ++ p->trace.msg16.val0 = val0; ++ p->trace.msg16.val1 = val1; ++ p->trace.msg16.msg = fmt; ++ } ++} ++#define TRACE_MSG2(fmt,val0,val1) acm_trace_msg_2xU16(__FUNCTION__,fmt,(u16)val0,(u16)val1) ++ ++static __inline__ void acm_trace_msg_4xU8(char *fn, char *fmt, u8 val0, u8 val1, u8 val2, u8 val3) ++{ ++ if (acm_traces) { ++ acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg8_n); ++ p->trace.msg8.val0 = val0; ++ p->trace.msg8.val1 = val1; ++ p->trace.msg8.val2 = val2; ++ p->trace.msg8.val3 = val3; ++ p->trace.msg8.msg = fmt; ++ } ++} ++#define TRACE_MSG4(fmt,val0,val1,val2,val3) acm_trace_msg_4xU8(__FUNCTION__,fmt,(u8)val0,(u8)val1,(u8)val2,(u8)val3) ++ ++#else ++ ++#define TRACE_SETUP(setup) ++#define TRACE_MSG(msg) ++#define TRACE_MSG1(fmt,val) ++#define TRACE_MSG2(fmt,val0,val1) ++#define TRACE_MSG4(fmt,val0,val1,val2,val3) ++ ++#endif ++ ++int acm_trace_init (char *str); ++void acm_trace_exit (char *str); ++ +diff -Nru a/drivers/usbd/au1x00_bi/Config.in b/drivers/usbd/au1x00_bi/Config.in +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/au1x00_bi/Config.in Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,27 @@ ++ ++# USB device configuration from the device viewpoint (e.g. Linux running inside a USB device, not as host) ++# ++# Copyright (c) 2002-2003 Belcarra ++# ++ ++# ++# CONFIG_SOC_AU1X00 and/or CONFIG_MIPS_AU1X00 are the new and preferred tags (as of 2.4.20) ++# ++# CONFIG_MIPS_AU1000, CONFIG_MIPS_AU1100, CONFIG_MIPS_AU1500 are deprecated (prior to 2.4.20) ++# ++ ++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 AU1X000 Bus Interface' ++ ++ dep_tristate ' AU1X00 (AMD/Alchemy) support' CONFIG_USBD_AU1X00_BUS $CONFIG_USBD ++ if [ "$CONFIG_USBD_AU1X00_BUS" = "m" -o "$CONFIG_USBD_AU1X00_BUS" = "y" ]; then ++ int ' AU1X00 System Clock' CONFIG_USBD_AU1X00_SCLOCK 400 ++ fi ++ define_bool CONFIG_AU1000_USB_DEVICE y ++ define_bool CONFIG_AU1X00_USB_DEVICE y ++ ++ endmenu ++fi +diff -Nru a/drivers/usbd/au1x00_bi/Makefile b/drivers/usbd/au1x00_bi/Makefile +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/au1x00_bi/Makefile Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,88 @@ ++# ++# 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_bi.o ++ ++# Objects that export symbols. ++ ++export-objs := ++ ++# Multipart objects. ++ ++au1x00_bi-objs := au1x00.o usbd-bi.o trace.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_USBD_AU1X00_BUS) += au1x00_bi.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 ++USBD=$(TOPDIR)/drivers/usbd ++BI_DIR=$(USBD)/au1x00_bi ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(USBD) -I$(BI_DIR) ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(USBD) -I$(BI_DIR) ++ ++vpath %.c $(USBD) ++ ++# Link rules for multi-part drivers. ++ ++au1x00_bi.o: $(au1x00_bi-objs) ++ $(LD) -r -o $@ $(au1x00_bi-objs) ++ ++# dependencies: ++ ++# local ++ ++ +diff -Nru a/drivers/usbd/au1x00_bi/au1x00.c b/drivers/usbd/au1x00_bi/au1x00.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/au1x00_bi/au1x00.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,1268 @@ ++/* ++ * usbd/au1x00_bi/au1x00.c -- USB Device Controller driver. ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ * 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. ++ * ++ * 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. ++ */ ++ ++ ++#include <udc.h> ++#include "au1x00.h" ++ ++#include <asm/io.h> ++#include <asm/au1000.h> ++#include <asm/au1000_dma.h> ++#include <asm/mipsregs.h> ++ ++ ++#ifdef CONFIG_MIPS_FREEHAND ++#include <linux/i2c.h> ++#include <linux/sensors.h> /* for reading serial number */ ++#endif ++ ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_DESCRIPTION ("USB Device AU1x00 Bus Interface"); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++MODULE_LICENSE("GPL"); ++#endif ++USBD_MODULE_INFO ("au1x00_bi 2.0-beta"); ++const char *usbd_bi_module_info(void) ++{ return __usbd_module_info; } ++ ++#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_interrupts; ++unsigned int udc_saw_bus_activity; ++int au_halt_dma_expired; ++u32 au1x00_new_silicon; ++u32 au1x00_inten; ++static int udc_connected_status; ++ ++__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: 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",}, ++}; ++__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__ udc_epn_interrupt_enable(int epn) ++{ ++ au_inten(au1x00_inten | (1 << epn)); ++} ++ ++void __inline__ udc_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 ++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 struct urb *au_rcv_complete_irq(struct usb_endpoint_instance *endpoint, int len, int urb_bad) ++{ ++ return bi_rcv_complete_irq(endpoint, len, urb_bad); ++} ++ ++/* ********************************************************************************************* */ ++/* au_start_in_ep0 - start transmit ++ */ ++static void au_start_in_ep0 (struct usb_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct urb *urb = endpoint->tx_urb; ++ int last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ TRACE_MSG16("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 usb_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ u32 cs; ++ struct urb *tx_urb; ++ int last; ++ TRACE_MSG32("EP0 IN: tx_urb: %p", (int)endpoint->tx_urb); ++ if ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) { // clear stall if present ++ TRACE_MSG32("CLEAR STALL %d", 0); ++ cs &= ~USBDEV_CS_STALL; ++ au_writel(cs, ep->cs); ++ return; ++ } ++ if (!(tx_urb = bi_tx_complete_irq(endpoint, 0))) { // wait for setup if no more data ++ endpoint->state = WAIT_FOR_SETUP; ++ return; ++ } ++ TRACE_MSG8("EP0 IN actual: %d last: %d sent: %d flags: %x", endpoint->tx_urb->actual_length, ++ endpoint->last, endpoint->sent, endpoint->tx_urb->flags); ++ if (bi_tx_sendzlp(endpoint)) { // check if tx_urb we have is finished ++ TRACE_MSG("EP0 IN BULK - sending ZLP"); ++ tx_urb->flags &= ~USBD_URB_SENDZLP; ++ send_zlp(0); ++ bi_tx_complete_irq(endpoint, 0); ++ return; ++ } ++ if (tx_urb->actual_length > endpoint->sent) { ++ if ((tx_urb->actual_length - endpoint->sent) < endpoint->wMaxPacketSize) ++ TRACE_MSG32("EP0 IN starting short packet %d", tx_urb->actual_length - endpoint->sent); ++ else ++ TRACE_MSG32("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 usb_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usb_device_request request; ++ int i; ++ u32 cs; ++ u32 bytes; ++ TRACE_MSG("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 urb *rcv_urb = bi_rcv_next_irq(endpoint); ++ TRACE_MSG32("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; ++ } ++ bi_tx_cancelled_irq(endpoint); ++ bi_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_MSG32("ERROR SETUP SET not eight bytes: %d", bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); ++ return; ++ } ++ TRACE_MSG8("SETUP bmRequestType: %02x bRequest %02x state: %d status: %d", request.bmRequestType, ++ request.bRequest, usbd_bus->device_state, usbd_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 (usbd_bus->device_state == STATE_DEFAULT); ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_irq (usbd_bus, DEVICE_RESET, 0); ++ usbd_bus_event_irq (usbd_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 != usbd_bus->device_state); ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_irq (usbd_bus, DEVICE_RESET, 0); ++ usbd_bus_event_irq (usbd_bus, DEVICE_ADDRESS_ASSIGNED, 0); ++ break; ++ } ++ if (bi_recv_setup_irq(&request)) { ++ TRACE_MSG32("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_MSG32("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_MSG32("ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); ++ if ((request.bmRequestType & ~USB_REQ_DIRECTION_MASK)) { ++ TRACE_MSG32("ep0 Class or Vendor, send ZLP %d", cs); ++ send_zlp(0); ++ return; ++ } ++ } ++ TRACE_MSG32("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 usb_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct urb *urb = endpoint->tx_urb; ++ unsigned char *bp = urb->buffer + endpoint->sent; ++ int indma = ep->indma; ++ int last; ++ TRACE_MSG32("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_MSG16("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_MSG("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) ++ udc_epn_interrupt_disable(epn); ++ dma_cache_wback_inv((unsigned long) bp, last); ++ au_writel(last << 1, ep->cs); ++ au_start_dma_in(indma, bp, last); ++} ++ ++static void au_in_bulk(unsigned int epn, struct ep_regs *ep, struct usb_endpoint_instance *endpoint) ++{ ++ struct urb *tx_urb; ++ int rc = 0; ++ u32 cs = au_readl(ep->cs); ++ u32 wrs = au_readl(ep->wrs); ++ TRACE_MSG16("BULK IN EPN - cs: %x wrs: %x", cs, wrs); ++ if (!au1x00_new_silicon) ++ if (epn && (ep->last > 8)) { ++ TRACE_MSG16("BULK IN EPN - DMA ACTIVE epn %d last %d", epn, ep->last); ++ udc_epn_interrupt_disable(epn); ++ return; ++ } ++ if (wrs) { // check for underflow or overflow ++ rc = 1; ++ if (wrs & USBDEV_FSTAT_UF) { ++ TRACE_MSG16("BULK IN EPN - UF epn %d wrs: %x", epn, wrs); ++ rc = 1; // set rc to indicate an error ++ } ++ if (wrs & USBDEV_FSTAT_OF) ++ TRACE_MSG16("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_MSG32("BULK IN EPN - CLEAR STALL %d", epn); ++ cs &= ~USBDEV_CS_STALL; ++ au_writel(cs, ep->cs); ++ return; ++ } ++ TRACE_MSG8("BULK IN EPN epn: %d rc: %d last: %d sent: %d", epn, rc, endpoint->last, endpoint->sent); ++ if ((tx_urb = bi_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_MSG32("BULK IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ return; ++ } ++ } ++ udc_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 usb_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct urb *urb = endpoint->tx_urb; ++ unsigned char *bp = urb->buffer + endpoint->sent; ++ int last; ++ TRACE_MSG16("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_MSG16("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 ? udc_epn_interrupt_enable : udc_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 usb_endpoint_instance *endpoint) ++{ ++ struct urb *tx_urb = bi_tx_complete_irq(endpoint, 0); ++ u32 cs = au_readl(ep->cs); ++ u32 wrs = au_readl(ep->wrs); ++ TRACE_MSG16("ISO IN EPN - cs: %x wrs: %x", cs, wrs); ++ if (!au1x00_new_silicon) ++ if (epn && (ep->last > 8)) { ++ udc_epn_interrupt_disable(epn); ++ return; ++ } ++ TRACE_MSG8("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_MSG32("ISO IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ return; ++ } ++ else ++ TRACE_MSG("ISO IN EPN - nothing to send"); ++ udc_epn_interrupt_disable(epn); // disable interrupts ++} ++/* ********************************************************************************************* */ ++ ++static void au_start_out_bulk(unsigned int epn, struct usb_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ int outdma = ep->outdma; ++ TRACE_MSG16("START OUT BULK %d %d", epn, endpoint->wMaxPacketSize); ++ if (!endpoint->rcv_urb) { ++ TRACE_MSG("START OUT BULK DISABLE"); ++ udc_epn_interrupt_disable(epn); ++ return; ++ } ++ if (endpoint->rcv_error) { ++ TRACE_MSG("START OUT BULK reseting rcv_error"); ++ endpoint->rcv_error = 0; ++ } ++ au_start_dma_out(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 usb_endpoint_instance *endpoint) ++{ ++ int bytes = 0; ++ int outdma = ep->outdma; ++ struct dma_chan *chan = get_dma_chan(outdma); ++ struct urb *urb; ++ struct urb *completed_urb = NULL; ++ u32 cs; ++ u32 rds; ++ u32 nrds; ++ au_halt_dma(chan); ++ cs = au_readl(ep->cs); ++ rds = au_readl(ep->rds); ++ TRACE_MSG16("BULK OUT CS: %04x RD: %04x", cs, rds); ++ bytes = endpoint->wMaxPacketSize - au_get_dma_residue(chan); ++ if (!(urb = bi_rcv_next_irq(endpoint))) { ++ TRACE_MSG("BULK OUT EPN - no rcv_urb"); ++ udc_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_MSG16("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_MSG16("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_MSG8("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_MSG8("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_MSG8("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_MSG8("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_MSG16("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_MSG8("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_MSG16("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_MSG8("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_MSG8("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_MSG8("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_MSG32("BULK OUT < 64 BYTES %d", bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ if (!endpoint->rcv_error) { ++ TRACE_MSG("BULK OUT COMPLETED URB"); ++ au_rcv_complete_irq(endpoint, bytes, 0); ++ } ++ else { ++ TRACE_MSG("BULK OUT - FLUSHING URB - reseting rcv_error"); ++ endpoint->rcv_error = 0; ++ } ++ } ++ CATCH(start_flushing) { ++ TRACE_MSG("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_MSG("BULK OUT - RESTARTING"); ++ au_start_out_bulk(epn, endpoint, ep); ++} ++ ++static void au_start_out_iso(unsigned int epn, struct usb_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 usb_endpoint_instance *endpoint) ++{ ++ int bytes = 0; ++ int outdma = ep->outdma; ++ struct dma_chan *chan = get_dma_chan(outdma); ++ struct urb *urb; ++ u32 cs; ++ u32 rds; ++ au_halt_dma(chan); ++ 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_MSG16("ISO OUT EPN - rcv_urb was NULL bytes: %d rds: %d", bytes, rds); ++ au_rcv_complete_irq(endpoint, bytes, 1); ++ TRACE_MSG("ISO OUT setting rcv_error"); ++ } ++ bytes = endpoint->wMaxPacketSize - au_get_dma_residue(chan); ++ 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); ++} ++/* ********************************************************************************************* */ ++/* udc_tx_dma_done - TX DMA interrupt handler ++ */ ++static void udc_tx_dma_done(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ int epn = (int) dev_id; ++ struct usb_endpoint_instance *endpoint = usbd_bus->endpoint_array + epn; ++ struct ep_regs *ep = &ep_regs[epn]; ++ int residue; ++ int indma = irq - 6; ++ struct dma_chan *chan = get_dma_chan(indma); ++ u32 mode = au_readl(chan->io + DMA_MODE_READ); ++ udc_interrupts++; ++ TRACE_MSG32("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); ++ RETURN_IF (!epn); ++ au_halt_dma(chan); ++ residue = au_get_dma_residue(chan); ++ TRACE_MSG16("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); ++ bi_tx_complete_irq (endpoint, residue?1:0); ++ udc_epn_interrupt_enable(epl2p[endpoint->bEndpointAddress&0xf]); ++ } ++ ep->last = 0; ++} ++ ++/* udc_int_req - usb interrupt handler ++ */ ++static void udc_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 ++ udc_interrupts++; ++#if 0 ++ if (udc_interrupts > 2000) { ++ TRACE_MSG("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_MSG16("INTSTAT: %04x %04x", intstat, au_readl(USBD_INTEN)); ++ while (local_intstat & 0x3f) { ++ int epn = 31 - au_clz(local_intstat); ++ local_intstat &= ~(1<<epn); ++#endif ++ if (udc_saw_bus_activity) { ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_irq (usbd_bus, DEVICE_BUS_ACTIVITY, 0); ++ } ++ ep = &ep_regs[epn]; ++ switch(ep->eptype) { ++ case USB_DIR_IN | USB_ENDPOINT_BULK: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: ++ //TRACE_MSG16("BULK IN %d %02x", epn, ep->eptype); ++ au_in_bulk(epn, ep, usbd_bus->endpoint_array + epn); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ //TRACE_MSG16("ISO IN %d %02x", epn, ep->eptype); ++ au_in_iso(epn, ep, usbd_bus->endpoint_array + epn); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_BULK: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: ++ //TRACE_MSG16("BULK OUT %d %02x", epn, ep->eptype); ++ au_out_bulk(epn, ep, usbd_bus->endpoint_array + epn); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ //TRACE_MSG16("ISO OUT %d %04d", epn, au_readl(NUSBD_FRAMENUM)); ++ au_out_iso(epn, ep, usbd_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(usbd_bus->endpoint_array + 0, &ep_regs[0]); ++ if (intstat & (1 << 1)) ++ au_in_ep0(usbd_bus->endpoint_array + 0, &ep_regs[0]); ++ if (intstat & USBDEV_INT_SOF) ++ if (USBD_SUSPENDED == usbd_bus->status) { ++ TRACE_MSG("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_MSG32("USB IRQ - %d", read_c0_count(CP0_COUNT) - cp0_count); ++#endif ++#if defined(RECORD_LATENCY) ++ cp0_record = read_c0_count(CP0_COUNT); ++#endif ++} ++ ++/* udc_int_sus -suspend interrupt handler ++ */ ++static void udc_int_sus (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ udc_interrupts++; ++ TRACE_MSG16("SUS - INACTIVE device: %d status: %d", usbd_bus->device_state, usbd_bus->status); ++ switch(usbd_bus->status) { ++ case USBD_OPENING: ++ case USBD_OK: ++ usbd_bus_event_irq (usbd_bus, DEVICE_BUS_INACTIVE, 0); ++ break; ++ default: ++ break; ++ } ++} ++/* ********************************************************************************************* */ ++/* udc_start_endpoint_in - start transmit ++ */ ++void udc_start_endpoint_in(struct usb_endpoint_instance *endpoint) ++{ ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ TRACE_MSG16("UDC START IN %02x %d", endpoint->bEndpointAddress, epn); ++ TRACE_MSG16("UDC START IN len: %d flags: %x", (unsigned int)endpoint->tx_urb->actual_length, endpoint->tx_urb->flags); ++ switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { ++ case USB_ENDPOINT_CONTROL: ++ TRACE_MSG32("UDC START IN EP0 %p", (u32)endpoint->rcv_urb); ++ au_in_ep0(endpoint, ep); ++ break; ++ case USB_ENDPOINT_BULK: ++ case USB_ENDPOINT_INTERRUPT: ++ TRACE_MSG32("UDC START IN BULK %p", (u32)endpoint->rcv_urb); ++ au_start_in_bulk(epn, endpoint, ep); ++ break; ++ case USB_ENDPOINT_ISOCHRONOUS: ++ TRACE_MSG32("UDC START IN ISO %p", (u32)endpoint->rcv_urb); ++ au_start_in_iso(epn, endpoint, ep); ++ break; ++ } ++ if (au1x00_new_silicon) ++ udc_epn_interrupt_enable(epn); ++} ++ ++/* udc_start_endpoint_out - start receive ++ */ ++void udc_start_endpoint_out(struct usb_endpoint_instance *endpoint) ++{ ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ TRACE_MSG16("UDC START OUT %02x %d", endpoint->bEndpointAddress, epn); ++ TRACE_MSG32("UDC START OUT len: %d", endpoint->rcv_urb->buffer_length); ++ switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { ++ case USB_ENDPOINT_CONTROL: ++ TRACE_MSG32("UDC START OUT EP0 %p", (u32)endpoint->rcv_urb); ++ break; ++ case USB_ENDPOINT_BULK: ++ case USB_ENDPOINT_INTERRUPT: ++ TRACE_MSG32("UDC START OUT BULK %p", (u32)endpoint->rcv_urb); ++ au_start_out_bulk(epn, endpoint, ep); ++ udc_epn_interrupt_enable(epn); ++ break; ++ case USB_ENDPOINT_ISOCHRONOUS: ++ TRACE_MSG32("UDC START OUT ISO %p", (u32)endpoint->rcv_urb); ++ au_start_out_iso(epn, endpoint, ep); ++ break; ++ } ++} ++ ++void udc_cancel_in_irq(struct urb *urb) ++{ ++ int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ TRACE_MSG("CANCEL IN URB"); ++ au_in_bulk(epn, ep, urb->endpoint); ++} ++ ++void udc_cancel_out_irq(struct urb *urb) ++{ ++ int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ TRACE_MSG("CANCEL OUT URB"); ++ if (epn) ++ au_out_bulk(epn, ep, urb->endpoint); ++} ++ ++/* udc_init - initialize ++ */ ++int udc_init (void) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) ++#undef read_c0_prid ++#define read_c0_prid() read_32bit_cp0_register(CP0_PRID) ++#endif ++ u32 cp0_prid = read_c0_prid(); ++ 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; ++ default: ++ printk(KERN_INFO"%s: UNKNOWN CPU cp0_prid: %08x UNKNOWN UDC\n", __FUNCTION__, cp0_prid); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* udc_serial_init - set a serial number if available ++ */ ++int udc_serial_init (void) ++{ ++#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 udc_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); ++ usbd_bus->serial_number_str = lstrdup(chData); ++ return 0; ++#else ++ return -EINVAL; ++#endif ++} ++ ++/* udc_setup_ep - setup endpoint ++ */ ++void udc_setup_ep (unsigned int epn, struct usb_endpoint_instance *endpoint) ++{ ++ RETURN_IF (epn); ++ endpoint->state = WAIT_FOR_SETUP; ++} ++ ++/* udc_attached - is the USB cable connected ++ * Return non-zero if cable is connected. ++ * ++ * udc_connect - enable pullup resistor ++ * Turn on the USB connection by enabling the pullup resistor. ++ * ++ * udc_disconnect - disable pullup resistor ++ * Turn off the USB connection by disabling the pullup resistor. ++ */ ++ ++#if defined(CONFIG_MIPS_PICOENGINE_MVCI) ++int udc_attached (void) ++{ ++ return 1; ++} ++ ++int udc_connected(void) ++{ ++ return udc_connected_status; ++} ++ ++void udc_connect (void) ++{ ++ extern void pico_mvci_set_usb_pullup(int); ++ pico_mvci_set_usb_pullup(1); ++ udc_connected_status = 1; ++} ++ ++void udc_disconnect (void) ++{ ++ extern void pico_mvci_set_usb_pullup(int); ++ pico_mvci_set_usb_pullup(0); ++ udc_connected_status = 0; ++} ++ ++#elif defined(CONFIG_MIPS_FREEHAND_NATIVE) ++/* have to do this because version A boards don't have pullups ++ * XXX checkme ++*/ ++int udc_attached (void) ++{ ++ return 1; ++} ++ ++int udc_connected(void) ++{ ++ return udc_connected_status; ++} ++ ++void udc_connect (void) ++{ ++ au1000gpio_set(GPIO01); ++ udc_connected_status = 1; ++} ++ ++void udc_disconnect (void) ++{ ++ au1000gpio_clear(GPIO01); ++ udc_connected_status = 0; ++} ++ ++#endif ++ ++/* udc_framenum - get current framenum ++ */ ++int udc_framenum (void) ++{ ++ return au_readl(NUSBD_FRAMENUM); ++} ++ ++/* udc_all_interrupts - enable interrupts ++ */ ++void udc_all_interrupts (void) ++{ ++ au_inten(0x0033|USBDEV_INT_SOF); // Only enable receive interrupts. ++} ++ ++/* udc_suspended_interrupts - enable suspended interrupts ++ */ ++void udc_suspended_interrupts (void) ++{ ++ au_inten(0x0033|USBDEV_INT_SOF); ++} ++ ++/* udc_disable_interrupts - disable interrupts. ++ */ ++void udc_disable_interrupts (void) ++{ ++ au_inten(0); ++} ++ ++/* udc_disable - disable the UDC ++ */ ++void udc_disable (void) ++{ ++ au_writel(0x0000, USBD_ENABLE); ++} ++ ++/* udc_release_udc_irq - release UDC irq ++ */ ++void udc_release_udc_irq (void) ++{ ++ 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 ++} ++ ++/* udc_request_udc_irq - request UDC interrupt ++ */ ++int udc_request_udc_irq (void) ++{ ++ RETURN_EINVAL_IF (request_irq (AU1000_USB_DEV_REQ_INT, udc_int_req, SA_INTERRUPT, UDC_NAME "UDC Req", NULL)); ++ if (request_irq (AU1000_USB_DEV_SUS_INT, udc_int_sus, SA_INTERRUPT, UDC_NAME "UDC Sus", NULL) != 0) { ++ udc_release_udc_irq(); ++ free_irq (AU1000_USB_DEV_REQ_INT, NULL); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int request_dma(int ep, int id, char *str, void dma_done(int , void *, struct pt_regs *)) ++{ ++ int dma; ++ if ((dma = request_au1000_dma(id, str, dma_done, SA_INTERRUPT | SA_SAMPLE_RANDOM, (void *)ep)) < 0) { ++ printk(KERN_INFO"request_io[%d] dma: %d id: %x %s FAILED\n", ep, dma, id, str); ++ return -1; ++ } ++ return dma; ++} ++ ++/* udc_request_io - request IO region ++ */ ++int udc_request_io (void) ++{ ++ int i; ++ 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, udc_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; ++ } ++ return 0; ++} ++ ++/* udc_release_io - release IO region ++ */ ++void udc_release_io (void) ++{ ++ int j; ++ for (j = 0; j < 6; j++) { ++ struct ep_regs *ep = &ep_regs[j]; ++ if (ep->indma != -1) { ++ free_au1000_dma(ep->indma); ++ ep->indma = -1; ++ } ++ if (ep->outdma != -1) { ++ free_au1000_dma(ep->outdma); ++ ep->outdma = -1; ++ } ++ } ++} ++ ++int udc_assign_endpoint( __u8 physicalEndpoint, int used[6], struct usb_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 udc_request_endpoints(struct usb_endpoint_map *endpoint_map_array, int endpointsRequested, ++ struct usb_endpoint_request *requestedEndpoints) ++{ ++ struct usb_device_description *device_description; ++ int i; ++ int used[6]; ++ memset(used, 0, sizeof(used)); ++ for (i = 0; i < endpointsRequested; i++) { ++ struct usb_endpoint_map *endpoint_map = endpoint_map_array + i; ++ u8 bmAttributes = requestedEndpoints[i].bmAttributes; ++ u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; ++ endpoint_map->bmAttributes[0] = bmAttributes; ++ endpoint_map->wMaxPacketSize[0] = 0x40; ++ 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(!udc_assign_endpoint(4, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ CONTINUE_IF(!udc_assign_endpoint(5, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ CONTINUE_IF(!udc_assign_endpoint(4, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ CONTINUE_IF(!udc_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(!udc_assign_endpoint(2, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ CONTINUE_IF(!udc_assign_endpoint(3, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ CONTINUE_IF(!udc_assign_endpoint(2, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ CONTINUE_IF(!udc_assign_endpoint(3, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ break; ++ } ++ CONTINUE_IF(bmAttributes & USB_ENDPOINT_OPT); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int udc_set_endpoints(int endpointsRequested, struct usb_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 usb_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; ++} ++/* ********************************************************************************************* */ ++struct udc_ops udc_ops = { ++ max_endpoints: UDC_MAX_ENDPOINTS, ++ ep0_packetsize: EP0_PACKETSIZE, ++ name: UDC_NAME, ++ start_endpoint_in: udc_start_endpoint_in, ++ start_endpoint_out: udc_start_endpoint_out, ++ request_endpoints: udc_request_endpoints, ++ set_endpoints: udc_set_endpoints, ++ cancel_in_irq: udc_cancel_in_irq, ++ cancel_out_irq: udc_cancel_out_irq, ++ setup_ep: udc_setup_ep, ++#if defined(CONFIG_MIPS_PICOENGINE_MVCI) ||defined(CONFIG_MIPS_FREEHAND_NATIVE) ++ attached: udc_attached, ++ connected: udc_connected, ++ connect: udc_connect, ++ disconnect: udc_disconnect, ++#endif ++ framenum: udc_framenum, ++ all_interrupts: udc_all_interrupts, ++ suspended_interrupts: udc_suspended_interrupts, ++ disable_interrupts: udc_disable_interrupts, ++ disable: udc_disable, ++ init: udc_init, ++ request_udc_irq: udc_request_udc_irq, ++ release_udc_irq: udc_release_udc_irq, ++ release_io: udc_release_io, ++ serial_init: udc_serial_init, ++ request_io: udc_request_io, ++}; ++ +diff -Nru a/drivers/usbd/au1x00_bi/au1x00.h b/drivers/usbd/au1x00_bi/au1x00.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/au1x00_bi/au1x00.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,112 @@ ++/* ++ * usbd/au1x00_bi/au1100.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++/* ++ * 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; ++ int indma; ++ int 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_REV_MASK 0x000000ff ++ +diff -Nru a/drivers/usbd/ep0.c b/drivers/usbd/ep0.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/ep0.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,565 @@ ++/* ++ * usbd/ep0.c ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@lineo.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/* ++ * This function driver implements support for all of the USB 2.0 Chapter ++ * nine requests. ++ * ++ * Any request that is not required by Chapter nine is passed to the other ++ * function drivers recv_setup routine. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++//EXPORT_NO_SYMBOLS; ++ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++ ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++ ++#include "usbd-chap9.h" ++#include "usbd-mem.h" ++#include "usbd.h" ++#include "usbd-func.h" ++#include "usbd-bus.h" // for usbd_recv_setup_irq() definition ++#include "usbd-admin.h" ++ ++struct usb_function_instance * ep0_function; ++ ++/* C.f. 9.4 Standard Requests and table 9.3 ++ * ++ * Encode valid requests into a bitmap for each recipient type for ++ * both directions. ++ * ++ */ ++ ++#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_GET_CONFIGURATION) | ++ 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) , ++ // 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, ++}; ++ ++ ++/* Endpoint ZEro Configuration *************************************************************** */ ++ ++/* ep0_event_irq - respond to USB event ++ * ++ * Process USB events. ++ */ ++static void ep0_event_irq (struct usb_function_instance *function, usb_device_event_t event, int dummy ) ++{ ++ switch (event) { ++ case DEVICE_CREATE: ++ default: ++ break; ++ } ++} ++ ++/* copy_config - copy data into urb 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_IF (!length); ++ memcpy (cp, data, length); ++ return length; ++} ++ ++/* copy_config - copy data into urb buffer ++ */ ++static int copy_endpoint (struct usb_function_instance *function, u8 *cp, ++ struct usb_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 usb_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_get_descriptor - copy descriptor into urb buffer ++ * ++ * Return non-zero for error. ++ */ ++int usbd_get_descriptor (struct usb_bus_instance *bus, u8 *buffer, int max, int descriptor_type, int index) ++{ ++ struct usb_function_driver *function_driver = bus->function_instance->function_driver; ++ int actual_length = 0; ++ ++ switch (descriptor_type) { ++ case USB_DESCRIPTOR_TYPE_DEVICE: ++ { ++ struct usb_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 usb_device_descriptor *) buffer; ++ device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize; ++ } ++ break; ++ ++#ifdef CONFIG_USBD_HIGH_SPEED ++ case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: // c.f. 9.6.2 Device Qualifier ++ { ++ struct usb_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_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION: ++ case USB_DESCRIPTOR_TYPE_CONFIGURATION: ++ { ++ int hs = bus->HighSpeedFlag ? descriptor_type == USB_DESCRIPTOR_TYPE_CONFIGURATION: ++ descriptor_type == USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION; ++#else ++ case USB_DESCRIPTOR_TYPE_CONFIGURATION: ++ { ++ int hs = 0; ++#endif ++ int interface; ++ ++ struct usb_configuration_instance *configuration_instance = ++ &function_driver->configuration_instance_array[index]; ++ ++ struct usb_configuration_descriptor *configuration_descriptor = ++ configuration_instance->configuration_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 bNumInterfaces for specified configuration ++ for (interface = 0; interface < configuration_descriptor->bNumInterfaces; interface++) { ++ ++ int alternate; ++ struct usb_interface_instance *interface_instance = ++ configuration_instance->interface_instance_array + interface; ++ ++ // iterate across interface alternates ++ for (alternate = 0; alternate < interface_instance->alternates; alternate++) { ++ ++ int class; ++ int endpoint; ++ ++ struct usb_alternate_instance *alternate_instance = ++ interface_instance->alternates_instance_array + alternate; ++ ++ //struct usb_interface_descriptor *usb_interface_descriptor; ++ ++ // copy descriptor for this interface ++ actual_length += copy_config (buffer + actual_length, ++ alternate_instance->interface_descriptor, actual_length, max); ++ ++ // iterate across classes for this alternate interface ++ for (class = 0; class < alternate_instance->classes; class++) ++ actual_length += copy_config (buffer + actual_length, ++ *(alternate_instance->class_list + class), actual_length, max); ++ ++ // iterate across endpoints for this alternate interface ++ //interface_descriptor = alternate_instance->interface_descriptor; ++ ++ for (endpoint = 0; endpoint < alternate_instance->endpoints ; endpoint++) { ++ ++ //printk(KERN_INFO"%s: endpoint: %d index: %d\n", ++ // __FUNCTION__, 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); ++ } ++ } ++ } ++ } ++ break; ++ ++ case USB_DESCRIPTOR_TYPE_STRING: ++ { ++ struct usb_string_descriptor *string_descriptor; ++ RETURN_EINVAL_IF (!(string_descriptor = usbd_get_string (index))); ++ actual_length += copy_config (buffer + actual_length, string_descriptor, actual_length, max); ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return actual_length; ++} ++ ++/* ep0_recv_setup_irq - process a device request ++ * ++ * Process a received device request. If not a Chapter nine request pass it ++ * to the other loaded function driver recv_setup_irq() function. ++ * ++ * Return non-zero to indicate failure. ++ */ ++static int ep0_recv_setup_irq (struct usb_device_request *request) ++{ ++ struct usb_function_instance *function = ep0_function; ++ struct usb_bus_instance *bus = ep0_function->bus; ++ ++#if 0 ++ printk(KERN_INFO"%s: bus: %p bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %d\n", __FUNCTION__, ++ function->bus, request->bmRequestType, request->bRequest, ++ le16_to_cpu(request->wValue), le16_to_cpu(request->wIndex), le16_to_cpu(request->wLength), ++ request->bRequest); ++#endif ++ ++ // handle USB Standard Request only (c.f. USB Spec table 9-2, D6..5 must be 0) ++ ++ THROW_IF ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0, non_standard); ++ ++ THROW_IF(!( (request->bmRequestType & USB_DIR_IN ? d2h_standard_requests : h2d_standard_requests) ++ [request->bmRequestType & 0x3] & STD(request->bRequest)), non_standard ); ++ ++ CATCH(non_standard) { ++ return usbd_recv_setup_irq(bus->function_instance, request); ++ } ++ ++ switch (bus->device_state) { ++ case STATE_CREATED: ++ case STATE_ATTACHED: ++ case STATE_POWERED: ++ return -EINVAL; ++ ++ case STATE_INIT: ++ case STATE_DEFAULT: ++ switch (request->bRequest) { ++ case USB_REQ_GET_STATUS: ++ case USB_REQ_GET_INTERFACE: ++ case USB_REQ_SYNCH_FRAME: ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ case USB_REQ_SET_DESCRIPTOR: ++ case USB_REQ_SET_INTERFACE: ++ printk(KERN_INFO"%s: bad device_state\n", __FUNCTION__); ++ return -EINVAL; ++ ++ case USB_REQ_SET_CONFIGURATION: ++ case USB_REQ_SET_ADDRESS: ++ case USB_REQ_GET_DESCRIPTOR: ++ case USB_REQ_GET_CONFIGURATION: ++ break; ++ } ++ case STATE_ADDRESSED: ++ case STATE_CONFIGURED: ++ case STATE_SUSPENDED: ++ break; ++ case STATE_UNKNOWN: ++ printk(KERN_INFO"%s: suspended or unknown\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ // handle all requests that return data (direction bit set on bm RequestType) ++ if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { ++ ++ struct urb *urb; ++ int rc = 0; ++ switch (request->bRequest) { ++ case USB_REQ_SYNCH_FRAME: ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ case USB_REQ_SET_ADDRESS: ++ case USB_REQ_SET_DESCRIPTOR: ++ case USB_REQ_SET_CONFIGURATION: ++ case USB_REQ_SET_INTERFACE: ++ printk(KERN_INFO"%s: bad direction\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ RETURN_EINVAL_IF(!le16_to_cpu(request->wLength)); ++ ++ // allocate urb, no callback, urb will be automatically de-allocated ++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb (function, 0, le16_to_cpu(request->wLength), NULL))); ++ ++ switch (request->bRequest) { ++ ++ case USB_REQ_GET_STATUS: ++ urb->actual_length = 2; ++ urb->buffer[0] = urb->buffer[1] = 0; ++ switch (request->bmRequestType & USB_REQ_RECIPIENT_MASK) { ++ case USB_REQ_RECIPIENT_DEVICE: ++ urb->buffer[0] = USB_STATUS_SELFPOWERED; ++ break; ++ case USB_REQ_RECIPIENT_INTERFACE: ++ break; ++ case USB_REQ_RECIPIENT_ENDPOINT: ++ urb->buffer[0] = usbd_endpoint_halted (function, le16_to_cpu(request->wIndex)); ++ break; ++ case USB_REQ_RECIPIENT_OTHER: ++ urb->actual_length = 0; ++ default: ++ break; ++ } ++ rc = 0; ++ ++ break; ++ ++ case USB_REQ_GET_DESCRIPTOR: ++ rc = usbd_get_descriptor (bus, urb->buffer, ++ le16_to_cpu (request->wLength), ++ le16_to_cpu (request->wValue >> 8), ++ le16_to_cpu (request->wValue) & 0xff); ++ if (rc != -EINVAL) { ++ urb->actual_length = rc; ++ rc = 0; ++ } ++ break; ++ ++ case USB_REQ_GET_CONFIGURATION: ++ urb->actual_length = 1; ++ urb->buffer[0] = bus->ConfigurationValue; ++ break; ++ ++ case USB_REQ_GET_INTERFACE: ++ RETURN_EINVAL_IF(le16_to_cpu(request->wIndex) > bus->bNumInterfaces); ++ urb->actual_length = 1; ++ urb->buffer[0] = bus->alternates[le16_to_cpu(request->wIndex)]; ++ break; ++ default: ++ rc = 1; ++ } ++ ++ //printk(KERN_INFO"%s: actual: %d packetsize: %d wIndex: %d rc: %d\n", __FUNCTION__, ++ // urb->actual_length, urb->bus->driver->maxpacketsize, le16_to_cpu(request->wLength), rc); ++ ++ if (!(urb->actual_length % urb->bus->driver->maxpacketsize) && ++ (urb->actual_length < le16_to_cpu(request->wLength))) ++ { ++ //printk(KERN_INFO"%s: NEED ZLP\n", __FUNCTION__); ++ urb->flags |= USBD_URB_SENDZLP; ++ } ++ if (!rc) ++ RETURN_ZERO_IF(!usbd_send_urb(urb)); ++ //printk(KERN_INFO"%s: failed urb: %p\n", __FUNCTION__, urb); ++ usbd_dealloc_urb(urb); ++ return -EINVAL; ++ } ++ // handle the requests that do not return data ++ else { ++ ++ switch (request->bRequest) { ++ ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ switch (request->bmRequestType & USB_REQ_RECIPIENT_MASK) { ++ case USB_REQ_RECIPIENT_DEVICE: ++ // XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here ++ // XXX fall through for now as we do not support either ++ case USB_REQ_RECIPIENT_INTERFACE: ++ case USB_REQ_RECIPIENT_OTHER: ++ default: ++ return -EINVAL; ++ ++ case USB_REQ_RECIPIENT_ENDPOINT: ++ if (le16_to_cpu(request->wValue) == USB_ENDPOINT_HALT) ++ return usbd_device_feature (function, le16_to_cpu (request->wIndex) & 0x7f, ++ request->bRequest == USB_REQ_SET_FEATURE); ++ ++ else ++ return -EINVAL ; ++ } ++ ++ case USB_REQ_SET_ADDRESS: ++ // check if this is a re-address, reset first if it is (this shouldn't be possible) ++ RETURN_EINVAL_IF (bus->device_state != STATE_DEFAULT); ++ usbd_bus_event (bus, DEVICE_ADDRESS_ASSIGNED, le16_to_cpu(request->wValue)); ++ return 0; ++ ++ case USB_REQ_SET_DESCRIPTOR: ++ // XXX should we support this? ++ // This would require allocating a rcv urb and using usbd_start_recv() ++ return -EINVAL; ++ ++ case USB_REQ_SET_CONFIGURATION: ++ { ++ struct usb_function_driver *function_driver = bus->function_instance->function_driver; ++ int bNumConfigurations = function_driver->bNumConfigurations; ++ int bNumInterfaces; ++ u8 ConfigurationValue; ++ ++ ++ struct usb_configuration_instance *configuration_instance; ++ struct usb_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 = le16_to_cpu (request->wValue) & 0x7f; ++ ++ RETURN_EINVAL_IF(ConfigurationValue > bNumConfigurations); ++ ++ 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; ++ ++ bus->ConfigurationValue = ConfigurationValue + 1; ++ ++ // reset interface and alternate settings ++ ++ RETURN_EINVAL_IF (!(bus->alternates = ckmalloc(bNumInterfaces, GFP_ATOMIC))); ++ bus->bNumInterfaces = bNumInterfaces; ++ ++ //usbd_bus->device_event (bus, DEVICE_CONFIGURED, 0); ++ usbd_bus_event (bus, DEVICE_CONFIGURED, 0); ++ return 0; ++ } ++ ++ case USB_REQ_SET_INTERFACE: ++ { ++ int interface = le16_to_cpu(request->wIndex); ++ ++ RETURN_EINVAL_IF(interface > bus->bNumInterfaces); ++ ++ bus->alternates[interface] = le16_to_cpu(request->wValue); ++ usbd_bus_event (bus, DEVICE_SET_INTERFACE, 0); ++ return 0; ++ } ++ ++ case USB_REQ_GET_STATUS: ++ case USB_REQ_GET_DESCRIPTOR: ++ case USB_REQ_GET_CONFIGURATION: ++ case USB_REQ_GET_INTERFACE: ++ case USB_REQ_SYNCH_FRAME: // XXX should never see this (?) ++ printk(KERN_INFO"%s: unknown\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ } ++ return -EINVAL; ++} ++ ++ ++/* ep0_function_enable - enable the endpoint zero function ++ * ++ * Return non-zero on failure. ++ */ ++static int ep0_function_enable (struct usb_function_instance *function) ++{ ++ ep0_function = function; ++ return 0; ++} ++ ++/* ep0_function_disable - disable the endpoint zero function ++ */ ++static void ep0_function_disable (struct usb_function_instance *function) ++{ ++ ep0_function = NULL; ++} ++ ++ ++static struct usb_function_operations ep0_ops = { ++ event_irq: ep0_event_irq, ++ recv_setup_irq: ep0_recv_setup_irq, ++ function_enable: ep0_function_enable, ++ function_disable: ep0_function_disable, ++}; ++ ++struct usb_function_driver ep0_driver = { ++ name: "EP0", ++ fops: &ep0_ops, ++}; ++ ++EXPORT_SYMBOL(usbd_get_descriptor); +diff -Nru a/drivers/usbd/mouse_fd/Config.in b/drivers/usbd/mouse_fd/Config.in +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/mouse_fd/Config.in Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,26 @@ ++# ++# Mouse Function Driver ++# ++# Copyright (C) 2003 Belcarra ++# ++ ++ ++mainmenu_option next_comment ++ ++comment "Random Mouse Function" ++dep_tristate ' Mouse Function' CONFIG_USBD_MOUSE $CONFIG_USBD ++ ++if [ "$CONFIG_USBD_MOUSE" = "y" -o "$CONFIG_USBD_MOUSE" = "m" ]; then ++ hex 'VendorID (hex value)' CONFIG_USBD_MOUSE_VENDORID "12b9" ++ hex 'ProductID (hex value)' CONFIG_USBD_MOUSE_PRODUCTID "f003" ++ hex 'bcdDevice (binary-coded decimal)' CONFIG_USBD_MOUSE_BCDDEVICE "0100" ++ ++ string 'iManufacturer (string)' CONFIG_USBD_MOUSE_MANUFACTURER "Belcarra" ++ #string 'iProduct (string)' CONFIG_USBD_MOUSE_PRODUCT_NAME "Belcarra Mouse" ++ ++ string 'iConfiguration (string)' CONFIG_USBD_MOUSE_DESC "Acm Cfg" ++ string 'Comm Interface iInterface (string)' CONFIG_USBD_MOUSE_COMM_INTF "Comm Intf" ++ bool 'Mouse BH Test' CONFIG_USBD_MOUSE_BH ++ ++fi ++endmenu +diff -Nru a/drivers/usbd/mouse_fd/Makefile b/drivers/usbd/mouse_fd/Makefile +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/mouse_fd/Makefile Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,66 @@ ++# ++# Function driver for a Mouse USB Device ++# ++# Copyright (c) 2003 Belcarra ++ ++# Multipart objects. ++ ++O_TARGET := mouse_fd.o ++list-multi := mouse_fd.o ++ ++mouse_fd-objs := mouse.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_USBD_MOUSE) += mouse_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. ++ ++USBD=$(TOPDIR)/drivers/usbd ++MOUSED=$(USBD)/mouse_fd ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -I$(MOUSED) -I$(USBD) -Wno-unused -Wno-format ++EXTRA_CFLAGS_nostdinc += -I$(MOUSED) -I$(USBD) -Wno-unused -Wno-format ++ ++# Link rules for multi-part drivers. ++ ++mouse_fd.o: $(mouse_fd-objs) ++ $(LD) -r -o $@ $(mouse_fd-objs) ++ ++# dependencies: ++ ++mouse.o: $(USBD)/usbd.h $(USBD)/usbd-bus.h $(USBD)/usbd-func.h ++ ++ +diff -Nru a/drivers/usbd/mouse_fd/getmouse.c b/drivers/usbd/mouse_fd/getmouse.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/mouse_fd/getmouse.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,122 @@ ++/******************************************************************************* ++ * File: FILE ++ * Module: MODULE ++ * Author: AUTHOR ++ * Date: DATE ++ * ++ * Notes: ++ * ++ * $Id$ ++ * ++ * History: ++ * $Log$ ++ * ++ ******************************************************************************/ ++ ++/* ++ * ++ * ++ * 3.2. Non-Canonical Input Processing ++ * ++ * In non-canonical input processing mode, input is not assembled into lines and ++ * input processing (erase, kill, delete, etc.) does not occur. Two parameters ++ * control the behavior of this mode: c_cc[VTIME] sets the character timer, and ++ * c_cc[VMIN] sets the minimum number of characters to receive before satisfying ++ * the read. ++ * ++ * If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before ++ * the read is satisfied. As TIME is zero, the timer is not used. ++ * ++ * If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be ++ * satisfied if a single character is read, or TIME is exceeded (t = TIME *0.1 ++ * s). If TIME is exceeded, no character will be returned. ++ * ++ * If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read ++ * will be satisfied if MIN characters are received, or the time between two ++ * characters exceeds TIME. The timer is restarted every time a character is ++ * received and only becomes active after the first character has been received. ++ * ++ * If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of ++ * characters currently available, or the number of characters requested will be ++ * returned. According to Antonino (see contributions), you could issue a fcntl ++ * (fd, F_SETFL, FNDELAY); before reading to get the same result. ++ * ++ * By modifying newtio.c_cc[VTIME] and newtio.c_cc[VMIN] all modes described ++ * above can be tested. ++ * ++ */ ++ ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <fcntl.h> ++#include <termios.h> ++#include <stdio.h> ++ ++#define BAUDRATE B1200 ++#define MODEMDEVICE "/dev/ttyS0" ++#define _POSIX_SOURCE 1 /* POSIX compliant source */ ++#define FALSE 0 ++#define TRUE 1 ++ ++volatile int STOP=FALSE; ++ ++main() ++{ ++ int fd,c, res; ++ struct termios oldtio,newtio; ++ unsigned char buf[255]; ++ ++ int bytes; ++ unsigned char mouse[3]; ++ ++ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); ++ if (fd <0) {perror(MODEMDEVICE); exit(-1); } ++ ++ tcgetattr(fd,&oldtio); /* save current port settings */ ++ ++ bzero(&newtio, sizeof(newtio)); ++ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; ++ newtio.c_iflag = IGNPAR; ++ newtio.c_oflag = 0; ++ ++ /* set input mode (non-canonical, no echo,...) */ ++ newtio.c_lflag = 0; ++ ++ newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ ++ newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */ ++ ++ tcflush(fd, TCIFLUSH); ++ tcsetattr(fd,TCSANOW,&newtio); ++ ++ ++ bytes = 0; ++ while (STOP==FALSE) { /* loop for input */ ++ unsigned char c; ++ ++ res = read(fd,buf,1); /* returns after 5 chars have been input */ ++ buf[res]=0; /* so we can printf... */ ++ //fprintf(stderr, ":%02x:%d\n", buf[0], res); ++ //if (buf[0]=='z') STOP=TRUE; ++ ++ c = buf[0]; ++ ++ if ( c & 0x40 ) { ++ bytes = 1; ++ mouse[0] = c; ++ } ++ else if (bytes == 1) { ++ bytes = 2; ++ mouse[1] = c; ++ } ++ else if (bytes == 2) { ++ bytes = 0; ++ mouse[2] = c; ++ fprintf(stderr, "%02x %02x %02x\n", mouse[0], mouse[1], mouse[2]); ++ //printf("%c%c%c", mouse[0], mouse[1], mouse[2]); ++ } ++ } ++ tcsetattr(fd,TCSANOW,&oldtio); ++} ++ ++ ++/* End of FILE */ +diff -Nru a/drivers/usbd/mouse_fd/mouse.c b/drivers/usbd/mouse_fd/mouse.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/mouse_fd/mouse.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,548 @@ ++/* ++ * usbd/mouse_fd/mouse.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This is a 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_USBD_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_recv_setup_irq() 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. ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_LICENSE("PROPRIETARY"); ++MODULE_DESCRIPTION ("Belcarra Random Walk MOUSE Function"); ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++ ++#include "usbd-chap9.h" ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++USBD_MODULE_INFO ("mouse_fd 2.0-beta"); ++ ++/* Module Parameters ************************************************************************* */ ++ ++static u32 vendor_id; ++static u32 product_id; ++ ++MODULE_PARM (vendor_id, "i"); ++MODULE_PARM (product_id, "i"); ++ ++MODULE_PARM_DESC (vendor_id, "Device Vendor ID"); ++MODULE_PARM_DESC (product_id, "Device Product ID"); ++ ++/* ++ * ep0 testing.... ensure that this is exactly 16 bytes ++ */ ++#undef CONFIG_USBD_MOUSE_PRODUCT_NAME ++#define CONFIG_USBD_MOUSE_PRODUCT_NAME "Belcarra Mouse" ++ ++/* ++ * MOUSE Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++#define BULK_INT 0x00 ++#define ENDPOINTS 0x01 ++ ++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 ++}; ++ ++struct usb_endpoint_descriptor mouse_data = { ++ bLength: 0x07, ++ bDescriptorType: USB_DT_ENDPOINT, ++ bEndpointAddress: IN, ++ bmAttributes: INTERRUPT, ++ wMaxPacketSize: __constant_cpu_to_le16(0x10), ++ bInterval: 0x01, ++}; ++ ++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), ++}; ++ ++static struct usb_endpoint_descriptor *mouse_default[] = { &mouse_data, }; ++u8 mouse_indexes[] = { BULK_INT, }; ++static struct usb_generic_class_descriptor *mouse_hid_descriptors[] = { (struct usb_generic_class_descriptor *)&mouse_hid, }; ++ ++/* Data Interface Alternate description(s) ++ */ ++static __u8 mouse_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (mouse_default) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ 0x03, 0x01, 0x02, 0x00, ++}; ++ ++static struct usb_alternate_description mouse_data_alternate_descriptions[] = { ++ { iInterface:"Simple Mouse Interface - Interrupt", ++ interface_descriptor: (struct usb_interface_descriptor *)&mouse_data_alternate_descriptor, ++ classes:sizeof (mouse_hid_descriptors) / sizeof (struct usb_generic_class_descriptor *), ++ class_list: mouse_hid_descriptors, ++ endpoints:sizeof (mouse_default) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list: mouse_default, ++ endpoint_indexes: mouse_indexes, ++ }, ++}; ++ ++/* Interface description(s) ++ */ ++static struct usb_interface_description mouse_interfaces[] = { ++ { alternates:sizeof (mouse_data_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:mouse_data_alternate_descriptions,}, ++}; ++ ++ ++/* Configuration description(s) ++ */ ++static __u8 mouse_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength ++ sizeof (mouse_interfaces) / sizeof (struct usb_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ BMATTRIBUTE, BMAXPOWER, ++}; ++ ++struct usb_configuration_description mouse_description[] = { ++ { configuration_descriptor: (struct usb_configuration_descriptor *)mouse_configuration_descriptor, ++ iConfiguration:"USB Simple Serial Configuration", ++ bNumInterfaces:sizeof (mouse_interfaces) / sizeof (struct usb_interface_description), ++ interface_list:mouse_interfaces,}, ++}; ++ ++/* Device Description ++ */ ++static struct usb_device_descriptor mouse_device_descriptor = { ++ bLength: sizeof(struct usb_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_USBD_MOUSE_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_BCDDEVICE), ++}; ++ ++static struct usb_endpoint_request mouse_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++ ++struct usb_device_description mouse_device_description = { ++ device_descriptor: &mouse_device_descriptor, ++ iManufacturer: CONFIG_USBD_MOUSE_MANUFACTURER, ++ iProduct: CONFIG_USBD_MOUSE_PRODUCT_NAME, ++#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_USBD_SERIAL_NUMBER_STR, ++#endif ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: mouse_endpoint_requests, ++}; ++ ++ ++/* MOUSE ***************************************************************************************** */ ++ ++struct mouse_private { ++ struct usb_function_instance *function; ++ struct tq_struct notification_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 urb *tx_urb; ++ int wLength; ++ int x; ++ int y; ++ int last_x; ++ int last_y; ++ int n; ++}; ++ ++struct mouse_private mouse_private; ++ ++ ++/* Transmit Function *************************************************************************** */ ++ ++int get_xy[8] = { ++ 0, 0, 1, 1, ++ 0, 0, -1, -1, ++}; ++ ++void mouse_send(struct usb_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; ++#if 0 ++ printk(KERN_INFO"%s: x: %4d y: %4d rand: %02x x: %2d y: %2d new_x: %4d new_y: %4d urb: %02x %02x %02x %02x\n", ++ __FUNCTION__, ++ mouse->x, mouse->y, random, x, y, new_x, new_y, ++ mouse->tx_urb->buffer[0], mouse->tx_urb->buffer[1], ++ mouse->tx_urb->buffer[2], mouse->tx_urb->buffer[3] ); ++#endif ++ } ++ 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_send_urb(mouse->tx_urb); ++} ++ ++/* mouse_urb_sent - called to indicate URB transmit finished ++ * @urb: pointer to struct urb ++ * @rc: result ++ */ ++int mouse_urb_sent (struct urb *urb, int rc) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ struct usb_function_instance *function = mouse->function; ++ ++ RETURN_ZERO_IF(usbd_bus_status(function) == USBD_CLOSING); ++ RETURN_ZERO_IF(usbd_bus_status(function) != USBD_OK); ++ RETURN_ZERO_IF(usbd_device_state(function) != STATE_CONFIGURED); ++ ++ mouse_send(function); // re-send ++ return 0; ++} ++ ++/* USB Device Functions ************************************************************************ */ ++ ++/* mouse_event_irq - process a device event ++ * ++ */ ++void mouse_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ ++ switch (event) { ++ case DEVICE_CONFIGURED: ++ 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: ++ BREAK_IF(!mouse->connected); ++ mouse->connected = 0; ++ if (mouse->tx_urb) { ++ usbd_dealloc_urb (mouse->tx_urb); ++ mouse->tx_urb = NULL; ++ } ++ 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 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 ++ */ ++static int mouse_send_hid (void *data) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ struct usb_function_instance *function = mouse->function; ++ struct urb *urb = usbd_alloc_urb_ep0(function, mouse->wLength, NULL); ++ RETURN_EINVAL_IF (copy_report(urb, MouseHIDReport, sizeof(MouseHIDReport), mouse->wLength)); ++ RETURN_ZERO_IF(!usbd_send_urb(urb)); ++ usbd_dealloc_urb(urb); ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_USBD_MOUSE_BH ++/* mouse_hid_bh - Bottom half handler to send a HID report ++ */ ++static void mouse_hid_bh (void *data) ++{ ++ struct usb_function_instance *function = mouse_private.function; ++ mouse_send_hid(function); ++ MOD_DEC_USE_COUNT; ++} ++ ++/* mouse_schedule_bh - schedule a call for mouse_hid_bh ++ */ ++static int mouse_schedule_bh (void) ++{ ++ MOD_INC_USE_COUNT; ++ if (!schedule_task (&mouse_private.notification_bh)) { ++ MOD_DEC_USE_COUNT; ++ return -EINVAL; ++ } ++ return 0; ++} ++#endif /* CONFIG_USBD_MOUSE_BH */ ++ ++ ++ ++/* mouse_recv_setup_irq - called to indicate urb has been received ++ */ ++int mouse_recv_setup_irq (struct usb_device_request *request) ++{ ++ struct usb_function_instance *function = mouse_private.function; ++ ++ /* 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_USBD_MOUSE_BH ++ return mouse_schedule_bh(); ++#else ++ return mouse_send_hid(function); ++#endif ++ } ++ default: break; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++ ++ ++static int mouse_function_enable (struct usb_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ ++ MOD_INC_USE_COUNT; ++ 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; ++} ++ ++static void mouse_function_disable (struct usb_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ mouse->function = NULL; ++ mouse->writesize = 0; ++ mouse->function = NULL; ++ MOD_DEC_USE_COUNT; ++} ++ ++static struct usb_function_operations function_ops = { ++ event_irq: mouse_event_irq, ++ recv_setup_irq: mouse_recv_setup_irq, ++ function_enable: mouse_function_enable, ++ function_disable: mouse_function_disable, ++}; ++ ++static struct usb_function_driver function_driver = { ++ name:"mouse-random", ++ fops:&function_ops, ++ device_description:&mouse_device_description, ++ bNumConfigurations:sizeof (mouse_description) / sizeof (struct usb_configuration_description), ++ configuration_description:mouse_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_BCDDEVICE), ++}; ++ ++ ++/* USB Module init/exit ************************************************************************ */ ++/* ++ * mouse_modinit - module init ++ * ++ */ ++static int mouse_modinit (void) ++{ ++ printk (KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", __FUNCTION__, __usbd_module_info, vendor_id, product_id); ++ ++ if (vendor_id) ++ function_driver.idVendor = cpu_to_le16(vendor_id); ++ if (product_id) ++ 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_IF (usbd_register_function (&function_driver), error); ++ mouse_private.usb_driver_registered++; ++#ifdef CONFIG_USBD_MOUSE_BH ++ mouse_private.notification_bh.routine = mouse_hid_bh; ++ mouse_private.notification_bh.data = NULL; ++#endif ++ CATCH(error) { ++ if (mouse_private.usb_driver_registered) { ++ usbd_deregister_function (&function_driver); ++ mouse_private.usb_driver_registered = 0; ++ } ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* mouse_modexit - module cleanup ++ */ ++static void mouse_modexit (void) ++{ ++ if (mouse_private.usb_driver_registered) { ++ usbd_deregister_function (&function_driver); ++ } ++} ++ ++ ++module_init (mouse_modinit); ++module_exit (mouse_modexit); +diff -Nru a/drivers/usbd/network_fd/Config.in b/drivers/usbd/network_fd/Config.in +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/Config.in Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,97 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2003 Belcarra ++# ++ ++mainmenu_option next_comment ++comment "Network Function" ++ ++dep_tristate ' Network Function Driver' CONFIG_USBD_NETWORK $CONFIG_USBD ++ ++if [ "$CONFIG_USBD_NETWORK" = "y" -o "$CONFIG_USBD_NETWORK" = "m" ]; then ++ ++ hex 'VendorID (hex value)' CONFIG_USBD_NETWORK_VENDORID "12b9" ++ hex 'ProductID (hex value)' CONFIG_USBD_NETWORK_PRODUCTID "f001" ++ hex 'bcdDevice (binary-coded decimal)' CONFIG_USBD_NETWORK_BCDDEVICE "0100" ++ ++ string 'iManufacturer (string)' CONFIG_USBD_NETWORK_MANUFACTURER "Belcarra" ++ string 'iProduct (string)' CONFIG_USBD_NETWORK_PRODUCT_NAME "Belcarra BLAN Device" ++ ++ ++ comment '' ++ bool " MDLM-BLAN Networking mode (Personal Devices)" CONFIG_USBD_NETWORK_BLAN ++ if [ "$CONFIG_USBD_NETWORK_BLAN" = "y" ]; then ++ string ' iConfiguration (string)' CONFIG_USBD_NETWORK_BLAN_DESC "BLAN Net Cfg" ++ string ' iInterface (string)' CONFIG_USBD_NETWORK_BLAN_INTF "Comm/Data Intf" ++ fi ++ ++ if [ "$CONFIG_USBD_NETWORK_BLAN" = "y" ]; then ++ bool " CRC" CONFIG_USBD_NETWORK_BLAN_CRC ++ ++ if [ "$CONFIG_USBD_NETWORK_BLAN_CRC" = "y" ]; then ++ ++ bool " Pad Before CRC (to wMaxPacketSize-1)" CONFIG_USBD_NETWORK_BLAN_PADBEFORE ++ bool " Pad After CRC" CONFIG_USBD_NETWORK_BLAN_PADAFTER ++ if [ "$CONFIG_USBD_NETWORK_BLAN" = "y" -a "$CONFIG_USBD_NETWORK_BLAN_PADAFTER" = "y" ]; then ++ int ' Pad multiple' CONFIG_USBD_NETWORK_BLAN_PADBYTES "8" ++ fi ++ bool " Fermat Randomizer" CONFIG_USBD_NETWORK_BLAN_FERMAT ++ fi ++ bool " Do not Set Time" CONFIG_USBD_NETWORK_BLAN_DO_NOT_SETTIME ++ bool " Request Hostname" CONFIG_USBD_NETWORK_BLAN_HOSTNAME ++ bool " Infrastructure Device" CONFIG_USBD_NETWORK_BLAN_NOBRIDGE ++ comment '' ++ fi ++ ++ bool " MDLM-SAFE Networking mode (Bridge/Routers)" CONFIG_USBD_NETWORK_SAFE ++ if [ "$CONFIG_USBD_NETWORK_SAFE" = "y" ]; then ++ string ' Data Interface iConfiguration (string)' CONFIG_USBD_NETWORK_SAFE_DESC "SAFE Net Cfg" ++ string ' Data Interface iInterface (string)' CONFIG_USBD_NETWORK_SAFE_INTF "Data Intf" ++ fi ++ ++ ++ if [ "$CONFIG_USBD_NETWORK_SAFE" = "y" ]; then ++ bool " Do not Set Time" CONFIG_USBD_NETWORK_SAFE_DO_NOT_SETTIME ++ bool " CRC" CONFIG_USBD_NETWORK_SAFE_CRC ++ if [ "$CONFIG_USBD_NETWORK_SAFE_CRC" = "y" ]; then ++ bool " Pad Before CRC (to wMaxPacketSize-1)" CONFIG_USBD_NETWORK_SAFE_PADBEFORE ++ fi ++ bool " Infrastructure Device" CONFIG_USBD_NETWORK_SAFE_NOBRIDGE ++ comment '' ++ fi ++ ++ bool " CDC Networking mode (Bridge/Routers)" CONFIG_USBD_NETWORK_CDC ++ if [ "$CONFIG_USBD_NETWORK_CDC" = "y" ]; then ++ string ' iConfiguration (string)' CONFIG_USBD_NETWORK_CDC_DESC "SAFE Net Cfg" ++ string ' Data Interface iInterface (string)' CONFIG_USBD_NETWORK_CDC_COMM_INTF "Comm Intf" ++ string ' Data (diabled) iInterface (string)' CONFIG_USBD_NETWORK_CDC_NODATA_INTF "Data (Disabled) Intf" ++ string ' Comm Interface iInterface (string)' CONFIG_USBD_NETWORK_CDC_DATA_INTF "Dat Intf" ++ fi ++ ++ if [ "$CONFIG_USBD_NETWORK_SAFE" = "y" -a "$CONFIG_USBD_NETWORK_CDC" = "y" ]; then ++ comment 'Warning: CDC and MDLM-SAFE not allowed' ++ fi ++ ++ ++ bool " Failsafe BASIC Networking mode" CONFIG_USBD_NETWORK_BASIC ++ if [ "$CONFIG_USBD_NETWORK_BASIC" = "y" ]; then ++ string ' Data Interface iConfiguration (string)' CONFIG_USBD_NETWORK_BASIC_DESC "BASIC Net Cfg" ++ string ' Data Interface iInterface (string)' CONFIG_USBD_NETWORK_BASIC_INTF "Data Intf" ++ fi ++ ++ bool " Failsafe BASIC2 Networking mode" CONFIG_USBD_NETWORK_BASIC2 ++ if [ "$CONFIG_USBD_NETWORK_BASIC2" = "y" ]; then ++ string ' Data Interface iConfiguration (string)' CONFIG_USBD_NETWORK_BASIC2_DESC "BASIC Net Cfg" ++ string ' Comm Interface iInterface (string)' CONFIG_USBD_NETWORK_BASIC2_COMM_INTF "Comm Intf" ++ string ' Data Interface iInterface (string)' CONFIG_USBD_NETWORK_BASIC2_DATA_INTF "Data Intf" ++ fi ++ ++ ++ comment '' ++ bool ' Start Single Urb Test' CONFIG_USBD_NETWORK_START_SINGLE ++ bool ' EP0 Test' CONFIG_USBD_NETWORK_EP0TEST ++ ++fi ++ ++endmenu +diff -Nru a/drivers/usbd/network_fd/Makefile b/drivers/usbd/network_fd/Makefile +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/Makefile Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,66 @@ ++# ++# Network Function Driver ++# ++# Copyright (C) 2002-2003 Belcarra ++ ++ ++O_TARGET := network_fd.o ++list-multi := network_fd.o ++ ++network_fd-objs := network.o basic.o basic2.o blan.o cdc.o safe.o fermat.o ++ ++# Objects that export symbols. ++export-objs := network.o ++ ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_USBD_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. ++ ++USBD=$(TOPDIR)/drivers/usbd ++NETWORKD=$(USBD)/network_fd ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -I$(NETWORKD) -I$(USBD) -Wno-unused -Wno-format ++EXTRA_CFLAGS_nostdinc += -I$(NETWORKD) -I$(USBD) -Wno-unused -Wno-format ++ ++# Link rules for multi-part drivers. ++ ++network_fd.o: $(network_fd-objs) ++ $(LD) -r -o $@ $(network_fd-objs) ++ ++# dependencies: ++ ++network.o: network.h $(USBD)/usbd.h $(USBD)/usbd-bus.h $(USBD)/usbd-func.h ++ ++ +diff -Nru a/drivers/usbd/network_fd/basic.c b/drivers/usbd/network_fd/basic.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/basic.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,157 @@ ++/* ++ * usbd/network_fd/basic.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++#include "network.h" ++ ++ ++#ifdef CONFIG_USBD_NETWORK_BASIC ++/* USB BASIC Configuration ******************************************************************** */ ++ ++/* BASIC Communication Interface Class descriptors ++ */ ++static __u8 basic_data_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK, 0, 0x00, 0x00, }; ++static __u8 basic_data_2[] = { 0x07, USB_DT_ENDPOINT, IN, BULK, 0, 0x00, 0x00, }; ++static __u8 basic_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN, INTERRUPT,0, 0x00, 0x0a, }; ++ ++static struct usb_endpoint_descriptor *basic_default[] = { ++ (struct usb_endpoint_descriptor *) &basic_data_1, ++ (struct usb_endpoint_descriptor *) &basic_data_2, ++ (struct usb_endpoint_descriptor *) &basic_comm_1, }; ++u8 basic_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++/* BASIC Data Interface Alternate endpoints ++ */ ++static __u8 basic_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (basic_default) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ LINEO_CLASS, LINEO_SUBCLASS_BASIC_NET, LINEO_BASIC_NET_CRC, 0x00, ++}; ++ ++static struct usb_alternate_description basic_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_NETWORK_BASIC_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&basic_data_alternate_descriptor, ++ endpoints:sizeof (basic_default) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list: basic_default, ++ endpoint_indexes: basic_indexes, ++ }, ++}; ++ ++ ++/* BASIC Data Interface Alternate descriptions and descriptors ++ */ ++ ++/* BASIC Interface descriptions and descriptors ++ */ ++struct usb_interface_description basic_interfaces[] = { ++ { alternates:sizeof (basic_data_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:basic_data_alternate_descriptions,}, ++}; ++ ++/* BASIC Configuration descriptions and descriptors ++ */ ++__u8 basic_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength ++ sizeof (basic_interfaces) / sizeof (struct usb_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ BMATTRIBUTE, BMAXPOWER, ++}; ++ ++struct usb_configuration_description basic_description[] = { ++ { iConfiguration: CONFIG_USBD_NETWORK_BASIC_DESC, ++ configuration_descriptor: (struct usb_configuration_descriptor *)basic_configuration_descriptor, ++ bNumInterfaces:sizeof (basic_interfaces) / sizeof (struct usb_interface_description), ++ interface_list:basic_interfaces,}, ++ ++}; ++ ++/* BASIC Device Description ++ */ ++static struct usb_device_descriptor basic_device_descriptor = { ++ bLength: sizeof(struct usb_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_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++ ++static struct usb_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, }, ++}; ++ ++struct usb_device_description basic_device_description = { ++ device_descriptor: &basic_device_descriptor, ++ iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, ++#endif ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: basic_endpoint_requests, ++}; ++ ++ ++void basic_init (struct usb_function_instance *function) ++{ ++ basic_data_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; ++} ++ ++struct usb_function_driver basic_function_driver = { ++ name: "network-BASIC", ++ fops: &network_fd_function_ops, ++ device_description: &basic_device_description, ++ bNumConfigurations: sizeof (basic_description) / sizeof (struct usb_configuration_description), ++ configuration_description: basic_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++#endif /* CONFIG_USBD_NETWORK_BASIC */ ++ +diff -Nru a/drivers/usbd/network_fd/basic2.c b/drivers/usbd/network_fd/basic2.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/basic2.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,198 @@ ++/* ++ * usbd/network_fd/basic22.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++#include "network.h" ++ ++ ++#ifdef CONFIG_USBD_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, OUT, BULK, 0, 0x00, 0x00, }; ++static __u8 basic2_data_2[] = { 0x07, USB_DT_ENDPOINT, IN, BULK, 0, 0x00, 0x00, }; ++static __u8 basic2_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN, INTERRUPT,0, 0x00, 0x0a, }; ++ ++static __u8 *basic2_comm_endpoints[] = { ++ (struct usb_endpoint_descriptor *) &basic2_comm_1, }; ++ ++static __u8 *basic2_data_endpoints[] = { ++ (struct usb_endpoint_descriptor *) &basic2_data_1, ++ (struct usb_endpoint_descriptor *) &basic2_data_2, }; ++ ++u8 basic2_comm_indexes[] = { INT_IN, }; ++u8 basic2_data_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++ ++/* BASIC2 Data Interface Alternate endpoints ++ */ ++ ++static __u8 basic2_comm_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (basic2_comm_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_NETWORK_SUBCLASS, VENDOR, 0x00, ++}; ++static __u8 basic2_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x01, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (basic2_data_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, ++}; ++ ++static struct usb_alternate_description basic2_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_NETWORK_BASIC2_COMM_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&basic2_comm_alternate_descriptor, ++ endpoints:sizeof (basic2_comm_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list:basic2_comm_endpoints, ++ endpoint_indexes:basic2_comm_indexes, ++ }, ++}; ++ ++ ++static struct usb_alternate_description basic2_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_NETWORK_BASIC2_DATA_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&basic2_data_alternate_descriptor, ++ endpoints:sizeof (basic2_data_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list: basic2_data_endpoints, ++ endpoint_indexes: basic2_data_indexes, ++ }, ++}; ++ ++ ++/* BASIC Data Interface Alternate descriptions and descriptors ++ */ ++ ++/* BASIC Interface descriptions and descriptors ++ */ ++static struct usb_interface_description basic2_interfaces[] = { ++ { alternates:sizeof (basic2_comm_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:basic2_comm_alternate_descriptions,}, ++ ++ { alternates:sizeof (basic2_data_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:basic2_data_alternate_descriptions,}, ++}; ++ ++ ++/* BASIC Configuration descriptions and descriptors ++ */ ++__u8 basic2_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength ++ sizeof (basic2_interfaces) / sizeof (struct usb_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ BMATTRIBUTE, BMAXPOWER, ++}; ++ ++struct usb_configuration_description basic2_description[] = { ++ { iConfiguration: CONFIG_USBD_NETWORK_BASIC2_DESC, ++ configuration_descriptor: (struct usb_configuration_descriptor *)basic2_configuration_descriptor, ++ bNumInterfaces:sizeof (basic2_interfaces) / sizeof (struct usb_interface_description), ++ interface_list:basic2_interfaces,}, ++ ++}; ++ ++/* BASIC Device Description ++ */ ++static struct usb_device_descriptor basic2_device_descriptor = { ++ bLength: sizeof(struct usb_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_USBD_BASIC2_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_BCDDEVICE), ++}; ++ ++static struct usb_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, }, ++}; ++ ++struct usb_device_description basic2_device_description = { ++ device_descriptor: &basic2_device_descriptor, ++ iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, ++#endif ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: basic2_endpoint_requests, ++}; ++ ++ ++void basic2_init (struct usb_function_instance *function) ++{ ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ basic2_comm_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 1 : 0; ++ ++ printk(KERN_INFO"%s: alternate: %p endpoints: %d\n", __FUNCTION__, ++ basic2_data_alternate_descriptions, ++ basic2_data_alternate_descriptions->endpoints ++ ); ++ ++ printk(KERN_INFO"%s: finis\n", __FUNCTION__); ++} ++ ++ ++struct usb_function_driver basic2_function_driver = { ++ name: "network-BASIC2", ++ fops: &network_fd_function_ops, ++ device_description: &basic2_device_description, ++ bNumConfigurations: sizeof (basic2_description) / sizeof (struct usb_configuration_description), ++ configuration_description: basic2_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_BCDDEVICE), ++}; ++#endif /* CONFIG_USBD_NETWORK_BASIC2 */ ++ +diff -Nru a/drivers/usbd/network_fd/blan.c b/drivers/usbd/network_fd/blan.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/blan.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,251 @@ ++/* ++ * usbd/network_fd/blan.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++#include "network.h" ++ ++ ++#ifdef CONFIG_USBD_NETWORK_BLAN ++/* USB BLAN Configuration ******************************************************************** */ ++ ++/* ++ * BLAN Ethernet Configuration ++ */ ++ ++/* Communication Interface Class descriptors ++ */ ++ ++static __u8 blan_data_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK, 0, 0x00, 0x00, }; ++static __u8 blan_data_2[] = { 0x07, USB_DT_ENDPOINT, IN, BULK, 0, 0x00, 0x00, }; ++static __u8 blan_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN, INTERRUPT,0, 0x00, 0x0a, }; ++ ++static __u8 blan_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_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 */ ++ 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 __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 __u8 blan_class_5[] = { 0x07, CS_INTERFACE, USB_ST_NCT, 0x00, 0x00, 0x00, 0x00, }; ++ ++ ++static struct usb_endpoint_descriptor *blan_alt_endpoints[] = { ++ (struct usb_endpoint_descriptor *) blan_data_1, ++ (struct usb_endpoint_descriptor *) blan_data_2, ++ (struct usb_endpoint_descriptor *) blan_comm_1, }; ++ ++u8 blan_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++static struct usb_generic_class_descriptor *blan_comm_class_descriptors[] = { ++ (struct usb_generic_class_descriptor *) blan_class_1, ++ (struct usb_generic_class_descriptor *) blan_class_2, ++ (struct usb_generic_class_descriptor *) blan_class_3, ++ (struct usb_generic_class_descriptor *) blan_class_4, ++ (struct usb_generic_class_descriptor *) blan_class_5, }; ++ ++ ++/* Data Interface Alternate descriptions and descriptors ++ */ ++static __u8 blan_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (blan_alt_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_MDLM_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, ++}; ++ ++static struct usb_alternate_description blan_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_NETWORK_BLAN_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&blan_alternate_descriptor, ++ classes:sizeof (blan_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *), ++ class_list: blan_comm_class_descriptors, ++ endpoints:sizeof (blan_alt_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list: blan_alt_endpoints, ++ endpoint_indexes: blan_alt_indexes, ++ }, ++}; ++/* Interface descriptions and descriptors ++ */ ++static struct usb_interface_description blan_interfaces[] = { ++ { ++ alternates: sizeof (blan_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list: blan_alternate_descriptions, ++ }, ++}; ++ ++ ++/* Configuration descriptions and descriptors ++ */ ++ ++static __u8 blan_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength ++ sizeof (blan_interfaces) / sizeof (struct usb_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ BMATTRIBUTE, BMAXPOWER, ++}; ++ ++struct usb_configuration_description blan_description[] = { ++ { iConfiguration: CONFIG_USBD_NETWORK_BLAN_DESC, ++ configuration_descriptor: (struct usb_configuration_descriptor *)blan_configuration_descriptor, ++ bNumInterfaces:sizeof (blan_interfaces) / sizeof (struct usb_interface_description), ++ interface_list:blan_interfaces, }, ++}; ++ ++/* Device Description ++ */ ++ ++static struct usb_device_descriptor blan_device_descriptor = { ++ bLength: sizeof(struct usb_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_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++ ++static struct usb_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, }, ++}; ++ ++struct usb_device_description blan_device_description = { ++ device_descriptor: &blan_device_descriptor, ++ iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, ++#endif ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: blan_endpoint_requests, ++}; ++ ++ ++void blan_init (struct usb_function_instance *function) ++{ ++ struct usb_class_ethernet_networking_descriptor *ethernet; ++ struct usb_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 ++ //printk(KERN_INFO"%s: alloc mac string\n", __FUNCTION__); ++ if ((ethernet = (struct usb_class_ethernet_networking_descriptor *)blan_class_4)) ++ ethernet->iMACAddress = usbd_alloc_string(address_str); ++ //printk(KERN_INFO"%s: alloc mac string finis\n", __FUNCTION__); ++ } ++ //printk(KERN_INFO"%s: alloc channel string\n", __FUNCTION__); ++ if ((channel = (struct usb_class_network_channel_descriptor *)blan_class_5)) ++ channel->iName = usbd_alloc_string(system_utsname.nodename); ++ //printk(KERN_INFO"%s: alloc channel string finis\n", __FUNCTION__); ++ ++#ifdef CONFIG_USBD_NETWORK_BLAN_PADBYTES ++ blan_class_3[6] = CONFIG_USBD_NETWORK_BLAN_PADBYTES; ++ len += sprintf(buf + len, "PADBYTES: %02x ", blan_class_3[6]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN_PADBEFORE ++ blan_class_3[5] |= BMDATA_PADBEFORE; ++ len += sprintf(buf + len, "PADBEFORE: %02x ", blan_class_3[5]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN_PADAFTER ++ blan_class_3[5] |= BMDATA_PADAFTER; ++ len += sprintf(buf + len, "PADAFTER: %02x ", blan_class_3[5]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN_CRC ++ blan_class_3[5] |= BMDATA_CRC; ++ len += sprintf(buf + len, "CRC: %02x ", blan_class_3[5]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT ++ blan_class_3[5] |= BMDATA_FERMAT; ++ len += sprintf(buf + len, "FERMAT: %02x ",blan_class_3[5]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN_HOSTNAME ++ blan_class_3[5] |= BMDATA_HOSTNAME; ++ len += sprintf(buf + len, "HOSTNAME: %02x ",blan_class_3[5]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN_NOBRIDGE ++ blan_class_3[4] |= BMNETWORK_NOBRIDGE; ++ len += sprintf(buf + len, "NOBRIDGE: %02x ",blan_class_3[4]); ++#endif ++ if (strlen(buf)) ++ printk(KERN_INFO"%s: %s\n", __FUNCTION__, buf); ++} ++ ++struct usb_function_driver blan_function_driver = { ++ name: "network-BLAN", ++ fops: &network_fd_function_ops, ++ device_description: &blan_device_description, ++ bNumConfigurations: sizeof (blan_description) / sizeof (struct usb_configuration_description), ++ configuration_description: blan_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++#endif /* CONFIG_USBD_NETWORK_BLAN */ ++ +diff -Nru a/drivers/usbd/network_fd/cdc.c b/drivers/usbd/network_fd/cdc.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/cdc.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,222 @@ ++/* ++ * usbd/network_fd/cdc.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++#include "network.h" ++ ++ ++ ++#ifdef CONFIG_USBD_NETWORK_CDC ++/* USB CDC Configuration ********************************************************************* */ ++ ++/* CDC Communication Interface Class descriptors ++ */ ++static __u8 cdc_data_1[] = { 0x07, USB_DT_ENDPOINT, 0 | OUT, BULK, 0, 0x00, 0x00, }; ++static __u8 cdc_data_2[] = { 0x07, USB_DT_ENDPOINT, 0 | IN, BULK, 0, 0x00, 0x00, }; ++ ++static __u8 cdc_comm_1[] = { 0x07, USB_DT_ENDPOINT, 0 | IN, INTERRUPT,0, 0x00, 0x0a, }; ++ ++static __u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++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 __u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface, bSlaveInterface */}; ++ ++ ++static __u8 *cdc_comm_class_descriptors[] = { ++ (struct usb_generic_class_descriptor *) &cdc_class_1, ++ (struct usb_generic_class_descriptor *) &cdc_class_2, ++ (struct usb_generic_class_descriptor *) &cdc_class_3, }; ++ ++static __u8 *cdc_data_endpoints[] = { ++ (struct usb_endpoint_descriptor *) &cdc_data_1, ++ (struct usb_endpoint_descriptor *) &cdc_data_2, }; ++static __u8 *cdc_comm_endpoints[] = { ++ (struct usb_endpoint_descriptor *) &cdc_comm_1, }; ++ ++u8 cdc_comm_indexes[] = { INT_IN, }; ++u8 cdc_data_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++ ++/* Data Interface Alternate descriptions and descriptors ++ */ ++static __u8 cdc_comm_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (cdc_comm_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ENCM_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, ++}; ++static __u8 cdc_nodata_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x01, 0x00, // bInterfaceNumber, bAlternateSetting ++ 0x00, // bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, ++}; ++static __u8 cdc_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x01, 0x01, // bInterfaceNumber, bAlternateSetting ++ sizeof (cdc_data_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, ++}; ++ ++static struct usb_alternate_description cdc_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_NETWORK_CDC_COMM_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&cdc_comm_alternate_descriptor, ++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *), ++ class_list: cdc_comm_class_descriptors, ++ endpoints:sizeof (cdc_comm_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list: cdc_comm_endpoints, ++ endpoint_indexes: cdc_comm_indexes, ++ }, ++}; ++ ++ ++static struct usb_alternate_description cdc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_NETWORK_CDC_NODATA_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&cdc_nodata_alternate_descriptor, }, ++ { iInterface: CONFIG_USBD_NETWORK_CDC_DATA_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&cdc_data_alternate_descriptor, ++ endpoints:sizeof (cdc_data_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list: cdc_data_endpoints, ++ endpoint_indexes: cdc_data_indexes, ++ }, ++}; ++ ++/* Interface descriptions and descriptors ++ */ ++struct usb_interface_description cdc_interfaces[] = { ++ { alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:cdc_comm_alternate_descriptions,}, ++ ++ { alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:cdc_data_alternate_descriptions,}, ++}; ++ ++/* Configuration descriptions and descriptors ++ */ ++ ++__u8 cdc_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength ++ sizeof (cdc_interfaces) / sizeof (struct usb_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ BMATTRIBUTE, BMAXPOWER, ++}; ++ ++struct usb_configuration_description cdc_description[] = { ++ { iConfiguration: CONFIG_USBD_NETWORK_CDC_DESC, ++ configuration_descriptor: (struct usb_configuration_descriptor *)cdc_configuration_descriptor, ++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usb_interface_description), ++ interface_list:cdc_interfaces,}, ++}; ++ ++ ++ ++/* Device Description ++ */ ++ ++static struct usb_device_descriptor cdc_device_descriptor = { ++ bLength: sizeof(struct usb_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_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++ ++static struct usb_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, }, ++}; ++ ++struct usb_device_description cdc_device_description = { ++ device_descriptor: &cdc_device_descriptor, ++ iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, ++#endif ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: cdc_endpoint_requests, ++}; ++ ++ ++ ++void cdc_init (struct usb_function_instance *function) ++{ ++ struct usb_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 = (struct usb_class_ethernet_networking_descriptor *)cdc_class_2)) { ++ if (ethernet->iMACAddress) { ++ usbd_dealloc_string(ethernet->iMACAddress); ++ } ++ ethernet->iMACAddress = usbd_alloc_string(address_str); ++ } ++ } ++} ++ ++ ++struct usb_function_driver cdc_function_driver = { ++ name: "network-CDC", ++ fops: &network_fd_function_ops, ++ device_description: &cdc_device_description, ++ bNumConfigurations: sizeof (cdc_description) / sizeof (struct usb_configuration_description), ++ configuration_description: cdc_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++#endif /* CONFIG_USBD_NETWORK_CDC */ ++ +diff -Nru a/drivers/usbd/network_fd/fermat.c b/drivers/usbd/network_fd/fermat.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/fermat.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,132 @@ ++/* ++ * usbd/network_fd/fermat.c - Network Function Driver ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#ifdef CONFIG_USBD_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 <stdio.h> ++#include <memory.h> ++ ++// Use FERMAT in two ways: to encode, and to generate test data. ++ ++main(){ ++ //Note 3, 5, and 7 are primitive roots of 257 ++ // 11 is not a primitive root ++ FERMAT three, five, seven; ++ ++ FERMAT three2; ++ printf("Cycle lengths: 3,5,7 %d %d %d \n", ++ fermat_setup(&three, 3), ++ fermat_setup(&five, 5), ++ fermat_setup(&seven, 7)); ++ three2=three; // Copy data from three ++ fermat_xform(&three,three2.power,three2.length); ++ fermat_xform(&five,three2.power,three2.length); ++ fermat_xform(&seven,three2.power,three2.length); ++ fermat_xform(&seven,three2.power,three2.length); ++ fermat_xform(&five,three2.power,three2.length); ++ fermat_xform(&three,three2.power,three2.length); ++ ++ //At this stage, three2 and three should be identical ++ if(memcpy(&three,&three2,sizeof(FERMAT))){ ++ printf("Decoded intact\n"); ++ } ++ ++ fermat_init(); ++ fermat_encode(three2.power,256); ++ ++} ++#endif ++ ++#endif /* CONFIG_USBD_NETWORK_BLAN_FERMAT */ ++ +diff -Nru a/drivers/usbd/network_fd/fermat.h b/drivers/usbd/network_fd/fermat.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/fermat.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,39 @@ ++/* ++ * usbd/network_fd/fermat.h - Network Function Driver ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ */ ++ ++#ifndef 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 -Nru a/drivers/usbd/network_fd/network.c b/drivers/usbd/network_fd/network.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/network.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,2112 @@ ++/* ++ * usbd/network_fd/network.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ * 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/ ++ * ++ * ++ * Notes ++ * ++ * 1. 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. ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++MODULE_AUTHOR ("sl@belcarra.com, balden@belcarra.com"); ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++MODULE_LICENSE("GPL"); ++#endif ++ ++MODULE_DESCRIPTION ("USB Network Function"); ++ ++ ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/etherdevice.h> ++#include <net/arp.h> ++#include <linux/rtnetlink.h> ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/time.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++#include <linux/atmdev.h> ++#include <linux/pkt_sched.h> ++#include <linux/random.h> ++#include <linux/utsname.h> ++ ++#include <linux/ip.h> ++#include <linux/if_ether.h> ++#include <linux/in.h> ++#include <linux/inetdevice.h> ++ ++#include <linux/kmod.h> ++ ++#include <asm/uaccess.h> ++#include <asm/system.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++USBD_MODULE_INFO ("network_fd 2.0-beta"); ++ ++#include "network.h" ++#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT ++#include "fermat.h" ++#endif ++ ++ ++#if defined(CONFIG_USBD_NETWORK_CDC) ++ ++static u32 cdc = 0; ++MODULE_PARM (cdc, "i"); ++MODULE_PARM_DESC (cdc, "Enable CDC mode"); ++void cdc_init(struct usb_function_instance *); ++ ++extern struct usb_function_driver cdc_function_driver; ++ ++#if defined(CONFIG_USBD_NETWORK_NETWORK_CDC_MYDHCPD) ++extern u_int8_t * checkfordhcp(struct net_device *net_device, u_int8_t *, int, int* ); ++#endif ++ ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_BASIC ++static u32 basic = 0; ++MODULE_PARM (basic, "i"); ++MODULE_PARM_DESC (basic, "Enable BASIC mode"); ++void basic_init(struct usb_function_instance *); ++extern struct usb_function_driver basic_function_driver; ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_BASIC2 ++static u32 basic2 = 0; ++MODULE_PARM (basic, "i"); ++MODULE_PARM_DESC (basic2, "Enable BASIC2 mode"); ++void basic2_init(struct usb_function_instance *); ++extern struct usb_function_driver basic2_function_driver; ++#endif ++ ++ ++#ifdef CONFIG_USBD_NETWORK_SAFE ++static u32 safe = 0; ++MODULE_PARM (safe, "i"); ++MODULE_PARM_DESC (safe, "Enable SAFE mode"); ++void safe_init(struct usb_function_instance *); ++extern struct usb_function_driver safe_function_driver; ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_BLAN ++static u32 blan = 0; ++MODULE_PARM (blan, "i"); ++MODULE_PARM_DESC (blan, "Enable BLAN mode"); ++void blan_init(struct usb_function_instance *); ++extern struct usb_function_driver blan_function_driver; ++#endif ++ ++static struct usb_function_driver *function_driver = NULL; ++ ++ ++/* Module Parameters ************************************************************************* */ ++ ++wait_queue_head_t usb_netif_wq; ++#ifdef CONFIG_USBD_NET_NFS_SUPPORT ++int usb_is_configured; ++#endif ++ ++#define CONFIG_USBD_NETWORK_ALLOW_SETID 1 ++ ++ ++ ++#ifdef CONFIG_USBD_NETWORK_ALLOW_SETID ++// 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"); ++#endif ++ ++// override local mac address ++#ifdef CONFIG_USBD_NETWORK_LOCAL_MACADDR ++static char *local_mac_address_str = CONFIG_USBD_NETWORK_LOCAL_MACADDR; ++#else ++static char *local_mac_address_str; ++#endif ++MODULE_PARM (local_mac_address_str, "s"); ++MODULE_PARM_DESC (local_mac_address_str, "Local MAC"); ++ ++// override remote mac address ++#ifdef CONFIG_USBD_NETWORK_REMOTE_MACADDR ++static char *remote_mac_address_str = CONFIG_USBD_NETWORK_REMOTE_MACADDR; ++#else ++static char *remote_mac_address_str; ++#endif ++MODULE_PARM (remote_mac_address_str, "s"); ++MODULE_PARM_DESC (remote_mac_address_str, "Remote MAC"); ++ ++ ++#ifdef CONFIG_USBD_NETWORK_EP0TEST ++static u32 ep0test; ++MODULE_PARM (ep0test, "i"); ++MODULE_PARM_DESC (ep0test, "Test EP0 String Handling - set to ep0 packetsize [8,16,32,64]"); ++#endif ++ ++ ++static u32 zeroconf = 0; ++MODULE_PARM (zeroconf, "i"); ++MODULE_PARM_DESC (zeroconf, "Use usbz%d for network name"); ++ ++#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 RFC868_OFFSET_TO_EPOCH 0x83AA7E80 // RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT ++ ++ ++__u8 local_dev_addr[ETH_ALEN]; ++ ++__u8 remote_dev_addr[ETH_ALEN]; ++static __u8 zeros[ETH_ALEN]; ++ ++static __u32 ip_addr; ++static __u32 router_ip; ++static __u32 network_mask; ++static __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; ++ ++static void network_send_int_blan(struct usb_function_instance *function, int connected ); ++static void notification_schedule_bh (void); ++static int network_urb_sent_bulk (struct urb *urb, int urb_rc); ++static int network_urb_sent_int (struct 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 usb_device_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 urb *) ++ * int network_urb_sent (struct 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 hotplug_schedule_bh (void) ++ * ++ * ++ * Irq level functions: ++ * ++ * These are called at interrupt time do process or respond to USB setup ++ * commands. ++ * ++ * int network_recv_setup_irq (struct usb_device_request *) ++ * void network_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data) ++ * ++ * ++ * Enable and disable functions: ++ * ++ * void network_function_enable (struct usb_function_instance *, struct usb_function_instance *) ++ * void network_function_disable (struct usb_function_instance *function) ++ * ++ * ++ * Driver initialization and exit: ++ * ++ * static int network_create (void) ++ * static void network_destroy (void) ++ * ++ * int network_modinit (void) ++ * void network_modexit (void) ++ */ ++ ++//_________________________________________________________________________________________________ ++ ++/* ++ * 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_USBD_NETWORK_CRC_DUFF4 ++#undef CONFIG_USBD_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 ++ ++/** ++ * 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_USBD_NETWORK_CRC_DUFF4) && !defined(CONFIG_USBD_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; ++} ++ ++#else /* DUFFn */ ++ ++/** ++ * 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 crc32_copy (__u8 *dst, __u8 *src, int len, __u32 val) ++{ ++#if defined(CONFIG_USBD_NETWORK_CRC_DUFF8) ++ int n = (len + 7) / 8; ++ switch (len % 8) ++#elif defined(CONFIG_USBD_NETWORK_CRC_DUFF4) ++ int n = (len + 3) / 4; ++ switch (len % 4) ++#endif ++ { ++ case 0: do { ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++#if defined(CONFIG_USBD_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 ++ * ++ * Returns - CRC FCS ++ */ ++static __u32 __inline__ crc32_pad (__u8 *dst, int len, __u32 val) ++{ ++ for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = '\0')); ++ return val; ++} ++ ++//_________________________________________________________________________________________________ ++// network_send_int ++// ++ ++/* network_send_int_blan - send an interrupt notification response ++ * ++ * Generates a response urb on the notification (INTERRUPT) endpoint. ++ * Return a non-zero result code to STALL the transaction. ++ * ++ * This is called from either a scheduled task or from the process context ++ * that calls network_open() or network_close(). ++ * ++ */ ++static void network_send_int_blan(struct usb_function_instance *function, int connected ) ++{ ++ struct urb *urb; ++ struct cdc_notification_descriptor *cdc; ++ int rc; ++ struct usb_network_private *network_private = &Usb_network_private; ++ ++ //printk(KERN_INFO"%s: device: %p\n", __FUNCTION__, device); ++ ++ /* ++ * This needs to lock out interrupts as network_event_irq can ++ * change the NETWORK_ATTACHED status ++ */ ++ unsigned long flags; ++ local_irq_save(flags); ++ do { ++ BREAK_IF(!function); ++ ++ ++ BREAK_IF(network_private->network_type != network_blan); ++ BREAK_IF(!network_private->have_interrupt); ++ ++ BREAK_IF(!(network_private->flags & NETWORK_ATTACHED)); ++ ++ //printk(KERN_INFO"%s: connected: %d network: %d %d\n", __FUNCTION__, connected, ++ // network_private->network_type, network_blan); ++ ++ BREAK_IF(usbd_bus_status(function) != USBD_OK); ++ ++ if (network_private->int_urb) { ++ printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, network_private->int_urb); ++ usbd_cancel_urb_irq(network_private->int_urb); ++ network_private->int_urb = NULL; ++ } ++ ++ BREAK_IF(!(urb = usbd_alloc_urb (function, INT_IN, ++ sizeof(struct cdc_notification_descriptor), network_urb_sent_int))); ++ ++ urb->actual_length = sizeof(struct cdc_notification_descriptor); ++ memset(urb->buffer, 0, sizeof(struct cdc_notification_descriptor)); ++ ++ cdc = (struct cdc_notification_descriptor *)urb->buffer; ++ ++ cdc->bmRequestType = 0xa1; ++ cdc->bNotification = 0x00; ++ cdc->wValue = connected ? 0x01 : 0x00; ++ cdc->wIndex = 0x01; // XXX interface - check that this is correct ++ ++ ++ network_private->int_urb = urb; ++ //printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, urb); ++ BREAK_IF (!(rc = usbd_send_urb (urb))); ++ ++ printk(KERN_ERR"%s: usbd_send_urb failed err: %x\n", __FUNCTION__, rc); ++ urb->privdata = NULL; ++ network_private->int_urb = NULL; ++ usbd_dealloc_urb (urb); ++ ++ } while(0); ++ local_irq_restore(flags); ++} ++ ++ ++/* 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) ++{ ++ network_send_int_blan(Usb_network_private.function, Usb_network_private.flags & NETWORK_OPEN); ++ MOD_DEC_USE_COUNT; ++} ++ ++/* notification_schedule_bh - schedule a call for notification_bh ++ */ ++static void notification_schedule_bh (void) ++{ ++ MOD_INC_USE_COUNT; ++ if (!schedule_task (&Usb_network_private.notification_bh)) ++ MOD_DEC_USE_COUNT; ++} ++ ++ ++//______________________________________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) ++{ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ return 0; ++} ++ ++ ++/* network_uninit - ++ * ++ * Uninitializes the specified network device. ++ */ ++static void network_uninit (struct net_device *net_device) ++{ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ 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 *network_private = &Usb_network_private; ++ ++ // XXX should this be before or after the preceeding netif_running() test? ++ MOD_INC_USE_COUNT; ++ network_private->flags |= NETWORK_OPEN; ++ netif_wake_queue (net_device); ++ network_send_int_blan(network_private->function, 1); ++ ++#ifdef CONFIG_USBD_NET_NFS_SUPPORT ++ if (!usb_is_configured) { ++ if (!in_interrupt()) { ++ printk(KERN_ERR"Please replug USB cable and then ifconfig host interface.\n"); ++ interruptible_sleep_on(&usb_netif_wq); ++ } ++ else { ++ printk(KERN_ERR"Wanring! 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 *network_private = &Usb_network_private; ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ //netif_stop_queue (net_device); ++ ++ network_private->flags &= ~NETWORK_OPEN; ++ network_send_int_blan(network_private->function, 0); ++ ++ MOD_DEC_USE_COUNT; ++ 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 *network_private = &Usb_network_private; ++ return &network_private->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 sockaddr *addr = p; ++ unsigned long flags; ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ RETURN_EBUSY_IF(netif_running (net_device)); ++ local_irq_save(flags); ++ memcpy (net_device->dev_addr, addr->sa_data, net_device->addr_len); ++ 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 *network_private = &Usb_network_private; ++#if 0 ++ //printk(KERN_ERR"%s:\n", __FUNCTION__); ++ network_private->stats.tx_errors++; ++ network_private->stats.tx_dropped++; ++ usbd_cancel_urb_irq (network_private->bus, NULL); ++#endif ++#if 0 ++ // XXX attempt to wakeup the host... ++ if ((network_private->network_type == network_blan) && (network_private->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 *network_private = &Usb_network_private; ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ RETURN_EBUSY_IF(netif_running (net_device)); ++ RETURN_EBUSY_IF(mtu > network_private->mtu); ++ ++ net_device->mtu = mtu; ++ return 0; ++} ++ ++//_________________________________________________________________________________________________ ++// network_hard_start_xmit ++ ++/* network_hard_start_xmit - start sending an skb ++ * ++ * Starts a network device's transmit function. ++ * Called by kernel network layer to transmit a frame on this device. ++ * Grab locks and pass to @c netproto_do_xmit(). ++ * The network layer flow control is managed to prevent more than ++ * @c device->max_queue_entries from being outstanding. ++ * ++ * skb Pointer to the sk_buff structure to be sent. ++ * net_device Specifies the device by pointing to its net_device struct. ++ * ++ * Return non-zero (1) if busy. ++ */ ++static int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device) ++{ ++ struct usb_network_private *network_private = &Usb_network_private; ++ struct usb_function_instance *function = network_private->function; ++ struct urb *urb = NULL; ++ int len = skb->len; ++ int rc = 1; ++ int in_pkt_sz; ++ ++ down(&usbd_network_sem); ++ do { ++ ++ ++#if 0 ++ printk(KERN_INFO"%s: %s len: %d encap: %d\n", __FUNCTION__, net_device->name, skb->len, network_private->encapsulation); ++ printk(KERN_INFO"start_xmit: len: %x head: %p data: %p tail: %p\n", ++ 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("\ntx[%2x] ", i); ++ } ++ printk("%02x ", *cp++); ++ } ++ printk("\n"); ++ } ++#endif ++ ++ THROW_IF(!(network_private->flags & NETWORK_ATTACHED), not_ok); ++ THROW_IF(!netif_carrier_ok (net_device), not_ok); ++ THROW_IF(usbd_bus_status(function) != USBD_OK, not_ok); ++ ++#if defined(CONFIG_USBD_NETWORK_CDC) ++ // verify interface is enabled - non-zero altsetting means data is enabled ++ THROW_IF(!usbd_interface_AltSetting(function, DATA_INTF), not_ok); ++#endif ++ in_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_IN, usbd_high_speed(function)); ++ ++ // stop queue, it will be restart only when we are ready for another skb ++ netif_stop_queue (net_device); ++ ++ // lock and update some stats ++ network_private->stopped++; ++ network_private->queued_entries++; ++ network_private->queued_bytes += skb->len; ++ ++ ++ // Set the timestamp for tx timeout ++ net_device->trans_start = jiffies; ++ ++ switch (network_private->encapsulation) { ++ case simple_crc: ++ //printk(KERN_INFO"%s: BASIC_CRC\n", __FUNCTION__); ++ { ++ u32 crc; ++ ++ // allocate urb 5 bytes larger than required ++ if (!(urb = usbd_alloc_urb (function, BULK_IN, ++ skb->len + 5 + in_pkt_sz, network_urb_sent_bulk ))) ++ { ++ printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", ++ __FUNCTION__, skb->len, ++ usbd_endpoint_bEndpointAddress(function, BULK_IN, ++ usbd_high_speed(function))); ++ return -ENOMEM; ++ } ++ ++ // copy and crc skb->len bytes ++ crc = crc32_copy(urb->buffer, skb->data, skb->len, CRC32_INIT); ++ urb->actual_length = skb->len; ++ ++ // 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. ++ if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz - 4)) { ++ crc = crc32_pad(urb->buffer + urb->actual_length, 1, crc); ++ urb->actual_length++; ++ } ++ ++ // 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; ++ ++ break; ++ } ++ default: ++ break; ++ ++ } ++ if (!urb) { ++ printk(KERN_ERR"%s: unknown encapsulation\n", __FUNCTION__); ++ rc = -EINVAL; ++ break; ++ } ++ ++ // save skb for netproto_done ++ urb->privdata = (void *) skb; ++#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_USBD_NETWORK_BLAN_FERMAT) ++ if (network_private->fermat) { ++ fermat_encode(urb->buffer, urb->actual_length); ++ } ++#endif ++ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ if ((rc = usbd_send_urb (urb))) { ++ ++ printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc); ++ urb->privdata = NULL; ++ usbd_dealloc_urb (urb); ++ ++ switch (rc) { ++ ++ case -EINVAL: ++ case -EUNATCH: ++ printk(KERN_ERR"%s: not attached, send failed: %d\n", __FUNCTION__, rc); ++ network_private->stats.tx_errors++; ++ network_private->stats.tx_carrier_errors++; ++ netif_wake_queue (net_device); ++ break; ++ ++ case -ENOMEM: ++ printk(KERN_ERR"%s: no mem, send failed: %d\n", __FUNCTION__, rc); ++ network_private->stats.tx_errors++; ++ network_private->stats.tx_fifo_errors++; ++ netif_wake_queue (net_device); ++ break; ++ ++ case -ECOMM: ++ printk(KERN_ERR"%s: comm failure, send failed: %d %p\n", __FUNCTION__, rc, net_device); ++ network_private->stats.tx_dropped++; ++ break; ++ ++ } ++ dev_kfree_skb_any (skb); ++ //rc = NET_XMIT_DROP; XXX this is what we should do, blows up on some 2.4.20 kernels ++ rc = 0; ++ break; ++ } ++ ++ // XXX should we restart network queue ++ //printk(KERN_INFO"%s: OK: %d\n", __FUNCTION__, rc); ++ network_private->stats.tx_packets++; ++ network_private->stats.tx_bytes += len; ++ ++ if ((network_private->queued_entries < network_private->max_queue_entries) && ++ (network_private->queued_bytes < network_private->max_queue_bytes)) ++ netif_wake_queue (net_device); ++ ++ CATCH(not_ok) { ++ dev_kfree_skb_any (skb); ++ network_private->stats.tx_dropped++; ++ //netif_stop_queue(net_device); // XXX ++ //rc = NET_XMIT_DROP; XXX this is what we should do, blows up on some 2.4.20 kernels ++ rc = 0; ++ break; ++ } ++ ++ } while (0); ++ 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, ++}; ++ ++//_________________________________________________________________________________________________ ++ ++ ++/* network_recv - function to process an received data URB ++ * ++ * Passes received data to the network layer. Passes skb to network layer. ++ * ++ * Returns non-zero for failure. ++ */ ++static __inline__ int network_recv (struct usb_network_private *network_private, ++ 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)) { ++ printk(KERN_INFO"%s: device not present\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ // refuse if no carrier ++ if (!netif_carrier_ok (net_device)) { ++ printk(KERN_INFO"%s: no carrier\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ // refuse if the net device is down ++ if (!(net_device->flags & IFF_UP)) { ++ //printk(KERN_INFO"%s: not up net_dev->flags: %x\n", __FUNCTION__, net_device->flags); ++ network_private->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; ++ ++ //printk(KERN_INFO"%s: len: %x head: %p data: %p tail: %p\n", __FUNCTION__, ++ // skb->len, skb->head, skb->data, skb->tail); ++ ++ ++ // pass it up to kernel networking layer ++ if ((rc = netif_rx (skb))) { ++ //printk(KERN_INFO"%s: netif_rx rc: %d\n", __FUNCTION__, rc); ++ } ++ network_private->stats.rx_bytes += skb->len; ++ network_private->stats.rx_packets++; ++ ++ return 0; ++} ++ ++//_________________________________________________________________________________________________ ++ ++/* network_recv_urb - callback to process a received URB ++ * ++ * Returns non-zero for failure. ++ */ ++static int network_recv_urb (struct urb *urb, int rc) ++{ ++ struct net_device *net_device; ++ struct usb_network_private *network_private = &Usb_network_private; ++ struct usb_function_instance *function = network_private->function; ++ ++ struct sk_buff *skb = NULL; ++ int out_pkt_sz; ++ ++ RETURN_EINVAL_IF(!(network_private->flags & NETWORK_ATTACHED)); ++ RETURN_EINVAL_IF(!(net_device = &Network_net_device)); ++ ++ out_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function)); ++ ++#if 0 ++ printk(KERN_INFO"%s: urb: %p len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, ++ urb, urb->actual_length, network_private->maxtransfer, network_private->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 ++ ++ THROW_IF(urb->status != RECV_OK, error); ++ ++ // Is CDC active (we have received CONTROL WRITE setup packets indicating real CDC host) ++ switch (network_private->encapsulation) { ++ int len; ++ case simple_crc: ++ ++ len = urb->actual_length; ++ ++ // allocate skb of appropriate length, reserve 2 to align ip ++ THROW_IF(!(skb = dev_alloc_skb (len + 2)), error); ++ skb_reserve(skb, 2); ++ ++#if defined(CONFIG_USBD_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 *dst = skb_put(skb, len - 1); ++ u8 *src = urb->buffer; ++ int copied; ++ u32 crc; ++ ++ // 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, *dst++ = *src++)); ++ ++ for (; (len-- > 0) && (CRC32_GOOD != crc); crc = COMPUTE_FCS (crc, *dst++ = *src++)); ++ ++ skb_trim(skb, skb->len - len - 4); ++ ++ if (CRC32_GOOD != crc) { ++ //printk(KERN_INFO"%s: AAA frame: %03x\n", __FUNCTION__, urb->framenum); ++ THROW_IF(network_private->crc, crc_error); ++ } ++ else ++ network_private->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: ++ * ++ * | ++ * <data> <CRC><CRC><CRC><CRC>|<???> case 1 ++ * <data> <NUL><CRC><CRC><CRC>|<CRC> case 2 ++ * <data> <NUL><CRC><CRC><CRC><CRC>| case 3 ++ * <data> <NUL><CRC><CRC><CRC>|<CRC> | 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 ++ u32 crc = crc32_copy(skb_put(skb, len - 1), urb->buffer, len - 1, CRC32_INIT); ++ ++ // if the CRC is good then this is case 1 ++ if (CRC32_GOOD != crc) { ++ ++ crc = crc32_copy(skb_put(skb, 1), urb->buffer + len - 1, 1, crc); ++ ++ if (CRC32_GOOD != crc) { ++ //crc_errors[len%64]++; ++ printk(KERN_INFO"%s: A CRC error %08x %03x\n", __FUNCTION__, crc, urb->framenum); ++ THROW_IF(network_private->crc, crc_error); ++ } ++ else ++ network_private->crc = 1; ++ } ++ else ++ network_private->crc = 1; ++ } ++ else { ++ u32 crc = crc32_copy(skb_put(skb, len), urb->buffer, len, CRC32_INIT); ++ ++ if (CRC32_GOOD != crc) { ++ //crc_errors[len%64]++; ++ //printk(KERN_INFO"%s: CCC\n", __FUNCTION__); ++ THROW_IF(network_private->crc, crc_error); ++ } ++ else ++ network_private->crc = 1; ++ } ++ // trim IFF we are paying attention to crc ++ if (network_private->crc) ++ skb_trim(skb, skb->len - 4); ++#endif ++ ++ // pass it up, free skb if non zero ++ THROW_IF(network_recv (network_private, net_device, skb), skb_error); ++ ++ break; ++ default: ++ break; ++ } ++ ++ // catch a simple error, just increment missed error and general error ++ CATCH(error) { ++ ++ network_private->stats.rx_frame_errors++; ++ network_private->stats.rx_errors++; ++ ++ // catch error where skb may need to be released ++ CATCH(skb_error) { ++ ++ // catch a CRC error ++ ++ // XXX We need to track whether we have seen a correct CRC, until then ++ // we ignore CRC errors. ++ ++ CATCH(crc_error) { ++#if 0 ++ printk(KERN_INFO"%s: urb: %p status: %d len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, ++ urb, urb->status, urb->actual_length, network_private->maxtransfer, ++ network_private->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 ++ network_private->stats.rx_crc_errors++; ++ network_private->stats.rx_errors++; ++ } ++ ++ // catch an overrun error ++ // (Only used if CONFIG_USBD_NETWORK_CDC is defined.) ++ //CATCH(fifo_error) { ++ // network_private->stats.rx_fifo_errors++; ++ // network_private->stats.rx_errors++; ++ //} ++ ++ // if skb defined free it ++ if (skb) ++ dev_kfree_skb_any (skb); ++ } ++ network_private->stats.rx_dropped++; ++ //return -EINVAL; ++ } ++ //printk(KERN_INFO"%s: restart: %p\n", __FUNCTION__, urb); ++ return (usbd_start_recv (urb)); ++} ++ ++//_________________________________________________________________________________________________ ++// network_urb_sent ++ ++/* network_urb_sent_bulk - callback function to process a sent URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * urb Pointer to the urb that has been sent. ++ * rc Result code from the send operation. ++ * ++ * Returns non-zero for failure. ++ */ ++static int network_urb_sent_bulk (struct urb *urb, int urb_rc) ++{ ++ struct sk_buff *skb; ++ struct net_device *net_device; ++ unsigned long flags; ++ struct usb_network_private *network_private = &Usb_network_private; ++ struct usb_function_instance *function = network_private->function; ++ int rc = -EINVAL; ++ ++ //printk(KERN_INFO"%s: urb: %p device: %p address: %x urb_rc: %d\n", __FUNCTION__, ++ // urb, urb->device, urb->endpoint->bEndpointAddress, urb_rc); ++ ++ local_irq_save(flags); ++ do { ++ ++ BREAK_IF(!urb); ++ BREAK_IF(!(function = urb->function_instance)); ++ ++ switch (urb_rc) { ++ case SEND_FINISHED_ERROR: ++ network_private->stats.tx_errors++; ++ network_private->stats.tx_dropped++; ++ break; ++ case SEND_FINISHED_CANCELLED: ++ network_private->stats.tx_errors++; ++ network_private->stats.tx_carrier_errors++; ++ break; ++ default: ++ break; ++ } ++ ++ // XXX should we zap skb first if error? ++ RETURN_EINVAL_IF(!(network_private->flags & NETWORK_CREATED)); ++ ++ // retrieve skb pointer and unlink from urb pointers ++ skb = (struct sk_buff *) urb->privdata; ++ ++ urb->privdata = NULL; ++ usbd_dealloc_urb (urb); ++ ++ // tell netproto we are done with the skb, it will test for NULL ++ // netproto_done (interface, skb, urb_rc != SEND_FINISHED_OK); ++ ++ BREAK_IF(!skb); ++ BREAK_IF(!(net_device = &Network_net_device)); ++ ++ ++ network_private->avg_queue_entries += network_private->queued_entries; ++ network_private->queued_entries--; ++ network_private->samples++; ++ network_private->jiffies += jiffies - *(time_t *) (&skb->cb); ++ network_private->queued_bytes -= skb->len; ++ ++ dev_kfree_skb_any (skb); ++ ++ if (netif_queue_stopped (net_device)) { ++ netif_wake_queue (net_device); ++ network_private->restarts++; ++ } ++ rc = 0; ++ ++ } while (0); ++ local_irq_restore(flags); ++ return rc; ++} ++ ++/* network_urb_sent_int - callback for sent URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * Returns non-zero for failure. ++ */ ++static int network_urb_sent_int (struct urb *urb, int urb_rc) ++{ ++ struct usb_function_instance *function; ++ struct sk_buff *skb; ++ struct net_device *net_device; ++ unsigned long flags; ++ int rc = -EINVAL; ++ struct usb_network_private *network_private = &Usb_network_private; ++ ++ //printk(KERN_INFO"%s: urb: %p device: %p address: %x urb_rc: %d\n", __FUNCTION__, ++ // urb, urb->device, urb->endpoint->bEndpointAddress, urb_rc); ++ ++ local_irq_save(flags); ++ do { ++ ++ BREAK_IF(!urb); ++ BREAK_IF(!(function = urb->function_instance)); ++ //RETURN_EINVAL_IF(!(network_private->flags & NETWORK_ATTACHED)); ++ ++ //printk(KERN_INFO"%s: reseting int_urb: %p\n", __FUNCTION__, network_private->int_urb); ++ usbd_dealloc_urb (urb); ++ network_private->int_urb = NULL; ++ rc = 0; ++ ++ } while (0); ++ local_irq_restore(flags); ++ return rc; ++} ++ ++ ++//_________________________________________________________________________________________________ ++// network_recv_setup_irq ++// ++/* network_urb_received_ep0 - callback for sent URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * Returns non-zero for failure. ++ */ ++static int network_urb_received_ep0 (struct urb *urb, int urb_rc) ++{ ++ printk(KERN_INFO"%s: urb: %p status: %d\n", __FUNCTION__, urb, urb->status); ++ ++ RETURN_EINVAL_IF (RECV_OK != urb->status); ++ ++ printk(KERN_INFO"%s: %s\n", __FUNCTION__, urb->buffer); ++ ++ return -EINVAL; // caller will de-allocate ++} ++ ++/* network_recv_setup_irq - process a received SETUP URB ++ * ++ * Processes a received setup packet and CONTROL WRITE data. ++ * Results for a CONTROL READ are placed in urb->buffer. ++ * ++ * Returns non-zero for failure. ++ */ ++static int network_recv_setup_irq (struct usb_device_request *request) ++{ ++ struct usb_network_private *network_private = &Usb_network_private; ++ struct usb_function_instance *function = network_private->function; ++ struct urb *urb; ++ ++ ++ // 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; ++ network_private->encapsulation = simple_crc; ++ return 0; ++ ++ case BELCARRA_PING: ++ //printk(KERN_INFO"%s: H2D VENDOR IP: %08x\n", __FUNCTION__, ip_addr); ++ if ((network_private->network_type == network_blan)) ++ notification_schedule_bh(); ++ break; ++ ++#if !defined(CONFIG_USBD_NETWORK_BLAN_DO_NOT_SETTIME) || !defined(CONFIG_USBD_NETWORK_SAFE_DO_NOT_SETTIME) ++ case BELCARRA_SETTIME: ++ { ++ struct timeval 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; ++ ++ //printk(KERN_INFO"%s: H2D VENDOR TIME: %08x\n", __FUNCTION__, tv.tv_sec); ++ ++ // set the time ++ do_settimeofday(&tv); ++ } break; ++#endif ++ case BELCARRA_SETIP: ++ ip_addr = ntohl( request->wValue << 16 | request->wIndex); ++ 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_USBD_NETWORK_BLAN_FERMAT ++ case BELCARRA_SETFERMAT: ++ network_private->fermat = 1; ++ break; ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN_HOSTNAME ++ case BELCARRA_HOSTNAME: ++ //printk(KERN_INFO"%s: HOSTNAME\n", __FUNCTION__); ++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, le16_to_cpu(request->wLength), ++ network_urb_received_ep0) )); ++ RETURN_ZERO_IF(!usbd_start_recv(urb)); // return if no error ++ usbd_dealloc_urb(urb); // de-alloc if error ++ 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, Network_net_device.dev_addr, ETH_ALEN); ++ urb->actual_length = ETH_ALEN; ++ return 0; ++ } ++ } ++#endif ++ return 0; ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++ ++//______________________________________ Hotplug Functions ________________________________________ ++ ++#ifdef CONFIG_HOTPLUG ++ ++#define AGENT "network_fd" ++ ++/* hotplug_attach - call hotplug ++ */ ++static int hotplug_attach (__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", Network_net_device.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]); ++ ++ //printk (KERN_INFO "%s: attach %s %s %s\n", __FUNCTION__, ifname, ip_str, count_str); ++ ++ envp[i++] = "ACTION=attach"; ++ envp[i++] = ip_str; ++ envp[i++] = mask_str; ++ envp[i++] = router_str; ++ } ++ else { ++ //printk (KERN_INFO "%s: detach %s %s\n", __FUNCTION__, ifname, count_str); ++ envp[i++] = "ACTION=detach"; ++ } ++ ++ 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_function_instance *function = network_private->function; ++ ++ //printk(KERN_INFO"%s: BUS state: %d status: %d\n", __FUNCTION__, usbd_device_state(function), usbd_bus_status(function)); ++ ++ if (function && (USBD_OK == usbd_bus_status(function)) && (STATE_CONFIGURED == usbd_device_state(function))) { ++ if (hotplug_attached != network_private->hotplug_status) { ++ //printk(KERN_INFO"%s: ATTACH\n", __FUNCTION__); ++ network_private->hotplug_status = hotplug_attached; ++ hotplug_attach (ip_addr, network_mask, router_ip, 1); ++ } ++ } ++ else { ++ if (hotplug_detached != network_private->hotplug_status) { ++ //printk(KERN_INFO"%s: DETACH\n", __FUNCTION__); ++ network_private->hotplug_status = hotplug_detached; ++ hotplug_attach (ip_addr, network_mask, router_ip, 0); ++ } ++ } ++ MOD_DEC_USE_COUNT; ++} ++ ++/* hotplug_schedule_bh - schedule a call to hotplug bottom half ++ */ ++static void hotplug_schedule_bh (void) ++{ ++ MOD_INC_USE_COUNT; ++ if (!schedule_task (&Usb_network_private.hotplug_bh)) ++ MOD_DEC_USE_COUNT; ++} ++#endif /* CONFIG_HOTPLUG */ ++ ++ ++//_________________________________________________________________________________________________ ++ ++#ifdef CONFIG_USBD_NETWORK_START_SINGLE ++#define NETWORK_START_URBS 1 ++#else ++#define NETWORK_START_URBS 2 ++#endif ++ ++/* network_start_recv - start recv urb(s) ++ */ ++void network_start_recv(struct usb_function_instance *function) ++{ ++ int i; ++ for (i = 0; i < NETWORK_START_URBS; i++) { ++ struct urb *urb; ++ BREAK_IF(!(urb = usbd_alloc_urb (function, BULK_OUT, ++ usbd_endpoint_transferSize(function, BULK_OUT, usbd_high_speed(function)), ++ network_recv_urb))); ++ //printk(KERN_INFO"%s: i: %d start: %p\n", __FUNCTION__, i, urb); ++ if (usbd_start_recv(urb)) ++ usbd_dealloc_urb(urb); ++ } ++} ++ ++/* network_event_irg - Processes a USB event. ++ */ ++static void network_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data) ++{ ++ struct usb_network_private *network_private = &Usb_network_private; ++ ++ switch (event) { ++ ++ case DEVICE_RESET: ++ case DEVICE_DESTROY: ++ case DEVICE_BUS_INACTIVE: ++ //printk(KERN_INFO"%s: RST %x\n", __FUNCTION__, ip_addr); ++ { ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ // Return if argument is null. ++ ++ // XXX flush ++ ++ network_private->flags &= ~NETWORK_ATTACHED; ++ network_private->int_urb = NULL; ++ ++ // Disable our net-device. ++ // Apparently it doesn't matter if we should do this more than once. ++ ++ netif_stop_queue(&Network_net_device); ++ netif_carrier_off(&Network_net_device); ++ ++ // If we aren't already tearing things down, do it now. ++ if (!(network_private->flags & NETWORK_DESTROYING)) { ++ network_private->flags |= NETWORK_DESTROYING; ++ //network_private->device = NULL; ++ } ++ } ++ break; ++ ++ case DEVICE_CONFIGURED: ++ case DEVICE_BUS_ACTIVITY: ++ //printk(KERN_INFO"%s: CFG %x\n", __FUNCTION__, ip_addr); ++ network_private->flags |= NETWORK_ATTACHED; ++ ++ if ((network_private->network_type == network_blan) && (network_private->flags & NETWORK_OPEN)) ++ notification_schedule_bh(); ++ ++ netif_carrier_on (&Network_net_device); ++ netif_wake_queue (&Network_net_device); ++ ++ network_start_recv(function); ++ ++#ifdef CONFIG_USBD_NET_NFS_SUPPORT ++ if (!usb_is_configured) { ++ wake_up(&usb_netif_wq); ++ usb_is_configured = 1; ++ } ++#endif ++ 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; ++ } ++#ifdef CONFIG_HOTPLUG ++ hotplug_schedule_bh(); ++#endif ++} ++ ++//_________________________________________________________________________________________________ ++ ++/* network_function_enable - enable the function driver ++ * ++ * Called for usbd_function_enable() from usbd_register_device() ++ */ ++ ++static int network_function_enable (struct usb_function_instance *function) ++{ ++ //printk(KERN_INFO"%s: DOWN\n", __FUNCTION__); ++ down(&usbd_network_sem); ++ MOD_INC_USE_COUNT; // QQQ Should this be _before_ the down()? ++ ++ // set the network device address from the local device address ++ memcpy(Network_net_device.dev_addr, local_dev_addr, ETH_ALEN); ++ ++ //Usb_network_private.bus = function->bus; ++ Usb_network_private.function = function; ++ Usb_network_private.have_interrupt = usbd_endpoint_bEndpointAddress(function, INT_IN, usbd_high_speed(function)) ? 1 : 0; ++ ++ Usb_network_private.flags |= NETWORK_ENABLED; ++ ++#if defined(CONFIG_USBD_NETWORK_CDC) ++ cdc_init(function); ++#endif /* CONFIG_USBD_NETWORK_CDC */ ++ ++#ifdef CONFIG_USBD_NETWORK_BASIC ++ basic_init(function); ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_BASIC2 ++ basic2_init(function); ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_SAFE ++ safe_init(function); ++#endif ++#ifdef CONFIG_USBD_NETWORK_BLAN ++ blan_init(function); ++#endif ++ up(&usbd_network_sem); ++ //printk(KERN_INFO"%s: UP\n", __FUNCTION__); ++ return 0; ++} ++ ++/* network_functino_disable - disable the function driver ++ * ++ */ ++static void network_function_disable (struct usb_function_instance *function) ++{ ++ //printk(KERN_INFO"%s: DOWN\n", __FUNCTION__); ++ down(&usbd_network_sem); ++ Usb_network_private.flags &= ~NETWORK_ENABLED; ++ //Usb_network_private.bus = NULL; ++ Usb_network_private.function = NULL; ++ MOD_DEC_USE_COUNT; // QQQ Should this be _after_ the up()? ++ up(&usbd_network_sem); ++ //printk(KERN_INFO"%s: UP\n", __FUNCTION__); ++} ++ ++struct usb_function_operations network_fd_function_ops = { ++ recv_setup_irq: network_recv_setup_irq, ++ event_irq: network_event_irq, ++ function_enable: network_function_enable, ++ function_disable: network_function_disable, ++}; ++ ++//_________________________________________________________________________________________________ ++ ++/* network_create - create and initialize network private structure ++ * ++ * Returns non-zero for failure. ++ */ ++static int network_create (void) ++{ ++ struct usb_network_private *network_private = &Usb_network_private; ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ // Set some fields to generic defaults and register the network device with the kernel networking code ++ ++ memset(&network_private->stats, 0, sizeof network_private->stats); ++ ++ ether_setup (&Network_net_device); ++ RETURN_EINVAL_IF (register_netdev(&Network_net_device)); ++ ++ netif_stop_queue (&Network_net_device); ++ netif_carrier_off (&Network_net_device); ++ Network_net_device.flags &= ~IFF_UP; ++ ++ network_private->flags |= NETWORK_CREATED; ++ ++ network_private->maxtransfer = MAXFRAMESIZE + 4 + 64; ++ ++ network_private->flags |= NETWORK_REGISTERED; ++ ++ network_private->network_type = network_unknown; ++ ++ //printk(KERN_INFO"%s: finis\n", __FUNCTION__); ++ return 0; ++} ++ ++/* network_destroy - destroy network private struture ++ * ++ * Destroys the network interface referenced by the global variable @c network_private. ++ */ ++static void network_destroy (void) ++{ ++ if (Usb_network_private.flags & NETWORK_REGISTERED) { ++ netif_stop_queue (&Network_net_device); ++ netif_carrier_off (&Network_net_device); ++ unregister_netdev (&Network_net_device); ++ } ++ Usb_network_private.flags = 0; ++} ++ ++ ++//______________________________________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 - ++ */ ++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 - ++ */ ++int macstrtest(char *mac_address_str) ++{ ++ int l = 0; ++ ++ if (mac_address_str) { ++ l = strlen(mac_address_str); ++ } ++ return ((l != 0) && (l != 12)); ++} ++ ++/* network_modinit - driver intialization ++ * ++ * Returns non-zero for failure. ++ */ ++static int network_modinit (void) ++{ ++ network_type_t network_type = network_unknown; ++ ++ init_waitqueue_head(&usb_netif_wq); ++ ++ ++ printk(KERN_INFO "Copyright (c) 2002-2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n"); ++ printk(KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", __FUNCTION__, __usbd_module_info, vendor_id, product_id); ++ ++#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT ++ //printk(KERN_INFO "%s: fermat\n", __FUNCTION__); ++ fermat_init(); ++#endif ++ ++#if defined(CONFIG_USBD_NETWORK_CDC) ++ if (cdc) { ++ //printk(KERN_INFO "%s: cdc\n", __FUNCTION__); ++ network_type = network_cdc; ++ } ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_BASIC ++ if (basic) { ++ THROW_IF (network_type != network_unknown, select_error); ++ //printk(KERN_INFO "%s: basic\n", __FUNCTION__); ++ network_type = network_basic; ++ } ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_BASIC2 ++ if (basic2) { ++ THROW_IF (network_type != network_unknown, select_error); ++ //printk(KERN_INFO "%s: basic2\n", __FUNCTION__); ++ network_type = network_basic2; ++ } ++#endif ++ ++#ifdef CONFIG_USBD_NETWORK_BLAN ++ if (blan) { ++ //printk(KERN_INFO "%s: blan\n", __FUNCTION__); ++ THROW_IF (network_type != network_unknown, select_error); ++ network_type = network_blan; ++ } ++#endif ++ ++#if defined(CONFIG_USBD_NETWORK_CDC) ++ if (network_type == network_unknown) { ++ //printk(KERN_INFO "%s: cdc\n", __FUNCTION__); ++ network_type = network_cdc; ++ } ++#endif ++ ++ // still unknown - check for other bNumConfigurations ++ ++ if (network_type == network_unknown) { ++#if defined(CONFIG_USBD_NETWORK_BASIC) ++ //printk(KERN_INFO "%s: basic\n", __FUNCTION__); ++ THROW_IF (network_type != network_unknown, select_error); ++ network_type = network_basic; ++#endif ++#if defined(CONFIG_USBD_NETWORK_BASIC2) ++ //printk(KERN_INFO "%s: basic2\n", __FUNCTION__); ++ THROW_IF (network_type != network_unknown, select_error); ++ network_type = network_basic2; ++#endif ++#if defined(CONFIG_USBD_NETWORK_SAFE) ++ //printk(KERN_INFO "%s: safe\n", __FUNCTION__); ++ THROW_IF (network_type != network_unknown, select_error); ++ network_type = network_safe; ++#endif ++#if defined(CONFIG_USBD_NETWORK_BLAN) ++ //printk(KERN_INFO "%s: blan\n", __FUNCTION__); ++ THROW_IF (network_type != network_unknown, select_error); ++ network_type = network_blan; ++#endif ++ } ++ ++ // sanity check ++ THROW_IF (network_type == network_unknown, select_error); ++ ++ // select the function driver descriptors based on network_type ++ ++ switch (network_type) { ++ ++#if defined(CONFIG_USBD_NETWORK_BASIC) ++ case network_basic: ++ //printk(KERN_INFO "%s: basic\n", __FUNCTION__); ++ function_driver = &basic_function_driver; ++ break; ++#endif ++ ++#if defined(CONFIG_USBD_NETWORK_BASIC2) ++ case network_basic2: ++ //printk(KERN_INFO "%s: basic2\n", __FUNCTION__); ++ function_driver = &basic2_function_driver; ++ break; ++#endif ++ ++#if defined(CONFIG_USBD_NETWORK_SAFE) ++ case network_safe: ++ //printk(KERN_INFO "%s: blan bNumConfigurations: %d\n", __FUNCTION__, blan_function_driver.bNumConfigurations); ++ function_driver = &safe_function_driver; ++ break; ++#endif ++#if defined(CONFIG_USBD_NETWORK_BLAN) ++ case network_blan: ++ //printk(KERN_INFO "%s: blan bNumConfigurations: %d\n", __FUNCTION__, blan_function_driver.bNumConfigurations); ++ function_driver = &blan_function_driver; ++ break; ++#endif ++ ++#if defined(CONFIG_USBD_NETWORK_CDC) ++ case network_cdc: ++ function_driver = &cdc_function_driver; ++ break; ++#endif ++ default: ++ THROW(select_error); ++ break; ++ } ++ ++ strncpy(Network_net_device.name, network_type == zeroconf ? "usbz0" : (network_blan ? "usbl0" : "usbb0"), 6); ++ ++ ++ THROW_IF (!function_driver, select_error); ++ ++ CATCH(select_error) { ++ printk(KERN_INFO "%s: configuration selection error\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ ++#ifdef CONFIG_USBD_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. ++ * ++ */ ++ if (ep0test) { ++ switch (ep0test) { ++ case 8: function_driver->device_description->iProduct = "012"; break; ++ case 16: function_driver->device_description->iProduct = "0123456"; break; ++ case 32: function_driver->device_description->iProduct = "0123456789abcde"; break; ++ case 64: 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; ++ } ++ printk(KERN_INFO"%s: ep0test: iProduct set to: %s\n", __FUNCTION__, ++ function_driver->device_description->iProduct); ++ } ++ else ++ printk(KERN_INFO"%s: ep0test: not set\n", __FUNCTION__); ++#endif /* CONFIG_USBD_NETWORK_EP0TEST */ ++ ++ ++#ifdef CONFIG_USBD_NETWORK_ALLOW_SETID ++ //printk(KERN_INFO"%s: checking idVendor: %04x idProduct: %04x\n", __FUNCTION__, vendor_id, product_id); ++ ++ if (vendor_id) ++ function_driver->idVendor = cpu_to_le16(vendor_id); ++ ++ if (product_id) ++ function_driver->idProduct = cpu_to_le16(product_id); ++#endif ++ ++ if ((macstrtest(local_mac_address_str) || macstrtest(remote_mac_address_str))) { ++ printk(KERN_INFO"%s: bad size %s %s\n", __FUNCTION__, local_mac_address_str, remote_mac_address_str); ++ return -EINVAL; ++ } ++ ++ set_address(local_mac_address_str, local_dev_addr); ++ set_address(remote_mac_address_str, remote_dev_addr); ++ ++ RETURN_EINVAL_IF(network_create()); ++ ++ Usb_network_private.network_type = network_type; ++ ++ Usb_network_private.notification_bh.routine = notification_bh; ++ Usb_network_private.notification_bh.data = NULL; ++ ++#ifdef CONFIG_HOTPLUG ++ Usb_network_private.hotplug_bh.routine = hotplug_bh; ++ Usb_network_private.hotplug_bh.data = NULL; ++#endif ++ ++ memcpy(Network_net_device.dev_addr, local_dev_addr, ETH_ALEN); ++ ++ THROW_IF(make_crc_table(), error); ++ ++ Usb_network_private.encapsulation = simple_crc; ++ ++ THROW_IF(usbd_register_function (function_driver), error); ++ ++ return 0; ++ ++ CATCH(error) { ++ network_destroy(); ++ return -EINVAL; ++ } ++} ++ ++//_________________________________________________________________________________________________ ++ ++/* network_modexit - driver exit ++ * ++ * Cleans up the module. Deregisters the function driver and destroys the network object. ++ */ ++static void network_modexit (void) ++{ ++ //printk(KERN_INFO"%s: exiting\n", __FUNCTION__); ++ ++ while (Usb_network_private.notification_bh.sync) { ++ printk(KERN_ERR"%s: waiting for notificationhotplug bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ ++#ifdef CONFIG_HOTPLUG ++ while (Usb_network_private.hotplug_bh.sync) { ++ printk(KERN_ERR"%s: waiting for hotplug bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++#endif ++ ++ usbd_deregister_function (function_driver); ++ network_destroy(); ++ if (network_crc32_table) { ++ lkfree(network_crc32_table); ++ network_crc32_table = NULL; ++ } ++} ++ ++//_________________________________________________________________________________________________ ++ ++module_init (network_modinit); ++module_exit (network_modexit); ++ +diff -Nru a/drivers/usbd/network_fd/network.h b/drivers/usbd/network_fd/network.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/network.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,203 @@ ++/* ++ * usbd/network_fd/network.h - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef NETWORK_FD_H ++#define NETWORK_FD_H 1 ++ ++typedef enum network_encapsulation { ++ simple_net, simple_crc, ++} network_encapsulation_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_type_t; ++ ++struct usb_network_private { ++ ++ struct net_device_stats stats; /* network device statistics */ ++ ++ int flags; ++ struct usb_function_instance *function; ++ unsigned int maxtransfer; ++ rwlock_t rwlock; ++ ++ network_hotplug_status_t hotplug_status; ++ network_type_t network_type; ++ ++ int state; ++ ++ int mtu; ++ int crc; ++#if defined(CONFIG_USBD_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; ++ ++ struct urb *int_urb; ++ ++ network_encapsulation_t encapsulation; ++ ++ struct tq_struct notification_bh; ++ ++#ifdef CONFIG_HOTPLUG ++ struct tq_struct hotplug_bh; ++#endif ++}; ++ ++// XXX this needs to be co-ordinated with rndis.c maximum's ++#define MAXFRAMESIZE 2000 ++ ++#if !defined(CONFIG_USBD_MAXPOWER) ++ #define CONFIG_USBD_MAXPOWER 0 ++#endif ++ ++#if !defined(CONFIG_USBD_MANUFACTURER) ++ #define CONFIG_USBD_MANUFACTURER "Belcarra" ++#endif ++ ++ ++#if !defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ #define CONFIG_USBD_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_NOBRIDGE 0x02 ++ ++ ++/* ++ * BLAN Data Plane ++ */ ++//#define CONFIG_USBD_NETWORK_PADBYTES 8 ++//#define CONFIG_USBD_NETWORK_PADAFTER 1 ++//#undef CONFIG_USBD_NETWORK_PADBEFORE ++//#define CONFIG_USBD_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 usb_function_operations network_fd_function_ops; ++extern struct usb_network_private Usb_network_private; ++ ++struct usb_class_safe_networking_mdlm_descriptor { ++ __u8 bFunctionLength; // 0x06 ++ __u8 bDescriptorType; // 0x24 ++ __u8 bDescriptorSubtype; // 0x13 ++ __u8 bGuidDescriptorType; // 0x00 ++ __u8 bmNetworkCapabilities; ++ __u8 bmDataCapabilities; ++} __attribute__ ((packed)); ++ ++struct usb_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)); ++ ++ ++ ++ ++ ++#endif /* NETWORK_FD_H */ +diff -Nru a/drivers/usbd/network_fd/safe.c b/drivers/usbd/network_fd/safe.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/network_fd/safe.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,224 @@ ++/* ++ * usbd/network_fd/safe.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-func.h> ++ ++#include "network.h" ++ ++ ++#ifdef CONFIG_USBD_NETWORK_SAFE ++/* USB SAFE Configuration ******************************************************************** */ ++ ++/* ++ * SAFE Ethernet Configuration ++ */ ++ ++/* Communication Interface Class descriptors ++ */ ++ ++static __u8 safe_data_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK, 0, 0x00, 0x00, }; ++static __u8 safe_data_2[] = { 0x07, USB_DT_ENDPOINT, IN, BULK, 0, 0x00, 0x00, }; ++static __u8 safe_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN, INTERRUPT,0, 0x00, 0x0a, }; ++ ++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 , }; ++ ++ ++static __u8 *safe_alt_endpoints[] = { ++ (struct usb_endpoint_descriptor *) &safe_data_1, ++ (struct usb_endpoint_descriptor *) &safe_data_2, ++ (struct usb_endpoint_descriptor *) &safe_comm_1, }; ++static __u8 *safe_comm_class_descriptors[] = { ++ (struct usb_generic_class_descriptor *) &safe_class_1, ++ (struct usb_generic_class_descriptor *) &safe_class_2, ++ (struct usb_generic_class_descriptor *) &safe_class_3, ++ (struct usb_generic_class_descriptor *) &safe_class_4, }; ++ ++u8 safe_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++/* Data Interface Alternate descriptions and descriptors ++ */ ++static __u8 safe_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (safe_alt_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_MDLM_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, ++}; ++ ++static struct usb_alternate_description safe_alternate_descriptions[] = { ++ { iInterface: CONFIG_USBD_NETWORK_SAFE_INTF, ++ interface_descriptor: (struct usb_interface_descriptor *)&safe_alternate_descriptor, ++ classes:sizeof (safe_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *), ++ class_list: safe_comm_class_descriptors, ++ endpoints:sizeof (safe_alt_endpoints) / sizeof(struct usb_endpoint_descriptor *), ++ endpoint_list: safe_alt_endpoints, ++ endpoint_indexes: safe_alt_indexes, ++ }, ++}; ++ ++/* Interface descriptions and descriptors ++ */ ++static struct usb_interface_description safe_interfaces[] = { ++ { alternates:sizeof (safe_alternate_descriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:safe_alternate_descriptions, }, ++}; ++ ++ ++/* Configuration descriptions and descriptors ++ */ ++ ++static __u8 safe_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength ++ sizeof (safe_interfaces) / sizeof (struct usb_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ BMATTRIBUTE, BMAXPOWER, ++}; ++ ++struct usb_configuration_description safe_description[] = { ++ { iConfiguration: CONFIG_USBD_NETWORK_SAFE_DESC, ++ configuration_descriptor: (struct usb_configuration_descriptor *)safe_configuration_descriptor, ++ bNumInterfaces:sizeof (safe_interfaces) / sizeof (struct usb_interface_description), ++ interface_list:safe_interfaces, }, ++}; ++ ++/* Device Description ++ */ ++ ++//static __u8 safe_device_descriptor[sizeof(struct usb_device_descriptor)] = { ++// 0x12, USB_DT_DEVICE, ++// 0x00, 0x02, // bcdUSB ++// COMMUNICATIONS_DEVICE_CLASS, ++// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++//}; ++static struct usb_device_descriptor safe_device_descriptor = { ++ bLength: sizeof(struct usb_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_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++ ++static struct usb_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, }, ++}; ++ ++struct usb_device_description safe_device_description = { ++ device_descriptor: &safe_device_descriptor, ++ iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, ++#endif ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: safe_endpoint_requests, ++}; ++ ++void safe_init (struct usb_function_instance *function) ++{ ++ struct usb_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 = (struct usb_class_ethernet_networking_descriptor *)safe_class_4)) { ++ if (ethernet->iMACAddress) { ++ usbd_dealloc_string(ethernet->iMACAddress); ++ } ++ ethernet->iMACAddress = usbd_alloc_string(address_str); ++ } ++ } ++ ++ ++#ifdef CONFIG_USBD_NETWORK_SAFE_PADBEFORE ++ safe_class_3[5] |= BMDATA_PADBEFORE; ++ len += sprintf(buf + len, "PADBEFORE: %02x ", safe_class_3[5]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_SAFE_CRC ++ safe_class_3[5] |= BMDATA_CRC; ++ len += sprintf(buf + len, "CRC: %02x ", safe_class_3[5]); ++#endif ++#ifdef CONFIG_USBD_NETWORK_SAFE_NOBRIDGE ++ safe_class_3[4] |= BMNETWORK_NOBRIDGE; ++ len += sprintf(buf + len, "NOBRIDGE: %02x ",safe_class_3[4]); ++#endif ++ if (strlen(buf)) ++ printk(KERN_INFO"%s: %s\n", __FUNCTION__, buf); ++ ++} ++ ++struct usb_function_driver safe_function_driver = { ++ name: "network-SAFE", ++ fops: &network_fd_function_ops, ++ device_description: &safe_device_description, ++ bNumConfigurations: sizeof (safe_description) / sizeof (struct usb_configuration_description), ++ configuration_description: safe_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE), ++}; ++#endif /* CONFIG_USBD_NETWORK_SAFE */ ++ +diff -Nru a/drivers/usbd/trace.c b/drivers/usbd/trace.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/trace.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,509 @@ ++/* ++ * usbd/trace.c ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/version.h> ++ ++//EXPORT_NO_SYMBOLS; ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/pci.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++ ++#include <linux/proc_fs.h> ++#include <linux/vmalloc.h> ++ ++#include <asm/atomic.h> ++#include <asm/io.h> ++ ++#include <linux/proc_fs.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) ++#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1 ++#include <linux/timer.h> ++#else ++#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK ++#include <linux/tqueue.h> ++#endif ++ ++#include <linux/pci.h> ++#include <linux/cache.h> ++ ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++#include <asm/dma.h> ++#include <asm/mach/dma.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/hardware.h> ++#include <asm/types.h> ++#endif ++ ++#if defined(CONFIG_SOC_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1X00) || \ ++ defined(CONFIG_CPU_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1000) || \ ++ defined(CONFIG_MIPS_PB1500) || \ ++ defined(CONFIG_MIPS_PB1100) ++ ++#include <asm/au1000.h> ++#include <asm/au1000_dma.h> ++#include <asm/mipsregs.h> ++ ++#endif /* defined(..) */ ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#include <asm/arch/timers.h> ++#include <asm/arch/hardware.h> ++#endif /* defined(CONFIG_ARCH_SAMSUNG) */ ++ ++#if defined(CONFIG_ARCH_MX1ADS) ++#include "dbmx1_bi/dbmx1.h" ++#endif /* defined(CONFIG_ARCH_MX1ADS) */ ++ ++#include <asm/uaccess.h> ++#include <asm/io.h> ++#include <asm/pgtable.h> ++#include <asm/pgalloc.h> ++ ++#include "usbd-chap9.h" ++#include "usbd-mem.h" ++#include "usbd.h" ++#include "usbd-func.h" ++#include "usbd-bus.h" ++ ++#include "trace.h" ++#include "usbd-bi.h" ++ ++ ++int trace_first; ++int trace_next; ++trace_t *traces; ++ ++#if defined(CONFIG_USBD_BI_REGISTER_TRACE) && defined(CONFIG_PROC_FS) ++ ++trace_t *TRACE_NEXT(trace_types_t trace_type) ++{ ++ trace_t *p; ++ ++ p = traces + trace_next; ++ ++ #if defined(CONFIG_ARCH_SA1100) || \ ++ defined (CONFIG_ARCH_PXA) ++ ++ p->oscr = OSCR; ++ ++ #elif defined(CONFIG_SOC_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1X00) || \ ++ defined(CONFIG_CPU_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1000) || \ ++ defined(CONFIG_MIPS_PB1500) || \ ++ defined(CONFIG_MIPS_PB1100) ++ ++ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) ++ p->cp0_count = __read_32bit_c0_register(CP0_COUNT); ++ #else ++ p->cp0_count = read_c0_count(); ++ #endif ++ ++ #elif defined(CONFIG_ARCH_SAMSUNG) ++ p->tcnt1 = *(volatile u32 *)TCNT1; ++ #else ++ p->jiffies = jiffies; ++ #endif ++ ++ #if defined(CONFIG_SOC_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1X00) || \ ++ defined(CONFIG_CPU_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1000) || \ ++ defined(CONFIG_MIPS_PB1500) || \ ++ defined(CONFIG_MIPS_PB1100) ++ //p->sofs = au_readl(USBD_FRAMENUM); ++ #endif ++ ++ #if defined(CONFIG_ARCH_MX1ADS) ++ p->sofs = USBD_FRAME & 0x3ff; ++ #endif ++ ++ p->interrupts = udc_interrupts; ++ p->trace_type = trace_type; ++ ++ trace_next++; ++ trace_next = (trace_next == TRACE_MAX) ? 0 : trace_next; ++ ++ if (trace_next == trace_first) { ++ trace_first++; ++ trace_first = (trace_first == TRACE_MAX) ? 0 : trace_first; ++ } ++ ++ return p; ++} ++ ++/* Proc Filesystem *************************************************************************** */ ++ ++static __inline__ char *nullchk(char *s) ++{ ++ return s ? s : "(NULL)"; ++} ++/* * ++ * trace_proc_read - implement proc file system read. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Standard proc file system read function. ++ */ ++static ssize_t 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; ++ ++ #ifdef MODULE ++ MOD_INC_USE_COUNT; ++ #endif ++ // get a page, max 4095 bytes of data... ++ if (!(page = get_free_page (GFP_KERNEL))) { ++ ++ #ifdef MODULE ++ MOD_DEC_USE_COUNT; ++ #endif ++ return -ENOMEM; ++ } ++ ++ len = 0; ++ oindex = index = (*pos)++; ++ ++ if (index == 0) { ++ #if defined(CONFIG_ARCH_SAMSUNG) ++ len += sprintf ((char *) page + len, " Index Ints Ticks [%d]\n", CONFIG_USBD_SMDK2500_BCLOCK ); ++ #else ++ len += sprintf ((char *) page + len, " Index Ints Ticks\n"); ++ #endif ++ } ++ ++ index += 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", trace_first, trace_next, oindex, index, previous); ++ ++ if ( ++ ((trace_first < trace_next) && (index >= trace_first) && (index < trace_next)) || ++ ((trace_first > trace_next) && ((index < trace_next) || (index >= trace_first))) ++ ) ++ { ++ ++ #if defined(CONFIG_SOC_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1X00) || \ ++ defined(CONFIG_CPU_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1000) || \ ++ defined(CONFIG_MIPS_PB1500) || \ ++ defined(CONFIG_MIPS_PB1100) ++ ++ u32 ticks = 0; ++ ++ #elif defined(CONFIG_ARCH_SAMSUNG) ++ u32 ticks = 0; ++ #elif defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ u32 ticks = 0; ++ u64 jifs = 0; ++ #else ++ u64 jifs = 0; ++ #endif ++ ++ trace_t *p = traces + index; ++ unsigned char *cp; ++ int skip = 0; ++ ++ if (oindex > 0) { ++ trace_t *o = 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->oscr) { ++ ticks = (p->oscr > o->oscr) ? (p->oscr - o->oscr) : (o->oscr - p->oscr) ; ++ ticks = (ticks * 60) / 221; ++ } ++ ++ #elif defined(CONFIG_SOC_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1X00) || \ ++ defined(CONFIG_CPU_AU1X00) || \ ++ 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 = ticks / CONFIG_USBD_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_USBD_SMDK2500_BCLOCK; ++ } ++ #else ++ if (o->jiffies) { ++ jifs = p->jiffies - traces[previous].jiffies; ++ } ++ #endif ++ ++ 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 defined(CONFIG_SOC_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1X00) || \ ++ defined(CONFIG_CPU_AU1X00) || \ ++ defined(CONFIG_MIPS_AU1000) || \ ++ defined(CONFIG_MIPS_PB1500) || \ ++ defined(CONFIG_MIPS_PB1100) || \ ++ defined(CONFIG_ARCH_SA1100) || \ ++ defined(CONFIG_ARCH_PXA) ++ ++ if (ticks > 1024*1024) { ++ len += sprintf ((char *) page + len, "%8dM ", ticks>>20); ++ } ++ else { ++ len += sprintf ((char *) page + len, "%8d ", 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, "%4d ", (int)jifs>>20); ++ } ++ else { ++ len += sprintf ((char *) page + len, "%4d ", (int)jifs); ++ } ++ #endif ++ #if defined(CONFIG_ARCH_MX1ADS) ++ len += sprintf ((char *) page + len, "%6d ", (int)p->sofs); ++ #endif ++ ++ switch (p->trace_type) { ++ case trace_msg: ++ len += sprintf ((char *) page + len, " -- "); ++ len += sprintf ((char *) page + len, p->trace.msg.msg); ++ break; ++ ++ case trace_w: ++ len += sprintf ((char *) page + len, " --> "); ++ len += sprintf ((char *) page + len, "[%8x] W %s", p->trace.msg32.val, nullchk(p->trace.msg32.msg)); ++ break; ++ ++ case trace_r: ++ len += sprintf ((char *) page + len, "<-- "); ++ len += sprintf ((char *) page + len, "[%8x] R %s", p->trace.msg32.val, nullchk(p->trace.msg32.msg)); ++ break; ++ ++ case trace_msg32: ++ len += sprintf ((char *) page + len, " -- "); ++ len += sprintf ((char *) page + len, p->trace.msg32.msg, p->trace.msg32.val); ++ break; ++ ++ case 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 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 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 trace_recv: ++ case trace_sent: ++ cp = (unsigned char *)&p->trace.sent; ++ len += sprintf ((char *) page + len, ++ "%s %s [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ ( p->trace_type == trace_recv)?"<-- ":" -->", ++ ( p->trace_type == trace_recv)?"recv":"sent", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++ 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; ++} ++ ++/* * ++ * 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 trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos) ++{ ++ return count; ++} ++ ++static struct file_operations trace_proc_operations_functions = { ++ read:trace_proc_read, ++ write:trace_proc_write, ++}; ++ ++int trace_reinit (void) ++{ ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ //trace_first = trace_next = 0; ++ return 0; ++} ++ ++/** ++ * udc_request_udc_io - request UDC io region ++ * ++ * Return non-zero if not successful. ++ */ ++int trace_init (void) ++{ ++ struct proc_dir_entry *p; ++ trace_first = trace_next = 0; ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ if (!(traces = vmalloc(sizeof(trace_t) * TRACE_MAX))) { ++ printk(KERN_ERR"BITRACE malloc failed %p %d\n", traces, sizeof(trace_t) * TRACE_MAX); ++ return -EINVAL; ++ } ++ ++ memset(traces, 0, sizeof(trace_t) * TRACE_MAX); ++ ++ TRACE_MSG("init"); ++ TRACE_MSG("test"); ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry ("bitrace", 0, 0)) == NULL) ++ printk(KERN_INFO"BITRACE PROC FS failed\n"); ++ else ++ p->proc_fops = &trace_proc_operations_functions; ++ ++ #if defined(CONFIG_ARCH_SAMSUNG) ++ *(volatile u32 *)TMOD |= 0x3 << 3; ++ #endif ++ return 0; ++} ++ ++/** ++ * udc_release_io - release UDC io region ++ */ ++void trace_exit (void) ++{ ++ { ++ unsigned long flags; ++ local_irq_save (flags); ++ remove_proc_entry ("bitrace", NULL); ++ if (traces) { ++ trace_t *p = traces; ++ traces = 0; ++ vfree(p); ++ } ++ local_irq_restore (flags); ++ } ++} ++ ++#else ++int trace_reinit (void) ++{ ++ return 0; ++} ++ ++int trace_init (void) ++{ ++ return 0; ++} ++ ++void trace_exit (void) ++{ ++ return; ++} ++#endif ++ ++/* End of FILE */ ++ ++ ++//EXPORT_SYMBOL(trace_first); ++//EXPORT_SYMBOL(trace_next); ++//EXPORT_SYMBOL(traces); ++ +diff -Nru a/drivers/usbd/trace.h b/drivers/usbd/trace.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/trace.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,270 @@ ++/* ++ * usbd/trace.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ * 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. ++ * ++ */ ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#ifndef CONFIG_USBD_SMDK2500_BCLOCK ++#define CONFIG_USBD_SMDK2500_BCLOCK 66 ++#endif ++#endif ++ ++ ++typedef enum trace_int_types { ++ trace_int_udc, trace_int_ep0, trace_int_in, trace_int_out, trace_int_int ++} trace_int_types_t; ++ ++typedef struct trace_int { ++ u32 last; ++ u32 total; ++ u32 samples; ++} trace_int_t; ++ ++ ++typedef enum trace_types { ++ trace_setup, trace_msg, trace_msg32, trace_msg16, trace_msg8, trace_recv, trace_sent, trace_w, trace_r ++} trace_types_t; ++ ++ ++typedef struct trace_regs32 { ++ u32 reg; ++ char * msg; ++} trace_regs32_t; ++ ++ ++typedef struct trace_msg { ++ char *msg; ++} trace_msg_t; ++ ++typedef struct trace_msg32 { ++ u32 val; ++ char *msg; ++} trace_msg32_t; ++ ++typedef struct trace_msg16 { ++ u16 val0; ++ u16 val1; ++ char *msg; ++} trace_msg16_t; ++ ++typedef struct trace_msg8 { ++ u8 val0; ++ u8 val1; ++ u8 val2; ++ u8 val3; ++ char *msg; ++} trace_msg8_t; ++ ++ ++typedef struct trace { ++ trace_types_t trace_type; ++ u32 interrupts; ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ u32 oscr; ++ ++#elif defined(CONFIG_SOC_AU1X00) || defined(CONFIG_MIPS_AU1X00) || defined(CONFIG_CPU_AU1X00) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++ ++ u32 cp0_count; ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ //u32 tcnt0; ++ u32 tcnt1; ++ //u64 jiffies; ++#else ++ u64 jiffies; ++#endif ++#if defined(CONFIG_SOC_AU1X00) || defined(CONFIG_MIPS_AU1X00) || defined(CONFIG_CPU_AU1X00) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_ARCH_MX1ADS) ++ u64 sofs; ++#endif ++ union { ++ trace_msg_t msg; ++ trace_msg8_t msg8; ++ trace_msg16_t msg16; ++ trace_msg32_t msg32; ++ ++ struct usb_device_request setup; ++ unsigned char recv[8]; ++ unsigned char sent[8]; ++ ++ } trace; ++ ++} trace_t; ++ ++ ++#define TRACE_MAX 10000 ++ ++extern int trace_first; ++extern int trace_next; ++ ++extern trace_int_t *trace_ints; ++extern trace_t *traces; ++ ++#ifdef CONFIG_USBD_BI_REGISTER_TRACE ++ ++trace_t *TRACE_NEXT(trace_types_t trace_type); ++ ++static __inline__ void TRACE_SETUP(struct usb_device_request *setup) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(trace_setup); ++ p->trace_type = trace_setup; ++ memcpy(&p->trace.setup, setup, sizeof(struct usb_device_request)); ++ } ++} ++ ++static __inline__ void TRACE_MSG(char *msg) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(trace_msg); ++ p->trace.msg.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_W(char *msg, u32 val) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(trace_w); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_R(char *msg, u32 val) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(trace_r); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_MSG32(char *msg, u32 val) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(trace_msg32); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_MSG16(char *msg, u16 val0, u16 val1) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(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 (traces) { ++ trace_t *p = TRACE_NEXT(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 (traces) { ++ trace_t *p = TRACE_NEXT(trace_recv); ++ memcpy(&p->trace.recv, cp, 8); ++ } ++} ++ ++static __inline__ void TRACE_RECVN(unsigned char *cp, int bytes) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(trace_recv); ++ memset(&p->trace.recv, 0, 8); ++ memcpy(&p->trace.recv, cp, bytes); ++ } ++} ++ ++static __inline__ void TRACE_SENT(unsigned char *cp) ++{ ++ if (traces) { ++ trace_t *p = TRACE_NEXT(trace_sent); ++ memcpy(&p->trace.sent, cp, 8); ++ } ++} ++ ++#else ++ ++static __inline__ void TRACE_SETUP(struct usb_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_MSG8(char *msg, u8 val0, u8 val1, u8 val2, u8 vale) ++{ ++} ++static __inline__ void TRACE_RECVN(unsigned char *cp, int bytes) ++{ ++} ++ ++#endif ++ ++int trace_init (void); ++int trace_reinit (void); ++void trace_exit (void); ++ +diff -Nru a/drivers/usbd/udc.h b/drivers/usbd/udc.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/udc.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,48 @@ ++/* ++ * usbd/udc.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it under the terms of ++ * the GNU General Public License as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ++ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along with this program; if ++ * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <asm/atomic.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/types.h> ++#include <asm/uaccess.h> ++#include <asm/io.h> ++ ++#include <usbd-export.h> ++#include <usbd-build.h> ++#include <usbd-chap9.h> ++#include <usbd-mem.h> ++#include <usbd.h> ++#include <usbd-bus.h> ++#include <trace.h> ++#include <usbd-bi.h> ++ +diff -Nru a/drivers/usbd/usbd-admin.h b/drivers/usbd/usbd-admin.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-admin.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,61 @@ ++/* ++ * usbd/usbd-admin.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++/* ++ * This defines the public interface to administer the USB Device Software. ++ * ++ * Specifically it allows the USB Device software to be: ++ * ++ * enabled enable a specific function driver for use ++ * disabled disable the USB Device Software ++ * ++ * disconnected disable the USB Pullup Resistor (also known as soft-connect) ++ * connected enable the USB Pullup Resistor ++ * ++ * pm_off perform required functions for Power Management off ++ * pm_on perform required functions for Power Management on ++ * ++ * ++ */ ++ ++int usbd_enable(char *); ++int usbd_enable_irq(char *); ++int usbd_disable(char *); ++int usbd_disable_irq(char *); ++ ++int usbd_disconnect(char *); ++int usbd_connect(char *); ++int usbd_pm_on(char *); ++int usbd_pm_off(char *); ++ ++#ifdef CONFIG_USBD_DEPRECATED ++int usbd_load(char *); ++int usbd_unload(char *); ++int usbd_replug(char *); ++int usbd_unplug(char *); ++#endif ++ +diff -Nru a/drivers/usbd/usbd-bi.c b/drivers/usbd/usbd-bi.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-bi.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,753 @@ ++/* ++ * usbd/usbd-bi.c - USB Bus Interface Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it under the terms of ++ * the GNU General Public License as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ++ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along with this program; if ++ * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * 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. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/init.h> ++#include <linux/ctype.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <asm/uaccess.h> ++ ++#include "usbd-chap9.h" ++#include "usbd-mem.h" ++#include "usbd.h" ++#include "usbd-bus.h" ++#include "usbd-func.h" ++#include "trace.h" ++#include "usbd-bi.h" ++#include "usbd-admin.h" ++ ++#ifdef MODULE ++static char *serial_number_str; ++MODULE_PARM (serial_number_str, "s"); ++MODULE_PARM_DESC (serial_number_str, "Serial Number"); ++#else ++static char *serial_number_str; ++#endif ++ ++struct usb_bus_instance *usbd_bus; ++int have_cable_irq; ++ ++/* ******************************************************************************************* */ ++ ++int bi_attached (struct usb_bus_instance *bus) ++{ ++ return udc_ops.attached ? udc_ops.attached () : 1; ++} ++ ++int bi_connected (struct usb_bus_instance *bus) ++{ ++ return udc_ops.connected ? udc_ops.connected () : 1; ++} ++ ++int bi_disconnect (struct usb_bus_instance *bus, char *arg) ++{ ++ RETURN_EINVAL_IF (in_interrupt ()); ++ if (udc_ops.disconnect) udc_ops.disconnect (); ++ return 0; ++} ++ ++int bi_connect (struct usb_bus_instance *bus, char *arg) ++{ ++ RETURN_EINVAL_IF (in_interrupt ()); ++ if (udc_ops.connect) udc_ops.connect (); ++ return 0; ++} ++ ++int bi_request_endpoints(struct usb_endpoint_map *endpoint_map_array, int endpointsRequested, ++ struct usb_endpoint_request *requestedEndpoints) ++{ ++ int i; ++ RETURN_EINVAL_IF (udc_ops.request_endpoints(endpoint_map_array, endpointsRequested, requestedEndpoints)); ++ for (i = 0; i < endpointsRequested; i++) { ++ struct usb_endpoint_map *endpoint_map = endpoint_map_array + i; ++ TRACE_MSG8 ("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; ++} ++int bi_set_endpoints(int endpointsRequested, struct usb_endpoint_map *endpoint_map_array) ++{ ++ return udc_ops.set_endpoints ? udc_ops.set_endpoints(endpointsRequested, endpoint_map_array) : 0; ++} ++ ++/* ******************************************************************************************* */ ++/* bi_endpoint_halted - check if endpoint halted ++ * Used by the USB Device Core to check endpoint halt status. ++ */ ++int bi_endpoint_halted (struct usb_bus_instance *bus, int endpoint_index) ++{ ++ return udc_ops.endpoint_halted ? udc_ops.endpoint_halted(endpoint_index) : 0; ++} ++ ++/* bi_device_feature - handle set/clear feature requests ++ * Used by the USB Device Core to check endpoint halt status. ++ */ ++int bi_device_feature (struct usb_bus_instance *bus, int endpoint_index, int flag) ++{ ++ return 0; ++} ++ ++/* bi_disable_endpoints - disable udc and all endpoints ++ */ ++static void bi_disable_endpoints (struct usb_bus_instance *bus) ++{ ++ int i; ++ RETURN_IF (!bus || !bus->endpoint_array); ++ for (i = 1; i < udc_ops.max_endpoints; i++) { ++ struct usb_endpoint_instance *endpoint = (bus->endpoint_array + i); ++ CONTINUE_IF (!endpoint); ++ usbd_flush_endpoint (endpoint); ++ } ++} ++ ++ ++/* bi_device_event_irq - handle generic bus event ++ * Called by usb core layer to inform bus of an event. ++ */ ++int bi_device_event_irq (struct usb_bus_instance *bus, usb_device_event_t event, int data) ++{ ++ int epn; ++ int endpointsRequested = bus->function_instance->endpointsRequested; ++ struct usb_endpoint_map *endpoint_map_array = bus->function_instance->endpoint_map_array; ++ ++ TRACE_MSG32 ("EVENT %x", event); ++ switch (event) { ++ case DEVICE_UNKNOWN: ++ break; ++ ++ case DEVICE_INIT: ++ TRACE_MSG ("EVENT INIT"); ++ break; ++ ++ case DEVICE_CREATE: ++ TRACE_MSG ("EVENT CREATE"); ++ bi_disable_endpoints (bus); ++ if (udc_ops.enable) udc_ops.enable (); ++ if (udc_ops.suspended_interrupts) udc_ops.suspended_interrupts (); ++ break; ++ ++ case DEVICE_HUB_CONFIGURED: ++ TRACE_MSG ("EVENT HUB_CONFIGURED"); ++ bi_connect (usbd_bus, NULL); ++ break; ++ ++ case DEVICE_RESET: ++ TRACE_MSG ("EVENT RESET"); ++ if (udc_ops.set_address) udc_ops.set_address (0); ++ if (udc_ops.reset_ep) udc_ops.reset_ep (0); ++ if (udc_ops.suspended_interrupts) udc_ops.suspended_interrupts (); ++ bi_disable_endpoints (bus); ++ break; ++ ++ case DEVICE_ADDRESS_ASSIGNED: ++ TRACE_MSG ("EVENT ADDRESSED"); ++ if (udc_ops.set_address) udc_ops.set_address (data); ++ if (udc_ops.all_interrupts) udc_ops.all_interrupts (); ++ break; ++ ++ case DEVICE_CONFIGURED: ++ TRACE_MSG ("EVENT CONFIGURED"); ++ // iterate across the physical endpoint instance array to enable the endpoints ++ if (udc_ops.setup_ep) ++ for (epn = 1; epn < bus->endpoints; epn++) ++ udc_ops.setup_ep (epn, bus->endpoint_array + epn); ++ return 0; ++ ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG ("EVENT DE-CONFIGURED"); ++ break; ++ ++ case DEVICE_SET_INTERFACE: ++ TRACE_MSG ("EVENT SET INTERFACE"); ++ break; ++ ++ case DEVICE_SET_FEATURE: ++ TRACE_MSG ("EVENT SET FEATURE"); ++ break; ++ ++ case DEVICE_CLEAR_FEATURE: ++ TRACE_MSG ("EVENT CLEAR FEATURE"); ++ break; ++ ++ case DEVICE_BUS_INACTIVE: ++ TRACE_MSG ("EVENT INACTIVE"); ++ if (udc_ops.suspended_interrupts) udc_ops.suspended_interrupts (); // disable suspend interrupt ++ if (bi_attached (usbd_bus)) ++ usbd_bus_event_irq (bus, DEVICE_RESET, 0); ++ break; ++ ++ case DEVICE_BUS_ACTIVITY: ++ TRACE_MSG ("EVENT ACTIVITY"); ++ if (udc_ops.all_interrupts) udc_ops.all_interrupts (); // enable suspend interrupt ++ break; ++ ++ case DEVICE_POWER_INTERRUPTION: ++ TRACE_MSG ("POWER INTERRUPTION"); ++ break; ++ ++ case DEVICE_HUB_RESET: ++ TRACE_MSG ("HUB RESET"); ++ break; ++ ++ case DEVICE_DESTROY: ++ TRACE_MSG ("DEVICE DESTROY"); ++ bi_disconnect (usbd_bus, NULL); ++ bi_disable_endpoints (bus); ++ if (udc_ops.disable_interrupts) udc_ops.disable_interrupts (); ++ if (udc_ops.disable) udc_ops.disable (); ++ break; ++ ++ case DEVICE_CLOSE: ++ break; ++ } ++ return 0; ++} ++ ++ ++/* bi_start_endpoint_in ++ */ ++int bi_start_endpoint_in (struct usb_bus_instance *bus, struct usb_endpoint_instance *endpoint) ++{ ++ unsigned long flags; ++ ++ //printk (KERN_INFO"%s: bus: %p status: %d\n", __FUNCTION__, bus, bus->status); ++ ++ RETURN_ZERO_IF (!endpoint); ++ udc_interrupts++; ++ ++ TRACE_MSG32 ("BI START ENDPOINT IN: tx_urb: %x", (int)endpoint->tx_urb); ++ local_irq_save (flags); ++ // call udc_start_endpoint_in IFF we didn't previously have a tx urb ++ if (!endpoint->tx_urb && bi_tx_next_irq (endpoint)) { ++ TRACE_MSG16 ("BI START ENDPOINT IN: bEndpointAddress: %x actual_length: %d", ++ (int)endpoint->bEndpointAddress, endpoint->tx_urb->actual_length); ++ udc_ops.start_endpoint_in (endpoint); ++ } ++ local_irq_restore (flags); ++ //printk (KERN_INFO"%s: finis\n", __FUNCTION__); ++ return 0; ++} ++ ++/* bi_start_endpoint_out ++ */ ++int bi_start_endpoint_out (struct usb_bus_instance *bus, struct usb_endpoint_instance *endpoint) ++{ ++ unsigned long flags; ++ ++ //printk (KERN_INFO"%s: bus: %p status: %d\n", __FUNCTION__, bus, bus->status); ++ ++ RETURN_ZERO_IF (!endpoint); ++ udc_interrupts++; ++ TRACE_MSG32 ("BI START ENDPOINT OUT: rcv_urb: %x", (int)endpoint->rcv_urb); ++ local_irq_save (flags); ++ // call udc_start_endpoint_OUT IFF we didn't previously have a rcv urb ++ if (!endpoint->rcv_urb && bi_rcv_next_irq (endpoint)) { ++ TRACE_MSG16 ("BI START ENDPOINT OUT: bEndpointAddress: %x request_length: %d", ++ (int)endpoint->bEndpointAddress, endpoint->rcv_urb->request_length); ++ udc_ops.start_endpoint_out (endpoint); ++ } ++ local_irq_restore (flags); ++ //printk (KERN_INFO"%s: finis\n", __FUNCTION__); ++ return 0; ++} ++ ++ ++/* bi_cancel_urb_irq - cancel sending an urb ++ * Used by the USB Device Core to cancel an urb. ++ */ ++int bi_cancel_urb_irq (struct urb *urb) ++{ ++ RETURN_EINVAL_IF (!urb); ++ switch (urb->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { ++ case USB_DIR_IN: ++ //printk (KERN_INFO"%s: IN urb: %p\n", __FUNCTION__, urb); ++ // is this the active urb? ++ if (urb->endpoint->tx_urb == urb) { ++ urb->endpoint->tx_urb = NULL; ++ if (udc_ops.cancel_in_irq) udc_ops.cancel_in_irq (urb); ++ } ++ usbd_urb_sent_finished_irq (urb, SEND_FINISHED_CANCELLED); ++ break; ++ ++ case USB_DIR_OUT: ++ //printk (KERN_INFO"%s: OUT urb: %p\n", __FUNCTION__, urb); ++ // is this the active urb? ++ if (urb->endpoint->rcv_urb == urb) { ++ urb->endpoint->rcv_urb = NULL; ++ if (udc_ops.cancel_out_irq) udc_ops.cancel_out_irq (urb); ++ } ++ TRACE_MSG ("CANCEL RECV URB"); ++ usbd_urb_recv_finished_irq (urb, RECV_CANCELLED); ++ break; ++ } ++ return 0; ++} ++ ++struct urb * bi_rcv_complete_irq (struct usb_endpoint_instance *endpoint, int len, int urb_bad) ++{ ++ struct urb *rcv_urb; ++ ++ // if we had an urb then update actual_length, dispatch if neccessary ++ if (likely ( (int) (rcv_urb = endpoint->rcv_urb))) { ++ ++ //printk (KERN_ERR"%s: actual: %d buffer: %d\n", ++ // __FUNCTION__, rcv_urb->actual_length, rcv_urb->buffer_length); ++ ++ TRACE_MSG8 ("BI RCV COMPLETE: actual: %d len: %d bad: %d: status: %d", ++ rcv_urb->actual_length, len, urb_bad, rcv_urb->status); ++ ++ TRACE_MSG8 ("BI 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; ++ for (i = 0; i < rcv_urb->actual_length; TRACE_RECV (rcv_urb->buffer + i), i+= 8); ++#endif ++ endpoint->rcv_urb = NULL; ++ rcv_urb->jiffies = jiffies; ++ rcv_urb->framenum = udc_ops.framenum ? udc_ops.framenum () : 0; ++ TRACE_MSG32 ("BI RCV COMPLETE: finished length: %d", rcv_urb->actual_length); ++ TRACE_MSG32 ("BI RCV COMPLETE: framenum: %x", (int) rcv_urb->framenum); ++ usbd_urb_recv_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 bi_rcv_next_irq (endpoint); ++} ++ ++struct urb * bi_tx_complete_irq (struct usb_endpoint_instance *endpoint, int restart) ++{ ++ struct urb *tx_urb; ++ ++ // if we have a tx_urb advance or reset, finish if complete ++ if ( (likely ( (int)tx_urb = endpoint->tx_urb))) { ++ ++ TRACE_MSG32 ("BI TX CURRENT TX_URB: %p", (int)endpoint->tx_urb); ++ TRACE_MSG8 ("BI TX COMPLETE: actual: %d sent: %d last: %d: status: %d", ++ tx_urb->actual_length, endpoint->sent, endpoint->last, tx_urb->status); ++ ++ if (likely (!restart)) { ++ int sent = endpoint->last; ++ endpoint->sent += sent; ++ endpoint->last -= sent; ++ } ++ else ++ endpoint->last = 0; ++ ++ //printk (KERN_INFO"%s: act: %d last: %d sent: %d status: %d flags: %x\n", __FUNCTION__, ++ // endpoint->tx_urb->actual_length, endpoint->last, endpoint->sent, ++ // tx_urb->status, tx_urb->flags); ++ ++ // XXX is the test for bEndpointAddress still appropriate for CONTROL WRITES ++ if ( ( (tx_urb->actual_length - endpoint->sent) <= 0) && ! (tx_urb->flags & USBD_URB_SENDZLP) ) { ++ //if (endpoint->bEndpointAddress) ++ tx_urb->jiffies = jiffies; ++ tx_urb->framenum = udc_ops.framenum ? udc_ops.framenum () : 0; ++ TRACE_MSG32 ("BI TX COMPLETE: finished tx_urb: %p", (int)tx_urb); ++ TRACE_MSG32 ("BI TX COMPLETE: framenum: %x", (int)tx_urb->framenum); ++ usbd_urb_sent_finished_irq (tx_urb, SEND_FINISHED_OK); ++ endpoint->tx_urb = NULL; ++ endpoint->last = endpoint->sent = 0; ++ } ++ } ++ return bi_tx_next_irq (endpoint); ++} ++ ++/* ******************************************************************************************* */ ++/* bi_udc_init_irq - initialize USB Device Controller ++ * Get ready to use the USB Device Controller. ++ * Register the interrupt handlers and optional IO region. ++ * Return non-zero for error. ++ */ ++int bi_udc_init_irq (void) ++{ ++ THROW_IF (udc_ops.request_udc_irq ? udc_ops.request_udc_irq () : 0, irq_err); ++ THROW_IF (udc_ops.request_io ? udc_ops.request_io () : 0, io_err); ++ THROW_IF (udc_ops.init ? udc_ops.init () : 0, init_err); ++ CATCH (irq_err) { ++ printk (KERN_ERR"%s: could not request USB IRQ\n", __FUNCTION__); ++ CATCH (io_err) { ++ CATCH (init_err) { ++ printk (KERN_ERR"%s: could not request USB IO space\n", __FUNCTION__); ++ if (udc_ops.release_io) udc_ops.release_io (); ++ } ++ if (udc_ops.release_udc_irq) udc_ops.release_udc_irq (); ++ } ++ printk (KERN_ERR"%s: error\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++ ++/* bi_udc_exit - Stop using the USB Device Controller ++ * Stop using the USB Device Controller. ++ * Shutdown and free dma channels, de-register the interrupt handler. ++ */ ++void bi_udc_exit (struct usb_bus_instance *bus) ++{ ++ if (udc_ops.disable_ep) udc_ops.disable_ep (0); ++ bi_disable_endpoints (bus); ++ bi_disconnect (usbd_bus, NULL); ++ if (udc_ops.disable) udc_ops.disable (); ++ if (udc_ops.release_io) udc_ops.release_io (); ++ if (udc_ops.release_udc_irq) udc_ops.release_udc_irq (); ++} ++ ++ ++/* ************************************************************************************* */ ++ ++int bus_disable (struct usb_bus_instance *bus, char *arg); ++int bus_enable (struct usb_bus_instance *bus, char *arg); ++ ++int bi_pm_off (struct usb_bus_instance *bus, char *arg) ++{ ++ //printk (KERN_INFO"%s:\n", __FUNCTION__); ++ RETURN_EINVAL_IF (in_interrupt ()); ++ return bus_disable (bus, arg); ++} ++ ++int bi_pm_on (struct usb_bus_instance *bus, char *arg) ++{ ++ //printk (KERN_INFO"%s:\n", __FUNCTION__); ++ RETURN_EINVAL_IF (in_interrupt ()); ++ return bi_attached (usbd_bus) ? bus_enable (bus, arg) : 0; ++} ++ ++int bi_fix_serial_number_str (struct usb_bus_instance *bus) ++{ ++ char *sp, *dp; ++ for (sp = dp = usbd_bus->serial_number_str; sp && sp[0]; sp++) { ++ CONTINUE_IF (!isxdigit (sp[0])); ++ *dp++ = toupper (*sp); ++ } ++ if (dp) ++ *dp = '\0'; ++ return 0; ++} ++ ++int bi_serial_number (struct usb_bus_instance *bus, char *arg) ++{ ++ char *sp, *dp; ++ RETURN_EINVAL_IF (in_interrupt ()); ++ if (usbd_bus->serial_number_str) ++ lkfree (usbd_bus->serial_number_str); ++ usbd_bus->serial_number_str = lstrdup (arg); ++ bi_fix_serial_number_str (bus); ++ return 0; ++} ++ ++ ++/* ******************************************************************************************* */ ++ ++struct usb_bus_operations bi_ops = { ++ bus_enable: bus_enable, ++ bus_disable: bus_disable, ++ bus_pm_off: bi_pm_off, ++ bus_pm_on: bi_pm_on, ++ bus_serial_number: bi_serial_number, ++ start_endpoint_in: bi_start_endpoint_in, ++ start_endpoint_out: bi_start_endpoint_out, ++ cancel_urb_irq: bi_cancel_urb_irq, ++ endpoint_halted: bi_endpoint_halted, ++ device_feature: bi_device_feature, ++ device_event: bi_device_event_irq, ++ bus_disconnect: bi_disconnect, ++ bus_connect: bi_connect, ++ bus_attached: bi_attached, ++ bus_connected: bi_connected, ++ request_endpoints: bi_request_endpoints, ++ set_endpoints: bi_set_endpoints, ++}; ++ ++ ++struct usb_bus_driver bi_driver = { ++ bops: &bi_ops, ++}; ++ ++void bi_cable_event_irq (void) ++{ ++ (bi_attached (usbd_bus) ? usbd_enable_irq : usbd_disable_irq) (NULL); ++} ++ ++void bi_cable_event (void) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ bi_cable_event_irq (); ++ local_irq_restore (flags); ++} ++ ++ ++/* Prevent overlapp of bi administrative functions mainly: ++ * bus_enable ++ * bus_disable ++ * bi_modinit ++ * bi_modexit ++ */ ++DECLARE_MUTEX (usbd_bi_sem); ++ ++int bus_disable_sem (struct usb_bus_instance *bus, char *arg) ++{ ++ struct bi_data *data; ++ unsigned long flags; ++ ++ RETURN_ZERO_IF (usbd_bus_state_enabled != bus->bus_state); ++ MOD_DEC_USE_COUNT; ++ ++ bi_disconnect (usbd_bus, NULL); ++ if (udc_ops.disable) udc_ops.disable (); ++ ++ local_irq_save (flags); ++ if (bus->device_state != STATE_ATTACHED) { ++ usbd_bus_event_irq (bus, DEVICE_RESET, 0); ++ usbd_bus_event_irq (bus, DEVICE_POWER_INTERRUPTION, 0); ++ usbd_bus_event_irq (bus, DEVICE_HUB_RESET, 0); ++ } ++ usbd_bus_event_irq (bus, DEVICE_DESTROY, 0); ++ bi_disable_endpoints (bus); ++ bi_udc_exit (bus); ++ local_irq_restore (flags); ++ ++ usbd_disable_function (bus); ++ bus->bus_state = usbd_bus_state_disabled; ++ return 0; ++} ++ ++int bus_disable (struct usb_bus_instance *bus, char *arg) ++{ ++ RETURN_EINVAL_IF (in_interrupt ()); ++ down (&usbd_bi_sem); ++ bus_disable_sem (bus, arg); ++ up (&usbd_bi_sem); ++ return 0; ++} ++ ++void bi_startup_events(void) ++{ ++ if (udc_ops.startup_events) ++ udc_ops.startup_events(); ++ else { ++ usbd_bus_event_irq (usbd_bus, DEVICE_INIT, 0); ++ usbd_bus_event_irq (usbd_bus, DEVICE_CREATE, 0); ++ usbd_bus_event_irq (usbd_bus, DEVICE_HUB_CONFIGURED, 0); ++ usbd_bus_event_irq (usbd_bus, DEVICE_RESET, 0); ++ } ++} ++ ++int bus_enable (struct usb_bus_instance *bus, char *arg) ++{ ++ struct usb_endpoint_instance *endpoint; ++ int rc = -EINVAL; ++ unsigned long flags; ++ ++ //printk (KERN_INFO "%s: %s bus_state: %d\n", __FUNCTION__, arg ? arg : " (NULL)", bus->bus_state); ++ RETURN_EINVAL_IF (in_interrupt ()); ++ ++ down (&usbd_bi_sem); ++ bus_disable_sem (bus, arg); ++ MOD_INC_USE_COUNT; ++ ++ local_irq_save (flags); ++ do { ++ // check if we can see the UDC and register, then enable the function ++ BREAK_IF (bi_udc_init_irq ()); ++ BREAK_IF (usbd_enable_function_irq (bus, arg)); ++ rc = 0; ++ ++ // setup endpoint zero ++ endpoint = bus->endpoint_array + 0; ++ endpoint->bEndpointAddress = 0; ++ ++ //endpoint->tx_attributes = 0; ++ endpoint->wMaxPacketSize = udc_ops.ep0_packetsize; ++ endpoint->rcv_transferSize = 255; // XXX should this be higher ++ endpoint->wMaxPacketSize = udc_ops.ep0_packetsize; ++ if (udc_ops.setup_ep) udc_ops.setup_ep (0, endpoint); ++ ++ // hopefully device enumeration will finish this process ++ bi_startup_events (); ++ trace_reinit (); ++ } while (0); ++ local_irq_restore (flags); ++ up (&usbd_bi_sem); ++ ++ //printk (KERN_INFO"%s: finis\n", __FUNCTION__); ++ if (rc) { ++ printk (KERN_INFO "%s: failed\n", __FUNCTION__); ++ bi_udc_exit (NULL); ++ } ++ return rc; ++} ++ ++/* ************************************************************************************* */ ++ ++static int bi_modinit (void) ++{ ++ extern const char *usbd_bi_module_info (void); ++ //struct bi_data *data = NULL; ++ static int first = 0; ++ ++ printk (KERN_INFO "%s: %s serial: \"%s\"\n", __FUNCTION__, usbd_bi_module_info (), ++ serial_number_str && strlen (serial_number_str) ? serial_number_str : ""); ++ ++ down (&usbd_bi_sem); ++ ++ THROW_IF (trace_init (), error); ++ THROW_IF (usbd_bus, error); ++ ++ // Set the UDC defaults ++ udc_ops.disable_interrupts (); ++ bi_disconnect (usbd_bus, NULL); ++ ++ bi_driver.name = udc_ops.name; ++ bi_driver.max_endpoints = udc_ops.max_endpoints; ++ bi_driver.maxpacketsize = udc_ops.ep0_packetsize; ++ ++ // register this bus interface driver and create the device driver instance ++ THROW_IF (! (usbd_bus = usbd_register_bus (&bi_driver)), error); ++ ++ //THROW_IF (! (data = ckmalloc (sizeof (struct bi_data), GFP_KERNEL)), error); ++ //memset (data, 0, sizeof (struct bi_data)); ++ //usbd_bus->privdata = data; ++ ++ // see if we can scrounge up something to set a sort of unique device address ++ if (udc_ops.serial_init ? udc_ops.serial_init () : -EINVAL) { ++ if (serial_number_str && strlen (serial_number_str)) ++ bi_serial_number (usbd_bus, serial_number_str); ++ } ++ else ++ bi_fix_serial_number_str (usbd_bus); ++ ++ have_cable_irq = udc_ops.request_cable_irq ? !udc_ops.request_cable_irq () : 0; ++ up (&usbd_bi_sem); ++ ++#if defined (MODULE) || !defined (CONFIG_USBD_BI_DELAY_ENABLE) ++ // if we are connected OR if we don't have a cable irq fake an attach event ++ ++ // XXX this is not quite correct, if not attached then an enable ++ // will be required later if attached on a system that does not have ++ // cable event. ++ // ++ //if (!have_cable_irq || udc_attached ()) { ++ // Even if we have a cable irq, we need to check the current status, ++ // because the cable may have been attached before we installed the handler. ++ ++ if (bi_attached (usbd_bus)) { ++ printk (KERN_INFO"%s: cable attached\n", __FUNCTION__); ++ bi_cable_event (); ++ } ++#endif ++ return 0; ++ ++ CATCH (error) { ++ printk (KERN_ERR"%s: error loading module\n", __FUNCTION__); ++ //if (data) ++ // lkfree (data); ++ if (usbd_bus) ++ usbd_deregister_bus (usbd_bus); ++ usbd_bus = NULL; ++ trace_exit (); ++ up (&usbd_bi_sem); ++ printk (KERN_INFO"%s: UP\n", __FUNCTION__); ++ return -EINVAL; ++ } ++} ++ ++#ifdef MODULE ++/* bi_modexit - This is *only* used for drivers compiled and used as a module. ++ */ ++static void bi_modexit (void) ++{ ++ down (&usbd_bi_sem); ++ RETURN_IF (!usbd_bus); ++ ++ if (have_cable_irq) ++ udc_ops.release_cable_irq (); ++ ++ bus_disable_sem (usbd_bus, NULL); ++ ++ //if ( (usbd_bus->privdata)) ++ // lkfree (usbd_bus->privdata); ++ //usbd_bus->privdata = NULL; ++ ++ if (usbd_bus->serial_number_str) ++ lkfree (usbd_bus->serial_number_str); ++ ++ usbd_deregister_bus (usbd_bus); ++ usbd_bus = NULL; ++ trace_exit (); ++} ++ ++module_exit (bi_modexit); ++#endif ++ ++module_init (bi_modinit); ++ +diff -Nru a/drivers/usbd/usbd-bi.h b/drivers/usbd/usbd-bi.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-bi.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,196 @@ ++/* ++ * usbd/usbd-bi.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it under the terms of ++ * the GNU General Public License as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ++ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along with this program; if ++ * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++struct udc_ops { ++ ++ /* mandatory */ ++ int max_endpoints; ++ int ep0_packetsize; ++ char *name; ++ ++ void (*start_endpoint_in) ++ (struct usb_endpoint_instance *); /* start an IN urb */ ++ void (*start_endpoint_out) ++ (struct usb_endpoint_instance *); /* start an OUT urb */ ++ ++ int (*request_endpoints) ++ (struct usb_endpoint_map *, int, struct usb_endpoint_request *); ++ ++ /* optional */ ++ ++ int (*set_endpoints) ++ (int , struct usb_endpoint_map *); ++ ++ void (*cancel_in_irq) (struct urb *urb); /* cancel active urb for IN endpoint */ ++ void (*cancel_out_irq) (struct urb *urb); /* cancel active urb for OUT endpoint */ ++ ++ void (*reset_ep) (unsigned int ep); /* reset requested endpoint */ ++ int (*endpoint_halted) (unsigned int ep); /* Return non-zero if requested endpoint is halted */ ++ ++ void (*set_address) (unsigned char address);/* set the device USB address */ ++ int (* serial_init) (void); /* get device serial number if available */ ++ void (*setup_ep) (unsigned int ep, ++ struct usb_endpoint_instance *endpoint); /* setup specified endpoint for use */ ++ void (*disable_ep) (unsigned int ep); /* disable specified endpoint */ ++ void (*stall_ep) (u32 ); /* stall endpoint */ ++ ++ int (*attached) (void); /* return non-zero if the USB cable connected */ ++ int (*connected) (void); /* return non-zero if the USB pullup resistor is enabled */ ++ void (*connect) (void); /* enable USB pullup resistor to enable connection to host */ ++ void (*disconnect) (void); /* disable pullup resistor to disconnect from host */ ++ ++ int (*framenum) (void); /* fetch SOF framenum */ ++ ++ void (*all_interrupts) (void); /* enable all interrupts for normal operation */ ++ void (*suspended_interrupts) (void); /* enable interrupts for suspended operation */ ++ void (*disable_interrupts) (void); /* disable all interrupts */ ++ ++ void (*enable) (void); /* enable the UDC */ ++ void (*disable) (void); /* disable the UDC */ ++ ++ void (*startup_events) (void); /* perform UDC specific USB events */ ++ int (*init) (void); /* initialize USB Device Controller */ ++ ++ int (*request_udc_irq) (void); /* return non-zero if request for UDC interrupt fails */ ++ int (*request_cable_irq) (void); /* return non-zero if request for Cable interrupt fails */ ++ int (*request_io) (void); /* return non-zero if request UDC IO region fails */ ++ void (*release_udc_irq) (void); /* release USB Device Controller interrupt */ ++ void (*release_cable_irq) (void); /* release Cable interrupt */ ++ void (*release_io) (void); /* release UDC IO region */ ++}; ++ ++extern struct udc_ops udc_ops; ++extern unsigned int udc_interrupts; ++extern struct usb_bus_instance *usbd_bus; ++ ++void bi_cable_event_irq (void); /* called from cradle interrupt handler */ ++void bi_cable_event (void); ++ ++/* bi_rcv_next_irq - complete a receive ++ * Called from rcv interrupt to complete. ++ */ ++static __inline__ struct urb * bi_rcv_next_irq (struct usb_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_MSG32 ("BI RCV_URB: %p", (int)endpoint->rcv_urb); ++ return endpoint->rcv_urb; ++} ++ ++ ++/* bi_rcv_complete_irq - complete a receive ++ * Called from rcv interrupt to complete. ++ */ ++struct urb * bi_rcv_complete_irq (struct usb_endpoint_instance *endpoint, int len, int urb_bad); ++ ++static __inline__ void bi_rcv_cancelled_irq (struct usb_endpoint_instance *endpoint) ++{ ++ struct urb *rcv_urb; ++ ++ TRACE_MSG32 ("BI RCV CANCELLED: %p", (int) endpoint->rcv_urb); ++ RETURN_IF (! (rcv_urb = endpoint->rcv_urb)); ++ printk (KERN_INFO"%s: rcv_urb: %p\n", __FUNCTION__, endpoint->rcv_urb); ++ usbd_urb_recv_finished_irq (rcv_urb, RECV_CANCELLED); ++ endpoint->sent = endpoint->last = 0; ++ endpoint->rcv_urb = NULL; ++ printk (KERN_INFO"%s: rcv_urb: %p\n", __FUNCTION__, endpoint->rcv_urb); ++} ++ ++ ++/* bi_tx_next_irq - complete a receive ++ * Called from tx interrupt to complete. ++ */ ++static __inline__ struct urb * bi_tx_next_irq (struct usb_endpoint_instance *endpoint) ++{ ++ if (!endpoint->tx_urb) ++ if ( (endpoint->tx_urb = usbd_first_urb_detached_irq (&endpoint->tx))) { ++#if 0 ++ int i; ++ TRACE_MSG16 ("NEXT TX: length: %d flags: %x", endpoint->tx_urb->actual_length, endpoint->tx_urb->flags); ++ for (i = 0; i < endpoint->tx_urb->actual_length; TRACE_SENT (endpoint->tx_urb->buffer + i), i+= 8); ++#endif ++ endpoint->tx_urb->status = SEND_IN_PROGRESS; ++ } ++ TRACE_MSG32 ("BI TX NEXT TX_URB: %p", (int)endpoint->tx_urb); ++ return endpoint->tx_urb; ++} ++ ++ ++/* bi_tx_complete_irq - complete a transmit ++ * Called from tx interrupt to complete. ++ */ ++struct urb * bi_tx_complete_irq (struct usb_endpoint_instance *endpoint, int restart); ++ ++static __inline__ void bi_tx_cancelled_irq (struct usb_endpoint_instance *endpoint) ++{ ++ struct urb *tx_urb; ++ ++ TRACE_MSG32 ("BI TX CANCELLED: %p", (int) endpoint->tx_urb); ++ RETURN_IF (! (tx_urb = endpoint->tx_urb)); ++ usbd_urb_sent_finished_irq (tx_urb, SEND_FINISHED_CANCELLED); ++ endpoint->sent = endpoint->last = 0; ++ endpoint->tx_urb = NULL; ++} ++ ++static __inline__ int bi_tx_sendzlp (struct usb_endpoint_instance *endpoint) ++{ ++ struct urb *tx_urb = endpoint->tx_urb; ++ RETURN_ZERO_IF (!tx_urb || (tx_urb->actual_length != endpoint->sent) || ! (tx_urb->flags & USBD_URB_SENDZLP)); ++ tx_urb->flags &= ~USBD_URB_SENDZLP; ++ return 1; ++} ++ ++/* bi_rcv_complete_irq - complete a receive ++ * Called from rcv interrupt to complete. ++ */ ++static __inline__ void bi_rcv_fast_complete_irq (struct usb_endpoint_instance *endpoint, struct urb *rcv_urb) ++{ ++ TRACE_MSG32 ("BI RCV FAST COMPLETE: %d", rcv_urb->actual_length); ++ usbd_urb_recv_finished_irq (rcv_urb, RECV_OK); ++} ++ ++/* bi_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 bi_recv_setup_irq (struct usb_device_request *request) ++{ ++ struct usb_endpoint_instance *endpoint = usbd_bus->endpoint_array + 0; ++ TRACE_SETUP (request); ++ RETURN_EINVAL_IF (usbd_recv_setup_irq (usbd_bus->ep0, request)); // fail if already failed ++ 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; ++} ++ ++/* bi_ep0_reset_irq - reset ep0 endpoint ++ */ ++static void __inline__ bi_ep0_reset_endpoint_irq (struct usb_endpoint_instance *endpoint) ++{ ++ bi_tx_cancelled_irq (endpoint); ++ bi_rcv_cancelled_irq (endpoint); ++ endpoint->sent = endpoint->last = 0; ++} +diff -Nru a/drivers/usbd/usbd-bops.c b/drivers/usbd/usbd-bops.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-bops.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,889 @@ ++/* ++ * usbd/usbd-bops.c - USB Device Prototype ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++ ++#include "usbd-chap9.h" ++#include "usbd-mem.h" ++#include "usbd.h" ++#include "usbd-func.h" ++#include "usbd-bus.h" ++ ++/* Private defs from usbd-func.c *************************************************************** */ ++/* 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_function_enable (struct usb_bus_instance *, struct usb_function_instance *); ++void usbd_function_disable(struct usb_function_instance *); ++void usbd_func_event_irq(struct usb_bus_instance *, struct usb_function_instance *, usb_device_event_t , int ); ++int usbd_strings_init (void); ++void usbd_strings_exit(void); ++void usbd_urb_callback (struct urb *urb, int rc); ++ ++/* List support functions ******************************************************************** */ ++ ++/* ++ * 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))) ++ ++static inline struct usb_function_driver *list_entry_function (const struct list_head *le) ++{ ++ return list_entry (le, struct usb_function_driver, drivers); ++} ++ ++ ++/* ++ * Append an urb_link (or a whole list of ++ * urb_links) to the tail of another list ++ * of urb_links. ++ */ ++static __inline__ void urb_append_irq (urb_link * hd, struct urb *urb) ++{ ++ if (hd && urb) { ++ urb_link *new = &urb->link; ++ ++#ifdef C2L_SINGLETON_ONLY ++ // This _assumes_ the new urb is a singleton, ++ // but allows it to have an uninitialized link. ++ //printk(KERN_DEBUG"urb_append: hd: %p n:%p p:%p new: %p n:%p p:%p\n", hd, hd->next, hd->prev, new, new->next, new->prev); ++ ++ new->prev = hd->prev; ++ new->next = hd; ++ ++ hd->prev->next = new; ++ hd->prev = new; ++#else ++ // This allows the new urb to be a list of urbs, ++ // with new pointing at the first, but the link ++ // must be initialized. ++ // Order is important here... ++ urb_link *pul = hd->prev; ++ new->prev->next = hd; ++ hd->prev = new->prev; ++ new->prev = pul; ++ pul->next = new; ++#endif ++ } ++} ++ ++/* ++ * 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. ++ */ ++static __inline__ urb_link *first_urb_link_irq (urb_link * hd) ++{ ++ urb_link *nx; ++ return (!hd || !(nx = hd->next) || (nx == hd)) ? NULL : nx; ++} ++ ++/* ++ * Return the first urb in a list with a distinguished ++ * head "hd", or NULL if the list is empty. ++ * ++ * Called from interrupt. ++ */ ++static __inline__ struct urb *first_urb_irq (urb_link * hd) ++{ ++ urb_link *nx; ++ return (!(nx = first_urb_link_irq (hd))) ? NULL : p2surround (struct urb, link, nx); ++} ++ ++/* ++ * Detach and return the first urb in a list with a distinguished ++ * head "hd", or NULL if the list is empty. ++ */ ++struct urb *usbd_first_urb_detached_irq (urb_link * hd) ++{ ++ struct 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; ++} ++ ++static __inline__ void urb_append(urb_link *hd, struct urb *urb) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ urb_append_irq(hd, urb); ++ local_irq_restore (flags); ++} ++ ++/* usbd_urb_sent_finished_irq - 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. ++ */ ++void usbd_urb_sent_finished_irq (struct urb *urb, int rc) ++{ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ urb->status = rc; ++ urb->jiffies = jiffies; ++ if (USBD_OK != urb->bus->status) ++ usbd_urb_callback(urb, urb->status); ++ else { ++ urb_append_irq (&(urb->endpoint->done), urb); ++ RETURN_IF ( urb->bus->device_bh.sync); ++ queue_task (&urb->bus->device_bh, &tq_immediate); ++ mark_bh (IMMEDIATE_BH); ++ } ++} ++ ++ ++/* usbd_urb_recv_finished_irq - tell function that an urb has been received. ++ * ++ * 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. ++ */ ++void usbd_urb_recv_finished_irq (struct urb *urb, int rc) ++{ ++ urb->status = rc; ++ urb->jiffies = jiffies; ++ if (USBD_OK != urb->bus->status) ++ usbd_urb_callback (urb, rc); ++ else { ++ urb_append_irq (&(urb->endpoint->rcv), urb); ++ RETURN_IF (urb->bus->device_bh.sync); ++ queue_task (&urb->bus->device_bh, &tq_immediate); ++ mark_bh (IMMEDIATE_BH); ++ } ++} ++ ++ ++/* first_urb_detached - ++ * ++ * Detach and return the first urb in a list with a distinguished ++ * head "hd", or NULL if the list is empty. ++ * ++ */ ++static __inline__ struct urb *first_urb_detached (urb_link * hd) ++{ ++ struct urb *urb; ++ unsigned long flags; ++ local_irq_save (flags); ++ urb = usbd_first_urb_detached_irq (hd); ++ local_irq_restore (flags); ++ return urb; ++} ++ ++ ++/* Private defs from usbd-fops.c *************************************************************** */ ++ ++ ++extern struct usb_function_driver ep0_driver; ++extern struct list_head usbd_function_drivers; ++extern struct usb_bus_instance *usbd_bus_instance; ++ ++#define LANGID_ENGLISH "\011" ++#define LANGID_US_ENGLISH "\004" ++#define LANGIDs LANGID_US_ENGLISH LANGID_ENGLISH ++ ++/* usbd_flush_endpoint_irq - flush urbs from endpoint ++ * ++ * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs. ++ */ ++void usbd_flush_endpoint_irq (struct usb_endpoint_instance *endpoint) ++{ ++ struct urb *urb; ++ //printk(KERN_INFO"%s: bEndpointAddress: %d\n", __FUNCTION__, endpoint->bEndpointAddress); ++ if ((urb = endpoint->tx_urb)) ++ usbd_cancel_urb_irq(urb); ++ ++ for (; (urb = usbd_first_urb_detached_irq (&endpoint->tx)); usbd_cancel_urb_irq(urb)); ++ ++ if ((urb = endpoint->rcv_urb)) ++ usbd_cancel_urb_irq(urb); ++ ++ for (; (urb = usbd_first_urb_detached_irq (&endpoint->rdy)); usbd_cancel_urb_irq(urb)); ++} ++ ++/* usbd_flush_endpoint - flush urbs from endpoint ++ * ++ * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs. ++ */ ++void usbd_flush_endpoint (struct usb_endpoint_instance *endpoint) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ usbd_flush_endpoint_irq(endpoint); ++ local_irq_restore (flags); ++} ++ ++/* usbd_endpoint_halted ++ * ++ * Return non-zero if endpoint is halted. ++ */ ++int usbd_endpoint_halted (struct usb_function_instance *function, int endpoint) ++{ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ return function->bus->driver->bops->endpoint_halted (function->bus, endpoint); ++} ++ ++ ++/* usbd_device_feature - set usb device feature ++ * ++ * Return non-zero if error ++ */ ++int usbd_device_feature (struct usb_function_instance *function, int endpoint, int feature) ++{ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ return function->bus->driver->bops->device_feature (function->bus, endpoint, feature); ++} ++ ++/* usbd_cancel_urb_irq - cancel an urb being sent ++ * ++ * Return non-zero if error ++ */ ++int usbd_cancel_urb_irq (struct urb *urb) ++{ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ // XXX should we do usbd_dealloc_urb(urb); ++ return urb->bus->driver->bops->cancel_urb_irq (urb); ++} ++ ++/* usbd_send_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. ++ * ++ * Return non-zero if error ++ */ ++int usbd_send_urb (struct urb *urb) ++{ ++ //printk(KERN_INFO"%s: urb: %p length: %d\n", __FUNCTION__, urb, urb->actual_length); ++ //RETURN_EINVAL_IF (USBD_OK != urb->bus->status); ++ RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status)); ++ urb->status = SEND_IN_QUEUE; ++ urb->jiffies = jiffies; ++ urb_append (&(urb->endpoint->tx), urb); ++ return urb->bus->driver->bops->start_endpoint_in(urb->bus, urb->endpoint); ++} ++ ++/* usbd_start_recv - recycle a received urb ++ * ++ * Used by a USB Function interface driver to recycle an urb. ++ * ++ * Return non-zero if error ++ */ ++int usbd_start_recv (struct urb *urb) ++{ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status)); ++ urb->actual_length = 0; ++ urb->status = RECV_IN_QUEUE; ++ urb->jiffies = jiffies; ++ urb_append (&(urb->endpoint->rdy), urb); ++ urb->bus->driver->bops->start_endpoint_out(urb->bus, urb->endpoint); ++ //printk(KERN_INFO"%s: finis\n", __FUNCTION__, urb); ++ return 0; ++} ++ ++/* 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. ++ */ ++__u8 usbd_alloc_string_zero (char *str) ++{ ++ __u8 bLength; ++ __u16 *wData; ++ struct usb_string_descriptor *string; ++ ++ RETURN_ZERO_IF(usb_strings[0] != NULL); ++ ++ bLength = sizeof (struct usb_string_descriptor) + strlen (str); ++ ++ RETURN_ZERO_IF(!(string = ckmalloc (bLength, GFP_KERNEL))); ++ ++ string->bLength = bLength; ++ string->bDescriptorType = USB_DT_STRING; ++ ++ for (wData = string->wData; *str; str += 2) { ++ *wData = (__u16) ((str[0] << 8 | str[1])); ++ wData++; ++ } ++ usb_strings[0] = string; // store in string index array ++ return 0; ++} ++ ++ ++int usbd_strings_init (void) ++{ ++ //printk(KERN_INFO"%s: usb_strings: %p\n", __FUNCTION__, usb_strings); ++ RETURN_ZERO_IF(usb_strings); ++ usbd_maxstrings = MIN(usbd_maxstrings, 254); ++ ++ RETURN_EINVAL_IF (!(usb_strings = ckmalloc (sizeof (struct usb_string_descriptor *) * usbd_maxstrings, GFP_KERNEL))); ++ if (usbd_alloc_string_zero (LANGIDs) != 0) { ++ lkfree (usb_strings); ++ return -1; ++ } ++ return 0; ++} ++ ++void usbd_strings_exit(void) ++{ ++ int i; ++ //printk(KERN_INFO"%s: usb_strings: %p\n", __FUNCTION__, usb_strings); ++ RETURN_IF (!usb_strings); ++ for (i = 0; i < usbd_maxstrings; i++) ++ usbd_dealloc_string(i); ++ lkfree (usb_strings); ++ usb_strings = NULL; ++} ++ ++static usb_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 usb_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_device_event_irq - called to respond to various usb events ++ * ++ * Used by a Bus driver to indicate an event. ++ */ ++void usbd_bus_event_irq (struct usb_bus_instance *bus, usb_device_event_t event, int data) ++{ ++ RETURN_IF(!bus); ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ //printk(KERN_INFO"%s: --> event: %d status: %d state: %d\n", __FUNCTION__, event, bus->status, bus->device_state); ++ switch (event) { ++ case DEVICE_BUS_INACTIVE: ++ bus->suspended_state = bus->device_state; ++ //printk(KERN_INFO"%s: INACTIVE\n", __FUNCTION__); ++ /* FALL THROUGH */ ++ default: ++ bus->device_state = event_states[event]; ++ //printk(KERN_INFO"%s: DEFAUL\n", __FUNCTION__); ++ break; ++ case DEVICE_UNKNOWN: ++ //printk(KERN_INFO"%s: UNKNOWN\n", __FUNCTION__); ++ break; ++ case DEVICE_BUS_ACTIVITY: ++ bus->device_state = bus->suspended_state; ++ //printk(KERN_INFO"%s: ACTIVITY\n", __FUNCTION__); ++ break; ++ ++ case DEVICE_SET_INTERFACE: ++ case DEVICE_SET_FEATURE: ++ case DEVICE_CLEAR_FEATURE: ++ //printk(KERN_INFO"%s: SET\n", __FUNCTION__); ++ break; ++ } ++ ++ switch (event) { ++ case DEVICE_BUS_ACTIVITY: ++ case DEVICE_BUS_INACTIVE: ++ BREAK_IF(USBD_CLOSING != bus->status); ++ /* FALL THROUGH */ ++ default: ++ bus->status = event_status[event]; ++ //printk(KERN_INFO"%s: DEFAULT\n", __FUNCTION__); ++ break; ++ } ++ //printk(KERN_INFO"%s: <-- event: %d status: %d state: %d\n", __FUNCTION__, event, bus->status, bus->device_state); ++ ++ // if we lost configuration then get rid of alternate settings ++ if ((bus->device_state != STATE_CONFIGURED) && bus->bNumInterfaces && bus->alternates) { ++ bus->bNumInterfaces = 0; ++ lkfree(bus->alternates); ++ bus->alternates = NULL; ++ } ++ bus->driver->bops->device_event (bus, event, data); ++ usbd_func_event_irq (bus, bus->ep0, event, data); ++ usbd_func_event_irq (bus, bus->function_instance, event, data); ++} ++ ++ ++/* usbd_bus_event ++ */ ++void usbd_bus_event (struct usb_bus_instance *bus, usb_device_event_t event, int data) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ usbd_bus_event_irq (bus, event, data); ++ local_irq_restore (flags); ++} ++ ++ ++/* usbd_attached - return cable status ++ * ++ * Return non-zero if cable attached ++ */ ++int usbd_attached (struct usb_bus_instance *bus) ++{ ++ return bus->driver->bops->bus_attached (bus); ++} ++ ++/* usbd_connected - return pullup resistor control status ++ * * ++ * * Return non-zero if pullup enabled ++ * */ ++int usbd_connected (struct usb_bus_instance *bus) ++{ ++ return bus->driver->bops->bus_connected (bus); ++} ++ ++ ++/* usbb bus bottom half ********************************************************************** */ ++ ++/* ++ * Note that all of the functions from here to the end of the file protect against ++ * 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); ++ ++ ++/* usbd_device_bh - ++ * ++ * Bottom half handler to process sent or received urbs. ++ */ ++void usbd_device_bh (void *data) ++{ ++ int i; ++ struct usb_bus_instance *bus = data; ++ struct usb_endpoint_instance *endpoint; ++ ++ RETURN_IF (!bus || !(endpoint = bus->endpoint_array)); ++ down(&usbd_bus_sem); ++ // process received and sent urbs ++ for (i = 0; i < bus->endpoints; i++, endpoint++) { ++ struct urb *urb; ++ for (; (urb = first_urb_detached (&endpoint->rcv )); usbd_urb_callback (urb, urb->status)); ++ for (; (urb = first_urb_detached (&endpoint->done)); usbd_urb_callback (urb, urb->status)); ++ } ++ if (USBD_CLOSING == bus->status) ++ bus->device_bh.data = NULL; ++ up(&usbd_bus_sem); ++} ++ ++ ++/* usb-device USB BUS INTERFACE generic functions ******************************************** */ ++ ++/* ++ * 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. ++ */ ++static __inline__ void urb_link_init (urb_link * ul) ++{ ++ ul->prev = ul->next = ul; ++} ++ ++/* 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. ++ * ++ * Return non-zero if error ++ */ ++struct usb_bus_instance *usbd_register_bus (struct usb_bus_driver *driver) ++{ ++ int i; ++ struct usb_bus_instance *bus = NULL; ++ ++ //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__); ++ down(&usbd_bus_sem); ++ MOD_INC_USE_COUNT; // QQQ should this be before the down()? ++ ++ THROW_IF(usbd_bus_instance, error); ++ THROW_IF((bus = ckmalloc (sizeof (struct usb_bus_instance), GFP_ATOMIC)) == NULL, error); ++ ++ bus->driver = driver; ++ bus->endpoints = bus->driver->max_endpoints; ++ ++ THROW_IF(!(bus->endpoint_array = ckmalloc(sizeof (struct usb_endpoint_instance) * bus->endpoints, GFP_ATOMIC)), error); ++ ++ for (i = 0; i < bus->endpoints; i++) { ++ struct usb_endpoint_instance *endpoint = bus->endpoint_array + i; ++ endpoint->physical_endpoint = i; ++ urb_link_init (&endpoint->rcv); ++ urb_link_init (&endpoint->rdy); ++ urb_link_init (&endpoint->tx); ++ urb_link_init (&endpoint->done); ++ } ++ ++ bus->admin[usbd_admin_enable] = driver->bops->bus_enable; ++ bus->admin[usbd_admin_disable] = driver->bops->bus_disable; ++ bus->admin[usbd_admin_disconnect] = driver->bops->bus_disconnect; ++ bus->admin[usbd_admin_connect] = driver->bops->bus_connect; ++ bus->admin[usbd_admin_pm_off] = driver->bops->bus_pm_off; ++ bus->admin[usbd_admin_pm_on] = driver->bops->bus_pm_on; ++ bus->admin[usbd_admin_serial_number] = driver->bops->bus_serial_number; ++ ++ THROW_IF((bus->ep0 = ckmalloc (sizeof (struct usb_function_instance), GFP_ATOMIC)) == NULL, error) ++ bus->ep0->function_driver = &ep0_driver; ++ bus->ep0->endpointsRequested = 1; ++ THROW_IF(!(bus->ep0->endpoint_map_array = ckmalloc(sizeof(struct usb_endpoint_map) * 1, GFP_KERNEL)), error); ++ ++ 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; ++ printk(KERN_INFO"%s: FAILED\n", __FUNCTION__); ++ bus = NULL; ++ MOD_DEC_USE_COUNT; ++ } ++ ++ up(&usbd_bus_sem); ++ //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__); ++ 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. ++ */ ++void usbd_deregister_bus (struct usb_bus_instance *bus) ++{ ++ //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__); ++ 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; ++ MOD_DEC_USE_COUNT; // QQQ should this be after the up()? ++ up(&usbd_bus_sem); ++ //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__); ++} ++ ++ ++/* usb-device USB Device generic functions *************************************************** */ ++ ++/* usbd_enable_function_irq - called to enable the desired function ++ * ++ * Used by a USB Bus interface driver to create a virtual device. ++ * ++ * Return non-zero if error ++ */ ++int usbd_enable_function_irq (struct usb_bus_instance *bus, char *arg) ++{ ++ struct usb_function_instance *function = NULL; ++ struct list_head *lhd; ++ int len = 0; ++ int i; ++ int rc = -EINVAL; ++ int epn; ++ ++ //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__); ++ down(&usbd_bus_sem); ++ ++ if (arg && bus->arg) ++ lkfree(bus->arg); ++ ++ if (arg) { ++ bus->arg = lstrdup(arg); ++ len = strlen(arg); ++ } ++ ++ // initialize the strings pool ++ THROW_IF(usbd_strings_init (), error); ++ ++ for (i = 1; i < bus->endpoints; i++) { ++ struct usb_endpoint_instance *endpoint = bus->endpoint_array + i; ++ endpoint->rcv_urb = endpoint->tx_urb = NULL; ++ } ++ ++ list_for_each (lhd, &usbd_function_drivers) { ++ struct usb_function_driver *function_driver; ++ function_driver = list_entry_function (lhd); ++ ++ printk(KERN_INFO"%s: check: [%s] %s\n", __FUNCTION__, ++ (arg?arg:""), function_driver->name); ++ ++ // mode - single, either the first or a named function ++ CONTINUE_IF(arg && len && strncmp(function_driver->name, arg, len)); ++ ++ //printk(KERN_INFO"%s: found: %s\n", __FUNCTION__, function_driver->name); ++ ++ THROW_IF (!(function = ckmalloc (sizeof (struct usb_function_instance), GFP_ATOMIC)), error); ++ function->function_driver = function_driver; // XXX ++ ++ THROW_IF (!(function->endpoint_map_array = ckmalloc (sizeof(struct usb_endpoint_map) * ++ function->function_driver->device_description->endpointsRequested, ++ GFP_KERNEL)), error); ++ ++ THROW_IF(bus->driver->bops->request_endpoints( function->endpoint_map_array, ++ function->function_driver->device_description->endpointsRequested, ++ function->function_driver->device_description->requestedEndpoints), error); ++ ++ function->endpointsRequested = function->function_driver->device_description->endpointsRequested; ++ usbd_function_enable (bus, function); ++ break; ++ } ++ ++ if (NULL == function && NULL != arg && 0 != len) { ++ printk(KERN_INFO"Unknown function driver (len=%d) [%s], known drivers:\n",len,arg); ++ list_for_each (lhd, &usbd_function_drivers) { ++ struct usb_function_driver *function_driver = list_entry_function (lhd); ++ printk(KERN_INFO" [%s]\n", function_driver->name); ++ } ++ } ++ ++ THROW_IF (!(bus->function_instance = function), error); ++ ++ THROW_IF(bus->driver->bops->set_endpoints( function->function_driver->device_description->endpointsRequested, ++ function->endpoint_map_array), error); ++ // device bottom half ++ bus->device_bh.routine = usbd_device_bh; ++ bus->device_bh.data = bus; ++ bus->status = USBD_OK; ++ bus->bus_state = usbd_bus_state_enabled; ++ bus->ep0->endpoint_map_array->endpoint = bus->endpoint_array; ++ THROW_IF (usbd_function_enable (bus, bus->ep0), error); ++ ++ //printk(KERN_INFO"%s: %d endpoint: %p map: %p\n", __FUNCTION__, 0, ++ // bus->ep0->endpoint_map_array->endpoint, ++ // bus->ep0->endpoint_map_array); ++ ++ // iterate across the logical endpoint map to copy appropriate information ++ // into the physical endpoint instance array ++ ++ for (epn = 0; epn < bus->function_instance->endpointsRequested; epn++) { ++ ++ struct usb_endpoint_map *endpoint_map = bus->function_instance->endpoint_map_array + epn; ++ int physicalEndpoint = endpoint_map->physicalEndpoint[0]; ++ struct usb_endpoint_instance *endpoint = bus->endpoint_array + physicalEndpoint; ++ ++ //printk(KERN_INFO"%s: %d endpoint: %p map: %p bEndpointAddress: %02x\n", __FUNCTION__, ++ // epn, endpoint, endpoint_map, endpoint_map->bEndpointAddress[0]); ++ ++ 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->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; ++ } ++ } ++ ++ rc = 0; ++ ++ CATCH(error) { ++ printk(KERN_INFO"%s: FAILED\n", __FUNCTION__); ++ usbd_strings_exit(); ++ } ++ up(&usbd_bus_sem); ++ //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__); ++ return rc; ++} ++ ++/* usbd_disable_function - called to disable the current function ++ * ++ * Used by a USB Bus interface driver to destroy a virtual device. ++ */ ++void usbd_disable_function (struct usb_bus_instance *bus) ++{ ++ printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__); ++ down(&usbd_bus_sem); ++ // prevent any more bottom half scheduling ++ bus->status = USBD_CLOSING; ++ up(&usbd_bus_sem); ++ //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__); ++ ++ // wait for pending device bottom half to finish ++ while (bus->device_bh.data /*|| device->function_bh.data */) { ++ ++ //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... ++ queue_task(&bus->device_bh, &tq_immediate); ++ mark_bh (IMMEDIATE_BH); ++ // schedule_task(&device->device_bh); ++ ++ schedule_timeout (2000 * HZ); ++ } ++ ++ //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__); ++ 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->bNumInterfaces &&*/ bus->alternates) { ++ bus->bNumInterfaces = 0; ++ lkfree(bus->alternates); ++ bus->alternates = NULL; ++ } ++ if (bus->function_instance) { ++ if (bus->function_instance->endpoint_map_array) ++ lkfree(bus->function_instance->endpoint_map_array); ++ lkfree(bus->function_instance); ++ bus->function_instance = NULL; ++ } ++ usbd_strings_exit(); ++ bus->status = USBD_CLOSED; ++ up(&usbd_bus_sem); ++ printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__); ++} ++ ++usb_device_state_t usbd_device_state(struct usb_function_instance *function) ++{ ++ //printk(KERN_INFO"%s: %d\n", __FUNCTION__, function->bus->device_state); ++ return (function && function->bus) ? function->bus->device_state : STATE_UNKNOWN; ++} ++usb_device_state_t usbd_bus_state(struct usb_function_instance *function) ++{ ++ //printk(KERN_INFO"%s: %d\n", __FUNCTION__, function->bus->bus_state); ++ return (function && function->bus) ? function->bus->bus_state : usbd_bus_state_unknown; ++} ++usb_device_status_t usbd_bus_status(struct usb_function_instance *function) ++{ ++ //printk(KERN_INFO"%s: %d\n", __FUNCTION__, function->bus->status); ++ return (function && function->bus) ? function->bus->status : USBD_UNKNOWN; ++} ++ ++EXPORT_SYMBOL(usbd_register_bus); ++EXPORT_SYMBOL(usbd_deregister_bus); ++EXPORT_SYMBOL(usbd_enable_function_irq); ++EXPORT_SYMBOL(usbd_disable_function); ++EXPORT_SYMBOL(usbd_attached); ++EXPORT_SYMBOL(usbd_send_urb); ++EXPORT_SYMBOL(usbd_start_recv); ++EXPORT_SYMBOL(usbd_flush_endpoint); ++EXPORT_SYMBOL(usbd_cancel_urb_irq); ++EXPORT_SYMBOL(usbd_bus_event_irq); ++EXPORT_SYMBOL(usbd_bus_event); ++EXPORT_SYMBOL(usbd_urb_sent_finished_irq); ++EXPORT_SYMBOL(usbd_urb_recv_finished_irq); ++EXPORT_SYMBOL(usbd_device_state); ++EXPORT_SYMBOL(usbd_bus_state); ++EXPORT_SYMBOL(usbd_bus_status); ++EXPORT_SYMBOL(usbd_first_urb_detached_irq); ++ +diff -Nru a/drivers/usbd/usbd-build.h b/drivers/usbd/usbd-build.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-build.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1 @@ ++#define USBD_BUILD "000" +diff -Nru a/drivers/usbd/usbd-bus.h b/drivers/usbd/usbd-bus.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-bus.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,316 @@ ++/* ++ * usbd/usbd-bus.c - USB Device Bus Interface Driver Interface ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/* ++ * This file contains the USB Bus Interface Driver Interface definitions. ++ * ++ * 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 Bus Interface Drivers are structured such that the upper edge ++ * implements interfaces to the USB 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.) ++ * ++ */ ++ ++struct usb_endpoint_map; ++ ++/* 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 device_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 usb_bus_operations { ++ int (*bus_enable) (struct usb_bus_instance *, char *); ++ int (*bus_disable) (struct usb_bus_instance *, char *); ++ int (*bus_disconnect) (struct usb_bus_instance *, char *); ++ int (*bus_connect) (struct usb_bus_instance *, char *); ++ int (*bus_pm_off) (struct usb_bus_instance *, char *); ++ int (*bus_pm_on) (struct usb_bus_instance *, char *); ++ int (*bus_serial_number) (struct usb_bus_instance *, char *); ++ ++ int (*bus_attached) (struct usb_bus_instance *); ++ int (*bus_connected) (struct usb_bus_instance *); ++ int (*start_endpoint_in) (struct usb_bus_instance *, struct usb_endpoint_instance *); ++ int (*start_endpoint_out) (struct usb_bus_instance *, struct usb_endpoint_instance *); ++ int (*cancel_urb_irq) (struct urb *); ++ int (*endpoint_halted) (struct usb_bus_instance *, int); ++ int (*device_feature) (struct usb_bus_instance *, int, int); ++ int (*device_event) (struct usb_bus_instance *, usb_device_event_t, int); ++ int (*request_endpoints) (struct usb_endpoint_map *, int, struct usb_endpoint_request *); ++ int (*set_endpoints) (int , struct usb_endpoint_map *); ++}; ++ ++ ++/* 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. ++ */ ++struct usb_endpoint_map { ++ u8 configuration; ++ u8 interface; ++ 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 usb_endpoint_instance *endpoint; ++}; ++ ++ ++/* Endpoint configuration ++ * ++ * Per endpoint configuration data. Used to track which function driver owns ++ * an endpoint. ++ * ++ */ ++struct usb_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 ++ ++ // control ++ int status; // halted ++ int state; // available for use by bus interface driver ++ ++ // receive side ++ struct urb_link rcv; // received urbs ++ struct urb_link rdy; // empty urbs ready to receive ++ struct 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 urb_link done; // transmitted urbs ++ struct urb *tx_urb; // active urb ++ ++ __u32 sent; // data already sent ++ __u32 last; // data sent in last packet XXX do we need this ++}; ++ ++/* 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 ++ ++ ++/* 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 usb_bus_driver { ++ char *name; ++ u8 max_endpoints; // maximimum number of rx enpoints ++ u8 maxpacketsize; ++ u8 HighSpeedCapable; ++ struct usb_device_description *device_description; ++ struct usb_bus_operations *bops; ++}; ++ ++ ++typedef enum usbd_admin { ++ usbd_admin_enable = 0, ++ usbd_admin_disable = 1, ++ usbd_admin_connect = 2, ++ usbd_admin_disconnect = 3, ++ usbd_admin_pm_off = 4, ++ usbd_admin_pm_on = 5, ++ usbd_admin_serial_number = 6 ++} usbd_admin_t; ++ ++typedef int (*usbd_admin_proc_t) (struct usb_bus_instance *, char *); ++ ++ ++/* Function configuration structure ++ * ++ * This is allocated for each configured instance of a function driver. ++ * ++ * It stores pointers to the usb_function_driver for the appropriate function, ++ * and pointers to the USB HOST requested usb_configuration_description and ++ * usb_interface_description. ++ * ++ * The privdata pointer may be used by the function driver to store private ++ * per instance state information. ++ * ++ */ ++struct usb_function_instance { ++ struct usb_bus_instance *bus; ++ struct usb_function_driver *function_driver; ++ void *privdata; // private data for the function ++ __u8 endpointsRequested; // number of requested endpoints ++ struct usb_endpoint_map *endpoint_map_array; // map of endpoints requested by function driver ++}; ++ ++ ++/* 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 usb_bus_instance { ++ ++ struct usb_bus_driver *driver; ++ ++ usbd_admin_proc_t admin [8]; ++ ++ int endpoints; ++ struct usb_endpoint_instance *endpoint_array; // array of available configured endpoints ++ ++ char *serial_number_str; ++ ++ usb_device_status_t status; // device status ++ usbd_bus_state_t bus_state; ++ usb_device_state_t device_state; // current USB Device state ++ usb_device_state_t suspended_state; // previous USB Device state ++ ++ struct usb_function_instance *ep0; // ep0 configuration ++ struct usb_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 ++ ++ struct tq_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 usb_bus_instance *usbd_register_bus (struct usb_bus_driver *); ++void usbd_deregister_bus (struct usb_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 usb_bus_instance *, char *); ++void usbd_disable_function (struct usb_bus_instance *); ++ ++ ++void usbd_flush_endpoint_irq (struct usb_endpoint_instance *); ++void usbd_flush_endpoint (struct usb_endpoint_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_irq (struct usb_bus_instance *, usb_device_event_t, int); ++void usbd_bus_event (struct usb_bus_instance *, usb_device_event_t, int); ++ ++ ++/** ++ * usbd_recv_setup_irq - 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_recv_setup_irq (struct usb_function_instance*, struct usb_device_request *); ++ ++ ++void usbd_urb_sent_finished_irq (struct urb *, int ); ++void usbd_urb_recv_finished_irq (struct urb *, int ); ++ ++ ++/* ++ * Detach and return the first urb in a list with a distinguished ++ * head "hd", or NULL if the list is empty. ++ */ ++struct urb *usbd_first_urb_detached_irq (urb_link * hd); ++ ++/* ++ * Get a descriptor ++ */ ++int usbd_get_descriptor (struct usb_bus_instance *bus, u8 *buffer, int max, int descriptor_type, int index); ++ +diff -Nru a/drivers/usbd/usbd-chap9.h b/drivers/usbd/usbd-chap9.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-chap9.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,1054 @@ ++/* ++ * usbd/usbd-chap9.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++/* ++ * USB Descriptors are used to build a configuration database for each USB ++ * Function driver. ++ * ++ */ ++ ++ ++/* ++ * 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 ++ ++/* ++ * 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_DIR_OUT 0 ++#define USB_DIR_IN 0x80 ++#define USB_ENDPOINT_OPT 0x40 ++ ++ ++ ++/* ++ * Descriptor types ++ * C.f. USB Table 9-5 ++ */ ++#define USB_DT_DEVICE 0x01 ++#define USB_DT_CONFIG 0x02 ++#define USB_DT_STRING 0x03 ++#define USB_DT_INTERFACE 0x04 ++#define USB_DT_ENDPOINT 0x05 ++ ++ ++ ++#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) ++ ++/* ++ * 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 ++ ++/* ++ * 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 ++ ++/* ++ * 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 */ ++ ++/* ++ * 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 ++ ++/* ++ * 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 ++ ++ ++/* ++ * USB Spec Release number ++ */ ++ ++#define USB_BCD_VERSION 0x0200 ++ ++/* ++ * 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 ++ ++ ++/* ++ * 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 ++ ++/* ++ * get status bits ++ */ ++ ++#define USB_STATUS_SELFPOWERED 0x01 ++#define USB_STATUS_REMOTEWAKEUP 0x02 ++ ++#define USB_STATUS_HALT 0x01 ++ ++/* ++ * descriptor types ++ */ ++ ++#define USB_DESCRIPTOR_TYPE_DEVICE 0x01 ++#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02 ++#define USB_DESCRIPTOR_TYPE_STRING 0x03 ++#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04 ++#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 ++#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06 ++#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION 0x07 ++#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08 ++ ++/* ++ * standard feature selectors ++ */ ++#define USB_ENDPOINT_HALT 0x00 ++#define USB_DEVICE_REMOTE_WAKEUP 0x01 ++#define USB_TEST_MODE 0x02 ++ ++ ++/* USB Requests ++ * ++ */ ++ ++struct usb_device_request { ++ __u8 bmRequestType; ++ __u8 bRequest; ++ __u16 wValue; ++ __u16 wIndex; ++ __u16 wLength; ++} __attribute__ ((packed)); ++ ++ ++/* ++ * 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_STATE 0x22 ++#define CDC_CLASS_REQUEST_SEND_BREAK 0x23 ++ ++/* ++ * 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 ++ ++/* ++ * HID - Class Descriptors ++ * C.f. 7.1.1 ++ */ ++#define HID 0x21 ++#define HID_REPORT 0x22 ++#define HID_PHYSICAL 0x23 ++ ++/* ++ * HID Descriptor ++ * C.f. E.8 ++ */ ++struct hid_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u16 bcdHID; ++ u8 bCountryCode; ++ u8 bNumDescriptors; ++ u8 bReportType; ++ u16 wItemLength; ++} __attribute__((packed)); ++ ++/* ++ * ACM - Line Coding structure ++ * C.f CDC Table 50 ++ */ ++struct cdc_acm_line_coding { ++ __u32 dwDTERate; ++ __u8 bCharFormat; ++ __u8 bParityType; ++ __u8 bDataBits; ++} __attribute__((packed)); ++ ++/* ++ * ACM ++ * C.f CDC Table 50 ++ */ ++ ++ ++/* ++ * ACM ++ * C.f CDC Table 51 ++ */ ++ ++ ++/* USB Notification ++ * ++ */ ++ ++struct cdc_notification_descriptor { ++ __u8 bmRequestType; ++ __u8 bNotification; ++ __u16 wValue; ++ __u16 wIndex; ++ __u16 wLength; ++ __u8 data[2]; ++} __attribute__ ((packed)); ++ ++ ++ ++ ++/* ++ * 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 ++ ++// c.f. CDC 4.1 Table 14 ++#define AUDIO_CLASS 0x01 ++#define COMMUNICATIONS_DEVICE_CLASS 0x02 ++ ++// c.f. CDC 4.2 Table 15 ++#define COMMUNICATIONS_INTERFACE_CLASS 0x02 ++ ++// 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 ++ ++// 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 ++ ++// c.f. CDC 4.6 Table 18 ++#define DATA_INTERFACE_CLASS 0x0a ++ ++// c.f. CDC 4.7 Table 19 ++#define COMMUNICATIONS_NO_PROTOCOL 0x00 ++ ++ ++// 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 ++ ++// Audio Interface Class - c.f Appendix A.1 ++#define AUDIO_INTERFACE_CLASS 0x01 ++ ++// 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 ++ ++// Audio Interface Proctol - c.f. Appendix A.3 ++#define AUDIO_PR_PROTOCOL_UNDEFINED 0x00 ++ ++// 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 ++ ++// 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 ++ ++// 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 ++ ++// Audio Class-Specific Endpoint Descriptor Subtypes - c.f. A.8 ++#define AUDIO_DESCRIPTOR_UNDEFINED 0x00 ++#define AUDIO_EP_GENERAL 0x01 ++ ++// 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 ++ ++ ++ ++ ++ ++ ++/* ++ * 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 ++ ++ ++ ++/* ++ * commuications class description structures ++ * ++ * c.f. CDC 5.1 ++ * c.f. WCMC 6.7.2 ++ * ++ * XXX add the other dozen class descriptor description structures.... ++ */ ++ ++struct usb_header_description { ++ __u8 bDescriptorSubtype; ++ __u16 bcdCDC; ++}; ++ ++struct usb_call_management_description { ++ __u8 bmCapabilities; ++ __u8 bDataInterface; ++}; ++ ++struct usb_abstract_control_description { ++ __u8 bmCapabilities; ++}; ++ ++struct usb_union_function_description { ++ __u8 bMasterInterface; ++ __u8 bSlaveInterface[1]; ++ //__u8 bSlaveInterface[0]; // XXX FIXME ++}; ++ ++struct usb_ethernet_networking_description { ++ char *iMACAddress; ++ __u8 bmEthernetStatistics; ++ __u16 wMaxSegmentSize; ++ __u16 wNumberMCFilters; ++ __u8 bNumberPowerFilters; ++}; ++ ++struct usb_mobile_direct_line_model_description { ++ __u16 bcdVersion; ++ __u8 bGUID[16]; ++}; ++ ++struct usb_mobile_direct_line_model_detail_description { ++ __u8 bGuidDescriptorType; ++ __u8 bDetailData[2]; ++ //__u8 bDetailData[0]; // XXX FIXME ++}; ++ ++struct usb_class_description { ++ __u8 bDescriptorSubtype; ++ __u8 elements; ++ union { ++ struct usb_header_description header; ++ struct usb_call_management_description call_management; ++ struct usb_abstract_control_description abstract_control; ++ struct usb_union_function_description union_function; ++ struct usb_ethernet_networking_description ethernet_networking; ++ struct usb_mobile_direct_line_model_description mobile_direct; ++ struct usb_mobile_direct_line_model_detail_description mobile_direct_detail; ++ } description; ++}; ++ ++/* endpoint modifiers ++ * static struct usb_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 OUT 0x00 ++#define IN 0x80 ++ ++#define CONTROL 0x00 ++#define ISOCHRONOUS 0x01 ++#define BULK 0x02 ++#define INTERRUPT 0x03 ++ ++ ++/* configuration modifiers ++ */ ++#define BMATTRIBUTE_RESERVED 0x80 ++#define BMATTRIBUTE_SELF_POWERED 0x40 ++ ++/* ++ * The UUT tester specifically tests for MaxPower to be non-zero (> 0). ++ */ ++#if !defined(CONFIG_USBD_MAXPOWER) || (CONFIG_USBD_MAXPOWER == 0) ++ #define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED ++ #define BMAXPOWER 1 ++#else ++ #define BMATTRIBUTE BMATTRIBUTE_RESERVED ++ #define BMAXPOWER CONFIG_USBD_MAXPOWER ++#endif ++ ++ ++ ++ ++/* ++ * standard usb descriptor structures ++ */ ++ ++struct usb_endpoint_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; // 0x5 ++ __u8 bEndpointAddress; ++ __u8 bmAttributes; ++ __u16 wMaxPacketSize; ++ __u8 bInterval; ++} __attribute__ ((packed)); ++ ++struct usb_interface_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; // 0x04 ++ __u8 bInterfaceNumber; ++ __u8 bAlternateSetting; ++ __u8 bNumEndpoints; ++ __u8 bInterfaceClass; ++ __u8 bInterfaceSubClass; ++ __u8 bInterfaceProtocol; ++ __u8 iInterface; ++} __attribute__ ((packed)); ++ ++struct usb_configuration_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; // 0x2 ++ __u16 wTotalLength; ++ __u8 bNumInterfaces; ++ __u8 bConfigurationValue; ++ __u8 iConfiguration; ++ __u8 bmAttributes; ++ __u8 bMaxPower; ++} __attribute__ ((packed)); ++ ++struct usb_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; ++} __attribute__ ((packed)); ++ ++struct usb_string_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; // 0x03 ++ __u16 wData[0]; ++} __attribute__ ((packed)); ++ ++struct usb_device_qualifier_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u16 bcdUSB; ++ __u8 bDeviceClass; ++ __u8 bDeviceSubClass; ++ __u8 bDeviceProtocol; ++ __u8 bMaxPacketSize0; ++ __u8 bNumConfigurations; ++ __u8 bReserved; ++} __attribute__ ((packed)); ++ ++struct usb_generic_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++} __attribute__ ((packed)); ++ ++struct usb_generic_class_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++} __attribute__ ((packed)); ++ ++ ++/* ++ * communications class descriptor structures ++ * ++ * c.f. CDC 5.2 Table 25c ++ */ ++ ++struct usb_class_function_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++} __attribute__ ((packed)); ++ ++struct usb_class_function_descriptor_generic { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bmCapabilities; ++} __attribute__ ((packed)); ++ ++struct usb_class_header_function_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x00 ++ __u16 bcdCDC; ++} __attribute__ ((packed)); ++ ++struct usb_class_call_management_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x01 ++ __u8 bmCapabilities; ++ __u8 bDataInterface; ++} __attribute__ ((packed)); ++ ++struct usb_class_abstract_control_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x02 ++ __u8 bmCapabilities; ++} __attribute__ ((packed)); ++ ++struct usb_class_direct_line_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x03 ++} __attribute__ ((packed)); ++ ++struct usb_class_telephone_ringer_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x04 ++ __u8 bRingerVolSeps; ++ __u8 bNumRingerPatterns; ++} __attribute__ ((packed)); ++ ++struct usb_class_telephone_call_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x05 ++ __u8 bmCapabilities; ++} __attribute__ ((packed)); ++ ++struct usb_class_union_function_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x06 ++ __u8 bMasterInterface; ++ __u8 bSlaveInterface0[0]; ++} __attribute__ ((packed)); ++ ++struct usb_class_country_selection_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x07 ++ __u8 iCountryCodeRelDate; ++ __u16 wCountryCode0[0]; ++} __attribute__ ((packed)); ++ ++ ++struct usb_class_telephone_operational_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x08 ++ __u8 bmCapabilities; ++} __attribute__ ((packed)); ++ ++ ++struct usb_class_usb_terminal_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x09 ++ __u8 bEntityId; ++ __u8 bInterfaceNo; ++ __u8 bOutInterfaceNo; ++ __u8 bmOptions; ++ __u8 bChild0[0]; ++} __attribute__ ((packed)); ++ ++struct usb_class_network_channel_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x0a ++ __u8 bEntityId; ++ __u8 iName; ++ __u8 bChannelIndex; ++ __u8 bPhysicalInterface; ++} __attribute__ ((packed)); ++ ++struct usb_class_protocol_unit_function_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x0b ++ __u8 bEntityId; ++ __u8 bProtocol; ++ __u8 bChild0[0]; ++} __attribute__ ((packed)); ++ ++struct usb_class_extension_unit_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x0c ++ __u8 bEntityId; ++ __u8 bExtensionCode; ++ __u8 iName; ++ __u8 bChild0[0]; ++} __attribute__ ((packed)); ++ ++struct usb_class_multi_channel_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x0d ++ __u8 bmCapabilities; ++} __attribute__ ((packed)); ++ ++struct usb_class_capi_control_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x0e ++ __u8 bmCapabilities; ++} __attribute__ ((packed)); ++ ++struct usb_class_ethernet_networking_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x0f ++ __u8 iMACAddress; ++ __u32 bmEthernetStatistics; ++ __u16 wMaxSegmentSize; ++ __u16 wNumberMCFilters; ++ __u8 bNumberPowerFilters; ++} __attribute__ ((packed)); ++ ++struct usb_class_atm_networking_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x10 ++ __u8 iEndSystermIdentifier; ++ __u8 bmDataCapabilities; ++ __u8 bmATMDeviceStatistics; ++ __u16 wType2MaxSegmentSize; ++ __u16 wType3MaxSegmentSize; ++ __u16 wMaxVC; ++} __attribute__ ((packed)); ++ ++ ++struct usb_class_mdlm_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x12 ++ __u16 bcdVersion; ++ __u8 bGUID[16]; ++} __attribute__ ((packed)); ++ ++struct usb_class_mdlmd_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; // 0x13 ++ __u8 bGuidDescriptorType; ++ __u8 bDetailData[0]; ++ ++} __attribute__ ((packed)); ++ ++ ++/* ++ * descriptor union structures ++ */ ++ ++struct usb_descriptor { ++ union { ++ struct usb_generic_descriptor generic; ++ struct usb_generic_class_descriptor generic_class; ++ struct usb_endpoint_descriptor endpoint; ++ struct usb_interface_descriptor interface; ++ struct usb_configuration_descriptor configuration; ++ struct usb_device_descriptor device; ++ struct usb_string_descriptor string; ++ } descriptor; ++ ++} __attribute__ ((packed)); ++ ++struct usb_class_descriptor { ++ union { ++ struct usb_class_function_descriptor function; ++ struct usb_class_function_descriptor_generic generic; ++ struct usb_class_header_function_descriptor header_function; ++ struct usb_class_call_management_descriptor call_management; ++ struct usb_class_abstract_control_descriptor abstract_control; ++ struct usb_class_direct_line_descriptor direct_line; ++ struct usb_class_telephone_ringer_descriptor telephone_ringer; ++ struct usb_class_telephone_operational_descriptor telephone_operational; ++ struct usb_class_telephone_call_descriptor telephone_call; ++ struct usb_class_union_function_descriptor union_function; ++ struct usb_class_country_selection_descriptor country_selection; ++ struct usb_class_usb_terminal_descriptor usb_terminal; ++ struct usb_class_network_channel_descriptor network_channel; ++ struct usb_class_extension_unit_descriptor extension_unit; ++ struct usb_class_multi_channel_descriptor multi_channel; ++ struct usb_class_capi_control_descriptor capi_control; ++ struct usb_class_ethernet_networking_descriptor ethernet_networking; ++ struct usb_class_atm_networking_descriptor atm_networking; ++ struct usb_class_mdlm_descriptor mobile_direct; ++ struct usb_class_mdlmd_descriptor mobile_direct_detail; ++ } descriptor; ++ ++} __attribute__ ((packed)); ++ ++ ++/* ++ * 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 usb_audio_status_word { ++ __u8 bStatusType; ++ __u8 bOriginator; ++} __attribute__ ((packed)); ++ ++struct usb_audio_ac_interface_header_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u16 bcdADC; ++ __u16 wTotalLength; ++ __u8 binCollection; ++ __u8 bainterfaceNr[0]; ++} __attribute__ ((packed)); ++ ++struct usb_audio_input_terminal_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bTerminalID; ++ __u16 wTerminalType; ++ __u8 bAssocTerminal; ++ __u8 bNrChannels; ++ __u16 wChannelConfig; ++ __u8 iChannelNames; ++ __u8 iTerminal; ++} __attribute__ ((packed)); ++ ++struct usb_audio_input_terminal_description { ++ struct usb_audio_input_terminal_descriptor *audio_input_terminal_descriptor; ++ __u16 wTerminalType; ++ __u16 wChannelConfig; ++ char * iChannelNames; ++ char * iTerminal; ++}; ++ ++struct usb_audio_output_terminal_descriptor { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bTerminalID; ++ __u16 wTerminalType; ++ __u8 bAssocTerminal; ++ __u8 bSourceID; ++ __u8 iTerminal; ++} __attribute__ ((packed)); ++ ++struct usb_audio_output_terminal_description { ++ struct usb_audio_output_terminal_descriptor *audio_output_terminal_descriptor; ++ __u16 wTerminalType; ++ char * iTerminal; ++}; ++ ++struct usb_audio_mixer_unit_descriptor_a { ++ __u8 bFunctionLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bUniteID; ++ __u8 bNrinPins; ++} __attribute__ ((packed)); ++ ++struct usb_audio_mixer_unit_descriptor_b { ++ __u8 baSourceID; ++ __u8 bNrChannels; ++ __u16 wChannelConfig; ++ __u8 iChannelNames; ++ __u8 bmControls; ++ __u8 iMixer; ++} __attribute__ ((packed)); ++ ++struct usb_audio_mixer_unit_description { ++ struct usb_audio_mixer_unit_descriptor *audio_mixer_unit_descriptor; ++ __u16 wChannelConfig; ++ char * iMixer; ++}; ++ ++ ++struct usb_audio_as_general_interface_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bTerminalLink; ++ __u8 bDelay; ++ __u16 wFormatTag; ++} __attribute__ ((packed)); ++ ++struct usb_audio_as_general_interface_description { ++ struct usb_audio_as_general_interface_descriptor *audio_as_general_interface_descriptor; ++ __u16 wFormatTag; ++}; ++ ++struct usb_audio_as_format_type_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bFormatType; ++ __u8 bNrChannels; ++ __u8 bSubFrameSize; ++ __u8 bBitResolution; ++ __u8 bSamFreqType; ++ __u8 iSamFreq[3]; ++} __attribute__ ((packed)); ++ ++struct usb_audio_as_format_type_description { ++ struct usb_audio_as_format_type_descriptor *audio_as_format_type_descriptor; ++ __u16 wFormatTag; ++}; ++ ++ ++ ++ ++struct usb_audio_descriptor { ++ union { ++ struct usb_audio_ac_interface_header_descriptor header; ++ struct usb_audio_input_terminal_descriptor input; ++ struct usb_audio_output_terminal_descriptor output; ++ } descriptor; ++} __attribute__ ((packed)); ++ ++ ++ ++struct usb_ac_endpoint_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bEndpointAddress; ++ __u8 bmAttributes; ++ __u16 wMaxPacketSize; ++ __u8 bInterval; ++ __u8 bRefresh; ++ __u8 bSynchAddress; ++} __attribute__ ((packed)); ++ ++ ++struct usb_as_iso_endpoint_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bmAttributes; ++ __u8 bLockDelayUnits; ++ __u16 wLockDelay; ++} __attribute__ ((packed)); ++ ++ +diff -Nru a/drivers/usbd/usbd-export.h b/drivers/usbd/usbd-export.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-export.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1 @@ ++#define USBD_EXPORT_TAG "usbd-gpl-20040227-1420-usbd-amd-pb1x00-kit" +diff -Nru a/drivers/usbd/usbd-fops.c b/drivers/usbd/usbd-fops.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-fops.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,589 @@ ++/* ++ * usbd/usbd-fops.c - USB Function support ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++ ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++ ++#include "usbd-chap9.h" ++#include "usbd-mem.h" ++#include "usbd.h" ++#include "usbd-func.h" ++#include "usbd-bus.h" ++ ++ ++struct usb_string_descriptor **usb_strings; ++ ++extern struct list_head usbd_function_drivers; ++extern struct usb_bus_instance *usbd_bus_instance; ++ ++ ++/* usbd_get_string - find and return a string descriptor ++ * ++ * Find an indexed string and return a pointer to a it. ++ */ ++struct usb_string_descriptor *usbd_get_string (__u8 index) ++{ ++ RETURN_NULL_IF (index >= usbd_maxstrings); ++ return usb_strings[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. ++ */ ++__u8 usbd_alloc_string (char *str) ++{ ++ int i; ++ struct usb_string_descriptor *string; ++ __u8 bLength; ++ __u16 *wData; ++ ++ RETURN_ZERO_IF(!str || !strlen (str)); ++ ++ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, str); ++ ++ // find an empty string descriptor slot ++ for (i = 1; i < usbd_maxstrings; i++) { ++ ++ CONTINUE_IF (usb_strings[i] != NULL); ++ ++ bLength = sizeof (struct usb_string_descriptor) + 2 * strlen (str); ++ ++ RETURN_ZERO_IF(!(string = 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_dealloc_string - deallocate a string descriptor ++ * ++ * Find and remove an allocated string. ++ */ ++void usbd_dealloc_string (__u8 index) ++{ ++ struct usb_string_descriptor *string; ++ ++ if ((index < usbd_maxstrings) && (string = usb_strings[index])) { ++ usb_strings[index] = NULL; ++ lkfree (string); ++ } ++} ++ ++/* 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. ++ */ ++struct urb *usbd_alloc_urb (struct usb_function_instance *function, int endpoint_index, ++ int length, int (*callback) (struct urb *, int)) ++{ ++ struct urb *urb = NULL; ++ struct usb_endpoint_map *endpoint_map; ++ struct usb_endpoint_instance *endpoint; ++ unsigned long flags; ++ ++ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, function->function_driver->name); ++ RETURN_NULL_IF(!function); ++ 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 = ckmalloc (sizeof (struct 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 (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 { ++ THROW_IF(!(urb->buffer = ckmalloc (length, GFP_ATOMIC)), error); ++ } ++ } ++ ++ CATCH(error) { ++ printk(KERN_ERR"%s: dealloc %p\n", __FUNCTION__, urb); ++ usbd_dealloc_urb(urb); ++ urb = NULL; ++ } ++ local_irq_restore(flags); ++ //printk(KERN_INFO"%s: finis %p\n", __FUNCTION__, urb); ++ return urb; ++} ++ ++struct urb *usbd_alloc_urb_ep0 (struct usb_function_instance *function, int length, int (*callback) (struct urb *, int)) ++{ ++ return usbd_alloc_urb(function->bus->ep0, 0, length, callback); ++} ++ ++/* usbd_dealloc_urb - deallocate an URB and associated buffer ++ * ++ * Deallocate an urb structure and associated data. ++ */ ++void usbd_dealloc_urb (struct urb *urb) ++{ ++ RETURN_IF (!urb); ++ if (urb->buffer) ++ (urb->function_instance && urb->function_instance->function_driver->fops->dealloc_urb_data) ? ++ urb->function_instance->function_driver->fops->dealloc_urb_data : lkfree (urb->buffer); ++ lkfree (urb); ++} ++ ++/* usbd_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. ++ */ ++void usbd_urb_callback (struct urb *urb, int rc) ++{ ++ RETURN_IF (!urb); ++ if (!urb->callback || urb->callback(urb,rc)) ++ usbd_dealloc_urb(urb); ++} ++ ++ ++/* usbd_recv_setup_irq - process a received urb ++ * ++ * Used by a USB Bus interface driver to pass received data in a URB to the ++ * appropriate USB Function driver. ++ */ ++int usbd_recv_setup_irq (struct usb_function_instance *function, struct usb_device_request *request) ++{ ++ return function->function_driver->fops->recv_setup_irq(request); ++} ++ ++void *usbd_function_get_privdata(struct usb_function_instance *function) ++{ ++ return(function->privdata); ++} ++ ++void usbd_function_set_privdata(struct usb_function_instance *function, void *privdata) ++{ ++ function->privdata = privdata; ++} ++ ++int usbd_endpoint_wMaxPacketSize(struct usb_function_instance *function, int endpoint_index, int hs) ++{ ++ struct usb_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ return le16_to_cpu(endpoint_map[endpoint_index].wMaxPacketSize[hs]); ++} ++ ++int usbd_endpoint_bEndpointAddress(struct usb_function_instance *function, int endpoint_index, int hs) ++{ ++ struct usb_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ return endpoint_map[endpoint_index].bEndpointAddress[hs]; ++} ++ ++int usbd_endpoint_transferSize(struct usb_function_instance *function, int endpoint_index, int hs) ++{ ++ struct usb_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ return endpoint_map[endpoint_index].transferSize[hs]; ++} ++ ++void usbd_endpoint_update(struct usb_function_instance *function, int endpoint_index, ++ struct usb_endpoint_descriptor *endpoint, int hs) ++{ ++ endpoint->bEndpointAddress = usbd_endpoint_bEndpointAddress(function, endpoint_index, hs); ++ endpoint->wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, endpoint_index, hs); ++} ++ ++int usbd_endpoint_interface(struct usb_function_instance *function, int endpoint_index) ++{ ++ struct usb_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ return endpoint_map[endpoint_index].interface; ++} ++ ++int usbd_interface_AltSetting(struct usb_function_instance *function, int interface_index) ++{ ++ // XXX TODO - per function modifications for composite devices ++ return function->bus->alternates[interface_index]; ++} ++ ++int usbd_ConfigurationValue(struct usb_function_instance *function) ++{ ++ // XXX TODO - per function modifications for composite devices ++ return function->bus->ConfigurationValue; ++} ++ ++int usbd_high_speed(struct usb_function_instance *function) ++{ ++ // XXX TODO - per function modifications for composite devices ++ return function->bus->HighSpeedFlag; ++} ++ ++/* usb-device USB FUNCTION generic functions ************************************************* */ ++ ++/* 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. ++ */ ++static struct usb_alternate_instance *alloc_function_alternates (int bInterfaceNumber, ++ struct usb_interface_description *interface_description, ++ int alternates, struct usb_alternate_description *alternate_description_array) ++{ ++ int i; ++ struct usb_alternate_instance *alternate_instance_array; ++ ++ // allocate array of alternate instances ++ RETURN_NULL_IF(!(alternate_instance_array = ckmalloc (sizeof (struct usb_alternate_instance) * alternates, GFP_KERNEL))); ++ ++ // iterate across the alternate descriptions ++ for (i = 0; i < alternates; i++) { ++ ++ struct usb_alternate_description *alternate_description = alternate_description_array + i; ++ struct usb_alternate_instance *alternate_instance = alternate_instance_array + i; ++ struct usb_interface_descriptor *interface_descriptor; ++ ++ interface_descriptor = alternate_description->interface_descriptor; ++ if (interface_descriptor->bInterfaceNumber != bInterfaceNumber) { ++ printk(KERN_INFO"%s: bInterfaceNumber mis-match: %d should be %d\n", __FUNCTION__, ++ 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. ++ */ ++static struct usb_interface_instance *alloc_function_interfaces (int bNumInterfaces, ++ struct usb_interface_description *interface_description_array) ++{ ++ int interface; ++ struct usb_interface_instance *interface_instance_array; ++ ++ // allocate array of interface instances ++ RETURN_NULL_IF(!(interface_instance_array = ++ ckmalloc (sizeof (struct usb_interface_instance) * bNumInterfaces, GFP_KERNEL))); ++ ++ // iterate across the interface descriptions ++ for (interface = 0; interface < bNumInterfaces; interface++) { ++ ++ struct usb_interface_description *interface_description = interface_description_array + interface; ++ struct usb_interface_instance *interface_instance = interface_instance_array + interface; ++ ++ interface_instance->alternates_instance_array = ++ alloc_function_alternates (interface, interface_description, ++ interface_description->alternates, interface_description->alternate_list); ++ interface_instance->alternates = interface_description->alternates; ++ } ++ return interface_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. ++ */ ++static struct usb_configuration_instance *alloc_function_configurations (int bNumConfigurations, ++ struct usb_configuration_description *configuration_description_array) ++{ ++ int i; ++ struct usb_configuration_instance *configuration_instance_array; ++ ++ // allocate array ++ RETURN_NULL_IF(!(configuration_instance_array = ++ ckmalloc (sizeof (struct usb_configuration_instance) * bNumConfigurations, GFP_KERNEL))); ++ // fill in array ++ for (i = 0; i < bNumConfigurations; i++) { ++ int j; ++ int length; ++ ++ struct usb_configuration_description *configuration_description = configuration_description_array + i; ++ struct usb_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 = i + 1; ++ configuration_descriptor->wTotalLength = 0; ++ configuration_descriptor->bNumInterfaces = configuration_description->bNumInterfaces; ++ configuration_descriptor->iConfiguration = usbd_alloc_string (configuration_description->iConfiguration); ++ ++ // save the configuration descriptor in the configuration instance array ++ configuration_instance_array[i].configuration_descriptor = configuration_descriptor; ++ ++ RETURN_NULL_IF (!(configuration_instance_array[i].interface_instance_array = ++ alloc_function_interfaces (configuration_description->bNumInterfaces, ++ configuration_description->interface_list))); ++ ++ length = sizeof(struct usb_configuration_descriptor); ++ ++ for (j = 0; j < configuration_descriptor->bNumInterfaces; j++) { ++ ++ int alternate; ++ struct usb_interface_instance *interface_instance = ++ configuration_instance_array[i].interface_instance_array + j; ++ ++ //printk(KERN_INFO"%s: len: %02x:%02d configuration\n", __FUNCTION__, length, length); ++ for (alternate = 0; alternate < interface_instance->alternates; alternate++) { ++ int class; ++ int endpoint; ++ struct usb_alternate_instance *alternate_instance = ++ interface_instance->alternates_instance_array + alternate; ++ ++ length += sizeof (struct usb_interface_descriptor); ++ ++ //printk(KERN_INFO"%s: len: %02x:%02d interface\n", __FUNCTION__, length, length); ++ for (class = 0; class < alternate_instance->classes; class++) { ++ struct usb_generic_class_descriptor * class_descriptor = ++ *(alternate_instance->class_list + class); ++ length += class_descriptor->bLength; ++ //printk(KERN_INFO"%s: len: %02x:%02d class\n", __FUNCTION__, length, length); ++ } ++ ++ for (endpoint = 0; endpoint < alternate_instance->endpoints; endpoint++) { ++ struct usb_endpoint_descriptor * endpoint_descriptor = ++ *(alternate_instance->endpoint_list + endpoint); ++ length += endpoint_descriptor->bLength; ++ //printk(KERN_INFO"%s: len: %02x:%02d endpoint\n", __FUNCTION__, length, length); ++ } ++ } ++ } ++ configuration_descriptor->wTotalLength = cpu_to_le16 (length); ++ configuration_instance_array[i].bNumInterfaces = configuration_description->bNumInterfaces; ++ //printk(KERN_INFO"%s: configuration_instance_array[%d]: %p\n", __FUNCTION__, i, ++ // &configuration_instance_array[i]); ++ } ++ return configuration_instance_array; ++} ++ ++ ++void usbd_func_event_irq(struct usb_bus_instance *bus, struct usb_function_instance *function, ++ usb_device_event_t event , int data) ++{ ++ function->function_driver->fops->event_irq(function, event, data); ++} ++ ++ ++/* usbd_register_function - register a usb function driver ++ * ++ * Used by a USB Function driver to register itself with the usb device layer. ++ * ++ * It will create a usb_function_instance structure. ++ * ++ * The user friendly configuration/interface/endpoint descriptions are compiled into ++ * the equivalent ready to use descriptor records. ++ * ++ * All function drivers must register before any bus interface drivers. ++ * ++ */ ++int usbd_register_function (struct usb_function_driver *function_driver) ++{ ++ MOD_INC_USE_COUNT; ++ list_add_tail (&function_driver->drivers, &usbd_function_drivers); ++ return 0; ++} ++ ++ ++/* usbd_deregister_function - called by a USB FUNCTION driver to deregister itself ++ * ++ * Called by a USB Function driver De-register a usb function driver. ++ */ ++void usbd_deregister_function (struct usb_function_driver *function_driver) ++{ ++ list_del (&function_driver->drivers); ++ MOD_DEC_USE_COUNT; ++} ++ ++ ++int usbd_function_enable (struct usb_bus_instance *bus, struct usb_function_instance *function) ++{ ++ struct usb_function_driver *function_driver = function->function_driver; ++ struct usb_device_description *device_description; ++ struct usb_device_descriptor *device_descriptor; ++ int rc = 0; ++ ++ function->bus = bus; ++ ++ RETURN_EINVAL_IF(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 (bus->serial_number_str && strlen (bus->serial_number_str)) ++ device_descriptor->iSerialNumber = usbd_alloc_string (bus->serial_number_str); ++ 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; ++ ++ /* call alloc_function_configurations() to allocate the configuration descriptor array ++ */ ++ RETURN_EINVAL_IF (!(function_driver->configuration_instance_array = ++ alloc_function_configurations (function_driver->bNumConfigurations, ++ function_driver->configuration_description))); ++ function_driver->device_descriptor = device_descriptor; ++ function_driver->device_qualifier_descriptor = device_description->device_qualifier_descriptor; ++ return rc; ++} ++ ++ ++void usbd_function_disable (struct usb_function_instance *function) ++{ ++ int configuration; ++ struct usb_function_driver *function_driver = function->function_driver; ++ struct usb_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array; ++ ++ 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; ++ struct usb_configuration_instance *configuration_instance = configuration_instance_array + configuration; ++ ++ for (interface = 0; interface < configuration_instance->bNumInterfaces; interface++) { ++ ++ //int alternate; ++ struct usb_interface_instance *interface_instance = ++ configuration_instance->interface_instance_array + interface; ++ ++ lkfree (interface_instance->alternates_instance_array); ++ } ++ lkfree (configuration_instance->interface_instance_array); ++ //usbd_dealloc_descriptor_strings( (struct usb_descriptor *) ++ // configuration_instance_array[configuration].configuration_descriptor); ++ } ++ } ++ ++ //usbd_dealloc_descriptor_strings ((struct usb_descriptor *) function_driver->device_descriptor); ++ lkfree (configuration_instance_array); ++ ++ function->bus = NULL; ++} ++ ++EXPORT_SYMBOL(usbd_register_function ); ++EXPORT_SYMBOL(usbd_deregister_function); ++EXPORT_SYMBOL(usbd_alloc_string); ++EXPORT_SYMBOL(usbd_dealloc_string); ++EXPORT_SYMBOL(usbd_get_string); ++EXPORT_SYMBOL(usbd_alloc_urb); ++EXPORT_SYMBOL(usbd_alloc_urb_ep0); ++EXPORT_SYMBOL(usbd_dealloc_urb); ++EXPORT_SYMBOL(usbd_recv_setup_irq); ++EXPORT_SYMBOL(usbd_urb_callback); ++EXPORT_SYMBOL(usbd_endpoint_halted); ++EXPORT_SYMBOL(usbd_device_feature); ++EXPORT_SYMBOL(usbd_function_get_privdata); ++EXPORT_SYMBOL(usbd_function_set_privdata); ++ ++EXPORT_SYMBOL(usbd_endpoint_wMaxPacketSize); ++EXPORT_SYMBOL(usbd_endpoint_bEndpointAddress); ++EXPORT_SYMBOL(usbd_endpoint_interface); ++EXPORT_SYMBOL(usbd_endpoint_transferSize); ++EXPORT_SYMBOL(usbd_interface_AltSetting); ++EXPORT_SYMBOL(usbd_ConfigurationValue); ++EXPORT_SYMBOL(usbd_high_speed); ++EXPORT_SYMBOL(usbd_endpoint_update); +diff -Nru a/drivers/usbd/usbd-func.h b/drivers/usbd/usbd-func.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-func.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,256 @@ ++/* ++ * usbd/usbd-func.h - USB Device Function Driver Interface ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/* ++ * This file contains the USB Function Driver Interface definitions. ++ * ++ * This is the interface between the bottom of the USB Function drivers and ++ * the top of the USB Core and is comprised of: ++ * ++ * o public functions exported by the USB Core layer ++ * ++ * o structures and functions passed by the Bus Interface Driver to the ++ * USB Core layer ++ * ++ * ++ * USB Function Drivers are structured such that the upper edge implements ++ * some specific function (network, serial, mass storage etc) and the lower ++ * edge interfaces to the USB Device Core for USB services. ++ * ++ */ ++ ++/* ++ * USB Function Driver structures ++ * ++ * Descriptors: ++ * struct usb_endpoint_description ++ * struct usb_interface_description ++ * struct usb_configuration_description ++ * ++ * Driver description: ++ * struct usb_function_driver ++ * struct usb_function_operations ++ * ++ */ ++ ++struct usb_function_operations { ++ int (*function_enable) (struct usb_function_instance *); ++ void (*function_disable) (struct usb_function_instance *); ++ void (*event_irq) (struct usb_function_instance *, usb_device_event_t, int); ++ int (*recv_setup_irq) (struct usb_device_request *); ++ int (*alloc_urb_data) (struct urb *, int); ++ void (*dealloc_urb_data) (struct urb *); ++}; ++ ++ ++/* ++ * function driver definitions ++ */ ++struct usb_alternate_instance { ++ struct usb_interface_descriptor *interface_descriptor; ++ int classes; ++ struct usb_generic_class_descriptor **class_list; ++ int endpoints; ++ struct usb_endpoint_descriptor **endpoint_list; ++ u8 *endpoint_indexes; ++}; ++ ++ ++/* ++ * usb device description structures ++ */ ++ ++struct usb_alternate_description { ++ struct usb_interface_descriptor *interface_descriptor; ++ char *iInterface; ++ ++ // list of CDC class descriptions for this alternate interface ++ u8 classes; ++ struct usb_generic_class_descriptor **class_list; ++ ++ // list of endpoint descriptions for this alternate interface ++ u8 endpoints; ++ struct usb_endpoint_descriptor **endpoint_list; ++ ++ // list of indexes into endpoint request map for each endpoint descriptor ++ u8 *endpoint_indexes; ++}; ++ ++struct usb_interface_description { ++ // list of alternate interface descriptions for this interface ++ u8 alternates; ++ struct usb_alternate_description *alternate_list; ++}; ++ ++struct usb_configuration_description { ++ struct usb_configuration_descriptor *configuration_descriptor; ++ char *iConfiguration; ++ // list of interface descriptons for this configuration ++ u8 bNumInterfaces; ++ struct usb_interface_description *interface_list; ++ int configuration_type; ++}; ++ ++struct usb_device_description { ++ struct usb_device_descriptor *device_descriptor; ++ struct usb_device_qualifier_descriptor *device_qualifier_descriptor; ++ char *iManufacturer; ++ char *iProduct; ++ char *iSerialNumber; ++ u8 endpointsRequested; ++ struct usb_endpoint_request *requestedEndpoints; ++ struct usb_function_endpoint_config *endpointConfig; ++}; ++ ++ ++struct usb_interface_instance { ++ u8 alternates; ++ struct usb_alternate_instance *alternates_instance_array; ++}; ++ ++struct usb_configuration_instance { ++ int bNumInterfaces; ++ struct usb_configuration_descriptor *configuration_descriptor; ++ struct usb_interface_instance *interface_instance_array; ++ struct usb_function_driver *function_driver; ++}; ++ ++ ++extern struct usb_string_descriptor **usb_strings; ++__u8 usbd_alloc_string (char *); ++void usbd_dealloc_string (__u8 ); ++void usbd_dealloc_descriptor_strings (struct usb_descriptor *); ++struct usb_string_descriptor *usbd_get_string (__u8); ++ ++ ++ ++ ++/* 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 usb_function_driver { ++ const char *name; ++ struct usb_function_operations *fops; // functions ++ ++ // device & configuration descriptions ++ struct usb_device_description *device_description; ++ struct usb_configuration_description *configuration_description; ++ int bNumConfigurations; ++ ++ u16 idVendor; ++ u16 idProduct; ++ u16 bcdDevice; ++ ++ // constructed descriptors ++ struct usb_device_descriptor *device_descriptor; ++ struct usb_device_qualifier_descriptor *device_qualifier_descriptor; ++ struct usb_configuration_instance *configuration_instance_array; ++ ++ struct list_head drivers; // linked list ++}; ++ ++ ++struct usb_function_instance; ++ ++/* function driver registration ++ * ++ * Called by function drivers to register themselves when loaded ++ * or de-register when unloading. ++ */ ++//int usbd_strings_init(void); ++int usbd_register_function (struct usb_function_driver *); ++void usbd_deregister_function (struct usb_function_driver *); ++ ++/* Access to function privdata ++ */ ++void *usbd_function_get_privdata(struct usb_function_instance *function); ++void usbd_function_set_privdata(struct usb_function_instance *function, void *privdata); ++/* ++ * Called to queue urb's for send or recv ++ */ ++int usbd_send_urb (struct urb *urb); ++int usbd_start_recv (struct urb *); ++ ++/* ++ * queury endpoint astatus ++ */ ++int usbd_endpoint_halted (struct usb_function_instance *, int); ++int usbd_device_feature (struct usb_function_instance *, int, int); ++ ++ ++/* ++ * query bus state and status ++ */ ++usb_device_state_t usbd_device_state(struct usb_function_instance *); ++usbd_bus_state_t usbd_bus_state(struct usb_function_instance *); ++usb_device_status_t usbd_bus_status(struct usb_function_instance *); ++ ++ ++/* urb allocation ++ * ++ * Used to allocate and de-allocate urb structures. ++ * usb_cancel_urb is used to cancel a previously submitted data urb for sending. ++ */ ++struct urb *usbd_alloc_urb (struct usb_function_instance *, int, int length, int (*callback) (struct urb *, int)); ++struct urb *usbd_alloc_urb_ep0 (struct usb_function_instance *, int length, int (*callback) (struct urb *, int)); ++void usbd_dealloc_urb (struct urb *urb); ++int usbd_alloc_urb_data (struct urb *, int); ++int usbd_cancel_urb_irq (struct urb *); ++ ++/* endpoint information ++ * ++ * Used by function drivers to get specific endpoint information ++ */ ++int usbd_endpoint_wMaxPacketSize(struct usb_function_instance *, int, int); ++int usbd_endpoint_bEndpointAddress(struct usb_function_instance *, int, int); ++int usbd_endpoint_transferSize(struct usb_function_instance *, int, int); ++int usbd_endpoint_interface(struct usb_function_instance *, int); ++int usbd_interface_AltSetting(struct usb_function_instance *, int); ++int usbd_ConfigurationValue(struct usb_function_instance *); ++void usbd_endpoint_update(struct usb_function_instance *, int , struct usb_endpoint_descriptor *, int); ++int usbd_high_speed(struct usb_function_instance *); ++ +diff -Nru a/drivers/usbd/usbd-mem.h b/drivers/usbd/usbd-mem.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-mem.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,146 @@ ++/* ++ * usbd/usbd-mem.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++#ifndef MODULE ++#undef __init ++#define __init ++#undef __exit ++#define __exit ++#undef THIS_MODULE ++#define THIS_MODULE 0 ++#endif ++ ++#define USBD_MODULE_INFO(info) static const char __usbd_module_info[] = info " " USBD_BUILD " %D% %@%"; ++ ++ ++#ifndef MIN ++#define MIN(a,b) ((a) < (b) ? (a) : (b)) ++#endif ++#ifndef MAX ++#define MAX(a,b) ((a) > (b) ? (a) : (b)) ++#endif ++#define CATCH(x) while(0) x: ++#define THROW(x) goto 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(x) if (x) return; ++#define RETURN_ZERO_IF(x) if (x) return 0; ++#define RETURN_EINVAL_IF(x) if (x) return -EINVAL; ++#define RETURN_EFAULT_IF(x) if (x) return -EFAULT; ++#define RETURN_ENOMEM_IF(x) if (x) return -ENOMEM; ++#define RETURN_EBUSY_IF(x) if (x) return -EBUSY; ++#define RETURN_NULL_IF(x) if (x) return NULL; ++#define unless(x) if(!(x)) ++#define UNLESS(x) if(!(x)) ++#define RETURN_UNLESS(x) UNLESS (x) return; ++#define RETURN_ZERO_UNLESS(x) UNLESS (x) return 0; ++#define CONTINUE_UNLESS(x) UNLESS (x) continue; ++ ++ ++#ifndef likely ++#define likely(x) x ++#endif ++ ++#ifndef unlikely ++#define unlikely(x) x ++#endif ++ ++#define ckmalloc(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f) ++#define lstrdup(str) _lstrdup(__FUNCTION__, __LINE__, str) ++#define lkfree(p) _lkfree(__FUNCTION__, __LINE__, p) ++ ++#define MALLOC_TEST ++#undef MALLOC_DEBUG ++ ++#ifdef MALLOC_TEST ++extern int usbd_mallocs; ++#endif ++ ++ ++static __inline__ void *_ckmalloc (char *func, int line, int n, int f) ++{ ++ void *p; ++ if ((p = kmalloc (n, f)) == NULL) { ++ return NULL; ++ } ++ memset (p, 0, n); ++#ifdef MALLOC_TEST ++ ++usbd_mallocs; ++#endif ++#ifdef MALLOC_DEBUG ++ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, usbd_mallocs); ++#endif ++ return p; ++} ++ ++static __inline__ char *_lstrdup (char *func, int line, char *str) ++{ ++ int n; ++ char *s; ++ if (str && (n = strlen (str) + 1) && (s = kmalloc (n, GFP_ATOMIC))) { ++#ifdef MALLOC_TEST ++ ++usbd_mallocs; ++#endif ++#ifdef MALLOC_DEBUG ++ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, s, func, line, usbd_mallocs); ++#endif ++ return strcpy (s, str); ++ } ++ return NULL; ++} ++ ++static __inline__ void _lkfree (char *func, int line, void *p) ++{ ++ if (p) { ++#ifdef MALLOC_TEST ++ --usbd_mallocs; ++#endif ++#ifdef MALLOC_DEBUG ++ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, usbd_mallocs); ++#endif ++ kfree (p); ++#ifdef MALLOC_TEST ++ if (usbd_mallocs < 0) { ++ printk(KERN_INFO"%s: %p %s %d %d usbd_mallocs less zero!\n", __FUNCTION__, p, func, line, usbd_mallocs); ++ } ++#endif ++ } ++#ifdef MALLOC_DEBUG ++ else { ++ printk(KERN_INFO"%s: %s %d NULL\n", __FUNCTION__, func, line); ++ } ++#endif ++} ++ ++ ++ +diff -Nru a/drivers/usbd/usbd-procfs.c b/drivers/usbd/usbd-procfs.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd-procfs.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,337 @@ ++/* ++ * usbd/usbd-procfs.c - USB Device Core Layer ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++EXPORT_NO_SYMBOLS; ++ ++#include "usbd-export.h" ++#include "usbd-build.h" ++ ++#ifdef MODULE ++MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); ++MODULE_DESCRIPTION ("USB Device Core Support Procfs"); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++MODULE_LICENSE("GPL"); ++#endif ++ ++#endif ++ ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/proc_fs.h> ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++ ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++ ++#include "usbd-chap9.h" ++#include "usbd-mem.h" ++#include "usbd.h" ++#include "usbd-func.h" ++#include "usbd-bus.h" ++ ++USBD_MODULE_INFO ("usbdprocfs 2.0-beta"); ++ ++#define MAX_INTERFACES 2 ++ ++extern struct list_head usbd_function_drivers; ++extern struct usb_bus_instance *usbd_bus_instance; ++ ++ ++#ifdef CONFIG_USBD_PROCFS ++/* 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 = *sp; ++ int len = 0; ++ ++ //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 usb_configuration_descriptor *config = (struct usb_configuration_descriptor *) sp; ++ ++ int wTotalLength = le16_to_cpu(config->wTotalLength); ++ int bConfigurationValue = config->bConfigurationValue; ++ int interface; ++ int class; ++ int endpoint; ++ int total; ++ ++ interface = class = endpoint = 0; ++ ++ for (total = 0; wTotalLength; ) { ++ //printk(KERN_INFO"%s: wTotalLength: %d total: %d bLength: %d\n", ++ // __FUNCTION__, wTotalLength, total, sp[0]); ++ switch (sp[1]) { ++ case USB_DESCRIPTOR_TYPE_CONFIGURATION: ++ interface = class = endpoint = 0; ++ total += sprintf(buf + total, "\nConfiguration descriptor [%d ] ", ++ bConfigurationValue); ++ break; ++ case USB_DESCRIPTOR_TYPE_INTERFACE: ++ class = 0; ++ total += sprintf(buf + total, "\nInterface descriptor [%d:%d:%d ] ", ++ bConfigurationValue, ++interface, class); ++ break; ++ case USB_DESCRIPTOR_TYPE_ENDPOINT: ++ class = endpoint = 0; ++ total += sprintf(buf + total, "Endpint descriptor [%d:%d:%d:%d] ", ++ bConfigurationValue, interface, class, ++endpoint); ++ break; ++ default: ++ endpoint = 0; ++ total += sprintf(buf + total, "Class descriptor [%d:%d:%d ] ", ++ bConfigurationValue, interface, ++class); ++ 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. ++ * @file ++ * @buf ++ * @count ++ * @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))) { ++ 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 usb_function_instance *function_instance = usbd_bus_instance->function_instance; ++ struct usb_function_driver *function_driver = function_instance->function_driver; ++ struct usb_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_USBD_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_get_descriptor(usbd_bus_instance, config_descriptor, ++ sizeof(config_descriptor), ++ USB_DESCRIPTOR_TYPE_CONFIGURATION, 0)) > index) { ++ len += dump_config_descriptor((char *)page + len, config_descriptor ); ++ } ++#ifdef CONFIG_USBD_HIGH_SPEED ++ if ((config_size = usbd_get_descriptor(usbd_bus_instance, config_descriptor, ++ sizeof(config_descriptor), ++ USB_DESCRIPTOR_TYPE_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 usb_string_descriptor *string_descriptor; ++ ++ //len += sprintf ((char *) page + len, "\n\n"); ++ ++ if ((string_descriptor = usbd_get_string (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 (i)) != NULL) { ++ ++ len += sprintf((char *)page+len, "String [%2d:%2d] ", ++ i, string_descriptor->bLength); ++ ++ // bLength = sizeof(struct usb_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; ++} ++ ++static struct file_operations usbd_device_proc_operations_functions = { ++read:usbd_device_proc_read_functions, ++}; ++ ++#endif ++ ++ ++ ++/* Module init ******************************************************************************* */ ++ ++ ++static int usbd_procfs_init (void) ++{ ++#ifdef CONFIG_USBD_PROCFS ++ { ++ 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; ++ } ++#endif ++ return 0; ++} ++ ++static void usbd_procfs_exit (void) ++{ ++#ifdef CONFIG_USBD_PROCFS ++ // remove proc filesystem entry ++ remove_proc_entry ("usb-functions", NULL); ++#endif ++} ++ ++module_init (usbd_procfs_init); ++module_exit (usbd_procfs_exit); ++ +diff -Nru a/drivers/usbd/usbd.c b/drivers/usbd/usbd.c +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd.c Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,663 @@ ++/* ++ * usbd/usbd.c - USB Device Core Layer ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it under the terms of ++ * the GNU General Public License as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ++ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along with this program; if ++ * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/interrupt.h> ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/proc_fs.h> ++#include <linux/ctype.h> ++#include <linux/string.h> ++#if defined(CONFIG_PM) ++#include <linux/pm.h> ++#endif ++ ++#include "usbd-export.h" ++#include "usbd-build.h" ++#include "usbd-chap9.h" ++#include "usbd-mem.h" ++#include "usbd.h" ++#include "usbd-func.h" ++#include "usbd-bus.h" ++#include "usbd-admin.h" ++ ++MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); ++MODULE_DESCRIPTION ("USB Device Core Support"); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++MODULE_LICENSE("GPL"); ++#endif ++USBD_MODULE_INFO ("usbdcore 2.0-beta"); ++ ++int usbd_maxstrings = 20; ++extern int usbd_switch_init(void); ++LIST_HEAD (usbd_function_drivers); // list of all registered function modules ++struct usb_bus_instance *usbd_bus_instance; ++#ifdef MALLOC_TEST ++int usbd_mallocs; ++#endif ++ ++/* ******************************************************************************************* */ ++/* ++ * USBD Switch will make direct calls to the appropriate modinit/modexit and ++ * bus interface bi_modinit/bi_modexit functions to load and unload as appropriate. ++ * ++ * ++ * The following entry points are exported: ++ * ++ * int usbd_enable(void) ++ * int usbd_disable(char *name) ++ * int usbd_disconnect(char *name) ++ * int usbd_connect(char *name) ++ * int usbd_pm_on(char *name) ++ * int usbd_pm_off(char *name) ++ * ++ * int usbd_unload(void) ++ * int usbd_load(char *name) ++ * int usbd_unplug(char *name) ++ * int usbd_replug(char *name) ++ * ++ * ++ * If CONFIG_PROCFS is defined then the following commands can be given to /proc/usbd-switch: ++ * ++ * enable ++ * enable name ++ * disable ++ * disconnect ++ * connect ++ * ++ * serial_number ++ * debug ++ * nodebug ++ * ++ * ++ * There are four different styles of operations that can be used: ++ * ++ * 1. switch - change or configure the device function by sending any ++ * one of a number of commands to the switch interface [1] of ++ * usbd-switch: ++ * ++ * - echo "serial_number xxxxx" > /proc/usbd-switch ++ * - echo "enable network_fd" > /proc/usbd-switch ++ * - echo "disconnect" > /proc/usbd-switch ++ * - echo "connect" > /proc/usbd-switch ++ * - echo "disable" > /proc/usbd-switch ++ * ++ * serial_number - set the serial number string ++ * enable function - set current function and enable function and bus interface ++ * enable - enable current function and bus interface ++ * disable - disable bus interface and current function ++ * connect - enable current function and bus interface and connect pullup resistor ++ * disconnect - unload bus interface, current function and reset current function, ++ * and disconnect pullup resistor ++ * ++ * 2. simple power management ++ * ++ * - EXPORT_SYMBOL(usbd_enable); ++ * - EXPORT_SYMBOL(usbd_disable); ++ * - EXPORT_SYMBOL(usbd_disconnect); ++ * - EXPORT_SYMBOL(usbd_connect); ++ * - EXPORT_SYMBOL(usbd_pm_on); ++ * - EXPORT_SYMBOL(usbd_pm_off); ++ * ++ * Deprecated: ++ * ++ * - EXPORT_SYMBOL(usbd_load); ++ * - EXPORT_SYMBOL(usbd_unload); ++ * - EXPORT_SYMBOL(usbd_unplug); ++ * - EXPORT_SYMBOL(usbd_replug); ++ * ++ * usbd_enable() - load function and bus interface ++ * usbd_disable() - unload bus interface and function ++ * ++ * usbd_connect() - connect ++ * usbd_disconnect() - disconnect ++ * ++ * usbd_pm_on() - connect ++ * usbd_pm_off() - disconnect ++ * ++ * usbd_load() - load function and bus interface ++ * usbd_unload() - unload bus interface and function ++ * usbd_unplug() - disconnect ++ * usbd_replug() - connect ++ * ++ * 3. official power management CONFIG_PM [not tested yet] ++ * ++ * Perform unplug and plug functions appropriate for CONFIG_PM actions. ++ * ++ * ++ * N.B. ++ * ++ * [1] The switch interface is only available when the USB device stack ++ * and usbd-switch are compiled into the kernel. It is not available ++ * when the USB device stack consists of loadable modules. ++ * ++ * [2] Cable detection requires implementation of architecture specific ++ * code that knows about the appropriate interrupt, GPIO pins etc. ++ * ++ */ ++ ++ ++extern struct list_head usbd_function_drivers; ++ ++ ++/* ++ * This is a table of function drivers that are compiled into the kernel. ++ */ ++ ++#define MAX_FUNC_NAME_LEN 64 ++char current_function[MAX_FUNC_NAME_LEN+1]; ++int switch_debug = 0; ++ ++char *usbd_load_arg; ++ ++int switch_exiting; ++struct tq_struct enable_bh; ++struct tq_struct disable_bh; ++ ++ ++ ++/* ******************************************************************************************* */ ++ ++static DECLARE_MUTEX(usbd_sem); // protect against multiple administrative actions overlapping ++ ++extern struct usb_bus_instance *usbd_bus_instance; ++ ++char *usbd_admin_names[] = { ++ "enable", "disable", "connect", "disconnect", "pm_off", "pm_on", "serial_number", ++}; ++ ++char *usbd_admin_aliases[] = { ++ "load", "unload", "replug", "unplug", "pm_off", "pm_on", "serial_number_str", ++}; ++ ++ ++/* usbd_admin - ++ */ ++static int usbd_admin(usbd_admin_t usbd_admin_ndx, char *arg) ++{ ++ ++ printk(KERN_INFO"%s: %s current: %s arg: %s\n", __FUNCTION__, ++ usbd_admin_names[usbd_admin_ndx], ++ current_function[0] ? current_function : "(EMPTY)", ++ (arg && arg[0]) ? arg : "(EMPTY)"); ++ ++ RETURN_EINVAL_IF(in_interrupt() || down_interruptible(&usbd_sem)); ++ ++ /* ++ * ++ */ ++ if (usbd_admin_ndx == usbd_admin_enable && (NULL == arg || 0 == *arg)) { ++ // s/enable/enable current_function/ ++ arg = current_function; ++ } ++ ++ THROW_IF(!usbd_bus_instance, error); ++ ++ THROW_IF (!usbd_bus_instance->admin[usbd_admin_ndx] || ++ usbd_bus_instance->admin[usbd_admin_ndx](usbd_bus_instance, arg), error); ++ ++ if (usbd_admin_ndx == usbd_admin_enable && NULL != arg && arg != current_function) { ++ // update current_function ++ strncpy(current_function,arg,MAX_FUNC_NAME_LEN); ++ } ++ ++ up(&usbd_sem); ++ return 0; ++ ++ CATCH(error) { ++ printk(KERN_INFO"%s: %d error\n", __FUNCTION__, usbd_admin_ndx); ++ ++ up(&usbd_sem); ++ return -ENODEV; ++ } ++} ++ ++ ++/* usbd_enable - ++ */ ++int usbd_enable(char *arg) ++{ ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ RETURN_ZERO_IF(switch_exiting); ++ return usbd_admin(usbd_admin_enable, arg); ++} ++ ++void usbd_enable_bh(void *arg) ++{ ++ usbd_admin(usbd_admin_enable, (char *)arg); ++} ++ ++int usbd_enable_irq(char *arg) ++{ ++ RETURN_ZERO_IF(enable_bh.sync || switch_exiting); ++ schedule_task(&enable_bh); ++ return 0; ++} ++ ++ ++/* usbd_disable - ++ */ ++int usbd_disable(char *arg) ++{ ++ RETURN_ZERO_IF(switch_exiting); ++ return usbd_admin(usbd_admin_disable, arg); ++} ++ ++void usbd_disable_bh(void *arg) ++{ ++ usbd_admin(usbd_admin_disable, (char *)arg); ++} ++ ++int usbd_disable_irq(char *arg) ++{ ++ RETURN_ZERO_IF(disable_bh.sync || switch_exiting); ++ schedule_task(&disable_bh); ++ return 0; ++} ++ ++/* usbd_load - ++ */ ++int usbd_load(char *arg) ++{ ++ if (arg && strlen(arg)) { ++ if ( usbd_load_arg) ++ lkfree(usbd_load_arg); ++ usbd_load_arg = lstrdup(arg); ++ } ++ return usbd_admin(usbd_admin_enable, arg); ++} ++ ++/* usbd_unload - ++ */ ++int usbd_unload(char *arg) ++{ ++ return usbd_admin(usbd_admin_disable, arg); ++} ++ ++ ++/* usbd_connect - ++ */ ++int usbd_connect(char *arg) ++{ ++ return usbd_admin(usbd_admin_connect, arg); ++} ++ ++/* usbd_disconnect - ++ */ ++int usbd_disconnect(char *arg) ++{ ++ return usbd_admin(usbd_admin_disconnect, arg); ++} ++ ++ ++/* usbd_replug - ++ */ ++int usbd_replug(char *arg) ++{ ++ return usbd_admin(usbd_admin_connect, arg); ++} ++ ++/* usbd_unplug - ++ */ ++int usbd_unplug(char *arg) ++{ ++ return usbd_admin(usbd_admin_disconnect, arg); ++} ++ ++ ++ ++/* usbd_pm_off - ++ */ ++int usbd_pm_off(char *arg) ++{ ++ return usbd_admin(usbd_admin_pm_off, arg); ++} ++ ++/* usbd_pm_on - ++ */ ++int usbd_pm_on(char *arg) ++{ ++ return usbd_admin(usbd_admin_pm_on, arg); ++} ++ ++ ++ ++ ++/* ******************************************************************************************* */ ++#ifdef CONFIG_USBD_PROCFS ++ ++static int usbd_admin_procfs(char *arg) ++{ ++ usbd_admin_t admin; ++ RETURN_EINVAL_IF(!arg || !strlen(arg)); ++ ++ for (admin = usbd_admin_enable; admin <= usbd_admin_serial_number; admin++) { ++ char *cp = usbd_admin_names[admin]; ++ int len = strlen(cp); ++ if (!strncmp(arg, cp, len)) { ++ cp = arg + len; ++ while (cp && ((*cp == ' ') || (*cp == '\t'))) cp++; ++ return usbd_admin(admin, cp); ++ } ++ } ++ ++ for (admin = usbd_admin_enable; admin <= usbd_admin_serial_number; admin++) { ++ char *cp = usbd_admin_aliases[admin]; ++ int len = strlen(cp); ++ if (!strncmp(arg, cp, len)) { ++ cp = arg + len; ++ while (cp && ((*cp == ' ') || (*cp == '\t'))) cp++; ++ return usbd_admin(admin, cp); ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++/* usbd_proc_switch_write - handle a user switch request ++ */ ++static ssize_t usbd_proc_switch_write (struct file *file, const char *buf, size_t count, loff_t * pos) ++{ ++ ++ char command[MAX_FUNC_NAME_LEN+1]; ++ size_t n = count; ++ size_t l; ++ char c; ++ ++ command[0] = 0; ++ if (n > 0) { ++ l = MIN(n,MAX_FUNC_NAME_LEN); ++ if (copy_from_user (command, buf, l)) { ++ count = -EFAULT; ++ } ++ else { ++ if (l > 0 && command[l-1] == '\n') { ++ l -= 1; ++ } ++ command[l] = 0; ++ n -= l; ++ // flush remainder, if any ++ while (n > 0) { ++ // Not too efficient, but it shouldn't matter ++ if (copy_from_user (&c, buf + (count - n), 1)) { ++ count = -EFAULT; ++ break; ++ } ++ n -= 1; ++ } ++ } ++ } ++ ++ if (0 >= count) { ++ printk(KERN_INFO"%s: count <= 0 %d\n", __FUNCTION__, count); ++ return count; ++ } ++ ++ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, command); ++ if (!usbd_admin_procfs(command)) { ++ // do nothing ++ //printk(KERN_INFO"%s: back\n", __FUNCTION__); ++ } ++ ++ ++ /* ++ * debug - increment switch_debug ++ */ ++ else if (!strncmp("debug", command, 5)) { ++ switch_debug++; ++ if (switch_debug) ++ printk(KERN_INFO"%s: debug: %d\n", __FUNCTION__, switch_debug); ++ } ++ /* ++ * nodebug - reset switch_debug ++ */ ++ else if (!strncmp("nodebug", command, 7)) { ++ switch_debug = 0; ++ if (switch_debug) ++ printk(KERN_INFO"%s: no debug\n", __FUNCTION__); ++ } ++ ++ return count; ++} ++ ++/* usbd_proc_switch_read - implement proc file system read. ++ * Standard proc file system read function. ++ */ ++static ssize_t usbd_proc_switch_read (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len; ++ //int index; ++ char *bp,*ep; ++ struct list_head *lhd; ++ ++ if ((*pos)++ > 0) { ++ /* This is the second time, we finished on the first time, ++ indicate we have no more data by returning 0. */ ++ return(0); ++ } ++ ++ // get a page, max 4095 bytes of data... ++ if (!(page = get_free_page (GFP_KERNEL))) { ++ return -ENOMEM; ++ } ++ ++ bp = (char *) page; ++ ep = bp + 4095; ++ ++ bp += sprintf(bp, "USB cable "); ++ if (NULL != usbd_bus_instance && ++ NULL != usbd_bus_instance->driver && ++ NULL != usbd_bus_instance->driver->bops && ++ NULL != usbd_bus_instance->driver->bops->bus_attached && ++ usbd_bus_instance->driver->bops->bus_attached(usbd_bus_instance)) { ++ bp += sprintf(bp, "at"); ++ } else { ++ bp += sprintf(bp, "de"); ++ } ++ bp += sprintf(bp, "tached"); ++ bp += sprintf(bp, ", USB pullup resistor "); ++ if (NULL != usbd_bus_instance && ++ NULL != usbd_bus_instance->driver && ++ NULL != usbd_bus_instance->driver->bops && ++ NULL != usbd_bus_instance->driver->bops->bus_connected && ++ !usbd_bus_instance->driver->bops->bus_connected(usbd_bus_instance)) { ++ bp += sprintf(bp, "dis"); ++ } ++ bp += sprintf(bp, "connected"); ++ bp += sprintf(bp, "\nUSB Device function drivers:\n"); ++ list_for_each (lhd, &usbd_function_drivers) { ++ struct usb_function_driver *fd = list_entry(lhd, struct usb_function_driver, drivers); ++ char enabled = '-'; ++ if (usbd_bus_instance && usbd_bus_instance->function_instance && ++ usbd_bus_instance->function_instance->function_driver == fd) { ++ enabled = '+'; ++ } ++ if ((ep - bp) < 64) { ++ printk(KERN_INFO"%s: Too many usbd functions!\n", __FUNCTION__); ++ break; ++ } ++ bp += sprintf(bp," %c [%s]\n",enabled,fd->name); ++ } ++ len = bp - (char *) page; ++ ++ 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; ++} ++ ++static struct file_operations usbd_proc_switch_functions = { ++ write:usbd_proc_switch_write, ++ read:usbd_proc_switch_read, ++}; ++#endif ++ ++ ++/* ******************************************************************************************* */ ++ ++#if defined(CONFIG_PM) ++/* switch_pm_event ++ * ++ * Handle power management event ++ */ ++static int switch_pm_event (struct pm_dev *dev, pm_request_t rqst, void *unused) ++{ ++ switch (rqst) { ++ case PM_SUSPEND: ++ usbd_pm_off(usbd_load_arg); ++ break; ++ case PM_RESUME: ++ usbd_pm_on(usbd_load_arg); ++ break; ++ } ++ return 0; ++} ++#endif ++ ++/* ******************************************************************************************* */ ++ ++ ++ ++/* ******************************************************************************************* */ ++ ++#if defined(CONFIG_PM) ++struct pm_dev *switch_pm_info; ++#endif ++ ++/* usbd_switch_init ++ * ++ */ ++int usbd_switch_init(void) ++{ ++ ++#ifdef CONFIG_USBD_PROCFS ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ //printk(KERN_INFO"%s: creating /proc/usbd-switch\n", __FUNCTION__); ++ if ((p = create_proc_entry ("usbd-switch", 0666, 0)) == NULL) { ++ printk(KERN_ERR"%s: creating /proc/usbd-switch failed\n", __FUNCTION__); ++ return -ENOMEM; ++ } ++ p->proc_fops = &usbd_proc_switch_functions; ++#endif ++#ifdef CONFIG_PM ++ switch_pm_info = NULL; ++ ++ if (!(switch_pm_info = pm_register (PM_USB_DEV, PM_SYS_UNKNOWN, switch_pm_event))) ++ return -ENOMEM; ++ ++ switch_pm_info->state = 0; ++#endif ++ enable_bh.routine = usbd_enable_bh; ++ enable_bh.data = NULL; ++ disable_bh.routine = usbd_disable_bh; ++ disable_bh.data = NULL; ++ return 0; ++} ++ ++void usbd_switch_exit (void) ++{ ++ // This is *only* used for drivers compiled and used as a module. ++#ifdef CONFIG_PM ++ if (switch_pm_info) ++ pm_unregister_all(switch_pm_event); ++ switch_pm_info = NULL; ++#endif ++#ifdef CONFIG_USBD_PROCFS ++ remove_proc_entry("usbd-switch", NULL); ++#endif ++ switch_exiting = 1; ++ while (enable_bh.sync || disable_bh.sync) { ++ printk(KERN_INFO"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++} ++ ++ ++ ++/* Module init ******************************************************************************* */ ++ ++int usbd_switch_init(void); ++void usbd_switch_exit (void); ++ ++static int usbd_device_init (void) ++{ ++ printk (KERN_INFO "%s: %s %s\n", __FUNCTION__, __usbd_module_info, USBD_EXPORT_TAG); ++ ++ usbd_switch_init(); ++ ++ return 0; ++} ++ ++static void usbd_device_exit (void) ++{ ++ //printk (KERN_INFO "%s: %s exiting\n", __FUNCTION__, __usbd_module_info); ++ ++ usbd_switch_exit(); ++ ++ if ( usbd_load_arg) ++ lkfree(usbd_load_arg); ++ ++#ifdef MALLOC_TEST ++ if (usbd_mallocs) printk(KERN_INFO"%s: usbd_mallocs non-zero! %d\n", __FUNCTION__, usbd_mallocs); ++#endif ++} ++ ++module_init (usbd_device_init); ++module_exit (usbd_device_exit); ++ ++EXPORT_SYMBOL(usbd_function_drivers); ++EXPORT_SYMBOL(usbd_maxstrings); ++ ++EXPORT_SYMBOL(usbd_enable); ++EXPORT_SYMBOL(usbd_disable); ++EXPORT_SYMBOL(usbd_enable_irq); ++EXPORT_SYMBOL(usbd_disable_irq); ++ ++EXPORT_SYMBOL(usbd_connect); ++EXPORT_SYMBOL(usbd_disconnect); ++EXPORT_SYMBOL(usbd_pm_on); ++EXPORT_SYMBOL(usbd_pm_off); ++ ++EXPORT_SYMBOL(usbd_load); ++EXPORT_SYMBOL(usbd_unload); ++EXPORT_SYMBOL(usbd_unplug); ++EXPORT_SYMBOL(usbd_replug); ++ ++ ++EXPORT_SYMBOL(usbd_bus_instance); ++#ifdef MALLOC_TEST ++EXPORT_SYMBOL(usbd_mallocs); ++#endif ++ ++ +diff -Nru a/drivers/usbd/usbd.h b/drivers/usbd/usbd.h +--- /dev/null Wed Dec 31 16:00:00 1969 ++++ b/drivers/usbd/usbd.h Fri Feb 27 14:22:51 2004 +@@ -0,0 +1,239 @@ ++/* ++ * usbd/usbd.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it under the terms of ++ * the GNU General Public License as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ++ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along with this program; if ++ * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/* ++ * 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 ++ * ++ */ ++ ++ ++/* ++ * 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 usb_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 ++ ++} usb_device_event_t; ++ ++ ++ ++struct usb_bus_instance; ++struct usb_function_instance; ++ ++typedef struct urb_link { ++ struct urb_link *next; ++ struct urb_link *prev; ++} urb_link; ++ ++/* URB Status ++ * ++ */ ++typedef enum usbd_urb_status { ++ SEND_IN_QUEUE, ++ SEND_IN_PROGRESS, ++ SEND_FINISHED_OK, ++ SEND_FINISHED_ERROR, ++ SEND_FINISHED_CANCELLED, ++ RECV_IN_QUEUE, ++ RECV_IN_PROGRESS, ++ RECV_OK, ++ RECV_ERROR, ++ RECV_CANCELLED ++} usbd_urb_status_t; ++ ++ ++ ++/* USB Data 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..) ++ */ ++ ++struct urb { ++ ++ struct usb_bus_instance *bus; ++ struct usb_function_instance *function_instance; ++ struct usb_endpoint_instance *endpoint; ++ ++ int (*callback) (struct 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 *privdata; ++ ++ 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 ++}; ++ ++#define USBD_URB_SENDZLP 0x01 ++ ++ ++/* ++ * 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 usb_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, ++ STATE_UNKNOWN, // destroyed ++} usb_device_state_t; ++ ++/* ++ * Device status ++ * ++ * Overall state ++ */ ++typedef enum usb_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, ++} usb_device_status_t; ++ ++/* ++ * 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; ++ ++ ++ ++/* 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 usb_endpoint_request { ++ u8 configuration; // configuration endpoint will be in ++ u8 interface; // 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 ++}; ++ ++ ++ ++struct usb_function_operations; ++struct usb_function_driver; ++ ++ ++ ++/* ++ * USB Bus Interface Driver structures ++ * ++ * Driver description: ++ * ++ * struct usb_bus_operations ++ * struct usb_bus_driver ++ * ++ */ ++ ++struct usb_bus_operations; ++struct usb_bus_driver; ++extern int usbd_maxstrings; ++ ++ |