From acee7a5c93e24fecf629468492d4354c8797b13a Mon Sep 17 00:00:00 2001 From: Michael Lauer Date: Mon, 6 Feb 2006 17:58:07 +0000 Subject: nylon kernel patches courtesy Martin Dietze --- .../23-mtx-1_watchdog_autotrigger.patch | 98 + .../linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch | 257 + packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff | 12 + .../26-usbd-amd-pb1x00-kit-23may2003-update.diff | 130 + .../27-usbd-amd-pb1x00-kit-23may2003-usbd.diff | 15816 +++++++++++++++++++ .../29-au1000-pci-config-clear-errors.diff | 15 + packages/linux/linux-mtx-1-2.4.27/defconfig-mtx-1 | 94 +- packages/linux/linux-mtx-1_2.4.24.bb | 2 +- packages/linux/linux-mtx-1_2.4.27.bb | 19 +- 9 files changed, 16434 insertions(+), 9 deletions(-) create mode 100644 packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch create mode 100644 packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch create mode 100644 packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff create mode 100644 packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff create mode 100644 packages/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff create mode 100644 packages/linux/linux-mtx-1-2.4.27/29-au1000-pci-config-clear-errors.diff (limited to 'packages') diff --git a/packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch b/packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch new file mode 100644 index 0000000000..c6bb0aae68 --- /dev/null +++ b/packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch @@ -0,0 +1,98 @@ +--- linux/arch/mips/au1000/mtx-1/mtx-1_watchdog.c.orig 2005-03-22 17:18:01.000000000 +0100 ++++ linux/arch/mips/au1000/mtx-1/mtx-1_watchdog.c 2005-07-06 12:10:06.000000000 +0200 +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + + #include + +@@ -115,6 +116,8 @@ + + //---------[ File Functions ]----------------- + ++static char restart_after_close; ++ + static int mtx1wd_open (struct inode *inode, struct file *file) + { + if (MINOR(inode->i_rdev)!=WATCHDOG_MINOR) return -ENODEV; +@@ -134,6 +137,10 @@ + + static int mtx1wd_release (struct inode *inode, struct file *file) { + if (MINOR(inode->i_rdev)==WATCHDOG_MINOR) { ++ ++ if (restart_after_close) ++ start_wd_timer (); ++ + } + MOD_DEC_USE_COUNT; + return 0; +@@ -141,22 +148,53 @@ + + + static ssize_t mtx1wd_write (struct file *file, const char *buf, size_t count, loff_t *ppos) { +- if (ppos!=&file->f_pos) +- return -ESPIPE; + +- if (count) { + mtx1_trigger_wd (); +- return 1; ++ ++ if (count > 0) { ++ char buffer[10]; ++ int n = (count>9)?9:count; ++ ++ if (copy_from_user (&buffer, buf, n)) ++ return -EFAULT; ++ buffer[n]=0; ++ ++ if (count >= 4 && strncmp("auto", buffer, 4)==0) ++ restart_after_close = 1; ++ ++ else if (count >= 6 && strncmp("manual", buffer, 6)==0) ++ restart_after_close = 0; ++ ++ return n; + } + return 0; + } + + ++static ssize_t mtx1wd_read (struct file *file, char *buf, size_t count, loff_t *ppos) ++{ ++ char * state = restart_after_close ? "auto\n" : "manual\n"; ++ int n = strlen(state)+1; ++ ++ if (file->f_pos >= n) ++ return 0; ++ ++ if (count < n) ++ return -EINVAL; ++ ++ if(copy_to_user(buf, state, n)) ++ return -EFAULT; ++ ++ file->f_pos += n; ++ ++ return n; ++} ++ + + static struct file_operations mtx1wd_fops = { + .owner = THIS_MODULE, + .llseek = NULL, +- .read = NULL, ++ .read = mtx1wd_read, + .write = mtx1wd_write, + .readdir = NULL, + .poll = NULL, +@@ -194,6 +232,8 @@ + { + printk("MTX-1 watchdog driver\n"); + ++ restart_after_close = 0; ++ + mtx1_enable_wd (); + + //-- trigger it for the first time. diff --git a/packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch b/packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch new file mode 100644 index 0000000000..9c0808942a --- /dev/null +++ b/packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch @@ -0,0 +1,257 @@ +diff -Nru linux-old/arch/mips/au1000/mtx-1/Makefile linux/arch/mips/au1000/mtx-1/Makefile +--- linux-old/arch/mips/au1000/mtx-1/Makefile 2005-07-19 18:59:27.000000000 +0200 ++++ linux/arch/mips/au1000/mtx-1/Makefile 2005-07-07 10:15:01.000000000 +0200 +@@ -15,6 +15,6 @@ + + O_TARGET := mtx-1.o + +-obj-y := init.o board_setup.o irqmap.o mtx-1_watchdog.o ++obj-y := init.o board_setup.o irqmap.o mtx-1_watchdog.o mtx-1_sysbtn.o + + include $(TOPDIR)/Rules.make +diff -Nru linux-old/arch/mips/au1000/mtx-1/irqmap.c linux/arch/mips/au1000/mtx-1/irqmap.c +--- linux-old/arch/mips/au1000/mtx-1/irqmap.c 2005-07-19 17:42:05.000000000 +0200 ++++ linux/arch/mips/au1000/mtx-1/irqmap.c 2005-07-06 13:37:25.000000000 +0200 +@@ -50,10 +50,11 @@ + /* Need to define this. + */ + au1xxx_irq_map_t au1xxx_irq_map[] = { ++ { AU1500_GPIO_207, INTC_INT_RISE_AND_FALL_EDGE, 0 }, + { 0, 0, 0} + }; + +-int au1xxx_nr_irqs = 0; ++int au1xxx_nr_irqs = 1; + + #ifdef CONFIG_PCI + +diff -Nru linux-old/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c linux/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c +--- linux-old/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c 2005-07-07 10:29:24.000000000 +0200 +@@ -0,0 +1,226 @@ ++/* ++ * Driver for the MTX-1 System Button. ++ * ++ * (c) Copyright 2005 4G Systems , All Rights Reserved. ++ * http://www.4g-systems.biz ++ * ++ * 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. ++ * ++ * Neither Michael Stickel nor 4G Systeme GmbH admit liability nor provide ++ * warranty for any of this software. This material is provided ++ * "AS-IS" and at no charge. ++ * ++ * (c) Copyright 2005 4G Systems ++ * ++ * Release 0.01. ++ * ++ * Author: Michael Stickel michael.stickel@4g-systems.biz ++ * ++ * ++ * After the module is loaded there is a device /dev/misc/btn ++ * that can be read. It returns one char '1' if the button ++ * has been pressed an '0' if it has been released. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++ ++#ifndef FALSE ++# define FALSE (0) ++#endif ++ ++#ifndef TRUE ++# define TRUE (!FALSE) ++#endif ++ ++ ++//---------[ declarations ]----------------- ++ ++ ++static DECLARE_WAIT_QUEUE_HEAD(mtx1btn_wait_queue); ++static char state_changed; ++static char last_value; ++static char is_inuse; ++ ++ ++//---------[ Hardware Functions ]----------------- ++ ++// The MTX-1 Button is attached to GPIO207. ++#define MTX1_GPIO2_SYSBTN (7) ++#define MTX1_SYSBTN_IRQ (AU1500_GPIO_207) ++ ++ ++static char mtx1_getbtn (int btnno) ++{ ++ if (btnno==0) { ++ return (au_readl(GPIO2_PINSTATE) & (1<i_rdev)!=mtx1sysbtn_minor) return -ENODEV; ++ if (is_inuse) return -EBUSY; ++ is_inuse=1; ++ last_value = mtx1_getbtn(0); ++ state_changed = 0; ++ MOD_INC_USE_COUNT; ++ return 0; ++} ++ ++ ++static int mtx1sysbtn_release (struct inode *inode, struct file *file) { ++ if (MINOR(inode->i_rdev)==mtx1sysbtn_minor) { ++ is_inuse=0; ++ } ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++ ++static ssize_t mtx1sysbtn_read (struct file *file, char *buf, size_t count, loff_t *ppos) ++{ ++ if (count < 1) ++ return -EINVAL; ++ if (!state_changed) ++ interruptible_sleep_on (&mtx1btn_wait_queue); ++ state_changed = 0; ++ char c = last_value ? '1' : '0'; /* mtx1_getbtn(0) */ ++ if(copy_to_user(buf, &c, 1)) ++ return -EFAULT; ++ return 1; ++} ++ ++ ++static unsigned int mtx1sysbtn_poll (struct file *file, poll_table * wait) ++{ ++ unsigned int mask = 0; ++ ++ poll_wait (file, &mtx1btn_wait_queue, wait); ++ ++ if (state_changed) // state changed since last time. ++ mask |= POLLIN | POLLRDNORM; ++ ++ return mask; ++} ++ ++ ++static struct file_operations mtx1sysbtn_fops = { ++ .owner = THIS_MODULE, ++ .llseek = NULL, ++ .read = mtx1sysbtn_read, ++ .write = NULL, ++ .readdir = NULL, ++ .poll = mtx1sysbtn_poll, ++ .ioctl = NULL, ++ .mmap = NULL, ++ .open = mtx1sysbtn_open, ++ .flush = NULL, ++ .release = mtx1sysbtn_release ++}; ++ ++ ++static struct miscdevice mtx1sysbtn_miscdev = { ++ MISC_DYNAMIC_MINOR /* SYSBTN_MINOR */ , ++ "btn", ++ &mtx1sysbtn_fops ++}; ++ ++ ++ ++//---------[ Module Functions ]----------------- ++ ++ ++void __exit cleanup_mtx1_sysbtn (void) ++{ ++ is_inuse = 1; ++ mtx1_btn_stopirq (); ++ misc_deregister(&mtx1sysbtn_miscdev); ++} ++ ++ ++int __init init_mtx1_sysbtn (void) ++{ ++ printk("MTX-1 System Button driver\n"); ++ is_inuse = 1; ++ mtx1_initbuttons (); ++ if (misc_register (&mtx1sysbtn_miscdev) >= 0) { ++ mtx1sysbtn_minor = mtx1sysbtn_miscdev.minor; ++ if (mtx1_btn_startirq () == 0) { ++ is_inuse=0; ++ return 0; ++ } ++ misc_deregister(&mtx1sysbtn_miscdev); ++ } ++ return 1; ++} ++ ++__initcall(init_mtx1_sysbtn); ++__exitcall(cleanup_mtx1_sysbtn); ++ ++MODULE_AUTHOR("Michael Stickel"); ++MODULE_DESCRIPTION("Driver for the MTX-1 system button"); ++MODULE_LICENSE("GPL"); ++EXPORT_NO_SYMBOLS; diff --git a/packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff b/packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff new file mode 100644 index 0000000000..4f4775a960 --- /dev/null +++ b/packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff @@ -0,0 +1,12 @@ +--- linux.old/arch/mips/au1000/mtx-1/board_setup.c 2005-08-10 15:22:14.014147000 +0200 ++++ linux/arch/mips/au1000/mtx-1/board_setup.c 2005-08-10 15:26:34.346571264 +0200 +@@ -78,8 +78,7 @@ + + // initialize sys_pinfunc: + // disable second ethernet port (SYS_PF_NI2) +- // set U3/GPIO23 to GPIO23 (SYS_PF_U3) +- au_writel( SYS_PF_NI2 | SYS_PF_U3, SYS_PINFUNC ); ++ au_writel( SYS_PF_NI2, SYS_PINFUNC ); + + // initialize GPIO + au_writel( 0xFFFFFFFF, SYS_TRIOUTCLR ); diff --git a/packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff b/packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff new file mode 100644 index 0000000000..feaf08c563 --- /dev/null +++ b/packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff @@ -0,0 +1,130 @@ +diff -Nru a/arch/mips/au1000/common/irq.c b/arch/mips/au1000/common/irq.c +--- a/arch/mips/au1000/common/irq.c Fri Feb 27 14:22:51 2004 ++++ b/arch/mips/au1000/common/irq.c Fri Feb 27 14:22:51 2004 +@@ -458,7 +458,14 @@ + intc0_req0 |= au_readl(IC0_REQ0INT); + + if (!intc0_req0) return; +- ++#if 0 ++ /* ++ * This is no longer required. The find first bit operation ++ * is almost as fast at finding the correct interrupt, whereas ++ * leaving this in adds a small amount of overhead to all other ++ * interrupts and therefore induces more latency which may ++ * actually cause USB problems. ++ */ + /* + * Because of the tight timing of SETUP token to reply + * transactions, the USB devices-side packet complete +@@ -469,7 +476,7 @@ + do_IRQ(AU1000_USB_DEV_REQ_INT, regs); + return; + } +- ++#endif + irq = au_ffs(intc0_req0) - 1; + intc0_req0 &= ~(1< $@ ++ ++# 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_