/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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * 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 +#include +#include +#include + +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 , + * Tom Rushworth , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) +#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1 +#include +#else +#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK +#include +#endif + +#include +#include +#include + + +#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) +#include +#include +#include +#include +#include +#include +#endif + +#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) +#include +#include +#include +#endif + +#if defined(CONFIG_ARCH_SAMSUNG) +#include +#include +#endif + +#if defined(CONFIG_ARCH_MX1ADS) +#include "dbmx1_bi/dbmx1.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + + +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 , + * Tom Rushworth , + * + * 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 , + * Tom Rushworth , + * + * 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 +#include "au1x00.h" + +#include +#include +#include +#include + + +#ifdef CONFIG_MIPS_FREEHAND +#include +#include /* 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<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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + * 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * 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 +#include + +//EXPORT_NO_SYMBOLS; + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#define BAUDRATE B1200 +#define MODEMDEVICE "/dev/ttyS0" +#define _POSIX_SOURCE 1 /* POSIX compliant source */ +#define FALSE 0 +#define TRUE 1 + +volatile int STOP=FALSE; + +main() +{ + int fd,c, res; + struct termios oldtio,newtio; + unsigned char buf[255]; + + int bytes; + unsigned char mouse[3]; + + fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); + if (fd <0) {perror(MODEMDEVICE); exit(-1); } + + tcgetattr(fd,&oldtio); /* save current port settings */ + + bzero(&newtio, sizeof(newtio)); + newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; + newtio.c_iflag = IGNPAR; + newtio.c_oflag = 0; + + /* set input mode (non-canonical, no echo,...) */ + newtio.c_lflag = 0; + + newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ + newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */ + + tcflush(fd, TCIFLUSH); + tcsetattr(fd,TCSANOW,&newtio); + + + bytes = 0; + while (STOP==FALSE) { /* loop for input */ + unsigned char c; + + res = read(fd,buf,1); /* returns after 5 chars have been input */ + buf[res]=0; /* so we can printf... */ + //fprintf(stderr, ":%02x:%d\n", buf[0], res); + //if (buf[0]=='z') STOP=TRUE; + + c = buf[0]; + + if ( c & 0x40 ) { + bytes = 1; + mouse[0] = c; + } + else if (bytes == 1) { + bytes = 2; + mouse[1] = c; + } + else if (bytes == 2) { + bytes = 0; + mouse[2] = c; + fprintf(stderr, "%02x %02x %02x\n", mouse[0], mouse[1], mouse[2]); + //printf("%c%c%c", mouse[0], mouse[1], mouse[2]); + } + } + tcsetattr(fd,TCSANOW,&oldtio); +} + + +/* End of FILE */ diff -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 , + * Tom Rushworth , + * Bruce Balden + * + * 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 +#include +#include +#include + +#include +#include + +MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); +MODULE_LICENSE("PROPRIETARY"); +MODULE_DESCRIPTION ("Belcarra Random Walk MOUSE Function"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbd-chap9.h" +#include +#include +#include + +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 + * Stuart Lynne + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + * Stuart Lynne + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + * Stuart Lynne + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + * Stuart Lynne + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + */ + + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +#ifdef CONFIG_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 +#include + +// Use FERMAT in two ways: to encode, and to generate test data. + +main(){ + //Note 3, 5, and 7 are primitive roots of 257 + // 11 is not a primitive root + FERMAT three, five, seven; + + FERMAT three2; + printf("Cycle lengths: 3,5,7 %d %d %d \n", + fermat_setup(&three, 3), + fermat_setup(&five, 5), + fermat_setup(&seven, 7)); + three2=three; // Copy data from three + fermat_xform(&three,three2.power,three2.length); + fermat_xform(&five,three2.power,three2.length); + fermat_xform(&seven,three2.power,three2.length); + fermat_xform(&seven,three2.power,three2.length); + fermat_xform(&five,three2.power,three2.length); + fermat_xform(&three,three2.power,three2.length); + + //At this stage, three2 and three should be identical + if(memcpy(&three,&three2,sizeof(FERMAT))){ + printf("Decoded intact\n"); + } + + fermat_init(); + fermat_encode(three2.power,256); + +} +#endif + +#endif /* CONFIG_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 + * + * 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 + * Stuart Lynne + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * 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 +#include +#include +#include + +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +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: + * + * | + * | case 1 + * | case 2 + * | case 3 + * | | case 4 + * | + * + * This complicates CRC checking, there are four scenarios: + * + * 1. length is 1 more than multiple of packetsize with a trailing byte + * 2. length is 1 more than multiple of packetsize + * 3. length is multiple of packetsize + * 4. none of the above + * + * Finally, even though we always compute CRC, we do not actually throw + * things away until and unless we have previously seen a good CRC. + * This allows backwards compatibility with hosts that do not support + * adding a CRC to the frame. + * + */ + + // test if 1 more than packetsize multiple + if (1 == (len % out_pkt_sz)) { + + // copy and CRC up to the packetsize boundary + 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 + * Stuart Lynne + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#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 + * Stuart Lynne + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + */ + + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 , + * Tom Rushworth , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +//EXPORT_NO_SYMBOLS; + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) +#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1 +#include +#else +#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK +#include +#endif + +#include +#include + + +#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) +#include +#include +#include +#include +#include +#include +#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 +#include +#include + +#endif /* defined(..) */ + +#if defined(CONFIG_ARCH_SAMSUNG) +#include +#include +#endif /* defined(CONFIG_ARCH_SAMSUNG) */ + +#if defined(CONFIG_ARCH_MX1ADS) +#include "dbmx1_bi/dbmx1.h" +#endif /* defined(CONFIG_ARCH_MX1ADS) */ + +#include +#include +#include +#include + +#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 , + * Tom Rushworth , + * + * 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + * 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + * 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef 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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_PM) +#include +#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 , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * 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; + +