diff options
| -rw-r--r-- | driver-core.current/debugfs-fix-up-debugfs_create_size_t-inline-function.patch | 36 | ||||
| -rw-r--r-- | series | 46 | ||||
| -rw-r--r-- | staging/staging-add-aten2011-usb-to-serial-converter-driver.patch | 4143 | ||||
| -rw-r--r-- | staging/staging-add-b3dfg-driver.patch | 1070 | ||||
| -rw-r--r-- | staging/staging-b3dfg-fixups-and-improvements.patch | 1416 | ||||
| -rw-r--r-- | staging/staging-b3dfg-prepare-b3dfg-for-submission-upstream.patch | 277 | ||||
| -rw-r--r-- | staging/staging-poch-fix-verification-of-memory-area.patch | 29 | ||||
| -rw-r--r-- | staging/staging-sxg-add-multicast-support-for-sahara-sxg-driver.patch | 320 | ||||
| -rw-r--r-- | version | 2 |
9 files changed, 7264 insertions, 75 deletions
diff --git a/driver-core.current/debugfs-fix-up-debugfs_create_size_t-inline-function.patch b/driver-core.current/debugfs-fix-up-debugfs_create_size_t-inline-function.patch deleted file mode 100644 index fbe673cb2f265c..00000000000000 --- a/driver-core.current/debugfs-fix-up-debugfs_create_size_t-inline-function.patch +++ /dev/null @@ -1,36 +0,0 @@ -From foo@baz Mon Jan 26 17:24:33 PST 2009 -Date: Mon, 26 Jan 2009 17:24:33 -0800 -To: Greg KH <greg@kroah.com> -From: Greg Kroah-Hartman <gregkh@suse.de> -Subject: debugfs: fix up debugfs_create_size_t() inline function - -From: Greg Kroah-Hartman <gregkh@suse.de> - -If CONFIG_DEBUGFS is disabled, the fixup patch from Inaky will still -keep the debugfs.h file from compiling properly. - -This should now be resolved with this patch. - -Cc: Inaky Perez-Gonzalez <inaky@linux.intel.com> -Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - ---- - include/linux/debugfs.h | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - ---- a/include/linux/debugfs.h -+++ b/include/linux/debugfs.h -@@ -162,9 +162,10 @@ static inline struct dentry *debugfs_cre - return ERR_PTR(-ENODEV); - } - --struct dentry *debugfs_create_size_t(const char *name, mode_t mode, -- struct dentry *parent, -- size_t *value) -+static inline struct dentry *debugfs_create_size_t(const char *name, -+ mode_t mode, -+ struct dentry *parent, -+ size_t *value) - { - return ERR_PTR(-ENODEV); - } @@ -11,7 +11,6 @@ gregkh.pre/detect-atomic-counter-underflows.patch ################################# # Driver core patches for 2.6.29 ################################# -driver-core.current/debugfs-fix-up-debugfs_create_size_t-inline-function.patch driver-core.current/sync-patch-for-jp_jp-stable_kernel_rules.txt.patch driver-core.current/uio-add-missing-documentation-of-features-added-recently.patch driver-core.current/driver-core-fix-kernel-doc-parameter-name.patch @@ -19,43 +18,6 @@ driver-core.current/driver-core-fix-kernel-doc-parameter-name.patch ################################# # USB patches for 2.6.29 ################################# -usb.current/usb-cp2101-add-fasttrax-gps-evaluation-kit-vendor-product-id.patch -usb.current/usb-fix-toggle-mismatch-in-disable_endpoint-paths.patch -usb.current/usb-storage-add-unusual-devs-entry.patch -usb.current/usb-don-t-enable-wakeup-by-default-for-pci-host-controllers.patch -usb.current/usb-fix-suspend-resume-of-pci-usb-controllers.patch -usb.current/usb-fix-char-device-disconnect-handling.patch -usb.current/usb-omap1-ohci-buildfix.patch -usb.current/usb-musb-davinci-buildfix.patch -usb.current/usb-musb_hdrc-another-davinci-buildfix.patch -usb.current/usb-musb-free_irq-bugfix.patch -usb.current/usb-musb-tusb6010-buildfix.patch -usb.current/usb-musb-uses-endpoint-functions.patch -usb.current/usb-musb-cppi-bugfixes.patch -usb.current/usb-musb-cppi-dma-fix.patch -usb.current/usb-musb-kconfig-fix.patch -usb.current/usb-composite-fix-bug-should-test-set_alt-function-pointer-before-use-it.patch -usb.current/usb-composite-fix-bug-low-byte-of-w_index-is-the-usb-interface-number-not-the-whole-2-bytes-of-w_index.patch -usb.current/usb-cdc-acm-support-some-gps-data-loggers.patch -usb.current/usb-cdc-acm-quirk-for-mtk-gps.patch -usb.current/usb-remove-vernier-labpro-from-ldusb.patch -usb.current/usb-usblp.c-add-usblp_quirk_bidir-to-brother-hl-1440.patch -usb.current/usb-cp2101-device.patch -usb.current/usb-ftdi_sio-added-alti-2-vid-and-neptune-3-pid.patch -usb.current/usb-ftdi_sio-driver-support-of-bar-code-scanner-from-diebold.patch -usb.current/usb-add-kernel-doc-for-wusb_dev-in-struct-usb_device.patch -usb.current/usb-usbmon-implement-compat_ioctl.patch -usb.current/usb-storage-support-of-dane-elec-mediatouch-usb-device.patch -usb.current/usb-remove-zte-modem-from-unusual_devices.patch -usb.current/usb-option-driver-onda-device-mt503hs-has-wrong-id.patch -usb.current/usb-cdc-acm-add-another-conexant-modem-to-the-quirks.patch -usb.current/usb-new-id-for-ti_usb_3410_5052-driver.patch -usb.current/usb-gadget-fix-x-y.patch -usb.current/usb-unusual_dev-usb-storage-needs-to-ignore-a-device.patch -usb.current/usb-storage-add-another-unusual_dev-for-off-by-one-bug.patch -usb.current/usb-option-add-quanta-hsdpa-data-card-device-ids.patch - -usb.current/usb-driver-for-freescale-quicc-engine-usb-host-controller.patch ##################################################################### @@ -157,6 +119,7 @@ staging/staging-android-add-lowmemorykiller-documentation.patch staging/staging-android-task_get_unused_fd_flags-fix-the-wrong-usage-of-tsk-signal.patch staging/staging-agnx-drivers-staging-agnx-agnx.h-needs-linux-io.h.patch staging/staging-usbip-usbip_start_threads-handle-kernel_thread-failure.patch +staging/staging-poch-fix-verification-of-memory-area.patch # for after .29: @@ -175,6 +138,7 @@ staging/staging-sxg-fix-build-warnings-in-sxg_ethtool.patch staging/staging-sxg-remove-firmware-files-from-sgx_ethtool.c.patch staging/staging-sxg-fix-build-warnings-in-downloadb-firmware-files.patch staging/staging-sxg-fix-build-warnings-in-sxg.c.patch +staging/staging-sxg-add-multicast-support-for-sahara-sxg-driver.patch staging/staging-asus_oled-fix-sparse-warnings-about-using-plain-integer-as-null-pointer.patch staging/staging-asus_oled-do-not-initialise-statics-to-0-or-null.patch @@ -272,3 +236,9 @@ staging/staging-dst-fix-build-dependancy.patch staging/staging-add-stlc45xx-wi-fi-driver-for-stlc4550-4560.patch +staging/staging-add-aten2011-usb-to-serial-converter-driver.patch + +staging/staging-add-b3dfg-driver.patch +staging/staging-b3dfg-fixups-and-improvements.patch +staging/staging-b3dfg-prepare-b3dfg-for-submission-upstream.patch + diff --git a/staging/staging-add-aten2011-usb-to-serial-converter-driver.patch b/staging/staging-add-aten2011-usb-to-serial-converter-driver.patch new file mode 100644 index 00000000000000..190509522f1284 --- /dev/null +++ b/staging/staging-add-aten2011-usb-to-serial-converter-driver.patch @@ -0,0 +1,4143 @@ +From dfa82523e32a8b1fa87f236f2d4167f7bb252b58 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman <gregkh@suse.de> +Date: Tue, 27 Jan 2009 23:28:27 -0800 +Subject: Staging: add aten2011 usb to serial converter driver. + +Many thanks to Russell Lang <gsview@ghostgum.com.au> for his +help in getting this working on newer kernel versions and +for pointing out this driver in the first place. + +Cc: Russell Lang <gsview@ghostgum.com.au> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/uc2322/Kconfig | 10 + drivers/staging/uc2322/Makefile | 1 + drivers/staging/uc2322/TODO | 8 + drivers/staging/uc2322/aten2011.c | 3625 ++++++++++++++++++++++++++++++++ + drivers/staging/uc2322/aten2011.h | 383 +++ + drivers/staging/uc2322/aten2011_16C50.h | 58 + 8 files changed, 4088 insertions(+) + +--- a/drivers/staging/Kconfig ++++ b/drivers/staging/Kconfig +@@ -99,5 +99,7 @@ source "drivers/staging/dst/Kconfig" + + source "drivers/staging/stlc45xx/Kconfig" + ++source "drivers/staging/uc2322/Kconfig" ++ + endif # !STAGING_EXCLUDE_BUILD + endif # STAGING +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -32,3 +32,4 @@ obj-$(CONFIG_EPL) += epl/ + obj-$(CONFIG_ANDROID) += android/ + obj-$(CONFIG_DST) += dst/ + obj-$(CONFIG_STLC45XX) += stlc45xx/ ++obj-$(CONFIG_USB_SERIAL_ATEN2011) += uc2322/ +--- /dev/null ++++ b/drivers/staging/uc2322/aten2011_16C50.h +@@ -0,0 +1,58 @@ ++/* ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++#if !defined(_16C50_H) ++#define _16C50_H ++ ++/************************************* ++ * Bit definitions for each register * ++ *************************************/ ++#define LCR_BITS_5 0x00 /* 5 bits/char */ ++#define LCR_BITS_6 0x01 /* 6 bits/char */ ++#define LCR_BITS_7 0x02 /* 7 bits/char */ ++#define LCR_BITS_8 0x03 /* 8 bits/char */ ++#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */ ++ ++#define LCR_STOP_1 0x00 /* 1 stop bit */ ++#define LCR_STOP_1_5 0x04 /* 1.5 stop bits (if 5 bits/char) */ ++#define LCR_STOP_2 0x04 /* 2 stop bits (if 6-8 bits/char) */ ++#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */ ++ ++#define LCR_PAR_NONE 0x00 /* No parity */ ++#define LCR_PAR_ODD 0x08 /* Odd parity */ ++#define LCR_PAR_EVEN 0x18 /* Even parity */ ++#define LCR_PAR_MARK 0x28 /* Force parity bit to 1 */ ++#define LCR_PAR_SPACE 0x38 /* Force parity bit to 0 */ ++#define LCR_PAR_MASK 0x38 /* Mask for parity field */ ++ ++#define LCR_SET_BREAK 0x40 /* Set Break condition */ ++#define LCR_DL_ENABLE 0x80 /* Enable access to divisor latch */ ++ ++#define MCR_DTR 0x01 /* Assert DTR */ ++#define MCR_RTS 0x02 /* Assert RTS */ ++#define MCR_OUT1 0x04 /* Loopback only: Sets state of RI */ ++#define MCR_MASTER_IE 0x08 /* Enable interrupt outputs */ ++#define MCR_LOOPBACK 0x10 /* Set internal (digital) loopback mode */ ++#define MCR_XON_ANY 0x20 /* Enable any char to exit XOFF mode */ ++ ++#define ATEN2011_MSR_CTS 0x10 /* Current state of CTS */ ++#define ATEN2011_MSR_DSR 0x20 /* Current state of DSR */ ++#define ATEN2011_MSR_RI 0x40 /* Current state of RI */ ++#define ATEN2011_MSR_CD 0x80 /* Current state of CD */ ++ ++#endif /* if !defined(_16C50_H) */ ++ +--- /dev/null ++++ b/drivers/staging/uc2322/aten2011.c +@@ -0,0 +1,3625 @@ ++/* ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++/************************************************************************* ++ *** -------------------------------------------------------------------- ++ *** ++ *** Project Name: ATENINTL ++ *** ++ *** Module Name: ATEN2011 ++ *** ++ *** File: aten2011.c ++ *** ++ *** ++ *** File Revision: 1.2 ++ *** ++ *** Revision Date: 2009-01-16 ++ *** ++ *** ++ *** Purpose : It gives an interface between USB to 4 Serial ++ *** and serves as a Serial Driver for the high ++ *** level layers /applications. ++ *** ++ *** Change History: ++ *** Modified from ATEN revision 1.2 for Linux kernel 2.6.26 or later ++ *** ++ *** LEGEND : ++ *** ++ *** ++ *** DBG - Code inserted due to as part of debugging ++ *** DPRINTK - Debug Print statement ++ *** ++ *************************************************************************/ ++ ++/* all file inclusion goes here */ ++ ++ ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/tty.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++#include <linux/module.h> ++//#include <linux/spinlock.h> ++#include <linux/serial.h> ++//#include <linux/ioctl.h> ++#include <linux/usb.h> ++#include <asm/uaccess.h> ++ ++ ++#define KERNEL_2_6 1 ++ ++#include <linux/usb/serial.h> ++#include "aten2011.h" /* ATEN2011 Defines */ ++#include "aten2011_16C50.h" /* 16C50 UART defines */ ++ ++/* all defines goes here */ ++ ++/* ++ * Debug related defines ++ */ ++ ++/* 1: Enables the debugging -- 0: Disable the debugging */ ++ ++//#define printk // ++ ++#define ATEN_DEBUG 0 ++ ++#ifdef ATEN_DEBUG ++ static int debug = 0; ++ #define DPRINTK(fmt, args...) printk( "%s: " fmt, __FUNCTION__ , ## args) ++ ++#else ++ static int debug = 0; ++ #define DPRINTK(fmt, args...) ++ ++#endif ++//#undef DPRINTK ++// #define DPRINTK(fmt, args...) ++ ++ ++ ++ ++/* ++ * Version Information ++ */ ++#define DRIVER_VERSION "1.3.1" ++#define DRIVER_DESC "ATENINTL 2011 USB Serial Adapter" ++ ++/* ++ * Defines used for sending commands to port ++ */ ++ ++#define WAIT_FOR_EVER (HZ * 0 ) /* timeout urb is wait for ever*/ ++#define ATEN_WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ ++ ++#define ATEN_PORT1 0x0200 ++#define ATEN_PORT2 0x0300 ++#define ATEN_VENREG 0x0000 ++#define ATEN_MAX_PORT 0x02 ++#define ATEN_WRITE 0x0E ++#define ATEN_READ 0x0D ++ ++/* Requests */ ++#define ATEN_RD_RTYPE 0xC0 ++#define ATEN_WR_RTYPE 0x40 ++#define ATEN_RDREQ 0x0D ++#define ATEN_WRREQ 0x0E ++#define ATEN_CTRL_TIMEOUT 500 ++#define VENDOR_READ_LENGTH (0x01) ++ ++ ++int ATEN2011_Thr_cnt; ++//int ATEN2011_spectrum_2or4ports; //this says the number of ports in the device ++//int NoOfOpenPorts; ++ ++int RS485mode=0; //set to 1 for RS485 mode and 0 for RS232 mode ++ ++static struct usb_serial* ATEN2011_get_usb_serial (struct usb_serial_port *port, const ++char *function); ++static int ATEN2011_serial_paranoia_check (struct usb_serial *serial, const char ++*function); ++static int ATEN2011_port_paranoia_check (struct usb_serial_port *port, const char ++*function); ++ ++ ++/* setting and get register values */ ++static int ATEN2011_set_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 val); ++static int ATEN2011_get_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 * val); ++static int ATEN2011_set_Uart_Reg(struct usb_serial_port *port, __u16 reg, __u16 val); ++static int ATEN2011_get_Uart_Reg(struct usb_serial_port *port, __u16 reg, __u16 * val); ++ ++void ATEN2011_Dump_serial_port(struct ATENINTL_port *ATEN2011_port); ++ ++/************************************************************************/ ++/************************************************************************/ ++/* I N T E R F A C E F U N C T I O N S */ ++/* I N T E R F A C E F U N C T I O N S */ ++/************************************************************************/ ++/************************************************************************/ ++ ++static inline void ATEN2011_set_serial_private(struct usb_serial *serial, struct ATENINTL_serial *data) ++{ ++ usb_set_serial_data(serial, (void *)data ); ++} ++ ++static inline struct ATENINTL_serial * ATEN2011_get_serial_private(struct usb_serial *serial) ++{ ++ return (struct ATENINTL_serial*) usb_get_serial_data(serial); ++} ++ ++static inline void ATEN2011_set_port_private(struct usb_serial_port *port, struct ATENINTL_port *data) ++{ ++ usb_set_serial_port_data(port, (void*)data ); ++} ++ ++static inline struct ATENINTL_port * ATEN2011_get_port_private(struct usb_serial_port *port) ++{ ++ return (struct ATENINTL_port*) usb_get_serial_port_data(port); ++} ++ ++/* ++Description:- To set the Control register by calling usb_fill_control_urb function by passing usb_sndctrlpipe function as parameter. ++ ++Input Parameters: ++usb_serial_port: Data Structure usb_serialport correponding to that seril port. ++Reg: Register Address ++Val: Value to set in the Register. ++ */ ++ ++static int ATEN2011_set_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 val) ++{ ++ struct usb_device *dev = port->serial->dev; ++ val = val & 0x00ff; ++ DPRINTK("ATEN2011_set_reg_sync offset is %x, value %x\n",reg,val); ++ ++ ++ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ATEN_WRREQ, ++ ATEN_WR_RTYPE, val, reg, NULL, 0,ATEN_WDR_TIMEOUT); ++} ++ ++ ++ ++/* ++Description:- To set the Uart register by calling usb_fill_control_urb function by passing usb_rcvctrlpipe function as parameter. ++ ++Input Parameters: ++usb_serial_port: Data Structure usb_serialport correponding to that seril port. ++Reg: Register Address ++Val: Value to receive from the Register. ++ */ ++ ++static int ATEN2011_get_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 * val) ++{ ++ struct usb_device *dev = port->serial->dev; ++ int ret=0; ++ ++ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ATEN_RDREQ, ++ ATEN_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH,ATEN_WDR_TIMEOUT); ++ DPRINTK("ATEN2011_get_reg_sync offset is %x, return val %x\n",reg,*val); ++ *val = (*val) & 0x00ff; ++ return ret; ++} ++ ++ ++ ++/* ++Description:- To set the Uart register by calling usb_fill_control_urb function by passing usb_sndctrlpipe function as parameter. ++ ++Input Parameters: ++usb_serial_port: Data Structure usb_serialport correponding to that seril port. ++Reg: Register Address ++Val: Value to set in the Register. ++ */ ++ ++static int ATEN2011_set_Uart_Reg(struct usb_serial_port *port, __u16 reg, __u16 val) ++{ ++ ++ ++ struct usb_device *dev = port->serial->dev; ++ struct ATENINTL_serial *ATEN2011_serial; ++ int minor; ++ ATEN2011_serial = ATEN2011_get_serial_private(port->serial); ++ minor = port->serial->minor; ++ if (minor == SERIAL_TTY_NO_MINOR) ++ minor = 0; ++ val = val & 0x00ff; ++ // For the UART control registers, the application number need to be Or'ed ++ ++ if(ATEN2011_serial->ATEN2011_spectrum_2or4ports == 4) ++ { ++ val |= (((__u16)port->number - (__u16)(minor))+1)<<8; ++ DPRINTK("ATEN2011_set_Uart_Reg application number is %x\n",val); ++ } ++ else ++ { ++ if( ((__u16)port->number - (__u16)(minor)) == 0) ++ { ++ // val= 0x100; ++ val |= (((__u16)port->number - (__u16)(minor))+1)<<8; ++ DPRINTK("ATEN2011_set_Uart_Reg application number is %x\n",val); ++ } ++ else ++ { ++ // val=0x300; ++ val |= (((__u16)port->number - (__u16)(minor))+2)<<8; ++ DPRINTK("ATEN2011_set_Uart_Reg application number is %x\n",val); ++ } ++ } ++ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ATEN_WRREQ, ++ ATEN_WR_RTYPE, val, reg, NULL, 0,ATEN_WDR_TIMEOUT); ++ ++} ++ ++ ++/* ++Description:- To set the Control register by calling usb_fill_control_urb function by passing usb_rcvctrlpipe function as parameter. ++ ++Input Parameters: ++usb_serial_port: Data Structure usb_serialport correponding to that seril port. ++Reg: Register Address ++Val: Value to receive from the Register. ++ */ ++static int ATEN2011_get_Uart_Reg(struct usb_serial_port *port, __u16 reg, __u16 * val) ++{ ++ struct usb_device *dev = port->serial->dev; ++ int ret=0; ++ __u16 Wval; ++ struct ATENINTL_serial *ATEN2011_serial; ++ int minor = port->serial->minor; ++ ATEN2011_serial = ATEN2011_get_serial_private(port->serial); ++ if (minor == SERIAL_TTY_NO_MINOR) ++ minor = 0; ++ ++ //DPRINTK("application number is %4x \n",(((__u16)port->number - (__u16)(minor))+1)<<8); ++ /*Wval is same as application number*/ ++ if(ATEN2011_serial->ATEN2011_spectrum_2or4ports ==4) ++ { ++ Wval=(((__u16)port->number - (__u16)(minor))+1)<<8; ++ DPRINTK("ATEN2011_get_Uart_Reg application number is %x\n",Wval); ++ } ++ else ++ { ++ if( ((__u16)port->number - (__u16)(minor)) == 0) ++ { ++ // Wval= 0x100; ++ Wval=(((__u16)port->number - (__u16)(minor))+1)<<8; ++ DPRINTK("ATEN2011_get_Uart_Reg application number is %x\n",Wval); ++ } ++ else ++ { ++ // Wval=0x300; ++ Wval=(((__u16)port->number - (__u16)(minor))+2)<<8; ++ DPRINTK("ATEN2011_get_Uart_Reg application number is %x\n",Wval); ++ } ++ } ++ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ATEN_RDREQ, ++ ATEN_RD_RTYPE, Wval, reg, val,VENDOR_READ_LENGTH,ATEN_WDR_TIMEOUT); ++ *val = (*val) & 0x00ff; ++ return ret; ++} ++ ++ ++ ++void ATEN2011_Dump_serial_port(struct ATENINTL_port *ATEN2011_port) ++{ ++ ++ DPRINTK("***************************************\n"); ++ DPRINTK("Application number is %4x\n",ATEN2011_port->AppNum); ++ DPRINTK("SpRegOffset is %2x\n",ATEN2011_port->SpRegOffset); ++ DPRINTK("ControlRegOffset is %2x \n",ATEN2011_port->ControlRegOffset); ++ DPRINTK("DCRRegOffset is %2x \n",ATEN2011_port->DcrRegOffset); ++ //DPRINTK("ClkSelectRegOffset is %2x \n",ATEN2011_port->ClkSelectRegOffset); ++ DPRINTK("***************************************\n"); ++ ++} ++ ++/* all structre defination goes here */ ++/**************************************************************************** ++ * ATENINTL2011_4port_device ++ * Structure defining ATEN2011, usb serial device ++ ****************************************************************************/ ++static struct usb_serial_driver ATENINTL2011_4port_device = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ATEN2011", ++ }, ++ .description = DRIVER_DESC, ++ .id_table = ATENINTL_port_id_table, ++ .open = ATEN2011_open, ++ .close = ATEN2011_close, ++ .write = ATEN2011_write, ++ .write_room = ATEN2011_write_room, ++ .chars_in_buffer = ATEN2011_chars_in_buffer, ++ .throttle = ATEN2011_throttle, ++ .unthrottle = ATEN2011_unthrottle, ++ .calc_num_ports = ATEN2011_calc_num_ports, ++ ++#ifdef ATENSerialProbe ++ .probe = ATEN2011_serial_probe, ++#endif ++ .ioctl = ATEN2011_ioctl, ++ .set_termios = ATEN2011_set_termios, ++ .break_ctl = ATEN2011_break, ++// .break_ctl = ATEN2011_break_ctl, ++ .tiocmget = ATEN2011_tiocmget, ++ .tiocmset = ATEN2011_tiocmset, ++ .attach = ATEN2011_startup, ++ .shutdown = ATEN2011_shutdown, ++ .read_bulk_callback = ATEN2011_bulk_in_callback, ++ .read_int_callback = ATEN2011_interrupt_callback, ++}; ++ ++static struct usb_driver io_driver = { ++ .name = "ATEN2011", ++ .probe = usb_serial_probe, ++ .disconnect = usb_serial_disconnect, ++ .id_table = id_table_combined, ++}; ++ ++ ++ ++/************************************************************************/ ++/************************************************************************/ ++/* U S B C A L L B A C K F U N C T I O N S */ ++/* U S B C A L L B A C K F U N C T I O N S */ ++/************************************************************************/ ++/************************************************************************/ ++ ++/***************************************************************************** ++ * ATEN2011_interrupt_callback ++ * this is the callback function for when we have received data on the ++ * interrupt endpoint. ++ * Input : 1 Input ++ * pointer to the URB packet, ++ * ++ *****************************************************************************/ ++//#ifdef ATEN2011 ++static void ATEN2011_interrupt_callback (struct urb *urb) ++{ ++ int result; ++ int length ; ++ struct ATENINTL_port *ATEN2011_port; ++ struct ATENINTL_serial *ATEN2011_serial; ++ struct usb_serial *serial; ++ __u16 Data; ++ unsigned char *data; ++ __u8 sp[5],st; ++ int i; ++ __u16 wval; ++ int minor; ++ //printk("in the function ATEN2011_interrupt_callback Length %d, Data %x \n",urb->actual_length,(unsigned int)urb->transfer_buffer); ++ DPRINTK("%s"," : Entering\n"); ++ ++ ATEN2011_serial= (struct ATENINTL_serial *)urb->context; ++ if(!urb)// || ATEN2011_serial->status_polling_started == FALSE ) ++ { ++ DPRINTK("%s","Invalid Pointer !!!!:\n"); ++ return; ++ } ++ ++ switch (urb->status) ++ { ++ case 0: ++ /* success */ ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* this urb is terminated, clean up */ ++ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); ++ return; ++ default: ++ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); ++ goto exit; ++ } ++ length = urb->actual_length; ++ data = urb->transfer_buffer; ++ ++ //ATEN2011_serial= (struct ATENINTL_serial *)urb->context; ++ //serial = ATEN2011_get_usb_serial(port,__FUNCTION__); ++ serial = ATEN2011_serial->serial; ++ ++ /* ATENINTL get 5 bytes ++ * Byte 1 IIR Port 1 (port.number is 0) ++ * Byte 2 IIR Port 2 (port.number is 1) ++ * Byte 3 IIR Port 3 (port.number is 2) ++ * Byte 4 IIR Port 4 (port.number is 3) ++ * Byte 5 FIFO status for both */ ++ ++ if(length && length>5) ++ { ++ DPRINTK("%s \n","Wrong data !!!"); ++ return; ++ } ++ ++ /* MATRIX */ ++ if(ATEN2011_serial->ATEN2011_spectrum_2or4ports == 4) ++ { ++ sp[0]=(__u8)data[0]; ++ sp[1]=(__u8)data[1]; ++ sp[2]=(__u8)data[2]; ++ sp[3]=(__u8)data[3]; ++ st=(__u8)data[4]; ++ } ++ else ++ { ++ sp[0]=(__u8)data[0]; ++ sp[1]=(__u8)data[2]; ++ //sp[2]=(__u8)data[2]; ++ //sp[3]=(__u8)data[3]; ++ st=(__u8)data[4]; ++ ++ } ++ // printk("%s data is sp1:%x sp2:%x sp3:%x sp4:%x status:%x\n",__FUNCTION__,sp1,sp2,sp3,sp4,st); ++ for(i=0;i<serial->num_ports;i++) ++ { ++ ATEN2011_port = ATEN2011_get_port_private(serial->port[i]); ++ minor = serial->minor; ++ if (minor == SERIAL_TTY_NO_MINOR) ++ minor = 0; ++ if((ATEN2011_serial->ATEN2011_spectrum_2or4ports == 2) && (i != 0)) ++ wval = (((__u16)serial->port[i]->number - (__u16)(minor))+2)<<8; ++ else ++ wval = (((__u16)serial->port[i]->number - (__u16)(minor))+1)<<8; ++ if(ATEN2011_port->open != FALSE) ++ { ++ //printk("%s wval is:(for 2011) %x\n",__FUNCTION__,wval); ++ ++ if(sp[i] & 0x01) ++ { ++ DPRINTK("SP%d No Interrupt !!!\n",i); ++ } ++ else ++ { ++ switch(sp[i] & 0x0f) ++ { ++ case SERIAL_IIR_RLS: ++ DPRINTK("Serial Port %d: Receiver status error or ",i); ++ DPRINTK("address bit detected in 9-bit mode\n"); ++ ATEN2011_port->MsrLsr=1; ++ ATEN2011_get_reg(ATEN2011_port,wval,LINE_STATUS_REGISTER,&Data); ++ break; ++ case SERIAL_IIR_MS: ++ DPRINTK("Serial Port %d: Modem status change\n",i); ++ ATEN2011_port->MsrLsr=0; ++ ATEN2011_get_reg(ATEN2011_port,wval, MODEM_STATUS_REGISTER, &Data); ++ break; ++ } ++ } ++ } ++ ++ } ++exit: ++ if( ATEN2011_serial->status_polling_started == FALSE ) ++ return; ++ ++ result = usb_submit_urb (urb, GFP_ATOMIC); ++ if (result) ++ { ++ dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n", __FUNCTION__, result); ++ } ++ ++ return; ++ ++} ++//#endif ++static void ATEN2011_control_callback(struct urb *urb) ++{ ++ unsigned char *data; ++ struct ATENINTL_port *ATEN2011_port; ++ __u8 regval=0x0; ++ ++ if(!urb) ++ { ++ DPRINTK("%s","Invalid Pointer !!!!:\n"); ++ return; ++ } ++ ++ switch (urb->status) ++ { ++ case 0: ++ /* success */ ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* this urb is terminated, clean up */ ++ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); return; ++ default: ++ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); ++ goto exit; ++ } ++ ++ ++ ATEN2011_port = (struct ATENINTL_port *)urb->context; ++ ++ DPRINTK("%s urb buffer size is %d\n",__FUNCTION__,urb->actual_length); ++ DPRINTK("%s ATEN2011_port->MsrLsr is %d port %d\n",__FUNCTION__,ATEN2011_port->MsrLsr,ATEN2011_port->port_num); ++ data=urb->transfer_buffer; ++ regval=(__u8)data[0]; ++ DPRINTK("%s data is %x\n",__FUNCTION__,regval); ++ if(ATEN2011_port->MsrLsr==0) ++ handle_newMsr(ATEN2011_port,regval); ++ else if(ATEN2011_port->MsrLsr==1) ++ handle_newLsr(ATEN2011_port,regval); ++ ++exit: ++ return; ++} ++int handle_newMsr(struct ATENINTL_port *port,__u8 newMsr) ++{ ++ struct ATENINTL_port *ATEN2011_port; ++ struct async_icount *icount; ++ ATEN2011_port=port; ++ icount = &ATEN2011_port->icount; ++ if (newMsr & (ATEN_MSR_DELTA_CTS | ATEN_MSR_DELTA_DSR | ATEN_MSR_DELTA_RI | ATEN_MSR_DELTA_CD)) { ++ icount = &ATEN2011_port->icount; ++ ++ /* update input line counters */ ++ if (newMsr & ATEN_MSR_DELTA_CTS) { ++ icount->cts++; ++ } ++ if (newMsr & ATEN_MSR_DELTA_DSR) { ++ icount->dsr++; ++ } ++ if (newMsr & ATEN_MSR_DELTA_CD) { ++ icount->dcd++; ++ } ++ if (newMsr & ATEN_MSR_DELTA_RI) { ++ icount->rng++; ++ } ++ } ++ ++ ++ return 0; ++} ++int handle_newLsr(struct ATENINTL_port *port,__u8 newLsr) ++{ ++ struct async_icount *icount; ++ ++ dbg("%s - %02x", __FUNCTION__, newLsr); ++ ++ ++ if (newLsr & SERIAL_LSR_BI) { ++ // ++ // Parity and Framing errors only count if they ++ // occur exclusive of a break being ++ // received. ++ // ++ newLsr &= (__u8)(SERIAL_LSR_OE | SERIAL_LSR_BI); ++ } ++ ++ ++ /* update input line counters */ ++ icount = &port->icount; ++ if (newLsr & SERIAL_LSR_BI) { ++ icount->brk++; ++ } ++ if (newLsr & SERIAL_LSR_OE) { ++ icount->overrun++; ++ } ++ if (newLsr & SERIAL_LSR_PE) { ++ icount->parity++; ++ } ++ if (newLsr & SERIAL_LSR_FE) { ++ icount->frame++; ++ } ++ ++ ++ return 0; ++} ++static int ATEN2011_get_reg(struct ATENINTL_port *ATEN,__u16 Wval, __u16 reg, __u16 * val) ++{ ++ struct usb_device *dev = ATEN->port->serial->dev; ++ struct usb_ctrlrequest *dr=NULL; ++ unsigned char *buffer=NULL; ++ int ret=0; ++ buffer= (__u8 *)ATEN->ctrl_buf; ++ ++// dr=(struct usb_ctrlrequest *)(buffer); ++ dr=(void *)(buffer + 2); ++ dr->bRequestType = ATEN_RD_RTYPE; ++ dr->bRequest = ATEN_RDREQ; ++ dr->wValue = cpu_to_le16(Wval);//0; ++ dr->wIndex = cpu_to_le16(reg); ++ dr->wLength = cpu_to_le16(2); ++ ++ usb_fill_control_urb(ATEN->control_urb,dev,usb_rcvctrlpipe(dev,0),(unsigned char *)dr,buffer,2,ATEN2011_control_callback,ATEN); ++ ATEN->control_urb->transfer_buffer_length = 2; ++ ret=usb_submit_urb(ATEN->control_urb,GFP_ATOMIC); ++ return ret; ++} ++ ++/***************************************************************************** ++ * ATEN2011_bulk_in_callback ++ * this is the callback function for when we have received data on the ++ * bulk in endpoint. ++ * Input : 1 Input ++ * pointer to the URB packet, ++ * ++ *****************************************************************************/ ++static void ATEN2011_bulk_in_callback (struct urb *urb) ++{ ++ int status; ++ unsigned char *data ; ++ struct usb_serial *serial; ++ struct usb_serial_port *port; ++ struct ATENINTL_serial *ATEN2011_serial; ++ struct ATENINTL_port *ATEN2011_port; ++ struct tty_struct *tty; ++ if(!urb) ++ { ++ DPRINTK("%s","Invalid Pointer !!!!:\n"); ++ return; ++ } ++ ++ if (urb->status) ++ { ++ DPRINTK("nonzero read bulk status received: %d",urb->status); ++// if(urb->status==84) ++ //ThreadState=1; ++ return; ++ } ++ ++ ATEN2011_port= (struct ATENINTL_port*)urb->context; ++ if(!ATEN2011_port) ++ { ++ DPRINTK("%s","NULL ATEN2011_port pointer \n"); ++ return ; ++ } ++ ++ port = (struct usb_serial_port *)ATEN2011_port->port; ++ if (ATEN2011_port_paranoia_check (port, __FUNCTION__)) ++ { ++ DPRINTK("%s","Port Paranoia failed \n"); ++ return; ++ } ++ ++ serial = ATEN2011_get_usb_serial(port,__FUNCTION__); ++ if(!serial) ++ { ++ DPRINTK("%s\n","Bad serial pointer "); ++ return; ++ } ++ ++ DPRINTK("%s\n","Entering... \n"); ++ ++ data = urb->transfer_buffer; ++ ATEN2011_serial = ATEN2011_get_serial_private(serial); ++ ++ DPRINTK("%s","Entering ........... \n"); ++ ++ if (urb->actual_length) ++ { ++//MATRIX ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) ++ tty = tty_port_tty_get(&ATEN2011_port->port->port); ++#elif LINUX_VERSION_CODE == KERNEL_VERSION(2,6,27) ++ tty = ATEN2011_port->port->port.tty; ++#else ++ tty = ATEN2011_port->port->tty; ++#endif ++ if (tty) ++ { ++ tty_buffer_request_room(tty, urb->actual_length); ++ tty_insert_flip_string(tty, data, urb->actual_length); ++ DPRINTK(" %s \n",data); ++ tty_flip_buffer_push(tty); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) ++ tty_kref_put(tty); ++#endif ++ } ++ ++ ++ ATEN2011_port->icount.rx += urb->actual_length; ++ DPRINTK("ATEN2011_port->icount.rx is %d:\n",ATEN2011_port->icount.rx); ++//MATRIX ++ } ++ ++ if(!ATEN2011_port->read_urb) ++ { ++ DPRINTK("%s","URB KILLED !!!\n"); ++ return; ++ } ++ ++ if(ATEN2011_port->read_urb->status!=-EINPROGRESS) ++ { ++ ATEN2011_port->read_urb->dev = serial->dev; ++ ++ status = usb_submit_urb(ATEN2011_port->read_urb, GFP_ATOMIC); ++ ++ if (status) ++ { ++ DPRINTK(" usb_submit_urb(read bulk) failed, status = %d", status); ++ } ++ } ++} ++ ++/***************************************************************************** ++ * ATEN2011_bulk_out_data_callback ++ * this is the callback function for when we have finished sending serial data ++ * on the bulk out endpoint. ++ * Input : 1 Input ++ * pointer to the URB packet, ++ * ++ *****************************************************************************/ ++static void ATEN2011_bulk_out_data_callback (struct urb *urb) ++{ ++ struct ATENINTL_port *ATEN2011_port ; ++ struct tty_struct *tty; ++ if(!urb) ++ { ++ DPRINTK("%s","Invalid Pointer !!!!:\n"); ++ return; ++ } ++ ++ if (urb->status) ++ { ++ DPRINTK("nonzero write bulk status received:%d\n", urb->status); ++ return; ++ } ++ ++ ATEN2011_port = (struct ATENINTL_port *)urb->context; ++ if(!ATEN2011_port) ++ { ++ DPRINTK("%s","NULL ATEN2011_port pointer \n"); ++ return ; ++ } ++ ++ if (ATEN2011_port_paranoia_check (ATEN2011_port->port, __FUNCTION__)) ++ { ++ DPRINTK("%s","Port Paranoia failed \n"); ++ return; ++ } ++ ++ DPRINTK("%s \n","Entering ........."); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) ++ tty = tty_port_tty_get(&ATEN2011_port->port->port); ++#elif LINUX_VERSION_CODE == KERNEL_VERSION(2,6,27) ++ tty = ATEN2011_port->port->port.tty; ++#else ++ tty = ATEN2011_port->port->tty; ++#endif ++ ++ if (tty && ATEN2011_port->open) ++ { ++ /* let the tty driver wakeup if it has a special * ++ * write_wakeup function */ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { ++ (tty->ldisc.write_wakeup)(tty); ++ } ++#endif ++ ++ /* tell the tty driver that something has changed */ ++ wake_up_interruptible(&tty->write_wait); ++ } ++ ++ /* Release the Write URB */ ++ ATEN2011_port->write_in_progress = FALSE; ++ ++//schedule_work(&ATEN2011_port->port->work); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) ++ tty_kref_put(tty); ++#endif ++ ++} ++ ++ ++ ++ ++ ++ ++/************************************************************************/ ++/* D R I V E R T T Y I N T E R F A C E F U N C T I O N S */ ++/************************************************************************/ ++#ifdef ATENSerialProbe ++static int ATEN2011_serial_probe(struct usb_serial *serial, const struct usb_device_id *id) ++{ ++ ++ /*need to implement the mode_reg reading and updating\ ++ structures usb_serial_ device_type\ ++ (i.e num_ports, num_bulkin,bulkout etc)*/ ++ /* Also we can update the changes attach */ ++ return 1; ++} ++#endif ++ ++/***************************************************************************** ++ * SerialOpen ++ * this function is called by the tty driver when a port is opened ++ * If successful, we return 0 ++ * Otherwise we return a negative error number. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_open(struct tty_struct *tty, struct usb_serial_port *port, struct file * filp) ++#else ++static int ATEN2011_open(struct usb_serial_port *port, struct file * filp) ++#endif ++{ ++ int response; ++ int j; ++ struct usb_serial *serial; ++// struct usb_serial_port *port0; ++ struct urb *urb; ++ __u16 Data; ++ int status; ++ struct ATENINTL_serial *ATEN2011_serial; ++ struct ATENINTL_port *ATEN2011_port; ++ struct ktermios tmp_termios; ++ int minor; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ struct tty_struct *tty = NULL; ++#endif ++ if (ATEN2011_port_paranoia_check (port, __FUNCTION__)) ++ { ++ DPRINTK("%s","Port Paranoia failed \n"); ++ return -ENODEV; ++ } ++ ++ //ATEN2011_serial->NoOfOpenPorts++; ++ serial = port->serial; ++ ++ if (ATEN2011_serial_paranoia_check (serial, __FUNCTION__)) ++ { ++ DPRINTK("%s","Serial Paranoia failed \n"); ++ return -ENODEV; ++ } ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ ++ if (ATEN2011_port == NULL) ++ return -ENODEV; ++/* ++ if (ATEN2011_port->ctrl_buf==NULL) ++ { ++ ATEN2011_port->ctrl_buf = kmalloc(16,GFP_KERNEL); ++ if (ATEN2011_port->ctrl_buf == NULL) { ++ printk(", Can't allocate ctrl buff\n"); ++ return -ENOMEM; ++ } ++ ++ } ++ ++ if(!ATEN2011_port->control_urb) ++ { ++ ATEN2011_port->control_urb=kmalloc(sizeof(struct urb),GFP_KERNEL); ++ } ++*/ ++// port0 = serial->port[0]; ++ ++ ATEN2011_serial = ATEN2011_get_serial_private(serial); ++ ++ if (ATEN2011_serial == NULL )//|| port0 == NULL) ++ { ++ return -ENODEV; ++ } ++ // increment the number of opened ports counter here ++ ATEN2011_serial->NoOfOpenPorts++; ++ //printk("the num of ports opend is:%d\n",ATEN2011_serial->NoOfOpenPorts); ++ ++ ++ usb_clear_halt(serial->dev, port->write_urb->pipe); ++ usb_clear_halt(serial->dev, port->read_urb->pipe); ++ ++ /* Initialising the write urb pool */ ++ for (j = 0; j < NUM_URBS; ++j) ++ { ++ urb = usb_alloc_urb(0,GFP_ATOMIC); ++ ATEN2011_port->write_urb_pool[j] = urb; ++ ++ if (urb == NULL) ++ { ++ err("No more urbs???"); ++ continue; ++ } ++ ++ urb->transfer_buffer = NULL; ++ urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); ++ if (!urb->transfer_buffer) ++ { ++ err("%s-out of memory for urb buffers.", __FUNCTION__); ++ continue; ++ } ++ } ++ ++ ++/***************************************************************************** ++ * Initialize ATEN2011 -- Write Init values to corresponding Registers ++ * ++ * Register Index ++ * 1 : IER ++ * 2 : FCR ++ * 3 : LCR ++ * 4 : MCR ++ * ++ * 0x08 : SP1/2 Control Reg ++ *****************************************************************************/ ++ ++//NEED to check the fallowing Block ++ ++ status=0; ++ Data=0x0; ++ status=ATEN2011_get_reg_sync(port,ATEN2011_port->SpRegOffset,&Data); ++ if(status<0){ ++ DPRINTK("Reading Spreg failed\n"); ++ return -1; ++ } ++ Data |= 0x80; ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->SpRegOffset,Data); ++ if(status<0){ ++ DPRINTK("writing Spreg failed\n"); ++ return -1; ++ } ++ ++ Data &= ~0x80; ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->SpRegOffset,Data); ++ if(status<0){ ++ DPRINTK("writing Spreg failed\n"); ++ return -1; ++ } ++ ++ ++//End of block to be checked ++//**************************CHECK***************************// ++ ++ if(RS485mode==0) ++ Data = 0xC0; ++ else ++ Data = 0x00; ++ status=0; ++ status=ATEN2011_set_Uart_Reg(port,SCRATCH_PAD_REGISTER,Data); ++ if(status<0) { ++ DPRINTK("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); ++ return -1; ++ } ++ else DPRINTK("SCRATCH_PAD_REGISTER Writing success status%d\n",status); ++ ++ ++//**************************CHECK***************************// ++ ++ status=0; ++ Data=0x0; ++ status=ATEN2011_get_reg_sync(port,ATEN2011_port->ControlRegOffset,&Data); ++ if(status<0){ ++ DPRINTK("Reading Controlreg failed\n"); ++ return -1; ++ } ++ Data |= 0x08;//Driver done bit ++ /* ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->ControlRegOffset,Data); ++ if(status<0){ ++ DPRINTK("writing Controlreg failed\n"); ++ return -1; ++ } ++ */ ++ Data |= 0x20;//rx_disable ++ status=0; ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->ControlRegOffset,Data); ++ if(status<0){ ++ DPRINTK("writing Controlreg failed\n"); ++ return -1; ++ } ++ ++ //do register settings here ++ // Set all regs to the device default values. ++ //////////////////////////////////// ++ // First Disable all interrupts. ++ //////////////////////////////////// ++ ++ Data = 0x00; ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,INTERRUPT_ENABLE_REGISTER,Data); ++ if(status<0){ ++ DPRINTK("disableing interrupts failed\n"); ++ return -1; ++ } ++ // Set FIFO_CONTROL_REGISTER to the default value ++ Data = 0x00; ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,FIFO_CONTROL_REGISTER,Data); ++ if(status<0){ ++ DPRINTK("Writing FIFO_CONTROL_REGISTER failed\n"); ++ return -1; ++ } ++ ++ Data = 0xcf; //chk ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,FIFO_CONTROL_REGISTER,Data); ++ if(status<0){ ++ DPRINTK("Writing FIFO_CONTROL_REGISTER failed\n"); ++ return -1; ++ } ++ ++ Data = 0x03; //LCR_BITS_8 ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ ATEN2011_port->shadowLCR=Data; ++ ++ Data = 0x0b; // MCR_DTR|MCR_RTS|MCR_MASTER_IE ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ ATEN2011_port->shadowMCR=Data; ++ ++#ifdef Check ++ Data = 0x00; ++ status=0; ++ status = ATEN2011_get_Uart_Reg(port,LINE_CONTROL_REGISTER,&Data); ++ ATEN2011_port->shadowLCR=Data; ++ ++ Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 ++ status = 0; ++ status = ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ ++ Data = 0x0c; ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,DIVISOR_LATCH_LSB,Data); ++ ++ Data = 0x0; ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,DIVISOR_LATCH_MSB,Data); ++ ++ Data = 0x00; ++ status=0; ++ status = ATEN2011_get_Uart_Reg(port,LINE_CONTROL_REGISTER,&Data); ++ ++// Data = ATEN2011_port->shadowLCR; //data latch disable ++ Data = Data & ~SERIAL_LCR_DLAB; ++ status = 0; ++ status = ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ ATEN2011_port->shadowLCR=Data; ++#endif ++ //clearing Bulkin and Bulkout Fifo ++ Data = 0x0; ++ status = 0; ++ status = ATEN2011_get_reg_sync(port,ATEN2011_port->SpRegOffset,&Data); ++ ++ Data = Data | 0x0c; ++ status = 0; ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->SpRegOffset,Data); ++ ++ Data = Data & ~0x0c; ++ status = 0; ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->SpRegOffset,Data); ++ //Finally enable all interrupts ++ Data = 0x0; ++ Data = 0x0c; ++ status = 0; ++ status = ATEN2011_set_Uart_Reg(port,INTERRUPT_ENABLE_REGISTER,Data); ++ ++ //clearing rx_disable ++ Data = 0x0; ++ status = 0; ++ status = ATEN2011_get_reg_sync(port,ATEN2011_port->ControlRegOffset,&Data); ++ Data = Data & ~0x20; ++ status = 0; ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->ControlRegOffset,Data); ++ ++ // rx_negate ++ Data = 0x0; ++ status = 0; ++ status = ATEN2011_get_reg_sync(port,ATEN2011_port->ControlRegOffset,&Data); ++ Data = Data |0x10; ++ status = 0; ++ status = ATEN2011_set_reg_sync(port,ATEN2011_port->ControlRegOffset,Data); ++ ++ ++ /* force low_latency on so that our tty_push actually forces * ++ * the data through,otherwise it is scheduled, and with * ++ * high data rates (like with OHCI) data can get lost. */ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ tty = port->tty; ++#endif ++ if (tty) ++ tty->low_latency = 1; ++/* ++ printk("port number is %d \n",port->number); ++ printk("serial number is %d \n",port->serial->minor); ++ printk("Bulkin endpoint is %d \n",port->bulk_in_endpointAddress); ++ printk("BulkOut endpoint is %d \n",port->bulk_out_endpointAddress); ++ printk("Interrupt endpoint is %d \n",port->interrupt_in_endpointAddress); ++ printk("port's number in the device is %d\n",ATEN2011_port->port_num); ++*/ ++//////////////////////// ++//#ifdef CheckStatusPipe ++/* Check to see if we've set up our endpoint info yet * ++ * (can't set it up in ATEN2011_startup as the structures * ++ * were not set up at that time.) */ ++if(ATEN2011_serial->NoOfOpenPorts==1) ++{ ++ // start the status polling here ++ ATEN2011_serial->status_polling_started = TRUE; ++ //if (ATEN2011_serial->interrupt_in_buffer == NULL) ++ // { ++ /* If not yet set, Set here */ ++ ATEN2011_serial->interrupt_in_buffer = serial->port[0]->interrupt_in_buffer; ++ ATEN2011_serial->interrupt_in_endpoint = serial->port[0]->interrupt_in_endpointAddress; ++ //printk(" interrupt endpoint:%d \n",ATEN2011_serial->interrupt_in_endpoint); ++ ATEN2011_serial->interrupt_read_urb = serial->port[0]->interrupt_in_urb; ++ ++ /* set up interrupt urb */ ++ usb_fill_int_urb( \ ++ ATEN2011_serial->interrupt_read_urb, \ ++ serial->dev, \ ++ usb_rcvintpipe(serial->dev,ATEN2011_serial->interrupt_in_endpoint), \ ++ ATEN2011_serial->interrupt_in_buffer, \ ++ ATEN2011_serial->interrupt_read_urb->transfer_buffer_length,\ ++ ATEN2011_interrupt_callback, ATEN2011_serial, \ ++ ATEN2011_serial->interrupt_read_urb->interval ); ++ ++ /* start interrupt read for ATEN2011 * ++ * will continue as long as ATEN2011 is connected */ ++ ++ response = usb_submit_urb (ATEN2011_serial->interrupt_read_urb,GFP_KERNEL); ++ if (response) ++ { ++ DPRINTK("%s - Error %d submitting interrupt urb", __FUNCTION__, response); ++ } ++ // else ++ // printk(" interrupt URB submitted\n"); ++ ++ //} ++ ++} ++//#endif ++ ++ ++/////////////////////// ++ /* see if we've set up our endpoint info yet * ++ * (can't set it up in ATEN2011_startup as the * ++ * structures were not set up at that time.) */ ++ ++ DPRINTK("port number is %d \n",port->number); ++ DPRINTK("serial number is %d \n",port->serial->minor); ++ DPRINTK("Bulkin endpoint is %d \n",port->bulk_in_endpointAddress); ++ DPRINTK("BulkOut endpoint is %d \n",port->bulk_out_endpointAddress); ++ DPRINTK("Interrupt endpoint is %d \n",port->interrupt_in_endpointAddress); ++ DPRINTK("port's number in the device is %d\n",ATEN2011_port->port_num); ++ ATEN2011_port->bulk_in_buffer = port->bulk_in_buffer; ++ ATEN2011_port->bulk_in_endpoint = port->bulk_in_endpointAddress; ++ ATEN2011_port->read_urb = port->read_urb; ++ ATEN2011_port->bulk_out_endpoint = port->bulk_out_endpointAddress; ++ ++ minor = port->serial->minor; ++ if (minor == SERIAL_TTY_NO_MINOR) ++ minor = 0; ++ ++ /* set up our bulk in urb */ ++ if((ATEN2011_serial->ATEN2011_spectrum_2or4ports==2)&&(((__u16)port->number - (__u16)(minor)) != 0)) ++ { ++ usb_fill_bulk_urb( ++ ATEN2011_port->read_urb,serial->dev,\ ++ usb_rcvbulkpipe(serial->dev, (port->bulk_in_endpointAddress+2)),\ ++ port->bulk_in_buffer,\ ++ ATEN2011_port->read_urb->transfer_buffer_length, \ ++ ATEN2011_bulk_in_callback,ATEN2011_port); ++ } ++ else ++ usb_fill_bulk_urb( ++ ATEN2011_port->read_urb, \ ++ serial->dev, \ ++ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),\ ++ port->bulk_in_buffer, \ ++ ATEN2011_port->read_urb->transfer_buffer_length, \ ++ ATEN2011_bulk_in_callback,ATEN2011_port); ++ ++ DPRINTK("ATEN2011_open: bulkin endpoint is %d\n",port->bulk_in_endpointAddress); ++ response = usb_submit_urb (ATEN2011_port->read_urb,GFP_KERNEL); ++ if (response) ++ { ++ err("%s - Error %d submitting control urb", __FUNCTION__, response); ++ } ++ ++ /* initialize our wait queues */ ++ init_waitqueue_head(&ATEN2011_port->wait_open); ++ init_waitqueue_head(&ATEN2011_port->wait_chase); ++ init_waitqueue_head(&ATEN2011_port->delta_msr_wait); ++ init_waitqueue_head(&ATEN2011_port->wait_command); ++ ++ /* initialize our icount structure */ ++ memset (&(ATEN2011_port->icount), 0x00, sizeof(ATEN2011_port->icount)); ++ ++ /* initialize our port settings */ ++ ATEN2011_port->shadowMCR = MCR_MASTER_IE; /* Must set to enable ints! */ ++ ATEN2011_port->chaseResponsePending = FALSE; ++ /* send a open port command */ ++ ATEN2011_port->openPending = FALSE; ++ ATEN2011_port->open = TRUE; ++ //ATEN2011_change_port_settings(ATEN2011_port,old_termios); ++ /* Setup termios */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ ATEN2011_set_termios (tty, port, &tmp_termios); ++#else ++ ATEN2011_set_termios (port, &tmp_termios); ++#endif ++ ATEN2011_port->rxBytesAvail = 0x0; ++ ATEN2011_port->icount.tx=0; ++ ATEN2011_port->icount.rx=0; ++ ++ DPRINTK("\n\nusb_serial serial:%x ATEN2011_port:%x\nATEN2011_serial:%x usb_serial_port port:%x\n\n",(unsigned int)serial,(unsigned int)ATEN2011_port,(unsigned int)ATEN2011_serial,(unsigned int)port); ++ ++ ++ ++ ++ return 0; ++ ++} ++ ++ ++/***************************************************************************** ++ * ATEN2011_close ++ * this function is called by the tty driver when a port is closed ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_close(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) ++#else ++static void ATEN2011_close(struct usb_serial_port *port, struct file *filp) ++#endif ++{ ++ struct usb_serial *serial; ++ struct ATENINTL_serial *ATEN2011_serial; ++ struct ATENINTL_port *ATEN2011_port; ++ int no_urbs; ++ __u16 Data; ++ //__u16 Data1= 20; ++ ++ DPRINTK("%s\n","ATEN2011_close:entering..."); ++ /* MATRIX */ ++ //ThreadState = 1; ++ /* MATRIX */ ++ //printk("Entering... :ATEN2011_close\n"); ++ if (ATEN2011_port_paranoia_check (port, __FUNCTION__)) ++ { ++ DPRINTK("%s","Port Paranoia failed \n"); ++ return; ++ } ++ serial = ATEN2011_get_usb_serial (port, __FUNCTION__); ++ if (!serial) ++ { ++ DPRINTK("%s","Serial Paranoia failed \n"); ++ return; ++ } ++ // take the Adpater and port's private data ++ ATEN2011_serial = ATEN2011_get_serial_private(serial); ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ if ((ATEN2011_serial == NULL) || (ATEN2011_port == NULL)) ++ { ++ return; ++ } ++ if (serial->dev) ++ { ++ /* flush and block(wait) until tx is empty*/ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ ATEN2011_block_until_tx_empty(tty, ATEN2011_port); ++#else ++ ATEN2011_block_until_tx_empty(ATEN2011_port); ++#endif ++ } ++ // kill the ports URB's ++ for (no_urbs = 0; no_urbs < NUM_URBS;no_urbs++) ++ usb_kill_urb (ATEN2011_port->write_urb_pool[no_urbs]); ++ /* Freeing Write URBs*/ ++ for (no_urbs = 0; no_urbs< NUM_URBS; ++no_urbs) ++ { ++ if (ATEN2011_port->write_urb_pool[no_urbs]) ++ { ++ if (ATEN2011_port->write_urb_pool[no_urbs]->transfer_buffer) ++ kfree(ATEN2011_port->write_urb_pool[no_urbs]->transfer_buffer); ++ usb_free_urb (ATEN2011_port->write_urb_pool[no_urbs]); ++ } ++ } ++ /* While closing port, shutdown all bulk read, write * ++ * and interrupt read if they exists */ ++ if (serial->dev) ++ { ++ if (ATEN2011_port->write_urb) ++ { ++ DPRINTK("%s","Shutdown bulk write\n"); ++ usb_kill_urb (ATEN2011_port->write_urb); ++ } ++ if (ATEN2011_port->read_urb) ++ { ++ DPRINTK("%s","Shutdown bulk read\n"); ++ usb_kill_urb (ATEN2011_port->read_urb); ++ } ++ if((&ATEN2011_port->control_urb)) ++ { ++ DPRINTK("%s","Shutdown control read\n"); ++ // usb_kill_urb (ATEN2011_port->control_urb); ++ ++ } ++ } ++ //if(ATEN2011_port->ctrl_buf != NULL) ++ //kfree(ATEN2011_port->ctrl_buf); ++ // decrement the no.of open ports counter of an individual USB-serial adapter. ++ ATEN2011_serial->NoOfOpenPorts--; ++ DPRINTK("NoOfOpenPorts in close%d:in port%d\n",ATEN2011_serial->NoOfOpenPorts,port->number); ++ //printk("the num of ports opend is:%d\n",ATEN2011_serial->NoOfOpenPorts); ++ if(ATEN2011_serial->NoOfOpenPorts==0) ++ { ++ //stop the stus polling here ++ //printk("disabling the status polling flag to FALSE :\n"); ++ ATEN2011_serial->status_polling_started = FALSE; ++ if(ATEN2011_serial->interrupt_read_urb) ++ { ++ DPRINTK("%s","Shutdown interrupt_read_urb\n"); ++ //ATEN2011_serial->interrupt_in_buffer=NULL; ++ //usb_kill_urb (ATEN2011_serial->interrupt_read_urb); ++ } ++ } ++ if (ATEN2011_port->write_urb) ++ { ++ /* if this urb had a transfer buffer already (old tx) free it */ ++ if (ATEN2011_port->write_urb->transfer_buffer != NULL) ++ { ++ kfree(ATEN2011_port->write_urb->transfer_buffer); ++ } ++ usb_free_urb(ATEN2011_port->write_urb); ++ } ++ // clear the MCR & IER ++ Data = 0x00; ++ ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ Data = 0x00; ++ ATEN2011_set_Uart_Reg(port,INTERRUPT_ENABLE_REGISTER,Data); ++ ++ //ATEN2011_get_Uart_Reg(port,MODEM_CONTROL_REGISTER,&Data1); ++ //printk("value of MCR after closing the port is : 0x%x\n",Data1); ++ ++ ATEN2011_port->open = FALSE; ++ ATEN2011_port->closePending = FALSE; ++ ATEN2011_port->openPending = FALSE; ++ DPRINTK("%s \n","Leaving ............"); ++ ++} ++ ++ ++/***************************************************************************** ++ * SerialBreak ++ * this function sends a break to the port ++ *****************************************************************************/ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_break(struct tty_struct *tty, int break_state) ++#else ++static void ATEN2011_break(struct usb_serial_port *port, int break_state) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#endif ++ unsigned char data; ++ struct usb_serial *serial; ++ struct ATENINTL_serial *ATEN2011_serial; ++ struct ATENINTL_port *ATEN2011_port; ++ ++ DPRINTK("%s \n","Entering ..........."); ++ DPRINTK("ATEN2011_break: Start\n"); ++ ++ if (ATEN2011_port_paranoia_check (port, __FUNCTION__)) ++ { ++ DPRINTK("%s","Port Paranoia failed \n"); ++ return; ++ } ++ ++ serial = ATEN2011_get_usb_serial (port, __FUNCTION__); ++ if (!serial) ++ { ++ DPRINTK("%s","Serial Paranoia failed \n"); ++ return; ++ } ++ ++ ATEN2011_serial = ATEN2011_get_serial_private(serial); ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ ++ if ((ATEN2011_serial == NULL) || (ATEN2011_port == NULL)) ++ { ++ return; ++ } ++ ++ /* flush and chase */ ++ ATEN2011_port->chaseResponsePending = TRUE; ++ ++ if (serial->dev) ++ { ++ ++ /* flush and block until tx is empty*/ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ ATEN2011_block_until_chase_response(tty, ATEN2011_port); ++#else ++ ATEN2011_block_until_chase_response(ATEN2011_port); ++#endif ++ } ++ ++ if(break_state == -1) ++ { ++ data = ATEN2011_port->shadowLCR | LCR_SET_BREAK; ++ } ++ else ++ { ++ data = ATEN2011_port->shadowLCR & ~LCR_SET_BREAK; ++ } ++ ++ ATEN2011_port->shadowLCR = data; ++ DPRINTK("ATEN2011_break ATEN2011_port->shadowLCR is %x\n",ATEN2011_port->shadowLCR); ++ ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,ATEN2011_port->shadowLCR); ++ ++ return; ++} ++ ++ ++/************************************************************************ ++ * ++ * ATEN2011_block_until_chase_response ++ * ++ * This function will block the close until one of the following: ++ * 1. Response to our Chase comes from ATEN2011 ++ * 2. A timout of 10 seconds without activity has expired ++ * (1K of ATEN2011 data @ 2400 baud ==> 4 sec to empty) ++ * ++ ************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_block_until_chase_response(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port) ++#else ++static void ATEN2011_block_until_chase_response(struct ATENINTL_port *ATEN2011_port) ++#endif ++{ ++ int timeout = 1*HZ; ++ int wait = 10; ++ int count ; ++ ++ ++ while (1) ++ { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ count = ATEN2011_chars_in_buffer(tty); ++#else ++ count = ATEN2011_chars_in_buffer(ATEN2011_port->port); ++#endif ++ ++ /* Check for Buffer status */ ++ if(count<=0) ++ { ++ ATEN2011_port->chaseResponsePending = FALSE; ++ return; ++ } ++ ++ /* Block the thread for a while */ ++ interruptible_sleep_on_timeout (&ATEN2011_port->wait_chase, timeout); ++ /* No activity.. count down section */ ++ wait--; ++ if (wait == 0) ++ { ++ dbg("%s - TIMEOUT", __FUNCTION__); ++ return; ++ } ++ else ++ { ++ /* Reset timout value back to seconds */ ++ wait = 10; ++ } ++ } ++ ++} ++ ++ ++/************************************************************************ ++ * ++ * ATEN2011_block_until_tx_empty ++ * ++ * This function will block the close until one of the following: ++ * 1. TX count are 0 ++ * 2. The ATEN2011 has stopped ++ * 3. A timout of 3 seconds without activity has expired ++ * ++ ************************************************************************/ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_block_until_tx_empty(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port) ++#else ++static void ATEN2011_block_until_tx_empty(struct ATENINTL_port *ATEN2011_port) ++#endif ++{ ++ int timeout = HZ/10; ++ int wait = 30; ++ int count; ++ ++ while (1) ++ { ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ count = ATEN2011_chars_in_buffer(tty); ++#else ++ count = ATEN2011_chars_in_buffer(ATEN2011_port->port); ++#endif ++ ++ /* Check for Buffer status */ ++ if(count<=0) ++ { ++ return; ++ } ++ ++ /* Block the thread for a while */ ++ interruptible_sleep_on_timeout (&ATEN2011_port->wait_chase, timeout); ++ ++ /* No activity.. count down section */ ++ wait--; ++ if (wait == 0) ++ { ++ dbg("%s - TIMEOUT", __FUNCTION__); ++ return; ++ } ++ else ++ { ++ /* Reset timout value back to seconds */ ++ wait = 30; ++ } ++ } ++} ++ ++/***************************************************************************** ++ * ATEN2011_write_room ++ * this function is called by the tty driver when it wants to know how many ++ * bytes of data we can accept for a specific port. ++ * If successful, we return the amount of room that we have for this port ++ * Otherwise we return a negative error number. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_write_room (struct tty_struct *tty) ++#else ++static int ATEN2011_write_room (struct usb_serial_port *port) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#endif ++ int i; ++ int room = 0; ++ struct ATENINTL_port *ATEN2011_port; ++ ++ ++// DPRINTK("%s \n"," ATEN2011_write_room:entering ..........."); ++ ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ DPRINTK("%s \n"," ATEN2011_write_room:leaving ..........."); ++ return -1; ++ } ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ if (ATEN2011_port == NULL) ++ { ++ DPRINTK("%s \n","ATEN2011_break:leaving ..........."); ++ return -1; ++ } ++ ++ for (i = 0; i < NUM_URBS; ++i) ++ { ++ if (ATEN2011_port->write_urb_pool[i]->status != -EINPROGRESS) ++ { ++ room += URB_TRANSFER_BUFFER_SIZE; ++ } ++ } ++ ++ dbg("%s - returns %d", __FUNCTION__, room); ++ return (room); ++ ++} ++ ++ ++/***************************************************************************** ++ * ATEN2011_chars_in_buffer ++ * this function is called by the tty driver when it wants to know how many ++ * bytes of data we currently have outstanding in the port (data that has ++ * been written, but hasn't made it out the port yet) ++ * If successful, we return the number of bytes left to be written in the ++ * system, ++ * Otherwise we return a negative error number. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_chars_in_buffer(struct tty_struct *tty) ++#else ++static int ATEN2011_chars_in_buffer(struct usb_serial_port *port) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#endif ++ int i; ++ int chars = 0; ++ struct ATENINTL_port *ATEN2011_port; ++ ++ //DPRINTK("%s \n"," ATEN2011_chars_in_buffer:entering ..........."); ++ ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return -1; ++ } ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ if (ATEN2011_port == NULL) ++ { ++ DPRINTK("%s \n","ATEN2011_break:leaving ..........."); ++ return -1; ++ } ++ ++ for (i = 0; i < NUM_URBS; ++i) ++ { ++ if (ATEN2011_port->write_urb_pool[i]->status == -EINPROGRESS) ++ { ++ chars += URB_TRANSFER_BUFFER_SIZE; ++ } ++ } ++ dbg("%s - returns %d", __FUNCTION__, chars); ++ return (chars); ++ ++} ++ ++ ++/***************************************************************************** ++ * SerialWrite ++ * this function is called by the tty driver when data should be written to ++ * the port. ++ * If successful, we return the number of bytes written, otherwise we ++ * return a negative error number. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) ++#else ++static int ATEN2011_write (struct usb_serial_port *port, const unsigned char *data, int count) ++#endif ++{ ++ int status; ++ int i; ++ int bytes_sent = 0; ++ int transfer_size; ++ int from_user=0; ++ int minor; ++ ++ struct ATENINTL_port *ATEN2011_port; ++ struct usb_serial *serial; ++ struct ATENINTL_serial *ATEN2011_serial; ++ struct urb *urb; ++ //__u16 Data; ++ const unsigned char *current_position = data; ++ unsigned char * data1; ++ DPRINTK("%s \n","entering ..........."); ++ //DPRINTK("ATEN2011_write: ATEN2011_port->shadowLCR is %x\n",ATEN2011_port->shadowLCR); ++ ++ #ifdef NOTATEN2011 ++ Data = 0x00; ++ status=0; ++ status = ATEN2011_get_Uart_Reg(port,LINE_CONTROL_REGISTER,&Data); ++ ATEN2011_port->shadowLCR = Data; ++ DPRINTK("ATEN2011_write: LINE_CONTROL_REGISTER is %x\n",Data); ++ DPRINTK("ATEN2011_write: ATEN2011_port->shadowLCR is %x\n",ATEN2011_port->shadowLCR); ++ ++ //Data = 0x03; ++ //status = ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ //ATEN2011_port->shadowLCR=Data;//Need to add later ++ ++ Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 ++ status = 0; ++ status = ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ ++ //Data = 0x0c; ++ //status = ATEN2011_set_Uart_Reg(port,DIVISOR_LATCH_LSB,Data); ++ Data = 0x00; ++ status=0; ++ status = ATEN2011_get_Uart_Reg(port,DIVISOR_LATCH_LSB,&Data); ++ DPRINTK("ATEN2011_write:DLL value is %x\n",Data); ++ ++ Data = 0x0; ++ status=0; ++ status = ATEN2011_get_Uart_Reg(port,DIVISOR_LATCH_MSB,&Data); ++ DPRINTK("ATEN2011_write:DLM value is %x\n",Data); ++ ++ Data = Data & ~SERIAL_LCR_DLAB; ++ DPRINTK("ATEN2011_write: ATEN2011_port->shadowLCR is %x\n",ATEN2011_port->shadowLCR); ++ status = 0; ++ status = ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ #endif ++ ++ if (ATEN2011_port_paranoia_check (port, __FUNCTION__)) ++ { ++ DPRINTK("%s","Port Paranoia failed \n"); ++ return -1; ++ } ++ ++ serial = port->serial; ++ if (ATEN2011_serial_paranoia_check (serial, __FUNCTION__)) ++ { ++ DPRINTK("%s","Serial Paranoia failed \n"); ++ return -1; ++ } ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ if(ATEN2011_port==NULL) ++ { ++ DPRINTK("%s","ATEN2011_port is NULL\n"); ++ return -1; ++ } ++ ++ ATEN2011_serial =ATEN2011_get_serial_private(serial); ++ if(ATEN2011_serial==NULL) ++ { ++ DPRINTK("%s","ATEN2011_serial is NULL \n"); ++ return -1; ++ } ++ ++ ++ /* try to find a free urb in the list */ ++ urb = NULL; ++ ++ for (i = 0; i < NUM_URBS; ++i) ++ { ++ if (ATEN2011_port->write_urb_pool[i]->status != -EINPROGRESS) ++ { ++ urb = ATEN2011_port->write_urb_pool[i]; ++ DPRINTK("\nURB:%d",i); ++ break; ++ } ++ } ++ ++ if (urb == NULL) ++ { ++ dbg("%s - no more free urbs", __FUNCTION__); ++ goto exit; ++ } ++ ++ if (urb->transfer_buffer == NULL) ++ { ++ urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); ++ ++ if (urb->transfer_buffer == NULL) ++ { ++ err("%s no more kernel memory...", __FUNCTION__); ++ goto exit; ++ } ++ } ++ transfer_size = min (count, URB_TRANSFER_BUFFER_SIZE); ++ ++ if (from_user) ++ { ++ if (copy_from_user (urb->transfer_buffer, current_position, transfer_size)) { ++ bytes_sent = -EFAULT; ++ goto exit; ++ } ++ } ++ else ++ { ++ memcpy (urb->transfer_buffer, current_position, transfer_size); ++ } ++ //usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, urb->transfer_buffer); ++ ++ /* fill urb with data and submit */ ++ minor = port->serial->minor; ++ if (minor == SERIAL_TTY_NO_MINOR); ++ minor = 0; ++ if((ATEN2011_serial->ATEN2011_spectrum_2or4ports==2)&&(((__u16)port->number - (__u16)(minor)) != 0)) ++ { ++ usb_fill_bulk_urb (urb, ++ ATEN2011_serial->serial->dev, ++ usb_sndbulkpipe(ATEN2011_serial->serial->dev, ++ (port->bulk_out_endpointAddress)+2), ++ urb->transfer_buffer, ++ transfer_size, ++ ATEN2011_bulk_out_data_callback, ++ ATEN2011_port); ++ } ++ else ++ ++ ++ ++ usb_fill_bulk_urb (urb, ++ ATEN2011_serial->serial->dev, ++ usb_sndbulkpipe(ATEN2011_serial->serial->dev, ++ port->bulk_out_endpointAddress), ++ urb->transfer_buffer, ++ transfer_size, ++ ATEN2011_bulk_out_data_callback, ++ ATEN2011_port); ++ ++ data1=urb->transfer_buffer; ++ DPRINTK("\nbulkout endpoint is %d",port->bulk_out_endpointAddress); ++ //for(i=0;i < urb->actual_length;i++) ++ // DPRINTK("Data is %c\n ",data1[i]); ++ ++ /* send it down the pipe */ ++ status = usb_submit_urb(urb,GFP_ATOMIC); ++ ++ if (status) ++ { ++ err("%s - usb_submit_urb(write bulk) failed with status = %d", __FUNCTION__, status); ++ bytes_sent = status; ++ goto exit; ++ } ++ bytes_sent = transfer_size; ++ ATEN2011_port->icount.tx += transfer_size; ++ DPRINTK("ATEN2011_port->icount.tx is %d:\n",ATEN2011_port->icount.tx); ++exit: ++ ++ return bytes_sent; ++ ++} ++ ++ ++/***************************************************************************** ++ * SerialThrottle ++ * this function is called by the tty driver when it wants to stop the data ++ * being read from the port. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_throttle(struct tty_struct *tty) ++#else ++static void ATEN2011_throttle(struct usb_serial_port *port) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#else ++ struct tty_struct *tty; ++#endif ++ struct ATENINTL_port *ATEN2011_port; ++ int status; ++ ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return; ++ } ++ ++ DPRINTK("- port %d\n", port->number); ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ ++ if (ATEN2011_port == NULL) ++ return; ++ ++ if (!ATEN2011_port->open) ++ { ++ DPRINTK("%s\n","port not opened"); ++ return; ++ } ++ ++ DPRINTK("%s","Entering .......... \n"); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ tty = port->tty; ++#endif ++ if (!tty) ++ { ++ dbg ("%s - no tty available", __FUNCTION__); ++ return; ++ } ++ ++ /* if we are implementing XON/XOFF, send the stop character */ ++ if (I_IXOFF(tty)) ++ { ++ unsigned char stop_char = STOP_CHAR(tty); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ status = ATEN2011_write(tty, port, &stop_char, 1); //FC4 ++#else ++ status = ATEN2011_write(port, &stop_char, 1); //FC4 ++#endif ++ if (status <= 0) ++ { ++ return; ++ } ++ } ++ ++ /* if we are implementing RTS/CTS, toggle that line */ ++ if (tty->termios->c_cflag & CRTSCTS) ++ { ++ ATEN2011_port->shadowMCR &= ~MCR_RTS; ++ status=0; ++ status=ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,ATEN2011_port->shadowMCR); ++ ++ if (status < 0) ++ { ++ return; ++ } ++ } ++ ++ return; ++} ++ ++ ++/***************************************************************************** ++ * ATEN2011_unthrottle ++ * this function is called by the tty driver when it wants to resume the data ++ * being read from the port (called after SerialThrottle is called) ++ *****************************************************************************/ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_unthrottle (struct tty_struct *tty) ++#else ++static void ATEN2011_unthrottle (struct usb_serial_port *port) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#else ++ struct tty_struct *tty; ++#endif ++ int status; ++ struct ATENINTL_port *ATEN2011_port = ATEN2011_get_port_private(port); ++ ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return; ++ } ++ ++ if (ATEN2011_port == NULL) ++ return; ++ ++ if (!ATEN2011_port->open) { ++ dbg("%s - port not opened", __FUNCTION__); ++ return; ++ } ++ ++ DPRINTK("%s","Entering .......... \n"); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ tty = port->tty; ++#endif ++ if (!tty) ++ { ++ dbg ("%s - no tty available", __FUNCTION__); ++ return; ++ } ++ ++ /* if we are implementing XON/XOFF, send the start character */ ++ if (I_IXOFF(tty)) ++ { ++ unsigned char start_char = START_CHAR(tty); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ status = ATEN2011_write (tty, port, &start_char, 1); //FC4 ++#else ++ status = ATEN2011_write (port, &start_char, 1); //FC4 ++#endif ++ if (status <= 0) ++ { ++ return; ++ } ++ } ++ ++ /* if we are implementing RTS/CTS, toggle that line */ ++ if (tty->termios->c_cflag & CRTSCTS) ++ { ++ ATEN2011_port->shadowMCR |= MCR_RTS; ++ status=0; ++ status=ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,ATEN2011_port->shadowMCR); ++ if (status < 0) ++ { ++ return; ++ } ++ } ++ ++ return; ++} ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_tiocmget(struct tty_struct *tty, struct file *file) ++#else ++static int ATEN2011_tiocmget(struct usb_serial_port *port, struct file *file) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#endif ++ //struct ti_port *tport = usb_get_serial_port_data(port); ++ struct ATENINTL_port *ATEN2011_port; ++ unsigned int result; ++ __u16 msr; ++ __u16 mcr; ++ //unsigned int mcr; ++ int status=0; ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ ++ DPRINTK("%s - port %d", __FUNCTION__, port->number); ++ ++ if (ATEN2011_port == NULL) ++ return -ENODEV; ++ ++ status=ATEN2011_get_Uart_Reg(port,MODEM_STATUS_REGISTER,&msr); ++ status=ATEN2011_get_Uart_Reg(port,MODEM_CONTROL_REGISTER,&mcr); ++// mcr = ATEN2011_port->shadowMCR; ++// COMMENT2: the Fallowing three line are commented for updating only MSR values ++ result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) ++ | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) ++ | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0) ++ | ((msr & ATEN2011_MSR_CTS) ? TIOCM_CTS : 0) ++ | ((msr & ATEN2011_MSR_CD) ? TIOCM_CAR : 0) ++ | ((msr & ATEN2011_MSR_RI) ? TIOCM_RI : 0) ++ | ((msr & ATEN2011_MSR_DSR) ? TIOCM_DSR : 0); ++ ++ DPRINTK("%s - 0x%04X", __FUNCTION__, result); ++ ++ return result; ++} ++ ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_tiocmset(struct tty_struct *tty, struct file *file, ++ unsigned int set, unsigned int clear) ++#else ++static int ATEN2011_tiocmset(struct usb_serial_port *port, struct file *file, ++ unsigned int set, unsigned int clear) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#endif ++ struct ATENINTL_port *ATEN2011_port; ++ //struct ti_port *tport = usb_get_serial_port_data(port); ++ unsigned int mcr; ++ unsigned int status; ++ ++ DPRINTK("%s - port %d", __FUNCTION__, port->number); ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ ++ if (ATEN2011_port == NULL) ++ return -ENODEV; ++ ++ ++ ++ mcr = ATEN2011_port->shadowMCR; ++ if (clear & TIOCM_RTS) ++ mcr &= ~MCR_RTS; ++ if (clear & TIOCM_DTR) ++ mcr &= ~MCR_DTR; ++ if (clear & TIOCM_LOOP) ++ mcr &= ~MCR_LOOPBACK; ++ ++ if (set & TIOCM_RTS) ++ mcr |= MCR_RTS; ++ if (set & TIOCM_DTR) ++ mcr |= MCR_DTR; ++ if (set & TIOCM_LOOP) ++ mcr |= MCR_LOOPBACK; ++ ++ ATEN2011_port->shadowMCR = mcr; ++ ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,mcr); ++ if(status <0) ++ { ++ DPRINTK("setting MODEM_CONTROL_REGISTER Failed\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++ ++ ++ ++ ++ ++/***************************************************************************** ++ * SerialSetTermios ++ * this function is called by the tty driver when it wants to change the termios structure ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) ++#else ++static void ATEN2011_set_termios(struct usb_serial_port *port, struct ktermios *old_termios) ++#endif ++{ ++ int status; ++ unsigned int cflag; ++ struct usb_serial *serial; ++ struct ATENINTL_port *ATEN2011_port; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ struct tty_struct *tty; ++#endif ++ DPRINTK("ATEN2011_set_termios: START\n"); ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return; ++ } ++ ++ serial = port->serial; ++ ++ if(ATEN2011_serial_paranoia_check(serial,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid Serial \n"); ++ return; ++ } ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++ ++ if (ATEN2011_port == NULL) ++ return; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ tty = port->tty; ++ ++ if (!port->tty || !port->tty->termios) ++ { ++ dbg ("%s - no tty or termios", __FUNCTION__); ++ return; ++ } ++#endif ++ ++ if (!ATEN2011_port->open) ++ { ++ dbg("%s - port not opened", __FUNCTION__); ++ return; ++ } ++ ++ DPRINTK("%s\n","setting termios - "); ++ ++ cflag = tty->termios->c_cflag; ++ ++ if (!cflag) ++ { ++ DPRINTK("%s %s\n",__FUNCTION__,"cflag is NULL"); ++ return; ++ } ++ ++ /* check that they really want us to change something */ ++ if (old_termios) ++ { ++ if ((cflag == old_termios->c_cflag) && ++ (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) ++ { ++ DPRINTK("%s\n","Nothing to change"); ++ return; ++ } ++ } ++ ++ dbg("%s - clfag %08x iflag %08x", __FUNCTION__, ++ tty->termios->c_cflag, ++ RELEVANT_IFLAG(tty->termios->c_iflag)); ++ ++ if (old_termios) ++ { ++ dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__, ++ old_termios->c_cflag, ++ RELEVANT_IFLAG(old_termios->c_iflag)); ++ } ++ ++ dbg("%s - port %d", __FUNCTION__, port->number); ++ ++ /* change the port settings to the new ones specified */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ ATEN2011_change_port_settings(tty, ATEN2011_port, old_termios); ++#else ++ ATEN2011_change_port_settings(ATEN2011_port, old_termios); ++#endif ++ ++ if(!ATEN2011_port->read_urb) ++ { ++ DPRINTK("%s","URB KILLED !!!!!\n"); ++ return; ++ } ++ ++ if(ATEN2011_port->read_urb->status!=-EINPROGRESS) ++ { ++ ATEN2011_port->read_urb->dev = serial->dev; ++ status = usb_submit_urb(ATEN2011_port->read_urb, GFP_ATOMIC); ++ if (status) ++ { ++ DPRINTK(" usb_submit_urb(read bulk) failed, status = %d", status); ++ } ++ } ++ return; ++} ++ ++/* ++static void ATEN2011_break_ctl( struct usb_serial_port *port, int break_state ) ++{ ++ //struct usb_serial *serial = port->serial; ++ ++// if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0) ++ // err("Set break_ctl %d", break_state); ++} ++*/ ++ ++ ++ ++/***************************************************************************** ++ * get_lsr_info - get line status register info ++ * ++ * Purpose: Let user call ioctl() to get info when the UART physically ++ * is emptied. On bus types like RS485, the transmitter must ++ * release the bus after transmitting. This must be done when ++ * the transmit shift register is empty, not be done when the ++ * transmit holding register is empty. This functionality ++ * allows an RS485 driver to be written in user space. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int get_lsr_info(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port, unsigned int *value) ++#else ++static int get_lsr_info(struct ATENINTL_port *ATEN2011_port, unsigned int *value) ++#endif ++{ ++ int count; ++ unsigned int result = 0; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ count = ATEN2011_chars_in_buffer(tty); ++#else ++ count = ATEN2011_chars_in_buffer(ATEN2011_port->port); ++#endif ++ if(count == 0) ++ { ++ dbg("%s -- Empty", __FUNCTION__); ++ result = TIOCSER_TEMT; ++ } ++ ++ if (copy_to_user(value, &result, sizeof(int))) ++ return -EFAULT; ++ return 0; ++} ++ ++/***************************************************************************** ++ * get_number_bytes_avail - get number of bytes available ++ * ++ * Purpose: Let user call ioctl to get the count of number of bytes available. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int get_number_bytes_avail(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port, unsigned int *value) ++#else ++static int get_number_bytes_avail(struct ATENINTL_port *ATEN2011_port, unsigned int *value) ++#endif ++{ ++ unsigned int result = 0; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ struct tty_struct *tty = ATEN2011_port->port->tty; ++#endif ++ ++ if (!tty) ++ return -ENOIOCTLCMD; ++ ++ result = tty->read_cnt; ++ ++ dbg("%s(%d) = %d", __FUNCTION__, ATEN2011_port->port->number, result); ++ if (copy_to_user(value, &result, sizeof(int))) ++ return -EFAULT; ++ ++ return -ENOIOCTLCMD; ++} ++ ++ ++/***************************************************************************** ++ * set_modem_info ++ * function to set modem info ++ *****************************************************************************/ ++ ++static int set_modem_info(struct ATENINTL_port *ATEN2011_port, unsigned int cmd, unsigned int *value) ++{ ++ unsigned int mcr ; ++ unsigned int arg; ++ __u16 Data; ++ int status; ++ struct usb_serial_port *port; ++ ++ if (ATEN2011_port == NULL) ++ return -1; ++ ++ ++ port = (struct usb_serial_port*)ATEN2011_port->port; ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return -1; ++ } ++ ++ mcr = ATEN2011_port->shadowMCR; ++ ++ if (copy_from_user(&arg, value, sizeof(int))) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case TIOCMBIS: ++ if (arg & TIOCM_RTS) ++ mcr |= MCR_RTS; ++ if (arg & TIOCM_DTR) ++ mcr |= MCR_RTS; ++ if (arg & TIOCM_LOOP) ++ mcr |= MCR_LOOPBACK; ++ break; ++ ++ case TIOCMBIC: ++ if (arg & TIOCM_RTS) ++ mcr &= ~MCR_RTS; ++ if (arg & TIOCM_DTR) ++ mcr &= ~MCR_RTS; ++ if (arg & TIOCM_LOOP) ++ mcr &= ~MCR_LOOPBACK; ++ break; ++ ++ case TIOCMSET: ++ /* turn off the RTS and DTR and LOOPBACK ++ * and then only turn on what was asked to */ ++ mcr &= ~(MCR_RTS | MCR_DTR | MCR_LOOPBACK); ++ mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0); ++ mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0); ++ mcr |= ((arg & TIOCM_LOOP) ? MCR_LOOPBACK : 0); ++ break; ++ } ++ ++ ATEN2011_port->shadowMCR = mcr; ++ ++ Data = ATEN2011_port->shadowMCR; ++ status=0; ++ status = ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ if(status <0) ++ { ++ DPRINTK("setting MODEM_CONTROL_REGISTER Failed\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * get_modem_info ++ * function to get modem info ++ *****************************************************************************/ ++ ++static int get_modem_info(struct ATENINTL_port *ATEN2011_port, unsigned int *value) ++{ ++ unsigned int result = 0; ++ __u16 msr; ++ unsigned int mcr = ATEN2011_port->shadowMCR; ++ int status=0; ++ status=ATEN2011_get_Uart_Reg(ATEN2011_port->port,MODEM_STATUS_REGISTER,&msr); ++ result = ((mcr & MCR_DTR) ? TIOCM_DTR: 0) /* 0x002 */ ++ | ((mcr & MCR_RTS) ? TIOCM_RTS: 0) /* 0x004 */ ++ | ((msr & ATEN2011_MSR_CTS) ? TIOCM_CTS: 0) /* 0x020 */ ++ | ((msr & ATEN2011_MSR_CD) ? TIOCM_CAR: 0) /* 0x040 */ ++ | ((msr & ATEN2011_MSR_RI) ? TIOCM_RI: 0) /* 0x080 */ ++ | ((msr & ATEN2011_MSR_DSR) ? TIOCM_DSR: 0); /* 0x100 */ ++ ++ ++ dbg("%s -- %x", __FUNCTION__, result); ++ ++ if (copy_to_user(value, &result, sizeof(int))) ++ return -EFAULT; ++ return 0; ++} ++ ++/***************************************************************************** ++ * get_serial_info ++ * function to get information about serial port ++ *****************************************************************************/ ++ ++static int get_serial_info(struct ATENINTL_port *ATEN2011_port, struct serial_struct * retinfo) ++{ ++ struct serial_struct tmp; ++ ++ if (ATEN2011_port == NULL) ++ return -1; ++ ++ ++ if (!retinfo) ++ return -EFAULT; ++ ++ memset(&tmp, 0, sizeof(tmp)); ++ ++ tmp.type = PORT_16550A; ++ tmp.line = ATEN2011_port->port->serial->minor; ++ if (tmp.line == SERIAL_TTY_NO_MINOR) ++ tmp.line = 0; ++ tmp.port = ATEN2011_port->port->number; ++ tmp.irq = 0; ++ tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; ++ tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; ++ tmp.baud_base = 9600; ++ tmp.close_delay = 5*HZ; ++ tmp.closing_wait = 30*HZ; ++ ++ ++ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) ++ return -EFAULT; ++ return 0; ++} ++ ++/***************************************************************************** ++ * SerialIoctl ++ * this function handles any ioctl calls to the driver ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) ++#else ++static int ATEN2011_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) ++#endif ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ struct usb_serial_port *port = tty->driver_data; ++#else ++ struct tty_struct *tty; ++#endif ++ struct ATENINTL_port *ATEN2011_port; ++ struct async_icount cnow; ++ struct async_icount cprev; ++ struct serial_icounter_struct icount; ++ int ATENret=0; ++ //int retval; ++ //struct tty_ldisc *ld; ++ ++ //printk("%s - port %d, cmd = 0x%x\n", __FUNCTION__, port->number, cmd); ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return -1; ++ } ++ ++ ATEN2011_port = ATEN2011_get_port_private(port); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ tty = ATEN2011_port->port->tty; ++#endif ++ ++ if (ATEN2011_port == NULL) ++ return -1; ++ ++ dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd); ++ ++ switch (cmd) ++ { ++ /* return number of bytes available */ ++ ++ case TIOCINQ: ++ dbg("%s (%d) TIOCINQ", __FUNCTION__, port->number); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ return get_number_bytes_avail(tty, ATEN2011_port, (unsigned int *) arg); ++#else ++ return get_number_bytes_avail(ATEN2011_port, (unsigned int *) arg); ++#endif ++ break; ++ ++ case TIOCOUTQ: ++ dbg("%s (%d) TIOCOUTQ", __FUNCTION__, port->number); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ return put_user(ATEN2011_chars_in_buffer(tty), ++ (int __user *) arg); ++#else ++ return put_user(tty->driver->ops->chars_in_buffer ? ++ tty->driver->ops->chars_in_buffer(tty) : 0, ++ (int __user *) arg); ++#endif ++ break; ++ ++ /* //2.6.17 block ++ case TCFLSH: ++ retval = tty_check_change(tty); ++ if (retval) ++ return retval; ++ ++ ld = tty_ldisc_ref(tty); ++ switch (arg) { ++ case TCIFLUSH: ++ if (ld && ld->flush_buffer) ++ ld->flush_buffer(tty); ++ break; ++ case TCIOFLUSH: ++ if (ld && ld->flush_buffer) ++ ld->flush_buffer(tty); ++ // fall through ++ case TCOFLUSH: ++ if (tty->driver->flush_buffer) ++ tty->driver->flush_buffer(tty); ++ break; ++ default: ++ tty_ldisc_deref(ld); ++ return -EINVAL; ++ } ++ tty_ldisc_deref(ld); ++ return 0; ++ */ ++ case TIOCSERGETLSR: ++ dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__, port->number); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ return get_lsr_info(tty, ATEN2011_port, (unsigned int *) arg); ++#else ++ return get_lsr_info(ATEN2011_port, (unsigned int *) arg); ++#endif ++ return 0; ++ ++ case TIOCMBIS: ++ case TIOCMBIC: ++ case TIOCMSET: ++ dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__, port->number); ++ // printk("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__, port->number); ++ ATENret=set_modem_info(ATEN2011_port, cmd, (unsigned int *) arg); ++ // printk(" %s: ret:%d\n",__FUNCTION__,ATENret); ++ return ATENret; ++ ++ case TIOCMGET: ++ dbg("%s (%d) TIOCMGET", __FUNCTION__, port->number); ++ return get_modem_info(ATEN2011_port, (unsigned int *) arg); ++ ++ case TIOCGSERIAL: ++ dbg("%s (%d) TIOCGSERIAL", __FUNCTION__, port->number); ++ return get_serial_info(ATEN2011_port, (struct serial_struct *) arg); ++ ++ case TIOCSSERIAL: ++ dbg("%s (%d) TIOCSSERIAL", __FUNCTION__, port->number); ++ break; ++ ++ case TIOCMIWAIT: ++ dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number); ++ cprev = ATEN2011_port->icount; ++ while (1) { ++ //interruptible_sleep_on(&ATEN2011_port->delta_msr_wait); ++ // ATEN2011_port->delta_msr_cond=0; ++ //wait_event_interruptible(ATEN2011_port->delta_msr_wait,(ATEN2011_port->delta_msr_cond==1)); ++ ++ /* see if a signal did it */ ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ cnow = ATEN2011_port->icount; ++ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && ++ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) ++ return -EIO; /* no change => error */ ++ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ++ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ++ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ++ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { ++ return 0; ++ } ++ cprev = cnow; ++ } ++ /* NOTREACHED */ ++ break; ++ ++ case TIOCGICOUNT: ++ cnow = ATEN2011_port->icount; ++ icount.cts = cnow.cts; ++ icount.dsr = cnow.dsr; ++ icount.rng = cnow.rng; ++ icount.dcd = cnow.dcd; ++ icount.rx = cnow.rx; ++ icount.tx = cnow.tx; ++ icount.frame = cnow.frame; ++ icount.overrun = cnow.overrun; ++ icount.parity = cnow.parity; ++ icount.brk = cnow.brk; ++ icount.buf_overrun = cnow.buf_overrun; ++ ++ dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__, port->number, icount.rx, icount.tx ); ++ if (copy_to_user((void *)arg, &icount, sizeof(icount))) ++ return -EFAULT; ++ return 0; ++ ++ case TIOCEXBAUD: ++ return 0; ++ default: ++ break; ++ } ++ ++ return -ENOIOCTLCMD; ++} ++ ++ ++/***************************************************************************** ++ * ATEN2011_send_cmd_write_baud_rate ++ * this function sends the proper command to change the baud rate of the ++ * specified port. ++ *****************************************************************************/ ++ ++static int ATEN2011_send_cmd_write_baud_rate (struct ATENINTL_port *ATEN2011_port, int baudRate) ++{ ++ int divisor = 0; ++ int status; ++ __u16 Data; ++ unsigned char number ; ++ __u16 clk_sel_val; ++ struct usb_serial_port *port; ++ int minor; ++ ++ if (ATEN2011_port == NULL) ++ return -1; ++ ++ port = (struct usb_serial_port*)ATEN2011_port->port; ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return -1; ++ } ++ ++ if(ATEN2011_serial_paranoia_check(port->serial,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid Serial \n"); ++ return -1; ++ } ++ ++ ++ DPRINTK("%s","Entering .......... \n"); ++ ++ minor = ATEN2011_port->port->serial->minor; ++ if (minor == SERIAL_TTY_NO_MINOR) ++ minor = 0; ++ number = ATEN2011_port->port->number - minor; ++ ++ dbg("%s - port = %d, baud = %d", __FUNCTION__, ATEN2011_port->port->number, baudRate); ++ //reset clk_uart_sel in spregOffset ++ if(baudRate >115200) ++ { ++ #ifdef HW_flow_control ++ //NOTE: need to see the pther register to modify ++ //setting h/w flow control bit to 1; ++ status=0; ++ //Data = ATEN2011_port->shadowMCR ; ++ Data = 0x2b; ++ ATEN2011_port->shadowMCR=Data; ++ status=ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ if(status<0) ++ { ++ DPRINTK("Writing spreg failed in set_serial_baud\n"); ++ return -1; ++ } ++ #endif ++ ++ } ++ else ++ { ++ #ifdef HW_flow_control ++ //setting h/w flow control bit to 0; ++ status=0; ++ //Data = ATEN2011_port->shadowMCR ; ++ Data = 0xb; ++ ATEN2011_port->shadowMCR=Data; ++ status=ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ if(status<0) ++ { ++ DPRINTK("Writing spreg failed in set_serial_baud\n"); ++ return -1; ++ } ++ ++ #endif ++ ++ ++ } ++ ++ ++ if(1)//baudRate <= 115200) ++ { ++ clk_sel_val=0x0; ++ Data=0x0; ++ status=0; ++ status = ATEN2011_calc_baud_rate_divisor (baudRate, &divisor,&clk_sel_val); ++ status= ATEN2011_get_reg_sync(port,ATEN2011_port->SpRegOffset,&Data); ++ if(status<0) ++ { ++ DPRINTK("reading spreg failed in set_serial_baud\n"); ++ return -1; ++ } ++ Data = (Data & 0x8f)|clk_sel_val; ++ status=0; ++ status= ATEN2011_set_reg_sync(port,ATEN2011_port->SpRegOffset,Data); ++ if(status<0) ++ { ++ DPRINTK("Writing spreg failed in set_serial_baud\n"); ++ return -1; ++ } ++ /* Calculate the Divisor */ ++ ++ ++ if (status) ++ { ++ err("%s - bad baud rate", __FUNCTION__); ++ DPRINTK("%s\n","bad baud rate"); ++ return status; ++ } ++ /* Enable access to divisor latch */ ++ Data = ATEN2011_port->shadowLCR | SERIAL_LCR_DLAB; ++ ATEN2011_port->shadowLCR = Data; ++ ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ ++ /* Write the divisor */ ++ Data = LOW8 (divisor);//: commented to test ++ DPRINTK("set_serial_baud Value to write DLL is %x\n",Data); ++ ATEN2011_set_Uart_Reg(port,DIVISOR_LATCH_LSB,Data); ++ ++ Data = HIGH8 (divisor); //: commented to test ++ DPRINTK("set_serial_baud Value to write DLM is %x\n",Data); ++ ATEN2011_set_Uart_Reg(port,DIVISOR_LATCH_MSB,Data); ++ ++ /* Disable access to divisor latch */ ++ Data = ATEN2011_port->shadowLCR & ~SERIAL_LCR_DLAB; ++ ATEN2011_port->shadowLCR = Data; ++ ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ ++ } ++ ++ return status; ++} ++ ++ ++ ++/***************************************************************************** ++ * ATEN2011_calc_baud_rate_divisor ++ * this function calculates the proper baud rate divisor for the specified ++ * baud rate. ++ *****************************************************************************/ ++static int ATEN2011_calc_baud_rate_divisor (int baudRate, int *divisor,__u16 *clk_sel_val) ++{ ++ //int i; ++ //__u16 custom,round1, round; ++ ++ dbg("%s - %d", __FUNCTION__, baudRate); ++ ++ if(baudRate <=115200) ++ { ++ *divisor = 115200/baudRate; ++ *clk_sel_val = 0x0; ++ } ++ if((baudRate > 115200) && (baudRate <= 230400)) ++ { ++ *divisor = 230400/baudRate; ++ *clk_sel_val=0x10; ++ } ++ else if((baudRate > 230400) && (baudRate <= 403200)) ++ { ++ *divisor = 403200/baudRate; ++ *clk_sel_val=0x20; ++ } ++ else if((baudRate > 403200) && (baudRate <= 460800)) ++ { ++ *divisor = 460800/baudRate; ++ *clk_sel_val=0x30; ++ } ++ else if((baudRate > 460800) && (baudRate <= 806400)) ++ { ++ *divisor = 806400/baudRate; ++ *clk_sel_val=0x40; ++ } ++ else if((baudRate >806400) && (baudRate <= 921600)) ++ { ++ *divisor = 921600/baudRate; ++ *clk_sel_val=0x50; ++ } ++ else if((baudRate > 921600) && (baudRate <= 1572864)) ++ { ++ *divisor = 1572864/baudRate; ++ *clk_sel_val=0x60; ++ } ++ else if((baudRate > 1572864) && (baudRate <= 3145728)) ++ { ++ *divisor = 3145728/baudRate; ++ *clk_sel_val=0x70; ++ } ++ return 0; ++ ++ #ifdef NOTATEN2011 ++ ++ for (i = 0; i < NUM_ENTRIES(ATEN2011_divisor_table); i++) ++ { ++ if ( ATEN2011_divisor_table[i].BaudRate == baudrate ) ++ { ++ *divisor = ATEN2011_divisor_table[i].Divisor; ++ return 0; ++ } ++ } ++ ++ /* After trying for all the standard baud rates * ++ * Try calculating the divisor for this baud rate */ ++ ++ if (baudrate > 75 && baudrate < 230400) ++ { ++ /* get the divisor */ ++ custom = (__u16)(230400L / baudrate); ++ ++ /* Check for round off */ ++ round1 = (__u16)(2304000L / baudrate); ++ round = (__u16)(round1 - (custom * 10)); ++ if (round > 4) { ++ custom++; ++ } ++ *divisor = custom; ++ ++ DPRINTK(" Baud %d = %d\n",baudrate, custom); ++ return 0; ++ } ++ ++ DPRINTK("%s\n"," Baud calculation Failed..."); ++ return -1; ++ #endif ++} ++ ++ ++ ++/***************************************************************************** ++ * ATEN2011_change_port_settings ++ * This routine is called to set the UART on the device to match ++ * the specified new settings. ++ *****************************************************************************/ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_change_port_settings(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port, struct ktermios *old_termios) ++#else ++static void ATEN2011_change_port_settings(struct ATENINTL_port *ATEN2011_port, struct ktermios *old_termios) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ struct tty_struct *tty; ++#endif ++ int baud; ++ unsigned cflag; ++ unsigned iflag; ++ __u8 mask = 0xff; ++ __u8 lData; ++ __u8 lParity; ++ __u8 lStop; ++ int status; ++ __u16 Data; ++ struct usb_serial_port *port; ++ struct usb_serial *serial; ++ ++ if (ATEN2011_port == NULL) ++ return ; ++ ++ port = (struct usb_serial_port *)ATEN2011_port->port; ++ ++ if(ATEN2011_port_paranoia_check(port,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid port \n"); ++ return ; ++ } ++ ++ if(ATEN2011_serial_paranoia_check(port->serial,__FUNCTION__) ) ++ { ++ DPRINTK("%s","Invalid Serial \n"); ++ return ; ++ } ++ ++ serial = port->serial; ++ ++ dbg("%s - port %d", __FUNCTION__, ATEN2011_port->port->number); ++ ++ if ((!ATEN2011_port->open) && (!ATEN2011_port->openPending)) ++ { ++ dbg("%s - port not opened", __FUNCTION__); ++ return; ++ } ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++ tty = ATEN2011_port->port->tty; ++#endif ++ ++ if ((!tty) || (!tty->termios)) ++ { ++ dbg("%s - no tty structures", __FUNCTION__); ++ return; ++ } ++ ++ DPRINTK("%s","Entering .......... \n"); ++ ++ lData = LCR_BITS_8; ++ lStop = LCR_STOP_1; ++ lParity = LCR_PAR_NONE; ++ ++ cflag = tty->termios->c_cflag; ++ iflag = tty->termios->c_iflag; ++ ++ /* Change the number of bits */ ++ ++//COMMENT1: the below Line"if(cflag & CSIZE)" is added for the errors we get for serial loop data test i.e serial_loopback.pl -v ++ //if(cflag & CSIZE) ++ { ++ switch (cflag & CSIZE) ++ { ++ case CS5: lData = LCR_BITS_5; ++ mask = 0x1f; ++ break; ++ ++ case CS6: lData = LCR_BITS_6; ++ mask = 0x3f; ++ break; ++ ++ case CS7: lData = LCR_BITS_7; ++ mask = 0x7f; ++ break; ++ default: ++ case CS8: lData = LCR_BITS_8; ++ break; ++ } ++ } ++ /* Change the Parity bit */ ++ if (cflag & PARENB) ++ { ++ if (cflag & PARODD) ++ { ++ lParity = LCR_PAR_ODD; ++ dbg("%s - parity = odd", __FUNCTION__); ++ } ++ else ++ { ++ lParity = LCR_PAR_EVEN; ++ dbg("%s - parity = even", __FUNCTION__); ++ } ++ ++ } ++ else ++ { ++ dbg("%s - parity = none", __FUNCTION__); ++ } ++ ++ if(cflag & CMSPAR) ++ { ++ lParity = lParity | 0x20; ++ } ++ ++ /* Change the Stop bit */ ++ if (cflag & CSTOPB) ++ { ++ lStop = LCR_STOP_2; ++ dbg("%s - stop bits = 2", __FUNCTION__); ++ } ++ else ++ { ++ lStop = LCR_STOP_1; ++ dbg("%s - stop bits = 1", __FUNCTION__); ++ } ++ ++ ++ /* Update the LCR with the correct value */ ++ ATEN2011_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); ++ ATEN2011_port->shadowLCR |= (lData | lParity | lStop); ++ ++ ATEN2011_port->validDataMask = mask; ++ DPRINTK("ATEN2011_change_port_settings ATEN2011_port->shadowLCR is %x\n",ATEN2011_port->shadowLCR); ++ /* Disable Interrupts */ ++ Data = 0x00; ++ ATEN2011_set_Uart_Reg(port,INTERRUPT_ENABLE_REGISTER,Data); ++ ++ ++ Data = 0x00; ++ ATEN2011_set_Uart_Reg(port,FIFO_CONTROL_REGISTER,Data); ++ ++ Data = 0xcf; ++ ATEN2011_set_Uart_Reg(port,FIFO_CONTROL_REGISTER,Data); ++ ++ /* Send the updated LCR value to the ATEN2011 */ ++ Data = ATEN2011_port->shadowLCR; ++ ++ ATEN2011_set_Uart_Reg(port,LINE_CONTROL_REGISTER,Data); ++ ++ ++ Data = 0x00b; ++ ATEN2011_port->shadowMCR = Data; ++ ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ Data = 0x00b; ++ ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ ++ /* set up the MCR register and send it to the ATEN2011 */ ++ ++ ATEN2011_port->shadowMCR = MCR_MASTER_IE; ++ if (cflag & CBAUD) ++ { ++ ATEN2011_port->shadowMCR |= (MCR_DTR | MCR_RTS); ++ } ++ ++ ++ if (cflag & CRTSCTS) ++ { ++ ATEN2011_port->shadowMCR |= (MCR_XON_ANY); ++ ++ ++ } ++ else ++ { ++ ATEN2011_port->shadowMCR &= ~(MCR_XON_ANY); ++ } ++ ++ ++ Data = ATEN2011_port->shadowMCR; ++ ATEN2011_set_Uart_Reg(port,MODEM_CONTROL_REGISTER,Data); ++ ++ ++ ++ /* Determine divisor based on baud rate */ ++ baud = tty_get_baud_rate(tty); ++ ++ if (!baud) ++ { ++ /* pick a default, any default... */ ++ DPRINTK("%s\n","Picked default baud..."); ++ baud = 9600; ++ } ++ ++ ++ dbg("%s - baud rate = %d", __FUNCTION__, baud); ++ status = ATEN2011_send_cmd_write_baud_rate (ATEN2011_port, baud); ++ ++ /* Enable Interrupts */ ++ Data = 0x0c; ++ ATEN2011_set_Uart_Reg(port,INTERRUPT_ENABLE_REGISTER,Data); ++ ++ if(ATEN2011_port->read_urb->status!=-EINPROGRESS) ++ { ++ ATEN2011_port->read_urb->dev = serial->dev; ++ ++ status = usb_submit_urb(ATEN2011_port->read_urb, GFP_ATOMIC); ++ ++ if (status) ++ { ++ DPRINTK(" usb_submit_urb(read bulk) failed, status = %d", status); ++ } ++ } ++ //wake_up(&ATEN2011_port->delta_msr_wait); ++ //ATEN2011_port->delta_msr_cond=1; ++ DPRINTK("ATEN2011_change_port_settings ATEN2011_port->shadowLCR is End %x\n",ATEN2011_port->shadowLCR); ++ ++ return; ++} ++ ++ ++static int ATEN2011_calc_num_ports(struct usb_serial *serial) ++{ ++ ++ __u16 Data=0x00; ++ int ret =0; ++ int ATEN2011_2or4ports; ++ ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),\ ++ ATEN_RDREQ,ATEN_RD_RTYPE,0,GPIO_REGISTER,&Data,VENDOR_READ_LENGTH,ATEN_WDR_TIMEOUT); ++ ++ //printk("ATEN2011_calc_num_ports GPIO is %x\n",Data); ++ ++ ++/* ghostgum: here is where the problem appears to bet */ ++/* Which of the following are needed? */ ++/* Greg used the serial->type->num_ports=2 */ ++/* But the code in the ATEN2011_open relies on serial->num_ports=2 */ ++ if((Data&0x01)==0) ++ { ++ ATEN2011_2or4ports=2; ++ serial->type->num_ports=2; ++ serial->num_ports=2; ++ } ++ //else if(serial->interface->cur_altsetting->desc.bNumEndpoints == 9) ++ else ++ { ++ ATEN2011_2or4ports =4; ++ serial->type->num_ports=4; ++ serial->num_ports=4; ++ ++ } ++ ++ return ATEN2011_2or4ports; ++} ++ ++ ++/**************************************************************************** ++ * ATEN2011_startup ++ ****************************************************************************/ ++ ++static int ATEN2011_startup (struct usb_serial *serial) ++{ ++ struct ATENINTL_serial *ATEN2011_serial; ++ struct ATENINTL_port *ATEN2011_port; ++ struct usb_device *dev; ++ int i,status; ++ int minor; ++ ++ __u16 Data; ++ DPRINTK("%s \n"," ATEN2011_startup :entering.........."); ++ ++ if(!serial) ++ { ++ DPRINTK("%s\n","Invalid Handler"); ++ return -1; ++ } ++ ++ dev = serial->dev; ++ ++ DPRINTK("%s\n","Entering..."); ++ ++ /* create our private serial structure */ ++ ATEN2011_serial = kzalloc (sizeof(struct ATENINTL_serial), GFP_KERNEL); ++ if (ATEN2011_serial == NULL) ++ { ++ err("%s - Out of memory", __FUNCTION__); ++ return -ENOMEM; ++ } ++ ++ /* resetting the private structure field values to zero */ ++ memset (ATEN2011_serial, 0, sizeof(struct ATENINTL_serial)); ++ ++ ATEN2011_serial->serial = serial; ++ //initilize status polling flag to FALSE ++ ATEN2011_serial->status_polling_started = FALSE; ++ ++ ATEN2011_set_serial_private(serial,ATEN2011_serial); ++ ATEN2011_serial->ATEN2011_spectrum_2or4ports = ATEN2011_calc_num_ports(serial); ++ /* we set up the pointers to the endpoints in the ATEN2011_open * ++ * function, as the structures aren't created yet. */ ++ ++ /* set up port private structures */ ++ for (i = 0; i < serial->num_ports; ++i) ++ { ++ ATEN2011_port = kmalloc(sizeof(struct ATENINTL_port), GFP_KERNEL); ++ if (ATEN2011_port == NULL) ++ { ++ err("%s - Out of memory", __FUNCTION__); ++ ATEN2011_set_serial_private(serial,NULL); ++ kfree(ATEN2011_serial); ++ return -ENOMEM; ++ } ++ memset(ATEN2011_port, 0, sizeof(struct ATENINTL_port)); ++ ++ ++ /* Initialize all port interrupt end point to port 0 int endpoint * ++ * Our device has only one interrupt end point comman to all port */ ++ ++ ++ ++ // serial->port[i]->interrupt_in_endpointAddress = serial->port[0]->interrupt_in_endpointAddress; ++ ++ ++ ATEN2011_port->port = serial->port[i]; ++// ++ ATEN2011_set_port_private(serial->port[i],ATEN2011_port); ++ ++ ++ minor = serial->port[i] ->serial->minor; ++ if (minor == SERIAL_TTY_NO_MINOR) ++ minor = 0; ++ ATEN2011_port->port_num=((serial->port[i]->number - minor)+1); ++ ++ ATEN2011_port->AppNum = (((__u16)serial->port[i]->number - \ ++ (__u16)(minor))+1)<<8; ++ ++ if(ATEN2011_port->port_num ==1) ++ { ++ ATEN2011_port->SpRegOffset =0x0; ++ ATEN2011_port->ControlRegOffset =0x1; ++ ATEN2011_port->DcrRegOffset =0x4 ; ++ //ATEN2011_port->ClkSelectRegOffset = ; ++ } ++ else if((ATEN2011_port->port_num ==2)&&(ATEN2011_serial->ATEN2011_spectrum_2or4ports ==4)) ++ { ++ ATEN2011_port->SpRegOffset =0x8; ++ ATEN2011_port->ControlRegOffset =0x9; ++ ATEN2011_port->DcrRegOffset =0x16; ++ //ATEN2011_port->ClkSelectRegOffset = ; ++ } ++ else if((ATEN2011_port->port_num ==2)&&(ATEN2011_serial->ATEN2011_spectrum_2or4ports ==2)) ++ { ++ ATEN2011_port->SpRegOffset =0xa; ++ ATEN2011_port->ControlRegOffset =0xb; ++ ATEN2011_port->DcrRegOffset =0x19; ++ //ATEN2011_port->ClkSelectRegOffset = ; ++ } ++ else if((ATEN2011_port->port_num ==3)&&(ATEN2011_serial->ATEN2011_spectrum_2or4ports ==4)) ++ { ++ ATEN2011_port->SpRegOffset =0xa; ++ ATEN2011_port->ControlRegOffset =0xb; ++ ATEN2011_port->DcrRegOffset =0x19; ++ //ATEN2011_port->ClkSelectRegOffset = ; ++ } ++ else if((ATEN2011_port->port_num ==4)&&(ATEN2011_serial->ATEN2011_spectrum_2or4ports ==4)) ++ { ++ ATEN2011_port->SpRegOffset =0xc; ++ ATEN2011_port->ControlRegOffset =0xd; ++ ATEN2011_port->DcrRegOffset =0x1c ; ++ //ATEN2011_port->ClkSelectRegOffset = ; ++ } ++ ATEN2011_Dump_serial_port(ATEN2011_port); ++ ++ ATEN2011_set_port_private(serial->port[i],ATEN2011_port); ++ ++ ++ //enable rx_disable bit in control register ++ ++ status=ATEN2011_get_reg_sync(serial->port[i],ATEN2011_port->ControlRegOffset,&Data); ++ if(status<0) { ++ DPRINTK("Reading ControlReg failed status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("ControlReg Reading success val is %x, status%d\n",Data,status); ++ Data |= 0x08;//setting driver done bit ++ Data |= 0x04;//sp1_bit to have cts change reflect in modem status reg ++ ++ //Data |= 0x20; //rx_disable bit ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],ATEN2011_port->ControlRegOffset,Data); ++ if(status<0) { ++ DPRINTK("Writing ControlReg failed(rx_disable) status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("ControlReg Writing success(rx_disable) status%d\n",status); ++ ++ //Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 and 0x24 in DCR3 ++ Data = 0x01; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],(__u16)(ATEN2011_port->DcrRegOffset+0),Data); ++ if(status<0) { ++ DPRINTK("Writing DCR0 failed status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("DCR0 Writing success status%d\n",status); ++ ++ Data = 0x05; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],(__u16)(ATEN2011_port->DcrRegOffset+1),Data); ++ if(status<0) { ++ DPRINTK("Writing DCR1 failed status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("DCR1 Writing success status%d\n",status); ++ ++ Data = 0x24; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],(__u16)(ATEN2011_port->DcrRegOffset+2),Data); ++ if(status<0) { ++ DPRINTK("Writing DCR2 failed status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("DCR2 Writing success status%d\n",status); ++ ++ // write values in clkstart0x0 and clkmulti 0x20 ++ Data = 0x0; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],CLK_START_VALUE_REGISTER,Data); ++ if(status<0) { ++ DPRINTK("Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("CLK_START_VALUE_REGISTER Writing success status%d\n",status); ++ ++ ++ Data = 0x20; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],CLK_MULTI_REGISTER,Data); ++ if(status<0) { ++ DPRINTK("Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("CLK_MULTI_REGISTER Writing success status%d\n",status); ++ ++ ++ //write value 0x0 to scratchpad register ++ /* ++ if(RS485mode==0) ++ Data = 0xC0; ++ else ++ Data = 0x00; ++ status=0; ++ status=ATEN2011_set_Uart_Reg(serial->port[i],SCRATCH_PAD_REGISTER,Data); ++ if(status<0) { ++ DPRINTK("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); ++ break; ++ } ++ else DPRINTK("SCRATCH_PAD_REGISTER Writing success status%d\n",status); ++ */ ++ ++ /* ++ //Threshold Registers ++ if(ATEN2011_serial->ATEN2011_spectrum_2or4ports==4) ++ { ++ Data = 0x00; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ (__u16)(THRESHOLD_VAL_SP1_1+(__u16)ATEN2011_Thr_cnt),Data); ++ DPRINTK("THRESHOLD_VAL offset is%x\n", (__u16)(THRESHOLD_VAL_SP1_1+(__u16)ATEN2011_Thr_cnt)); ++ if(status<0) { ++ DPRINTK("Writing THRESHOLD_VAL failed status-0x%x\n",status); ++ break; ++ } ++ else DPRINTK("THRESHOLD_VAL Writing success status%d\n",status); ++ ATEN2011_Thr_cnt++; ++ ++ Data = 0x01; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ (__u16)(THRESHOLD_VAL_SP1_1+(__u16)ATEN2011_Thr_cnt),Data); ++ DPRINTK("THRESHOLD_VAL offsetis%x\n",(__u16)(THRESHOLD_VAL_SP1_1+(__u16)ATEN2011_Thr_cnt)); ++ if(status<0) { ++ DPRINTK("Writing THRESHOLD_VAL failed status-0x%x\n",status); ++ break; ++ } ++ else DPRINTK("THRESHOLD_VAL Writing success status%d\n",status); ++ ATEN2011_Thr_cnt++; ++ } ++ ++ else ++ { ++ ++ if(ATEN2011_port->port_num==1) ++ { ++ Data = 0x00; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ 0x3f,Data); ++ DPRINTK("THRESHOLD_VAL offset is 0x3f\n"); ++ if(status<0) { ++ DPRINTK("Writing THRESHOLD_VAL failed status-0x%x\n",status); ++ break; ++ } ++ Data = 0x01; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ 0x40,Data); ++ DPRINTK("THRESHOLD_VAL offset is 0x40\n"); ++ if(status<0) { ++ DPRINTK("Writing THRESHOLD_VAL failed status-0x%x\n",status); ++ break; ++ ++ } ++ } ++ else ++ { ++ Data = 0x00; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ 0x43,Data); ++ DPRINTK("THRESHOLD_VAL offset is 0x43\n"); ++ if(status<0) { ++ DPRINTK("Writing THRESHOLD_VAL failed status-0x%x\n",status); ++ break; ++ } ++ Data = 0x01; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ 0x44,Data); ++ DPRINTK("THRESHOLD_VAL offset is 0x44\n"); ++ if(status<0) { ++ DPRINTK("Writing THRESHOLD_VAL failed status-0x%x\n",status); ++ break; ++ ++ } ++ ++ ++ } ++ ++ } ++ */ ++ //Zero Length flag register ++ if((ATEN2011_port->port_num != 1)&&(ATEN2011_serial->ATEN2011_spectrum_2or4ports==2 )) ++ { ++ ++ Data = 0xff; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ (__u16)(ZLP_REG1+((__u16)ATEN2011_port->port_num)),Data); ++ DPRINTK("ZLIP offset%x\n",(__u16)(ZLP_REG1+((__u16)ATEN2011_port->port_num))); ++ if(status<0) { ++ DPRINTK("Writing ZLP_REG%d failed status-0x%x\n",i+2,status); ++ break; ++ } ++ else DPRINTK("ZLP_REG%d Writing success status%d\n",i+2,status); ++ } ++ else ++ { ++ Data = 0xff; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[i],\ ++ (__u16)(ZLP_REG1+((__u16)ATEN2011_port->port_num)-0x1),Data); ++ DPRINTK("ZLIP offset%x\n",(__u16)(ZLP_REG1+((__u16)ATEN2011_port->port_num)-0x1)); ++ if(status<0) { ++ DPRINTK("Writing ZLP_REG%d failed status-0x%x\n",i+1,status); ++ break; ++ } ++ else DPRINTK("ZLP_REG%d Writing success status%d\n",i+1,status); ++ ++ ++ } ++ ATEN2011_port->control_urb = usb_alloc_urb(0,GFP_ATOMIC); ++ ATEN2011_port->ctrl_buf = kmalloc(16,GFP_KERNEL); ++ ++ ++ } ++ ++ ++ ATEN2011_Thr_cnt=0; ++ //Zero Length flag enable ++ Data = 0x0f; ++ status=0; ++ status=ATEN2011_set_reg_sync(serial->port[0],ZLP_REG5,Data); ++ if(status<0) { ++ DPRINTK("Writing ZLP_REG5 failed status-0x%x\n",status); ++ return -1; ++ } ++ else DPRINTK("ZLP_REG5 Writing success status%d\n",status); ++ ++ /* setting configuration feature to one */ ++ usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8)0x03, 0x00,0x01,0x00, 0x00, 0x00, 5*HZ); ++ ATEN2011_Thr_cnt =0 ; ++ return 0; ++} ++ ++ ++ ++/**************************************************************************** ++ * ATEN2011_shutdown ++ * This function is called whenever the device is removed from the usb bus. ++ ****************************************************************************/ ++ ++static void ATEN2011_shutdown (struct usb_serial *serial) ++{ ++ int i; ++ struct ATENINTL_port *ATEN2011_port; ++ DPRINTK("%s \n"," shutdown :entering.........."); ++ ++/* MATRIX */ ++ //ThreadState = 1; ++/* MATRIX */ ++ ++ if(!serial) ++ { ++ DPRINTK("%s","Invalid Handler \n"); ++ return; ++ } ++ ++ /* check for the ports to be closed,close the ports and disconnect */ ++ ++ /* free private structure allocated for serial port * ++ * stop reads and writes on all ports */ ++ ++ for (i=0; i < serial->num_ports; ++i) ++ { ++ ATEN2011_port = ATEN2011_get_port_private(serial->port[i]); ++ kfree(ATEN2011_port->ctrl_buf); ++ usb_kill_urb(ATEN2011_port->control_urb); ++ kfree(ATEN2011_port); ++ ATEN2011_set_port_private(serial->port[i],NULL); ++ } ++ ++ /* free private structure allocated for serial device */ ++ ++ kfree(ATEN2011_get_serial_private(serial)); ++ ATEN2011_set_serial_private(serial,NULL); ++ ++ DPRINTK("%s\n","Thank u :: "); ++ ++} ++ ++ ++/* Inline functions to check the sanity of a pointer that is passed to us */ ++static int ATEN2011_serial_paranoia_check (struct usb_serial *serial, const char *function) ++{ ++ if (!serial) { ++ dbg("%s - serial == NULL", function); ++ return -1; ++ } ++// if (serial->magic != USB_SERIAL_MAGIC) { ++// dbg("%s - bad magic number for serial", function); ++// return -1; ++// } ++ if (!serial->type) { ++ dbg("%s - serial->type == NULL!", function); ++ return -1; ++ } ++ ++ return 0; ++} ++static int ATEN2011_port_paranoia_check (struct usb_serial_port *port, const char *function) ++{ ++ if (!port) { ++ dbg("%s - port == NULL", function); ++ return -1; ++ } ++// if (port->magic != USB_SERIAL_PORT_MAGIC) { ++// dbg("%s - bad magic number for port", function); ++// return -1; ++// } ++ if (!port->serial) { ++ dbg("%s - port->serial == NULL", function); ++ return -1; ++ } ++ ++ return 0; ++} ++static struct usb_serial* ATEN2011_get_usb_serial (struct usb_serial_port *port, const char *function) { ++ /* if no port was specified, or it fails a paranoia check */ ++ if (!port || ++ ATEN2011_port_paranoia_check (port, function) || ++ ATEN2011_serial_paranoia_check (port->serial, function)) { ++ /* then say that we don't have a valid usb_serial thing, which will * end up genrating -ENODEV return values */ ++ return NULL; ++ } ++ ++ return port->serial; ++} ++ ++ ++ ++/**************************************************************************** ++ * ATENINTL2011_init ++ * This is called by the module subsystem, or on startup to initialize us ++ ****************************************************************************/ ++ int __init ATENINTL2011_init(void) ++{ ++ int retval; ++ ++ DPRINTK("%s \n"," ATEN2011_init :entering.........."); ++ ++ /* Register with the usb serial */ ++ retval = usb_serial_register (&ATENINTL2011_4port_device); ++ ++ if(retval) ++ goto failed_port_device_register; ++ ++/* info(DRIVER_DESC " " DRIVER_VERSION); */ ++ printk(KERN_INFO KBUILD_MODNAME ":" ++ DRIVER_DESC " " DRIVER_VERSION "\n"); ++ ++ ++ /* Register with the usb */ ++ retval = usb_register(&io_driver); ++ ++ if (retval) ++ goto failed_usb_register; ++ ++ if(retval == 0) ++ { ++ DPRINTK("%s\n","Leaving..."); ++ return 0; ++ } ++ ++ ++failed_usb_register: ++ usb_serial_deregister(&ATENINTL2011_4port_device); ++ ++failed_port_device_register: ++ ++ return retval; ++} ++ ++/**************************************************************************** ++ * ATENINTL2011_exit ++ * Called when the driver is about to be unloaded. ++ ****************************************************************************/ ++void __exit ATENINTL2011_exit (void) ++{ ++ ++ DPRINTK("%s \n"," ATEN2011_exit :entering.........."); ++ ++ usb_deregister (&io_driver); ++ ++ usb_serial_deregister (&ATENINTL2011_4port_device); ++ ++ DPRINTK("%s\n","End..."); ++} ++ ++module_init(ATENINTL2011_init); ++module_exit(ATENINTL2011_exit); ++ ++/* Module information */ ++MODULE_DESCRIPTION( DRIVER_DESC ); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM_DESC(debug, "Debug enabled or not"); ++ +--- /dev/null ++++ b/drivers/staging/uc2322/aten2011.h +@@ -0,0 +1,383 @@ ++/* ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++#if !defined(_ATEN_CIP_H_) ++#define _ATEN_CIP_H_ ++ ++ ++#define MAX_RS232_PORTS 2 /* Max # of RS-232 ports per device */ ++ ++#include <linux/version.h> ++ ++/* ++ * All typedef goes here ++ */ ++ ++ ++/* typedefs that the insideout headers need */ ++ ++#ifndef TRUE ++ #define TRUE (1) ++#endif ++ ++#ifndef FALSE ++ #define FALSE (0) ++#endif ++ ++#ifndef LOW8 ++ #define LOW8(val) ((unsigned char)(val & 0xff)) ++#endif ++ ++#ifndef HIGH8 ++ #define HIGH8(val) ((unsigned char)((val & 0xff00) >> 8)) ++#endif ++ ++#ifndef NUM_ENTRIES ++ #define NUM_ENTRIES(x) (sizeof(x)/sizeof((x)[0])) ++#endif ++ ++#define MAX_SERIALNUMBER_LEN 12 ++ ++/* The following table is used to map the USBx port number to ++ * the device serial number (or physical USB path), */ ++ ++#define MAX_ATENPORTS 2 ++#define MAX_NAME_LEN 64 ++ ++#define RAID_REG1 0x30 ++#define RAID_REG2 0x31 ++ ++#define ZLP_REG1 0x3A //Zero_Flag_Reg1 58 ++#define ZLP_REG2 0x3B //Zero_Flag_Reg2 59 ++#define ZLP_REG3 0x3C //Zero_Flag_Reg3 60 ++#define ZLP_REG4 0x3D //Zero_Flag_Reg4 61 ++#define ZLP_REG5 0x3E //Zero_Flag_Reg5 62 ++ ++#define THRESHOLD_VAL_SP1_1 0x3F ++#define THRESHOLD_VAL_SP1_2 0x40 ++#define THRESHOLD_VAL_SP2_1 0x41 ++#define THRESHOLD_VAL_SP2_2 0x42 ++ ++#define THRESHOLD_VAL_SP3_1 0x43 ++#define THRESHOLD_VAL_SP3_2 0x44 ++#define THRESHOLD_VAL_SP4_1 0x45 ++#define THRESHOLD_VAL_SP4_2 0x46 ++ ++ ++/* For higher baud Rates use TIOCEXBAUD */ ++#define TIOCEXBAUD 0x5462 ++ ++#define BAUD_1152 0 /* 115200bps * 1 */ ++#define BAUD_2304 1 /* 230400bps * 2 */ ++#define BAUD_4032 2 /* 403200bps * 3.5 */ ++#define BAUD_4608 3 /* 460800bps * 4 */ ++#define BAUD_8064 4 /* 806400bps * 7 */ ++#define BAUD_9216 5 /* 921600bps * 8 */ ++ ++#define CHASE_TIMEOUT (5*HZ) /* 5 seconds */ ++#define OPEN_TIMEOUT (5*HZ) /* 5 seconds */ ++#define COMMAND_TIMEOUT (5*HZ) /* 5 seconds */ ++ ++#ifndef SERIAL_MAGIC ++ #define SERIAL_MAGIC 0x6702 ++#endif ++ ++#define PORT_MAGIC 0x7301 ++ ++ ++ ++/* vendor id and device id defines */ ++ ++#define USB_VENDOR_ID_ATENINTL 0x0557 ++#define ATENINTL_DEVICE_ID_2011 0x2011 ++#define ATENINTL_DEVICE_ID_7820 0x7820 ++ ++/* Product information read from the ATENINTL. Provided for later upgrade */ ++ ++/* Interrupt Rotinue Defines */ ++ ++#define SERIAL_IIR_RLS 0x06 ++#define SERIAL_IIR_RDA 0x04 ++#define SERIAL_IIR_CTI 0x0c ++#define SERIAL_IIR_THR 0x02 ++#define SERIAL_IIR_MS 0x00 ++ ++/* ++ * Emulation of the bit mask on the LINE STATUS REGISTER. ++ */ ++#define SERIAL_LSR_DR 0x0001 ++#define SERIAL_LSR_OE 0x0002 ++#define SERIAL_LSR_PE 0x0004 ++#define SERIAL_LSR_FE 0x0008 ++#define SERIAL_LSR_BI 0x0010 ++#define SERIAL_LSR_THRE 0x0020 ++#define SERIAL_LSR_TEMT 0x0040 ++#define SERIAL_LSR_FIFOERR 0x0080 ++ ++//MSR bit defines(place holders) ++#define ATEN_MSR_CTS 0x01 ++#define ATEN_MSR_DSR 0x02 ++#define ATEN_MSR_RI 0x04 ++#define ATEN_MSR_CD 0x08 ++#define ATEN_MSR_DELTA_CTS 0x10 ++#define ATEN_MSR_DELTA_DSR 0x20 ++#define ATEN_MSR_DELTA_RI 0x40 ++#define ATEN_MSR_DELTA_CD 0x80 ++ ++// Serial Port register Address ++#define RECEIVE_BUFFER_REGISTER ((__u16)(0x00)) ++#define TRANSMIT_HOLDING_REGISTER ((__u16)(0x00)) ++#define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01)) ++#define INTERRUPT_IDENT_REGISTER ((__u16)(0x02)) ++#define FIFO_CONTROL_REGISTER ((__u16)(0x02)) ++#define LINE_CONTROL_REGISTER ((__u16)(0x03)) ++#define MODEM_CONTROL_REGISTER ((__u16)(0x04)) ++#define LINE_STATUS_REGISTER ((__u16)(0x05)) ++#define MODEM_STATUS_REGISTER ((__u16)(0x06)) ++#define SCRATCH_PAD_REGISTER ((__u16)(0x07)) ++#define DIVISOR_LATCH_LSB ((__u16)(0x00)) ++#define DIVISOR_LATCH_MSB ((__u16)(0x01)) ++ ++#define SP_REGISTER_BASE ((__u16)(0x08)) ++#define CONTROL_REGISTER_BASE ((__u16)(0x09)) ++#define DCR_REGISTER_BASE ((__u16)(0x16)) ++ ++#define SP1_REGISTER ((__u16)(0x00)) ++#define CONTROL1_REGISTER ((__u16)(0x01)) ++#define CLK_MULTI_REGISTER ((__u16)(0x02)) ++#define CLK_START_VALUE_REGISTER ((__u16)(0x03)) ++#define DCR1_REGISTER ((__u16)(0x04)) ++#define GPIO_REGISTER ((__u16)(0x07)) ++ ++#define CLOCK_SELECT_REG1 ((__u16)(0x13)) ++#define CLOCK_SELECT_REG2 ((__u16)(0x14)) ++ ++#define SERIAL_LCR_DLAB ((__u16)(0x0080)) ++ ++/* ++ * URB POOL related defines ++ */ ++#define NUM_URBS 16 /* URB Count */ ++#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ ++ ++struct ATENINTL_product_info ++{ ++ __u16 ProductId; /* Product Identifier */ ++ __u8 NumPorts; /* Number of ports on ATENINTL */ ++ __u8 ProdInfoVer; /* What version of structure is this? */ ++ ++ __u32 IsServer :1; /* Set if Server */ ++ __u32 IsRS232 :1; /* Set if RS-232 ports exist */ ++ __u32 IsRS422 :1; /* Set if RS-422 ports exist */ ++ __u32 IsRS485 :1; /* Set if RS-485 ports exist */ ++ __u32 IsReserved :28; /* Reserved for later expansion */ ++ ++ __u8 CpuRev; /* CPU revision level (chg only if s/w visible) */ ++ __u8 BoardRev; /* PCB revision level (chg only if s/w visible) */ ++ ++ __u8 ManufactureDescDate[3]; /* MM/DD/YY when descriptor template was compiled */ ++ __u8 Unused1[1]; /* Available */ ++}; ++ ++// different USB-serial Adapter's ID's table ++static struct usb_device_id ATENINTL_port_id_table [] = { ++ { USB_DEVICE(USB_VENDOR_ID_ATENINTL,ATENINTL_DEVICE_ID_2011) }, ++ { USB_DEVICE(USB_VENDOR_ID_ATENINTL,ATENINTL_DEVICE_ID_7820) }, ++ { } /* terminating entry */ ++}; ++ ++static __devinitdata struct usb_device_id id_table_combined [] = { ++ { USB_DEVICE(USB_VENDOR_ID_ATENINTL,ATENINTL_DEVICE_ID_2011) }, ++ { USB_DEVICE(USB_VENDOR_ID_ATENINTL,ATENINTL_DEVICE_ID_7820) }, ++ { } /* terminating entry */ ++}; ++ ++MODULE_DEVICE_TABLE (usb, id_table_combined); ++ ++/* This structure holds all of the local port information */ ++struct ATENINTL_port ++{ ++ int port_num; /*Actual port number in the device(1,2,etc)*/ ++ __u8 bulk_out_endpoint; /* the bulk out endpoint handle */ ++ unsigned char *bulk_out_buffer; /* buffer used for the bulk out endpoint */ ++ struct urb *write_urb; /* write URB for this port */ ++ __u8 bulk_in_endpoint; /* the bulk in endpoint handle */ ++ unsigned char *bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ ++ struct urb *read_urb; /* read URB for this port */ ++ __s16 rxBytesAvail;/*the number of bytes that we need to read from this device */ ++ __s16 rxBytesRemaining; /* the number of port bytes left to read */ ++ char write_in_progress; /* TRUE while a write URB is outstanding */ ++ __u8 shadowLCR; /* last LCR value received */ ++ __u8 shadowMCR; /* last MCR value received */ ++ __u8 shadowMSR; /* last MSR value received */ ++ __u8 shadowLSR; /* last LSR value received */ ++ __u8 shadowXonChar; /* last value set as XON char in ATENINTL */ ++ __u8 shadowXoffChar; /* last value set as XOFF char in ATENINTL */ ++ __u8 validDataMask; ++ __u32 baudRate; ++ char open; ++ char openPending; ++ char commandPending; ++ char closePending; ++ char chaseResponsePending; ++ wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ ++ wait_queue_head_t wait_open; /* for handling sleeping while waiting for open to finish */ ++ wait_queue_head_t wait_command; /* for handling sleeping while waiting for command to finish */ ++ wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */ ++ int delta_msr_cond; ++ struct async_icount icount; ++ struct usb_serial_port *port; /* loop back to the owner of this object */ ++ /*Offsets*/ ++ __u16 AppNum; ++ __u8 SpRegOffset; ++ __u8 ControlRegOffset; ++ __u8 DcrRegOffset; ++ __u8 ClkSelectRegOffset; ++ //for processing control URBS in interrupt context ++ struct urb *control_urb; ++ // __le16 rx_creg; ++ char *ctrl_buf; ++ int MsrLsr; ++ ++ struct urb *write_urb_pool[NUM_URBS]; ++ /* we pass a pointer to this as the arguement sent to cypress_set_termios old_termios */ ++ struct ktermios tmp_termios; /* stores the old termios settings */ ++ spinlock_t lock; /* private lock */ ++}; ++ ++ ++/* This structure holds all of the individual serial device information */ ++struct ATENINTL_serial ++{ ++ char name[MAX_NAME_LEN+1]; /* string name of this device */ ++ struct ATENINTL_product_info product_info; /* Product Info */ ++ __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ ++ unsigned char *interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ ++ struct urb * interrupt_read_urb; /* our interrupt urb */ ++ __u8 bulk_in_endpoint; /* the bulk in endpoint handle */ ++ unsigned char *bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ ++ struct urb *read_urb; /* our bulk read urb */ ++ __u8 bulk_out_endpoint; /* the bulk out endpoint handle */ ++ __s16 rxBytesAvail; /* the number of bytes that we need to read from this device */ ++ __u8 rxPort; /* the port that we are currently receiving data for */ ++ __u8 rxStatusCode; /* the receive status code */ ++ __u8 rxStatusParam; /* the receive status paramater */ ++ __s16 rxBytesRemaining; /* the number of port bytes left to read */ ++ struct usb_serial *serial; /* loop back to the owner of this object */ ++ int ATEN2011_spectrum_2or4ports; //this says the number of ports in the device ++ // Indicates about the no.of opened ports of an individual USB-serial adapater. ++ unsigned int NoOfOpenPorts; ++ // a flag for Status endpoint polling ++ unsigned char status_polling_started; ++}; ++ ++/* baud rate information */ ++struct ATEN2011_divisor_table_entry ++{ ++ __u32 BaudRate; ++ __u16 Divisor; ++}; ++ ++/* Define table of divisors for ATENINTL 2011 hardware * ++ * These assume a 3.6864MHz crystal, the standard /16, and * ++ * MCR.7 = 0. */ ++#ifdef NOTATEN2011 ++static struct ATEN2011_divisor_table_entry ATEN2011_divisor_table[] = { ++ { 50, 2304}, ++ { 110, 1047}, /* 2094.545455 => 230450 => .0217 % over */ ++ { 134, 857}, /* 1713.011152 => 230398.5 => .00065% under */ ++ { 150, 768}, ++ { 300, 384}, ++ { 600, 192}, ++ { 1200, 96}, ++ { 1800, 64}, ++ { 2400, 48}, ++ { 4800, 24}, ++ { 7200, 16}, ++ { 9600, 12}, ++ { 19200, 6}, ++ { 38400, 3}, ++ { 57600, 2}, ++ { 115200, 1}, ++}; ++#endif ++ ++/* local function prototypes */ ++/* function prototypes for all URB callbacks */ ++static void ATEN2011_interrupt_callback(struct urb *urb); ++static void ATEN2011_bulk_in_callback(struct urb *urb); ++static void ATEN2011_bulk_out_data_callback(struct urb *urb); ++static void ATEN2011_control_callback(struct urb *urb); ++static int ATEN2011_get_reg(struct ATENINTL_port *ATEN,__u16 Wval, __u16 reg, __u16 * val); ++int handle_newMsr(struct ATENINTL_port *port,__u8 newMsr); ++int handle_newLsr(struct ATENINTL_port *port,__u8 newLsr); ++/* function prototypes for the usbserial callbacks */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static int ATEN2011_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); ++static void ATEN2011_close(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); ++static int ATEN2011_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count); ++static int ATEN2011_write_room(struct tty_struct *tty); ++static int ATEN2011_chars_in_buffer(struct tty_struct *tty); ++static void ATEN2011_throttle(struct tty_struct *tty); ++static void ATEN2011_unthrottle(struct tty_struct *tty); ++static void ATEN2011_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); ++static int ATEN2011_tiocmset(struct tty_struct *tty, struct file *file, ++ unsigned int set, unsigned int clear); ++static int ATEN2011_tiocmget(struct tty_struct *tty, struct file *file); ++static int ATEN2011_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); ++static void ATEN2011_break(struct tty_struct *tty, int break_state); ++#else ++static int ATEN2011_open(struct usb_serial_port *port, struct file *filp); ++static void ATEN2011_close(struct usb_serial_port *port, struct file *filp); ++static int ATEN2011_write(struct usb_serial_port *port, const unsigned char *data, int count); ++static int ATEN2011_write_room(struct usb_serial_port *port); ++static int ATEN2011_chars_in_buffer(struct usb_serial_port *port); ++static void ATEN2011_throttle(struct usb_serial_port *port); ++static void ATEN2011_unthrottle(struct usb_serial_port *port); ++static void ATEN2011_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); ++static int ATEN2011_tiocmset(struct usb_serial_port *port, struct file *file, ++ unsigned int set, unsigned int clear); ++static int ATEN2011_tiocmget(struct usb_serial_port *port, struct file *file); ++static int ATEN2011_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); ++static void ATEN2011_break(struct usb_serial_port *port, int break_state); ++#endif ++ ++//static void ATEN2011_break_ctl(struct usb_serial_port *port, int break_state ); ++ ++static int ATEN2011_startup(struct usb_serial *serial); ++static void ATEN2011_shutdown(struct usb_serial *serial); ++//static int ATEN2011_serial_probe(struct usb_serial *serial, const struct usb_device_id *id); ++static int ATEN2011_calc_num_ports(struct usb_serial *serial); ++ ++/* function prototypes for all of our local functions */ ++static int ATEN2011_calc_baud_rate_divisor(int baudRate, int *divisor,__u16 *clk_sel_val); ++static int ATEN2011_send_cmd_write_baud_rate(struct ATENINTL_port *ATEN2011_port, int baudRate); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++static void ATEN2011_change_port_settings(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port, struct ktermios *old_termios); ++static void ATEN2011_block_until_chase_response(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port); ++static void ATEN2011_block_until_tx_empty(struct tty_struct *tty, struct ATENINTL_port *ATEN2011_port); ++#else ++static void ATEN2011_change_port_settings(struct ATENINTL_port *ATEN2011_port, struct ktermios *old_termios); ++static void ATEN2011_block_until_chase_response(struct ATENINTL_port *ATEN2011_port); ++static void ATEN2011_block_until_tx_empty(struct ATENINTL_port *ATEN2011_port); ++#endif ++ ++int __init ATENINTL2011_init(void); ++void __exit ATENINTL2011_exit(void); ++ ++#endif +--- /dev/null ++++ b/drivers/staging/uc2322/Kconfig +@@ -0,0 +1,10 @@ ++config USB_SERIAL_ATEN2011 ++ tristate "ATEN 2011 USB to serial device support" ++ depends on USB_SERIAL ++ default N ++ ---help--- ++ Say Y here if you want to use a ATEN 2011 dual port USB to serial ++ adapter. ++ ++ To compile this driver as a module, choose M here: the module will be ++ called aten2011. +--- /dev/null ++++ b/drivers/staging/uc2322/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_USB_SERIAL_ATEN2011) += aten2011.o +--- /dev/null ++++ b/drivers/staging/uc2322/TODO +@@ -0,0 +1,8 @@ ++TODO: ++ - checkpatch.pl cleanups ++ - sparse cleanups ++ - remove dead and useless code (auditing the tty ioctls to ++ verify that they really are correct and needed.) ++ ++Please send any patches to Greg Kroah-Hartman <greg@kroah.com> and ++Russell Lang <gsview@ghostgum.com.au>. diff --git a/staging/staging-add-b3dfg-driver.patch b/staging/staging-add-b3dfg-driver.patch new file mode 100644 index 00000000000000..bc16553ccf9828 --- /dev/null +++ b/staging/staging-add-b3dfg-driver.patch @@ -0,0 +1,1070 @@ +From a14d86995e4a1f5ce4de8801b35ca4b5d24723bd Mon Sep 17 00:00:00 2001 +From: Daniel Drake <dsd@gentoo.org> +Date: Wed, 28 Jan 2009 09:38:07 -0500 +Subject: Staging: add b3dfg driver + +From: Daniel Drake <ddrake@brontes3d.com> + +Initial b3dfg driver development as preformed by Daniel Drake. All +basic functionality is completed. + +Signed-off-by: Justin Bronder <jsbronder@brontes3d.com> +Cc: Daniel Drake <ddrake@brontes3d.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/staging/b3dfg/b3dfg.c | 1049 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1049 insertions(+) + +--- /dev/null ++++ b/drivers/staging/b3dfg/b3dfg.c +@@ -0,0 +1,1049 @@ ++ /* ++ * Brontes PCI frame grabber driver ++ * ++ * Copyright (C) 2008 3M Company ++ * Contact: Daniel Drake <ddrake@brontes3d.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/interrupt.h> ++#include <linux/spinlock.h> ++#include <linux/ioctl.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/types.h> ++#include <linux/cdev.h> ++#include <linux/list.h> ++#include <linux/poll.h> ++#include <linux/wait.h> ++#include <linux/mm.h> ++#include <linux/version.h> ++#include <linux/mutex.h> ++ ++#include <asm/atomic.h> ++#include <asm/uaccess.h> ++ ++/* TODO: ++ * locking ++ * queue/wait buffer presents filltime results for each frame? ++ * counting of dropped frames ++ * review endianness ++ */ ++ ++#ifdef DEBUG ++#define dbg(msg...) printk(msg) ++#else ++#define dbg(msg...) ++#endif ++ ++#define DRIVER_NAME "b3dfg" ++#define PFX DRIVER_NAME ": " ++#define B3DFG_MAX_DEVS 4 ++#define B3DFG_NR_TRIPLET_BUFFERS 4 ++#define B3DFG_NR_FRAME_BUFFERS (B3DFG_NR_TRIPLET_BUFFERS * 3) ++#define B3DFG_FRAMES_PER_BUFFER 3 ++ ++#define B3DFG_BAR_REGS 0 ++#define B3DFG_REGS_LENGTH 0x10000 ++ ++#define B3DFG_IOC_MAGIC 0xb3 /* dfg :-) */ ++#define B3DFG_IOCGFRMSZ _IOR(B3DFG_IOC_MAGIC, 1, int) ++#define B3DFG_IOCTNUMBUFS _IO(B3DFG_IOC_MAGIC, 2) ++#define B3DFG_IOCTTRANS _IO(B3DFG_IOC_MAGIC, 3) ++#define B3DFG_IOCTQUEUEBUF _IO(B3DFG_IOC_MAGIC, 4) ++#define B3DFG_IOCTPOLLBUF _IOWR(B3DFG_IOC_MAGIC, 5, struct b3dfg_poll) ++#define B3DFG_IOCTWAITBUF _IOWR(B3DFG_IOC_MAGIC, 6, struct b3dfg_wait) ++#define B3DFG_IOCGWANDSTAT _IOR(B3DFG_IOC_MAGIC, 7, int) ++ ++enum { ++ /* number of 4kb pages per frame */ ++ B3D_REG_FRM_SIZE = 0x0, ++ ++ /* bit 0: set to enable interrupts */ ++ B3D_REG_HW_CTRL = 0x4, ++ ++ /* bit 0-1 - 1-based ID of next pending frame transfer (0 = nothing pending) ++ * bit 2 indicates the previous DMA transfer has completed ++ * bit 8:15 - counter of number of discarded triplets */ ++ B3D_REG_DMA_STS = 0x8, ++ ++ /* bit 0: wand status (1 = present, 0 = disconnected) */ ++ B3D_REG_WAND_STS = 0xc, ++ ++ /* bus address for DMA transfers. lower 2 bits must be zero because DMA ++ * works with 32 bit word size. */ ++ B3D_REG_EC220_DMA_ADDR = 0x8000, ++ ++ /* bit 20:0 - number of 32 bit words to be transferred ++ * bit 21:31 - reserved */ ++ B3D_REG_EC220_TRF_SIZE = 0x8004, ++ ++ /* bit 0 - error bit ++ * bit 1 - interrupt bit (set to generate interrupt at end of transfer) ++ * bit 2 - start bit (set to start transfer) ++ * bit 3 - direction (0 = DMA_TO_DEVICE, 1 = DMA_FROM_DEVICE ++ * bit 4:31 - reserved */ ++ B3D_REG_EC220_DMA_STS = 0x8008, ++}; ++ ++enum b3dfg_buffer_state { ++ B3DFG_BUFFER_POLLED = 0, ++ B3DFG_BUFFER_PENDING, ++ B3DFG_BUFFER_POPULATED, ++}; ++ ++struct b3dfg_buffer { ++ unsigned char *frame[B3DFG_FRAMES_PER_BUFFER]; ++ u8 state; ++ struct list_head list; ++}; ++ ++struct b3dfg_dev { ++ /* no protection needed: all finalized at initialization time */ ++ struct pci_dev *pdev; ++ struct cdev chardev; ++ struct class_device *classdev; ++ void __iomem *regs; ++ unsigned int frame_size; ++ ++ /* we want to serialize some ioctl operations */ ++ struct mutex ioctl_mutex; ++ ++ /* preallocated frame buffers */ ++ unsigned char *frame_buffer[B3DFG_NR_FRAME_BUFFERS]; ++ ++ /* buffers_lock protects num_buffers, buffers, buffer_queue */ ++ spinlock_t buffer_lock; ++ int num_buffers; ++ struct b3dfg_buffer *buffers; ++ struct list_head buffer_queue; ++ ++ wait_queue_head_t buffer_waitqueue; ++ ++ atomic_t mapping_count; ++ ++ spinlock_t triplets_dropped_lock; ++ unsigned int triplets_dropped; ++ ++ /* FIXME: we need some locking here. this could be accessed in parallel ++ * from the queue_buffer ioctl and the interrupt handler. */ ++ int cur_dma_frame_idx; ++ dma_addr_t cur_dma_frame_addr; ++ ++ unsigned int transmission_enabled:1; ++ unsigned int triplet_ready:1; ++}; ++ ++static u8 b3dfg_devices[B3DFG_MAX_DEVS]; ++ ++static struct class *b3dfg_class; ++static dev_t b3dfg_devt; ++ ++static const struct pci_device_id b3dfg_ids[] __devinitdata = { ++ { PCI_DEVICE(0x0b3d, 0x0001) }, ++ ++ /* FIXME: remove this ID once all boards have been moved to 0xb3d. ++ * this is Eureka's vendor ID that we borrowed before we bought our own. */ ++ { PCI_DEVICE(0x1901, 0x0001) }, ++ { }, ++}; ++ ++/***** user-visible types *****/ ++ ++struct b3dfg_poll { ++ int buffer_idx; ++ unsigned int triplets_dropped; ++}; ++ ++struct b3dfg_wait { ++ int buffer_idx; ++ unsigned int timeout; ++ unsigned int triplets_dropped; ++}; ++ ++/**** register I/O ****/ ++ ++static u32 b3dfg_read32(struct b3dfg_dev *fgdev, u16 reg) ++{ ++ return ioread32(fgdev->regs + reg); ++} ++ ++static void b3dfg_write32(struct b3dfg_dev *fgdev, u16 reg, u32 value) ++{ ++ iowrite32(value, fgdev->regs + reg); ++} ++ ++/**** buffer management ****/ ++ ++/* program EC220 for transfer of a specific frame */ ++static void setup_frame_transfer(struct b3dfg_dev *fgdev, ++ struct b3dfg_buffer *buf, int frame, int acknowledge) ++{ ++ unsigned char *frm_addr; ++ dma_addr_t frm_addr_dma; ++ struct device *dev = &fgdev->pdev->dev; ++ unsigned int frame_size = fgdev->frame_size; ++ unsigned char dma_sts = 0xd; ++ ++ frm_addr = buf->frame[frame]; ++ frm_addr_dma = dma_map_single(dev, frm_addr, frame_size, DMA_FROM_DEVICE); ++ fgdev->cur_dma_frame_addr = frm_addr_dma; ++ fgdev->cur_dma_frame_idx = frame; ++ ++ b3dfg_write32(fgdev, B3D_REG_EC220_DMA_ADDR, cpu_to_le32(frm_addr_dma)); ++ b3dfg_write32(fgdev, B3D_REG_EC220_TRF_SIZE, cpu_to_le32(frame_size >> 2)); ++ ++ if (likely(acknowledge)) ++ dma_sts |= 0x2; ++ b3dfg_write32(fgdev, B3D_REG_EC220_DMA_STS, 0xf); ++} ++ ++/* retrieve a buffer pointer from a buffer index. also checks that the ++ * requested buffer actually exists. buffer_lock should be held by caller */ ++static inline struct b3dfg_buffer *buffer_from_idx(struct b3dfg_dev *fgdev, ++ int idx) ++{ ++ if (unlikely(idx >= fgdev->num_buffers)) ++ return NULL; ++ return &fgdev->buffers[idx]; ++} ++ ++/* caller should hold buffer lock */ ++static void free_all_buffers(struct b3dfg_dev *fgdev) ++{ ++ kfree(fgdev->buffers); ++ fgdev->buffers = NULL; ++ fgdev->num_buffers = 0; ++} ++ ++static void dequeue_all_buffers(struct b3dfg_dev *fgdev) ++{ ++ int i; ++ for (i = 0; i < fgdev->num_buffers; i++) { ++ struct b3dfg_buffer *buf = &fgdev->buffers[i]; ++ buf->state = B3DFG_BUFFER_POLLED; ++ list_del_init(&buf->list); ++ } ++} ++ ++/* initialize a buffer: allocate its frames, set default values */ ++static void init_buffer(struct b3dfg_dev *fgdev, struct b3dfg_buffer *buf, ++ int idx) ++{ ++ unsigned int addr_offset = idx * B3DFG_FRAMES_PER_BUFFER; ++ int i; ++ ++ memset(buf, 0, sizeof(struct b3dfg_buffer)); ++ for (i = 0; i < B3DFG_FRAMES_PER_BUFFER; i++) ++ buf->frame[i] = fgdev->frame_buffer[addr_offset + i]; ++ ++ INIT_LIST_HEAD(&buf->list); ++} ++ ++/* adjust the number of buffers, growing or shrinking the pool appropriately. */ ++static int set_num_buffers(struct b3dfg_dev *fgdev, int num_buffers) ++{ ++ int i; ++ struct b3dfg_buffer *buffers; ++ unsigned long flags; ++ ++ printk(KERN_INFO PFX "set %d buffers\n", num_buffers); ++ if (fgdev->transmission_enabled) { ++ printk(KERN_ERR PFX ++ "cannot set buffer count while transmission is enabled\n"); ++ return -EBUSY; ++ } ++ ++ if (atomic_read(&fgdev->mapping_count) > 0) { ++ printk(KERN_ERR PFX ++ "cannot set buffer count while memory mappings are active\n"); ++ return -EBUSY; ++ } ++ ++ if (num_buffers > B3DFG_NR_TRIPLET_BUFFERS) { ++ printk(KERN_ERR PFX "limited to %d triplet buffers\n", ++ B3DFG_NR_TRIPLET_BUFFERS); ++ return -E2BIG; ++ } ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ if (num_buffers == fgdev->num_buffers) { ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ return 0; ++ } ++ ++ /* free all buffers then allocate new ones */ ++ dequeue_all_buffers(fgdev); ++ free_all_buffers(fgdev); ++ ++ /* must unlock to allocate GFP_KERNEL memory */ ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ ++ if (num_buffers == 0) ++ return 0; ++ ++ buffers = kmalloc(num_buffers * sizeof(struct b3dfg_buffer), ++ GFP_KERNEL); ++ if (!buffers) ++ return -ENOMEM; ++ ++ for (i = 0; i < num_buffers; i++) ++ init_buffer(fgdev, &buffers[i], i); ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ fgdev->buffers = buffers; ++ fgdev->num_buffers = num_buffers; ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ ++ return 0; ++} ++ ++/* queue a buffer to receive data */ ++static int queue_buffer(struct b3dfg_dev *fgdev, int bufidx) ++{ ++ struct b3dfg_buffer *buf; ++ unsigned long flags; ++ int r = 0; ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ buf = buffer_from_idx(fgdev, bufidx); ++ if (unlikely(!buf)) { ++ r = -ENOENT; ++ goto out; ++ } ++ ++ if (unlikely(buf->state == B3DFG_BUFFER_PENDING)) { ++ printk(KERN_ERR PFX "buffer %d is already queued", bufidx); ++ r = -EINVAL; ++ goto out; ++ } ++ ++ buf->state = B3DFG_BUFFER_PENDING; ++ list_add_tail(&buf->list, &fgdev->buffer_queue); ++ ++ if (fgdev->transmission_enabled && fgdev->triplet_ready) { ++ dbg("triplet is ready, so pushing immediately\n"); ++ fgdev->triplet_ready = 0; ++ setup_frame_transfer(fgdev, buf, 0, 0); ++ } ++ ++out: ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ return r; ++} ++ ++/* non-blocking buffer poll. returns 1 if data is present in the buffer, ++ * 0 otherwise */ ++static int poll_buffer(struct b3dfg_dev *fgdev, void __user *arg) ++{ ++ struct b3dfg_poll p; ++ struct b3dfg_buffer *buf; ++ unsigned long flags; ++ int r = 1; ++ ++ if (copy_from_user(&p, arg, sizeof(p))) ++ return -EFAULT; ++ ++ if (unlikely(!fgdev->transmission_enabled)) { ++ printk(KERN_ERR PFX ++ "cannot poll buffers when transmission is disabled\n"); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ buf = buffer_from_idx(fgdev, p.buffer_idx); ++ if (unlikely(!buf)) { ++ r = -ENOENT; ++ goto out; ++ } ++ ++ if (buf->state != B3DFG_BUFFER_POPULATED) { ++ r = 0; ++ goto out; ++ } ++ ++ if (likely(buf->state == B3DFG_BUFFER_POPULATED)) { ++ buf->state = B3DFG_BUFFER_POLLED; ++ spin_lock(&fgdev->triplets_dropped_lock); ++ p.triplets_dropped = fgdev->triplets_dropped; ++ fgdev->triplets_dropped = 0; ++ spin_unlock(&fgdev->triplets_dropped_lock); ++ if (copy_to_user(arg, &p, sizeof(p))) ++ r = -EFAULT; ++ } ++ ++out: ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ return r; ++} ++ ++static u8 buffer_state(struct b3dfg_dev *fgdev, struct b3dfg_buffer *buf) ++{ ++ unsigned long flags; ++ u8 state; ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ state = buf->state; ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ return state; ++} ++ ++/* sleep until a specific buffer becomes populated */ ++static int wait_buffer(struct b3dfg_dev *fgdev, void __user *arg) ++{ ++ struct b3dfg_wait w; ++ struct b3dfg_buffer *buf; ++ unsigned long flags; ++ int r; ++ ++ if (copy_from_user(&w, arg, sizeof(w))) ++ return -EFAULT; ++ ++ if (unlikely(!fgdev->transmission_enabled)) { ++ printk(KERN_ERR PFX ++ "cannot wait on buffers when transmission is disabled\n"); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ buf = buffer_from_idx(fgdev, w.buffer_idx); ++ if (unlikely(!buf)) { ++ r = -ENOENT; ++ goto out; ++ } ++ ++ if (buf->state == B3DFG_BUFFER_POPULATED) { ++ r = 0; ++ goto out_triplets_dropped; ++ } ++ ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ /* FIXME: what prevents the buffer going away at this time? */ ++ ++ if (w.timeout > 0) { ++ r = wait_event_interruptible_timeout(fgdev->buffer_waitqueue, ++ buffer_state(fgdev, buf) == B3DFG_BUFFER_POPULATED, ++ (w.timeout * HZ) / 1000); ++ if (unlikely(r < 0)) ++ return r; ++ else if (unlikely(buffer_state(fgdev, buf) ++ != B3DFG_BUFFER_POPULATED)) ++ return -ETIMEDOUT; ++ w.timeout = r * 1000 / HZ; ++ } else { ++ r = wait_event_interruptible(fgdev->buffer_waitqueue, ++ buffer_state(fgdev, buf) == B3DFG_BUFFER_POPULATED); ++ if (unlikely(r)) ++ return -ERESTARTSYS; ++ } ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ /* FIXME: rediscover buffer? it might have changed during the unlocked ++ * time */ ++ buf->state = B3DFG_BUFFER_POLLED; ++ ++out_triplets_dropped: ++ spin_lock(&fgdev->triplets_dropped_lock); ++ w.triplets_dropped = fgdev->triplets_dropped; ++ fgdev->triplets_dropped = 0; ++ spin_unlock(&fgdev->triplets_dropped_lock); ++ if (copy_to_user(arg, &w, sizeof(w))) ++ r = -EFAULT; ++out: ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ return r; ++} ++ ++/**** virtual memory mapping ****/ ++ ++static void b3dfg_vma_open(struct vm_area_struct *vma) ++{ ++ struct b3dfg_dev *fgdev = vma->vm_file->private_data; ++ atomic_inc(&fgdev->mapping_count); ++} ++ ++static void b3dfg_vma_close(struct vm_area_struct *vma) ++{ ++ struct b3dfg_dev *fgdev = vma->vm_file->private_data; ++ atomic_dec(&fgdev->mapping_count); ++} ++ ++/* page fault handler */ ++static unsigned long b3dfg_vma_nopfn(struct vm_area_struct *vma, ++ unsigned long address) ++{ ++ struct b3dfg_dev *fgdev = vma->vm_file->private_data; ++ unsigned long off = address - vma->vm_start; ++ unsigned int frame_size = fgdev->frame_size; ++ unsigned int buf_size = frame_size * B3DFG_FRAMES_PER_BUFFER; ++ unsigned long flags; ++ unsigned char *addr; ++ ++ /* determine which buffer the offset lies within */ ++ unsigned int buf_idx = off / buf_size; ++ /* and the offset into the buffer */ ++ unsigned int buf_off = off % buf_size; ++ ++ /* determine which frame inside the buffer the offset lies in */ ++ unsigned int frm_idx = buf_off / frame_size; ++ /* and the offset into the frame */ ++ unsigned int frm_off = buf_off % frame_size; ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ if (unlikely(buf_idx > fgdev->num_buffers)) { ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ return NOPFN_SIGBUS; ++ } ++ ++ addr = fgdev->buffers[buf_idx].frame[frm_idx] + frm_off; ++ vm_insert_pfn(vma, vma->vm_start + off, ++ virt_to_phys(addr) >> PAGE_SHIFT); ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ return NOPFN_REFAULT; ++} ++ ++static struct vm_operations_struct b3dfg_vm_ops = { ++ .open = b3dfg_vma_open, ++ .close = b3dfg_vma_close, ++ .nopfn = b3dfg_vma_nopfn, ++}; ++ ++static int get_wand_status(struct b3dfg_dev *fgdev, int __user *arg) ++{ ++ u32 wndstat = b3dfg_read32(fgdev, B3D_REG_WAND_STS); ++ dbg("wand status %x\n", wndstat); ++ return __put_user(wndstat & 0x1, arg); ++} ++ ++static int enable_transmission(struct b3dfg_dev *fgdev) ++{ ++ u16 command; ++ unsigned long flags; ++ ++ printk(KERN_INFO PFX "enable transmission\n"); ++ ++ /* check we're a bus master */ ++ pci_read_config_word(fgdev->pdev, PCI_COMMAND, &command); ++ if (!(command & PCI_COMMAND_MASTER)) { ++ printk(KERN_ERR PFX "not a bus master, force-enabling\n"); ++ /* FIXME: why did we lose it in the first place? */ ++ pci_write_config_word(fgdev->pdev, PCI_COMMAND, ++ command | PCI_COMMAND_MASTER); ++ } ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ if (fgdev->num_buffers == 0) { ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ printk(KERN_ERR PFX "cannot start transmission to 0 buffers\n"); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ ++ spin_lock_irqsave(&fgdev->triplets_dropped_lock, flags); ++ fgdev->triplets_dropped = 0; ++ spin_unlock_irqrestore(&fgdev->triplets_dropped_lock, flags); ++ ++ fgdev->triplet_ready = 0; ++ fgdev->transmission_enabled = 1; ++ fgdev->cur_dma_frame_idx = -1; ++ b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 1); ++ return 0; ++} ++ ++static void disable_transmission(struct b3dfg_dev *fgdev) ++{ ++ unsigned long flags; ++ u32 tmp; ++ ++ printk(KERN_INFO PFX "disable transmission\n"); ++ ++ /* guarantee that no more interrupts will be serviced */ ++ fgdev->transmission_enabled = 0; ++ synchronize_irq(fgdev->pdev->irq); ++ ++ b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0); ++ ++ /* FIXME: temporary debugging only. if the board stops transmitting, ++ * hitting ctrl+c and seeing this message is useful for determining ++ * the state of the board. */ ++ tmp = b3dfg_read32(fgdev, B3D_REG_DMA_STS); ++ dbg("brontes DMA_STS reads %x after TX stopped\n", tmp); ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ dequeue_all_buffers(fgdev); ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++} ++ ++static int set_transmission(struct b3dfg_dev *fgdev, int enabled) ++{ ++ if (enabled && !fgdev->transmission_enabled) ++ return enable_transmission(fgdev); ++ else if (!enabled && fgdev->transmission_enabled) ++ disable_transmission(fgdev); ++ return 0; ++} ++ ++static irqreturn_t b3dfg_intr(int irq, void *dev_id) ++{ ++ struct b3dfg_dev *fgdev = dev_id; ++ struct device *dev; ++ struct b3dfg_buffer *buf = NULL; ++ unsigned int frame_size; ++ u32 sts; ++ u8 dropped; ++ int next_trf; ++ int need_ack = 1; ++ ++ if (unlikely(!fgdev->transmission_enabled)) { ++ printk("ignore interrupt, TX disabled\n"); ++ /* FIXME should return IRQ_NONE when we are stable */ ++ goto out; ++ } ++ ++ sts = b3dfg_read32(fgdev, B3D_REG_DMA_STS); ++ if (unlikely(sts == 0)) { ++ printk("ignore interrupt, brontes DMA status is 0\n"); ++ /* FIXME should return IRQ_NONE when we are stable */ ++ goto out; ++ } ++ ++ dropped = (sts >> 8) & 0xff; ++ dbg(KERN_INFO PFX "got intr, brontes DMASTS=%08x (dropped=%d comp=%d next_trf=%d)\n", sts, dropped, !!(sts & 0x4), sts & 0x3); ++ ++ if (unlikely(dropped > 0)) { ++ spin_lock(&fgdev->triplets_dropped_lock); ++ fgdev->triplets_dropped += dropped; ++ spin_unlock(&fgdev->triplets_dropped_lock); ++ } ++ ++ dev = &fgdev->pdev->dev; ++ frame_size = fgdev->frame_size; ++ ++ spin_lock(&fgdev->buffer_lock); ++ if (unlikely(list_empty(&fgdev->buffer_queue))) { ++ /* FIXME need more sanity checking here */ ++ dbg("driver has no buffer ready --> cannot program any more transfers\n"); ++ fgdev->triplet_ready = 1; ++ goto out_unlock; ++ } ++ ++ next_trf = sts & 0x3; ++ ++ if (sts & 0x4) { ++ u32 tmp; ++ ++ tmp = b3dfg_read32(fgdev, B3D_REG_EC220_DMA_STS); ++ /* last DMA completed */ ++ if (unlikely(tmp & 0x1)) { ++ printk(KERN_ERR PFX "EC220 reports error (%08x)\n", tmp); ++ /* FIXME flesh out error handling */ ++ goto out_unlock; ++ } ++ if (unlikely(fgdev->cur_dma_frame_idx == -1)) { ++ printk("ERROR completed but no last idx?\n"); ++ /* FIXME flesh out error handling */ ++ goto out_unlock; ++ } ++ dma_unmap_single(dev, fgdev->cur_dma_frame_addr, frame_size, ++ DMA_FROM_DEVICE); ++ ++ buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list); ++ if (likely(buf)) { ++ dbg("handle frame completion\n"); ++ if (fgdev->cur_dma_frame_idx == B3DFG_FRAMES_PER_BUFFER - 1) { ++ /* last frame of that triplet completed */ ++ dbg("triplet completed\n"); ++ buf->state = B3DFG_BUFFER_POPULATED; ++ list_del_init(&buf->list); ++ wake_up_interruptible(&fgdev->buffer_waitqueue); ++ } ++ } else { ++ printk("got frame but no buffer!\n"); ++ } ++ } ++ ++ if (next_trf) { ++ next_trf--; ++ ++ buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list); ++ dbg("program DMA transfer for frame %d\n", next_trf + 1); ++ if (likely(buf)) { ++ if (next_trf != fgdev->cur_dma_frame_idx + 1) { ++ printk("ERROR mismatch, next_trf %d vs cur_dma_frame_idx %d\n", ++ next_trf, fgdev->cur_dma_frame_idx); ++ /* FIXME this is where we should handle dropped triplets */ ++ goto out_unlock; ++ } ++ setup_frame_transfer(fgdev, buf, next_trf, 1); ++ need_ack = 0; ++ } else { ++ printk("cannot setup next DMA due to no buffer\n"); ++ } ++ } else { ++ fgdev->cur_dma_frame_idx = -1; ++ } ++ ++out_unlock: ++ spin_unlock(&fgdev->buffer_lock); ++out: ++ if (need_ack) { ++ dbg("acknowledging interrupt\n"); ++ b3dfg_write32(fgdev, B3D_REG_EC220_DMA_STS, 0x0b); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int b3dfg_open(struct inode *inode, struct file *filp) ++{ ++ struct b3dfg_dev *fgdev = ++ container_of(inode->i_cdev, struct b3dfg_dev, chardev); ++ ++ printk(KERN_INFO PFX "open\n"); ++ filp->private_data = fgdev; ++ return 0; ++} ++ ++static int b3dfg_release(struct inode *inode, struct file *filp) ++{ ++ struct b3dfg_dev *fgdev = filp->private_data; ++ printk(KERN_INFO PFX "release\n"); ++ set_transmission(fgdev, 0); ++ ++ /* no buffer locking needed, this is serialized */ ++ dequeue_all_buffers(fgdev); ++ return set_num_buffers(fgdev, 0); ++} ++ ++static long b3dfg_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ struct b3dfg_dev *fgdev = filp->private_data; ++ int r; ++ ++ switch (cmd) { ++ case B3DFG_IOCGFRMSZ: ++ return __put_user(fgdev->frame_size, (int __user *) arg); ++ case B3DFG_IOCGWANDSTAT: ++ return get_wand_status(fgdev, (int __user *) arg); ++ case B3DFG_IOCTNUMBUFS: ++ mutex_lock(&fgdev->ioctl_mutex); ++ r = set_num_buffers(fgdev, (int) arg); ++ mutex_unlock(&fgdev->ioctl_mutex); ++ return r; ++ case B3DFG_IOCTTRANS: ++ mutex_lock(&fgdev->ioctl_mutex); ++ r = set_transmission(fgdev, (int) arg); ++ mutex_unlock(&fgdev->ioctl_mutex); ++ return r; ++ case B3DFG_IOCTQUEUEBUF: ++ return queue_buffer(fgdev, (int) arg); ++ case B3DFG_IOCTPOLLBUF: ++ return poll_buffer(fgdev, (void __user *) arg); ++ case B3DFG_IOCTWAITBUF: ++ return wait_buffer(fgdev, (void __user *) arg); ++ default: ++ printk(KERN_ERR PFX "unrecognised ioctl %x\n", cmd); ++ return -EINVAL; ++ } ++} ++ ++static unsigned int b3dfg_poll(struct file *filp, poll_table *poll_table) ++{ ++ struct b3dfg_dev *fgdev = filp->private_data; ++ unsigned long flags; ++ int i; ++ int r = 0; ++ ++ /* don't let the user mess with buffer allocations etc. while polling */ ++ mutex_lock(&fgdev->ioctl_mutex); ++ ++ if (unlikely(!fgdev->transmission_enabled)) { ++ printk(KERN_ERR PFX "cannot poll() when transmission is disabled\n"); ++ r = POLLERR; ++ goto out; ++ } ++ ++ poll_wait(filp, &fgdev->buffer_waitqueue, poll_table); ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ for (i = 0; i < fgdev->num_buffers; i++) { ++ if (fgdev->buffers[i].state == B3DFG_BUFFER_POPULATED) { ++ r = POLLIN | POLLRDNORM; ++ goto out_buffer_unlock; ++ } ++ } ++ ++out_buffer_unlock: ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++out: ++ mutex_unlock(&fgdev->ioctl_mutex); ++ return r; ++} ++ ++static int b3dfg_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ struct b3dfg_dev *fgdev = filp->private_data; ++ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; ++ unsigned long vsize = vma->vm_end - vma->vm_start; ++ unsigned long bufdatalen; ++ unsigned long psize; ++ unsigned long flags; ++ int r = 0; ++ ++ /* don't let user mess with buffer allocations during mmap */ ++ mutex_lock(&fgdev->ioctl_mutex); ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); ++ bufdatalen = fgdev->num_buffers * fgdev->frame_size * 3; ++ psize = bufdatalen - offset; ++ ++ if (fgdev->num_buffers == 0) { ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ r = -ENOENT; ++ goto out; ++ } ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ if (vsize > psize) { ++ r = -EINVAL; ++ goto out; ++ } ++ ++ vma->vm_flags |= VM_IO | VM_RESERVED | VM_CAN_NONLINEAR | VM_PFNMAP; ++ vma->vm_ops = &b3dfg_vm_ops; ++ b3dfg_vma_open(vma); ++ ++out: ++ mutex_unlock(&fgdev->ioctl_mutex); ++ return r; ++} ++ ++static struct file_operations b3dfg_fops = { ++ .owner = THIS_MODULE, ++ .open = b3dfg_open, ++ .release = b3dfg_release, ++ .unlocked_ioctl = b3dfg_ioctl, ++ .poll = b3dfg_poll, ++ .mmap = b3dfg_mmap, ++}; ++ ++static void free_all_frame_buffers(struct b3dfg_dev *fgdev) ++{ ++ int i; ++ for (i = 0; i < B3DFG_NR_FRAME_BUFFERS; i++) ++ kfree(fgdev->frame_buffer[i]); ++} ++ ++/* initialize device and any data structures. called before any interrupts ++ * are enabled. */ ++static int b3dfg_init_dev(struct b3dfg_dev *fgdev) ++{ ++ int i; ++ u32 frm_size = b3dfg_read32(fgdev, B3D_REG_FRM_SIZE); ++ ++ /* disable interrupts. in abnormal circumstances (e.g. after a crash) the ++ * board may still be transmitting from the previous session. if we ensure ++ * that interrupts are disabled before we later enable them, we are sure ++ * to capture a triplet from the start, rather than starting from frame ++ * 2 or 3. disabling interrupts causes the FG to throw away all buffered ++ * data and stop buffering more until interrupts are enabled again. */ ++ b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0); ++ ++ fgdev->frame_size = frm_size * 4096; ++ for (i = 0; i < B3DFG_NR_FRAME_BUFFERS; i++) { ++ fgdev->frame_buffer[i] = kmalloc(fgdev->frame_size, GFP_KERNEL); ++ if (!fgdev->frame_buffer[i]) ++ goto err_no_mem; ++ } ++ ++ INIT_LIST_HEAD(&fgdev->buffer_queue); ++ init_waitqueue_head(&fgdev->buffer_waitqueue); ++ spin_lock_init(&fgdev->buffer_lock); ++ spin_lock_init(&fgdev->triplets_dropped_lock); ++ atomic_set(&fgdev->mapping_count, 0); ++ mutex_init(&fgdev->ioctl_mutex); ++ return 0; ++ ++err_no_mem: ++ free_all_frame_buffers(fgdev); ++ return -ENOMEM; ++} ++ ++/* find next free minor number, returns -1 if none are availabile */ ++static int get_free_minor(void) ++{ ++ int i; ++ for (i = 0; i < B3DFG_MAX_DEVS; i++) { ++ if (b3dfg_devices[i] == 0) ++ return i; ++ } ++ return -1; ++} ++ ++static int __devinit b3dfg_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ struct b3dfg_dev *fgdev = kzalloc(sizeof(*fgdev), GFP_KERNEL); ++ int r = 0; ++ int minor = get_free_minor(); ++ dev_t devno = MKDEV(MAJOR(b3dfg_devt), minor); ++ ++ if (fgdev == NULL) ++ return -ENOMEM; ++ ++ if (minor < 0) { ++ printk(KERN_ERR PFX "too many devices found!\n"); ++ return -EIO; ++ } ++ ++ b3dfg_devices[minor] = 1; ++ printk(KERN_INFO PFX "probe device at %s with IRQ %d\n", ++ pci_name(pdev), pdev->irq); ++ ++ cdev_init(&fgdev->chardev, &b3dfg_fops); ++ fgdev->chardev.owner = THIS_MODULE; ++ ++ r = cdev_add(&fgdev->chardev, devno, 1); ++ if (r) ++ goto err1; ++ ++ fgdev->classdev = class_device_create(b3dfg_class, NULL, devno, &pdev->dev, ++ DRIVER_NAME "%d", minor); ++ if (IS_ERR(fgdev->classdev)) { ++ r = PTR_ERR(fgdev->classdev); ++ goto err2; ++ } ++ ++ r = pci_enable_device(pdev); ++ if (r) ++ goto err3; ++ ++ if (pci_resource_len(pdev, B3DFG_BAR_REGS) != B3DFG_REGS_LENGTH) { ++ printk(KERN_ERR PFX "invalid register resource size\n"); ++ goto err4; ++ } ++ ++ if (pci_resource_flags(pdev, B3DFG_BAR_REGS) != IORESOURCE_MEM) { ++ printk(KERN_ERR PFX "invalid resource flags"); ++ goto err4; ++ } ++ ++ fgdev->regs = ioremap_nocache(pci_resource_start(pdev, B3DFG_BAR_REGS), ++ B3DFG_REGS_LENGTH); ++ if (!fgdev->regs) { ++ printk(KERN_ERR PFX "regs ioremap failed\n"); ++ goto err4; ++ } ++ ++ fgdev->pdev = pdev; ++ pci_set_drvdata(pdev, fgdev); ++ r = b3dfg_init_dev(fgdev); ++ if (r < 0) { ++ printk(KERN_ERR PFX "failed to initalize device\n"); ++ goto err5; ++ } ++ ++ r = request_irq(pdev->irq, b3dfg_intr, IRQF_SHARED, DRIVER_NAME, fgdev); ++ if (r) { ++ printk(KERN_ERR PFX "couldn't request irq %d\n", pdev->irq); ++ goto err6; ++ } ++ ++ return 0; ++ ++err6: ++ free_all_frame_buffers(fgdev); ++err5: ++ iounmap(fgdev->regs); ++err4: ++ pci_disable_device(pdev); ++err3: ++ class_device_unregister(fgdev->classdev); ++err2: ++ cdev_del(&fgdev->chardev); ++err1: ++ kfree(fgdev); ++ if (minor >= 0) ++ b3dfg_devices[minor] = 0; ++ return r; ++} ++ ++static void __devexit b3dfg_remove(struct pci_dev *pdev) ++{ ++ struct b3dfg_dev *fgdev = pci_get_drvdata(pdev); ++ unsigned int minor = MINOR(fgdev->chardev.dev); ++ ++ printk(KERN_INFO PFX "remove\n"); ++ ++ free_irq(pdev->irq, fgdev); ++ iounmap(fgdev->regs); ++ pci_disable_device(pdev); ++ class_device_unregister(fgdev->classdev); ++ cdev_del(&fgdev->chardev); ++ free_all_frame_buffers(fgdev); ++ kfree(fgdev); ++ b3dfg_devices[minor] = 0; ++} ++ ++static struct pci_driver b3dfg_driver = { ++ .name = DRIVER_NAME, ++ .id_table = b3dfg_ids, ++ .probe = b3dfg_probe, ++ .remove = b3dfg_remove, ++}; ++ ++static int __init b3dfg_module_init(void) ++{ ++ int r; ++ ++ printk(KERN_INFO PFX "loaded\n"); ++ ++ b3dfg_class = class_create(THIS_MODULE, DRIVER_NAME); ++ if (IS_ERR(b3dfg_class)) ++ return PTR_ERR(b3dfg_class); ++ ++ r = alloc_chrdev_region(&b3dfg_devt, 0, B3DFG_MAX_DEVS, DRIVER_NAME); ++ if (r) ++ goto err1; ++ ++ r = pci_register_driver(&b3dfg_driver); ++ if (r) ++ goto err2; ++ ++ return r; ++ ++err2: ++ unregister_chrdev_region(b3dfg_devt, B3DFG_MAX_DEVS); ++err1: ++ class_destroy(b3dfg_class); ++ return r; ++} ++ ++static void __exit b3dfg_module_exit(void) ++{ ++ printk(KERN_INFO PFX "unloaded\n"); ++ pci_unregister_driver(&b3dfg_driver); ++ unregister_chrdev_region(b3dfg_devt, B3DFG_MAX_DEVS); ++ class_destroy(b3dfg_class); ++} ++ ++module_init(b3dfg_module_init); ++module_exit(b3dfg_module_exit); ++MODULE_AUTHOR("Daniel Drake <ddrake@brontes3d.com>"); ++MODULE_DESCRIPTION("Brontes frame grabber driver"); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(pci, b3dfg_ids); ++ diff --git a/staging/staging-b3dfg-fixups-and-improvements.patch b/staging/staging-b3dfg-fixups-and-improvements.patch new file mode 100644 index 00000000000000..1ba4a9c7ec9e56 --- /dev/null +++ b/staging/staging-b3dfg-fixups-and-improvements.patch @@ -0,0 +1,1416 @@ +From 0e6d6eecf80c82be2e3d881dbd4cffbdd846f727 Mon Sep 17 00:00:00 2001 +From: Duane Griffin <duaneg@dghda.com> +Date: Wed, 28 Jan 2009 09:50:37 -0500 +Subject: Staging: b3dfg: fixups and improvements + +From: Duane Griffin <duaneg@dghda.com> + + - Added support for cable plug/unplug detection. + - Improvements to error handling. + - Switch to the pci_* DMA API. + - Removed set_num_buffers functionality. + - Locking review. + - Unconditionally disable transmission when releasing device. + +Signed-off-by: Justin Bronder <jsbronder@brontes3d.com> +Cc: Duane Griffin <duaneg@dghda.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/staging/b3dfg/b3dfg.c | 884 ++++++++++++++++++++++-------------------- + 1 file changed, 480 insertions(+), 404 deletions(-) + +--- a/drivers/staging/b3dfg/b3dfg.c ++++ b/drivers/staging/b3dfg/b3dfg.c +@@ -34,52 +34,52 @@ + #include <linux/wait.h> + #include <linux/mm.h> + #include <linux/version.h> +-#include <linux/mutex.h> + +-#include <asm/atomic.h> + #include <asm/uaccess.h> + + /* TODO: +- * locking + * queue/wait buffer presents filltime results for each frame? + * counting of dropped frames + * review endianness + */ + +-#ifdef DEBUG +-#define dbg(msg...) printk(msg) +-#else +-#define dbg(msg...) +-#endif ++static unsigned int b3dfg_nbuf = 2; ++ ++module_param_named(buffer_count, b3dfg_nbuf, uint, 0444); ++ ++MODULE_PARM_DESC(buffer_count, "Number of buffers (min 2, default 2)\n"); ++ ++MODULE_AUTHOR("Daniel Drake <ddrake@brontes3d.com>"); ++MODULE_DESCRIPTION("Brontes frame grabber driver"); ++MODULE_LICENSE("GPL"); + + #define DRIVER_NAME "b3dfg" +-#define PFX DRIVER_NAME ": " + #define B3DFG_MAX_DEVS 4 +-#define B3DFG_NR_TRIPLET_BUFFERS 4 +-#define B3DFG_NR_FRAME_BUFFERS (B3DFG_NR_TRIPLET_BUFFERS * 3) + #define B3DFG_FRAMES_PER_BUFFER 3 + + #define B3DFG_BAR_REGS 0 + #define B3DFG_REGS_LENGTH 0x10000 + +-#define B3DFG_IOC_MAGIC 0xb3 /* dfg :-) */ +-#define B3DFG_IOCGFRMSZ _IOR(B3DFG_IOC_MAGIC, 1, int) +-#define B3DFG_IOCTNUMBUFS _IO(B3DFG_IOC_MAGIC, 2) +-#define B3DFG_IOCTTRANS _IO(B3DFG_IOC_MAGIC, 3) +-#define B3DFG_IOCTQUEUEBUF _IO(B3DFG_IOC_MAGIC, 4) +-#define B3DFG_IOCTPOLLBUF _IOWR(B3DFG_IOC_MAGIC, 5, struct b3dfg_poll) +-#define B3DFG_IOCTWAITBUF _IOWR(B3DFG_IOC_MAGIC, 6, struct b3dfg_wait) +-#define B3DFG_IOCGWANDSTAT _IOR(B3DFG_IOC_MAGIC, 7, int) ++#define B3DFG_IOC_MAGIC 0xb3 /* dfg :-) */ ++#define B3DFG_IOCGFRMSZ _IOR(B3DFG_IOC_MAGIC, 1, int) ++#define B3DFG_IOCTNUMBUFS _IO(B3DFG_IOC_MAGIC, 2) ++#define B3DFG_IOCTTRANS _IO(B3DFG_IOC_MAGIC, 3) ++#define B3DFG_IOCTQUEUEBUF _IO(B3DFG_IOC_MAGIC, 4) ++#define B3DFG_IOCTPOLLBUF _IOWR(B3DFG_IOC_MAGIC, 5, struct b3dfg_poll) ++#define B3DFG_IOCTWAITBUF _IOWR(B3DFG_IOC_MAGIC, 6, struct b3dfg_wait) ++#define B3DFG_IOCGWANDSTAT _IOR(B3DFG_IOC_MAGIC, 7, int) + + enum { + /* number of 4kb pages per frame */ + B3D_REG_FRM_SIZE = 0x0, + +- /* bit 0: set to enable interrupts */ ++ /* bit 0: set to enable interrupts ++ * bit 1: set to enable cable status change interrupts */ + B3D_REG_HW_CTRL = 0x4, + +- /* bit 0-1 - 1-based ID of next pending frame transfer (0 = nothing pending) ++ /* bit 0-1 - 1-based ID of next pending frame transfer (0 = none) + * bit 2 indicates the previous DMA transfer has completed ++ * bit 3 indicates wand cable status change + * bit 8:15 - counter of number of discarded triplets */ + B3D_REG_DMA_STS = 0x8, + +@@ -110,41 +110,48 @@ enum b3dfg_buffer_state { + + struct b3dfg_buffer { + unsigned char *frame[B3DFG_FRAMES_PER_BUFFER]; +- u8 state; + struct list_head list; ++ u8 state; + }; + + struct b3dfg_dev { ++ + /* no protection needed: all finalized at initialization time */ + struct pci_dev *pdev; +- struct cdev chardev; +- struct class_device *classdev; ++ struct cdev chardev; ++ struct class_device *classdev; + void __iomem *regs; + unsigned int frame_size; + +- /* we want to serialize some ioctl operations */ +- struct mutex ioctl_mutex; +- +- /* preallocated frame buffers */ +- unsigned char *frame_buffer[B3DFG_NR_FRAME_BUFFERS]; +- +- /* buffers_lock protects num_buffers, buffers, buffer_queue */ ++ /* ++ * Protects buffer state, including buffer_queue, triplet_ready, ++ * cur_dma_frame_idx & cur_dma_frame_addr. ++ */ + spinlock_t buffer_lock; +- int num_buffers; + struct b3dfg_buffer *buffers; + struct list_head buffer_queue; + +- wait_queue_head_t buffer_waitqueue; ++ /* Last frame in triplet transferred (-1 if none). */ ++ int cur_dma_frame_idx; + +- atomic_t mapping_count; ++ /* Current frame's address for DMA. */ ++ dma_addr_t cur_dma_frame_addr; + ++ /* ++ * Protects cstate_tstamp. ++ * Nests inside buffer_lock. ++ */ ++ spinlock_t cstate_lock; ++ unsigned long cstate_tstamp; ++ ++ /* ++ * Protects triplets_dropped. ++ * Nests inside buffers_lock. ++ */ + spinlock_t triplets_dropped_lock; + unsigned int triplets_dropped; + +- /* FIXME: we need some locking here. this could be accessed in parallel +- * from the queue_buffer ioctl and the interrupt handler. */ +- int cur_dma_frame_idx; +- dma_addr_t cur_dma_frame_addr; ++ wait_queue_head_t buffer_waitqueue; + + unsigned int transmission_enabled:1; + unsigned int triplet_ready:1; +@@ -159,11 +166,13 @@ static const struct pci_device_id b3dfg_ + { PCI_DEVICE(0x0b3d, 0x0001) }, + + /* FIXME: remove this ID once all boards have been moved to 0xb3d. +- * this is Eureka's vendor ID that we borrowed before we bought our own. */ ++ * Eureka's vendor ID that we borrowed before we bought our own. */ + { PCI_DEVICE(0x1901, 0x0001) }, + { }, + }; + ++MODULE_DEVICE_TABLE(pci, b3dfg_ids); ++ + /***** user-visible types *****/ + + struct b3dfg_poll { +@@ -191,145 +200,61 @@ static void b3dfg_write32(struct b3dfg_d + + /**** buffer management ****/ + +-/* program EC220 for transfer of a specific frame */ +-static void setup_frame_transfer(struct b3dfg_dev *fgdev, +- struct b3dfg_buffer *buf, int frame, int acknowledge) ++/* ++ * Program EC220 for transfer of a specific frame. ++ * Called with buffer_lock held. ++ */ ++static int setup_frame_transfer(struct b3dfg_dev *fgdev, ++ struct b3dfg_buffer *buf, int frame) + { + unsigned char *frm_addr; + dma_addr_t frm_addr_dma; +- struct device *dev = &fgdev->pdev->dev; +- unsigned int frame_size = fgdev->frame_size; +- unsigned char dma_sts = 0xd; ++ unsigned int frm_size = fgdev->frame_size; + + frm_addr = buf->frame[frame]; +- frm_addr_dma = dma_map_single(dev, frm_addr, frame_size, DMA_FROM_DEVICE); ++ frm_addr_dma = pci_map_single(fgdev->pdev, frm_addr, ++ frm_size, PCI_DMA_FROMDEVICE); ++ if (pci_dma_mapping_error(frm_addr_dma)) ++ return -ENOMEM; ++ + fgdev->cur_dma_frame_addr = frm_addr_dma; + fgdev->cur_dma_frame_idx = frame; + + b3dfg_write32(fgdev, B3D_REG_EC220_DMA_ADDR, cpu_to_le32(frm_addr_dma)); +- b3dfg_write32(fgdev, B3D_REG_EC220_TRF_SIZE, cpu_to_le32(frame_size >> 2)); +- +- if (likely(acknowledge)) +- dma_sts |= 0x2; ++ b3dfg_write32(fgdev, B3D_REG_EC220_TRF_SIZE, cpu_to_le32(frm_size >> 2)); + b3dfg_write32(fgdev, B3D_REG_EC220_DMA_STS, 0xf); +-} +- +-/* retrieve a buffer pointer from a buffer index. also checks that the +- * requested buffer actually exists. buffer_lock should be held by caller */ +-static inline struct b3dfg_buffer *buffer_from_idx(struct b3dfg_dev *fgdev, +- int idx) +-{ +- if (unlikely(idx >= fgdev->num_buffers)) +- return NULL; +- return &fgdev->buffers[idx]; +-} + +-/* caller should hold buffer lock */ +-static void free_all_buffers(struct b3dfg_dev *fgdev) +-{ +- kfree(fgdev->buffers); +- fgdev->buffers = NULL; +- fgdev->num_buffers = 0; ++ return 0; + } + ++/* Caller should hold buffer lock */ + static void dequeue_all_buffers(struct b3dfg_dev *fgdev) + { + int i; +- for (i = 0; i < fgdev->num_buffers; i++) { ++ for (i = 0; i < b3dfg_nbuf; i++) { + struct b3dfg_buffer *buf = &fgdev->buffers[i]; + buf->state = B3DFG_BUFFER_POLLED; + list_del_init(&buf->list); + } + } + +-/* initialize a buffer: allocate its frames, set default values */ +-static void init_buffer(struct b3dfg_dev *fgdev, struct b3dfg_buffer *buf, +- int idx) +-{ +- unsigned int addr_offset = idx * B3DFG_FRAMES_PER_BUFFER; +- int i; +- +- memset(buf, 0, sizeof(struct b3dfg_buffer)); +- for (i = 0; i < B3DFG_FRAMES_PER_BUFFER; i++) +- buf->frame[i] = fgdev->frame_buffer[addr_offset + i]; +- +- INIT_LIST_HEAD(&buf->list); +-} +- +-/* adjust the number of buffers, growing or shrinking the pool appropriately. */ +-static int set_num_buffers(struct b3dfg_dev *fgdev, int num_buffers) +-{ +- int i; +- struct b3dfg_buffer *buffers; +- unsigned long flags; +- +- printk(KERN_INFO PFX "set %d buffers\n", num_buffers); +- if (fgdev->transmission_enabled) { +- printk(KERN_ERR PFX +- "cannot set buffer count while transmission is enabled\n"); +- return -EBUSY; +- } +- +- if (atomic_read(&fgdev->mapping_count) > 0) { +- printk(KERN_ERR PFX +- "cannot set buffer count while memory mappings are active\n"); +- return -EBUSY; +- } +- +- if (num_buffers > B3DFG_NR_TRIPLET_BUFFERS) { +- printk(KERN_ERR PFX "limited to %d triplet buffers\n", +- B3DFG_NR_TRIPLET_BUFFERS); +- return -E2BIG; +- } +- +- spin_lock_irqsave(&fgdev->buffer_lock, flags); +- if (num_buffers == fgdev->num_buffers) { +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- return 0; +- } +- +- /* free all buffers then allocate new ones */ +- dequeue_all_buffers(fgdev); +- free_all_buffers(fgdev); +- +- /* must unlock to allocate GFP_KERNEL memory */ +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- +- if (num_buffers == 0) +- return 0; +- +- buffers = kmalloc(num_buffers * sizeof(struct b3dfg_buffer), +- GFP_KERNEL); +- if (!buffers) +- return -ENOMEM; +- +- for (i = 0; i < num_buffers; i++) +- init_buffer(fgdev, &buffers[i], i); +- +- spin_lock_irqsave(&fgdev->buffer_lock, flags); +- fgdev->buffers = buffers; +- fgdev->num_buffers = num_buffers; +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- +- return 0; +-} +- + /* queue a buffer to receive data */ + static int queue_buffer(struct b3dfg_dev *fgdev, int bufidx) + { ++ struct device *dev = &fgdev->pdev->dev; + struct b3dfg_buffer *buf; + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&fgdev->buffer_lock, flags); +- buf = buffer_from_idx(fgdev, bufidx); +- if (unlikely(!buf)) { ++ if (bufidx < 0 || bufidx >= b3dfg_nbuf) { + r = -ENOENT; + goto out; + } ++ buf = &fgdev->buffers[bufidx]; + + if (unlikely(buf->state == B3DFG_BUFFER_PENDING)) { +- printk(KERN_ERR PFX "buffer %d is already queued", bufidx); ++ dev_dbg(dev, "buffer %d is already queued\n", bufidx); + r = -EINVAL; + goto out; + } +@@ -338,9 +263,11 @@ static int queue_buffer(struct b3dfg_dev + list_add_tail(&buf->list, &fgdev->buffer_queue); + + if (fgdev->transmission_enabled && fgdev->triplet_ready) { +- dbg("triplet is ready, so pushing immediately\n"); ++ dev_dbg(dev, "triplet is ready, pushing immediately\n"); + fgdev->triplet_ready = 0; +- setup_frame_transfer(fgdev, buf, 0, 0); ++ r = setup_frame_transfer(fgdev, buf, 0); ++ if (r) ++ dev_err(dev, "unable to map DMA buffer\n"); + } + + out: +@@ -352,139 +279,159 @@ out: + * 0 otherwise */ + static int poll_buffer(struct b3dfg_dev *fgdev, void __user *arg) + { ++ struct device *dev = &fgdev->pdev->dev; + struct b3dfg_poll p; + struct b3dfg_buffer *buf; + unsigned long flags; + int r = 1; ++ int arg_out = 0; + + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + + if (unlikely(!fgdev->transmission_enabled)) { +- printk(KERN_ERR PFX +- "cannot poll buffers when transmission is disabled\n"); ++ dev_dbg(dev, "cannot poll, transmission disabled\n"); + return -EINVAL; + } + +- spin_lock_irqsave(&fgdev->buffer_lock, flags); +- buf = buffer_from_idx(fgdev, p.buffer_idx); +- if (unlikely(!buf)) { +- r = -ENOENT; +- goto out; +- } ++ if (p.buffer_idx < 0 || p.buffer_idx >= b3dfg_nbuf) ++ return -ENOENT; + +- if (buf->state != B3DFG_BUFFER_POPULATED) { +- r = 0; +- goto out; +- } ++ buf = &fgdev->buffers[p.buffer_idx]; ++ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); + + if (likely(buf->state == B3DFG_BUFFER_POPULATED)) { ++ arg_out = 1; + buf->state = B3DFG_BUFFER_POLLED; ++ ++ /* IRQs already disabled by spin_lock_irqsave above. */ + spin_lock(&fgdev->triplets_dropped_lock); + p.triplets_dropped = fgdev->triplets_dropped; + fgdev->triplets_dropped = 0; + spin_unlock(&fgdev->triplets_dropped_lock); +- if (copy_to_user(arg, &p, sizeof(p))) +- r = -EFAULT; ++ } else { ++ r = 0; + } + +-out: + spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ ++ if (arg_out && copy_to_user(arg, &p, sizeof(p))) ++ r = -EFAULT; ++ + return r; + } + +-static u8 buffer_state(struct b3dfg_dev *fgdev, struct b3dfg_buffer *buf) ++static unsigned long get_cstate_change(struct b3dfg_dev *fgdev) ++{ ++ unsigned long flags, when; ++ ++ spin_lock_irqsave(&fgdev->cstate_lock, flags); ++ when = fgdev->cstate_tstamp; ++ spin_unlock_irqrestore(&fgdev->cstate_lock, flags); ++ return when; ++} ++ ++static int is_event_ready(struct b3dfg_dev *fgdev, struct b3dfg_buffer *buf, ++ unsigned long when) + { ++ int result; + unsigned long flags; +- u8 state; + + spin_lock_irqsave(&fgdev->buffer_lock, flags); +- state = buf->state; ++ spin_lock(&fgdev->cstate_lock); ++ result = (!fgdev->transmission_enabled || ++ buf->state == B3DFG_BUFFER_POPULATED || ++ when != fgdev->cstate_tstamp); ++ spin_unlock(&fgdev->cstate_lock); + spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- return state; ++ ++ return result; + } + + /* sleep until a specific buffer becomes populated */ + static int wait_buffer(struct b3dfg_dev *fgdev, void __user *arg) + { ++ struct device *dev = &fgdev->pdev->dev; + struct b3dfg_wait w; + struct b3dfg_buffer *buf; +- unsigned long flags; ++ unsigned long flags, when; + int r; + + if (copy_from_user(&w, arg, sizeof(w))) + return -EFAULT; + +- if (unlikely(!fgdev->transmission_enabled)) { +- printk(KERN_ERR PFX +- "cannot wait on buffers when transmission is disabled\n"); ++ if (!fgdev->transmission_enabled) { ++ dev_dbg(dev, "cannot wait, transmission disabled\n"); + return -EINVAL; + } + ++ if (w.buffer_idx < 0 || w.buffer_idx >= b3dfg_nbuf) ++ return -ENOENT; ++ ++ buf = &fgdev->buffers[w.buffer_idx]; ++ + spin_lock_irqsave(&fgdev->buffer_lock, flags); +- buf = buffer_from_idx(fgdev, w.buffer_idx); +- if (unlikely(!buf)) { +- r = -ENOENT; +- goto out; +- } + + if (buf->state == B3DFG_BUFFER_POPULATED) { +- r = 0; ++ r = w.timeout; + goto out_triplets_dropped; + } + + spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- /* FIXME: what prevents the buffer going away at this time? */ + ++ when = get_cstate_change(fgdev); + if (w.timeout > 0) { + r = wait_event_interruptible_timeout(fgdev->buffer_waitqueue, +- buffer_state(fgdev, buf) == B3DFG_BUFFER_POPULATED, ++ is_event_ready(fgdev, buf, when), + (w.timeout * HZ) / 1000); ++ + if (unlikely(r < 0)) +- return r; +- else if (unlikely(buffer_state(fgdev, buf) +- != B3DFG_BUFFER_POPULATED)) +- return -ETIMEDOUT; ++ goto out; ++ + w.timeout = r * 1000 / HZ; + } else { + r = wait_event_interruptible(fgdev->buffer_waitqueue, +- buffer_state(fgdev, buf) == B3DFG_BUFFER_POPULATED); +- if (unlikely(r)) +- return -ERESTARTSYS; ++ is_event_ready(fgdev, buf, when)); ++ ++ if (unlikely(r)) { ++ r = -ERESTARTSYS; ++ goto out; ++ } ++ } ++ ++ /* TODO: Inform the user via field(s) in w? */ ++ if (!fgdev->transmission_enabled || when != get_cstate_change(fgdev)) { ++ r = -EINVAL; ++ goto out; + } + + spin_lock_irqsave(&fgdev->buffer_lock, flags); +- /* FIXME: rediscover buffer? it might have changed during the unlocked +- * time */ ++ ++ if (buf->state != B3DFG_BUFFER_POPULATED) { ++ r = -ETIMEDOUT; ++ goto out_unlock; ++ } ++ + buf->state = B3DFG_BUFFER_POLLED; + + out_triplets_dropped: ++ ++ /* IRQs already disabled by spin_lock_irqsave above. */ + spin_lock(&fgdev->triplets_dropped_lock); + w.triplets_dropped = fgdev->triplets_dropped; + fgdev->triplets_dropped = 0; + spin_unlock(&fgdev->triplets_dropped_lock); ++ ++out_unlock: ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); + if (copy_to_user(arg, &w, sizeof(w))) + r = -EFAULT; + out: +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); + return r; + } + +-/**** virtual memory mapping ****/ +- +-static void b3dfg_vma_open(struct vm_area_struct *vma) +-{ +- struct b3dfg_dev *fgdev = vma->vm_file->private_data; +- atomic_inc(&fgdev->mapping_count); +-} +- +-static void b3dfg_vma_close(struct vm_area_struct *vma) +-{ +- struct b3dfg_dev *fgdev = vma->vm_file->private_data; +- atomic_dec(&fgdev->mapping_count); +-} +- +-/* page fault handler */ ++/* mmap page fault handler */ + static unsigned long b3dfg_vma_nopfn(struct vm_area_struct *vma, + unsigned long address) + { +@@ -492,7 +439,6 @@ static unsigned long b3dfg_vma_nopfn(str + unsigned long off = address - vma->vm_start; + unsigned int frame_size = fgdev->frame_size; + unsigned int buf_size = frame_size * B3DFG_FRAMES_PER_BUFFER; +- unsigned long flags; + unsigned char *addr; + + /* determine which buffer the offset lies within */ +@@ -505,29 +451,24 @@ static unsigned long b3dfg_vma_nopfn(str + /* and the offset into the frame */ + unsigned int frm_off = buf_off % frame_size; + +- spin_lock_irqsave(&fgdev->buffer_lock, flags); +- if (unlikely(buf_idx > fgdev->num_buffers)) { +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ if (unlikely(buf_idx >= b3dfg_nbuf)) + return NOPFN_SIGBUS; +- } + + addr = fgdev->buffers[buf_idx].frame[frm_idx] + frm_off; + vm_insert_pfn(vma, vma->vm_start + off, +- virt_to_phys(addr) >> PAGE_SHIFT); +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ virt_to_phys(addr) >> PAGE_SHIFT); ++ + return NOPFN_REFAULT; + } + + static struct vm_operations_struct b3dfg_vm_ops = { +- .open = b3dfg_vma_open, +- .close = b3dfg_vma_close, + .nopfn = b3dfg_vma_nopfn, + }; + + static int get_wand_status(struct b3dfg_dev *fgdev, int __user *arg) + { + u32 wndstat = b3dfg_read32(fgdev, B3D_REG_WAND_STS); +- dbg("wand status %x\n", wndstat); ++ dev_dbg(&fgdev->pdev->dev, "wand status %x\n", wndstat); + return __put_user(wndstat & 0x1, arg); + } + +@@ -535,47 +476,63 @@ static int enable_transmission(struct b3 + { + u16 command; + unsigned long flags; ++ struct device *dev = &fgdev->pdev->dev; + +- printk(KERN_INFO PFX "enable transmission\n"); ++ dev_dbg(dev, "enable transmission\n"); + +- /* check we're a bus master */ ++ /* check the cable is plugged in. */ ++ if (!b3dfg_read32(fgdev, B3D_REG_WAND_STS)) { ++ dev_dbg(dev, "cannot start transmission without wand\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Check we're a bus master. ++ * TODO: I think we can remove this having added the pci_set_master call ++ */ + pci_read_config_word(fgdev->pdev, PCI_COMMAND, &command); + if (!(command & PCI_COMMAND_MASTER)) { +- printk(KERN_ERR PFX "not a bus master, force-enabling\n"); +- /* FIXME: why did we lose it in the first place? */ ++ dev_err(dev, "not a bus master, force-enabling\n"); + pci_write_config_word(fgdev->pdev, PCI_COMMAND, + command | PCI_COMMAND_MASTER); + } + + spin_lock_irqsave(&fgdev->buffer_lock, flags); +- if (fgdev->num_buffers == 0) { ++ ++ /* Handle racing enable_transmission calls. */ ++ if (fgdev->transmission_enabled) { + spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- printk(KERN_ERR PFX "cannot start transmission to 0 buffers\n"); +- return -EINVAL; ++ goto out; + } +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); + +- spin_lock_irqsave(&fgdev->triplets_dropped_lock, flags); ++ spin_lock(&fgdev->triplets_dropped_lock); + fgdev->triplets_dropped = 0; +- spin_unlock_irqrestore(&fgdev->triplets_dropped_lock, flags); ++ spin_unlock(&fgdev->triplets_dropped_lock); + + fgdev->triplet_ready = 0; +- fgdev->transmission_enabled = 1; + fgdev->cur_dma_frame_idx = -1; +- b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 1); ++ fgdev->transmission_enabled = 1; ++ ++ spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ ++ /* Enable DMA and cable status interrupts. */ ++ b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0x03); ++ ++out: + return 0; + } + + static void disable_transmission(struct b3dfg_dev *fgdev) + { ++ struct device *dev = &fgdev->pdev->dev; + unsigned long flags; + u32 tmp; + +- printk(KERN_INFO PFX "disable transmission\n"); ++ dev_dbg(dev, "disable transmission\n"); + + /* guarantee that no more interrupts will be serviced */ ++ spin_lock_irqsave(&fgdev->buffer_lock, flags); + fgdev->transmission_enabled = 0; +- synchronize_irq(fgdev->pdev->irq); + + b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0); + +@@ -583,130 +540,238 @@ static void disable_transmission(struct + * hitting ctrl+c and seeing this message is useful for determining + * the state of the board. */ + tmp = b3dfg_read32(fgdev, B3D_REG_DMA_STS); +- dbg("brontes DMA_STS reads %x after TX stopped\n", tmp); ++ dev_dbg(dev, "DMA_STS reads %x after TX stopped\n", tmp); + +- spin_lock_irqsave(&fgdev->buffer_lock, flags); + dequeue_all_buffers(fgdev); + spin_unlock_irqrestore(&fgdev->buffer_lock, flags); ++ ++ wake_up_interruptible(&fgdev->buffer_waitqueue); + } + + static int set_transmission(struct b3dfg_dev *fgdev, int enabled) + { ++ int res = 0; ++ + if (enabled && !fgdev->transmission_enabled) +- return enable_transmission(fgdev); ++ res = enable_transmission(fgdev); + else if (!enabled && fgdev->transmission_enabled) + disable_transmission(fgdev); +- return 0; ++ ++ return res; ++} ++ ++/* Called in interrupt context. */ ++static void handle_cstate_unplug(struct b3dfg_dev *fgdev) ++{ ++ /* Disable all interrupts. */ ++ b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0); ++ ++ /* Stop transmission. */ ++ spin_lock(&fgdev->buffer_lock); ++ fgdev->transmission_enabled = 0; ++ ++ fgdev->cur_dma_frame_idx = -1; ++ fgdev->triplet_ready = 0; ++ if (fgdev->cur_dma_frame_addr) { ++ pci_unmap_single(fgdev->pdev, fgdev->cur_dma_frame_addr, ++ fgdev->frame_size, PCI_DMA_FROMDEVICE); ++ fgdev->cur_dma_frame_addr = 0; ++ } ++ dequeue_all_buffers(fgdev); ++ spin_unlock(&fgdev->buffer_lock); ++} ++ ++/* Called in interrupt context. */ ++static void handle_cstate_change(struct b3dfg_dev *fgdev) ++{ ++ u32 cstate = b3dfg_read32(fgdev, B3D_REG_WAND_STS); ++ unsigned long when; ++ struct device *dev = &fgdev->pdev->dev; ++ ++ dev_dbg(dev, "cable state change: %u\n", cstate); ++ ++ /* ++ * When the wand is unplugged we reset our state. The hardware will ++ * have done the same internally. ++ * ++ * Note we should never see a cable *plugged* event, as interrupts ++ * should only be enabled when transmitting, which requires the cable ++ * to be plugged. If we do see one it probably means the cable has been ++ * unplugged and re-plugged very rapidly. Possibly because it has a ++ * broken wire and is momentarily losing contact. ++ * ++ * TODO: At the moment if you plug in the cable then enable transmission ++ * the hardware will raise a couple of spurious interrupts, so ++ * just ignore them for now. ++ * ++ * Once the hardware is fixed we should complain and treat it as an ++ * unplug. Or at least track how frequently it is happening and do ++ * so if too many come in. ++ */ ++ if (cstate) { ++ dev_warn(dev, "ignoring unexpected plug event\n"); ++ return; ++ } ++ handle_cstate_unplug(fgdev); ++ ++ /* ++ * Record cable state change timestamp & wake anyone waiting ++ * on a cable state change. Be paranoid about ensuring events ++ * are not missed if we somehow get two interrupts in a jiffy. ++ */ ++ spin_lock(&fgdev->cstate_lock); ++ when = jiffies_64; ++ if (when <= fgdev->cstate_tstamp) ++ when = fgdev->cstate_tstamp + 1; ++ fgdev->cstate_tstamp = when; ++ wake_up_interruptible(&fgdev->buffer_waitqueue); ++ spin_unlock(&fgdev->cstate_lock); ++} ++ ++/* Called with buffer_lock held. */ ++static void transfer_complete(struct b3dfg_dev *fgdev) ++{ ++ struct b3dfg_buffer *buf; ++ struct device *dev = &fgdev->pdev->dev; ++ ++ pci_unmap_single(fgdev->pdev, fgdev->cur_dma_frame_addr, ++ fgdev->frame_size, PCI_DMA_FROMDEVICE); ++ fgdev->cur_dma_frame_addr = 0; ++ ++ buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list); ++ if (buf) { ++ dev_dbg(dev, "handle frame completion\n"); ++ if (fgdev->cur_dma_frame_idx == B3DFG_FRAMES_PER_BUFFER - 1) { ++ ++ /* last frame of that triplet completed */ ++ dev_dbg(dev, "triplet completed\n"); ++ buf->state = B3DFG_BUFFER_POPULATED; ++ list_del_init(&buf->list); ++ wake_up_interruptible(&fgdev->buffer_waitqueue); ++ } ++ } else { ++ dev_err(dev, "got frame but no buffer!\n"); ++ } ++} ++ ++/* ++ * Called with buffer_lock held. ++ * ++ * Note that idx is the (1-based) *next* frame to be transferred, while ++ * cur_dma_frame_idx is the (0-based) *last* frame to have been transferred (or ++ * -1 if none). Thus there should be a difference of 2 between them. ++ */ ++static bool setup_next_frame_transfer(struct b3dfg_dev *fgdev, int idx) ++{ ++ struct b3dfg_buffer *buf; ++ struct device *dev = &fgdev->pdev->dev; ++ bool need_ack = 1; ++ ++ dev_dbg(dev, "program DMA transfer for next frame: %d\n", idx); ++ ++ buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list); ++ if (buf) { ++ if (idx == fgdev->cur_dma_frame_idx + 2) { ++ if (setup_frame_transfer(fgdev, buf, idx - 1)) ++ dev_err(dev, "unable to map DMA buffer\n"); ++ need_ack = 0; ++ } else { ++ dev_err(dev, "frame mismatch, got %d, expected %d\n", ++ idx, fgdev->cur_dma_frame_idx + 2); ++ ++ /* FIXME: handle dropped triplets here */ ++ } ++ } else { ++ dev_err(dev, "cannot setup DMA, no buffer\n"); ++ } ++ ++ return need_ack; + } + + static irqreturn_t b3dfg_intr(int irq, void *dev_id) + { + struct b3dfg_dev *fgdev = dev_id; +- struct device *dev; +- struct b3dfg_buffer *buf = NULL; +- unsigned int frame_size; ++ struct device *dev = &fgdev->pdev->dev; + u32 sts; + u8 dropped; +- int next_trf; +- int need_ack = 1; ++ bool need_ack = 1; ++ irqreturn_t res = IRQ_HANDLED; + +- if (unlikely(!fgdev->transmission_enabled)) { +- printk("ignore interrupt, TX disabled\n"); +- /* FIXME should return IRQ_NONE when we are stable */ ++ sts = b3dfg_read32(fgdev, B3D_REG_DMA_STS); ++ if (unlikely(sts == 0)) { ++ dev_warn(dev, "ignore interrupt, DMA status is 0\n"); ++ res = IRQ_NONE; + goto out; + } + +- sts = b3dfg_read32(fgdev, B3D_REG_DMA_STS); +- if (unlikely(sts == 0)) { +- printk("ignore interrupt, brontes DMA status is 0\n"); +- /* FIXME should return IRQ_NONE when we are stable */ ++ if (unlikely(!fgdev->transmission_enabled)) { ++ dev_warn(dev, "ignore interrupt, TX disabled\n"); ++ res = IRQ_HANDLED; + goto out; + } + ++ /* Handle dropped frames, as reported by the hardware. */ + dropped = (sts >> 8) & 0xff; +- dbg(KERN_INFO PFX "got intr, brontes DMASTS=%08x (dropped=%d comp=%d next_trf=%d)\n", sts, dropped, !!(sts & 0x4), sts & 0x3); +- ++ dev_dbg(dev, "intr: DMA_STS=%08x (drop=%d comp=%d next=%d)\n", ++ sts, dropped, !!(sts & 0x4), sts & 0x3); + if (unlikely(dropped > 0)) { + spin_lock(&fgdev->triplets_dropped_lock); + fgdev->triplets_dropped += dropped; + spin_unlock(&fgdev->triplets_dropped_lock); + } + +- dev = &fgdev->pdev->dev; +- frame_size = fgdev->frame_size; ++ /* Handle a cable state change (i.e. the wand being unplugged). */ ++ if (sts & 0x08) { ++ handle_cstate_change(fgdev); ++ goto out; ++ } + + spin_lock(&fgdev->buffer_lock); + if (unlikely(list_empty(&fgdev->buffer_queue))) { ++ + /* FIXME need more sanity checking here */ +- dbg("driver has no buffer ready --> cannot program any more transfers\n"); ++ dev_info(dev, "buffer not ready for next transfer\n"); + fgdev->triplet_ready = 1; + goto out_unlock; + } + +- next_trf = sts & 0x3; +- ++ /* Has a frame transfer been completed? */ + if (sts & 0x4) { +- u32 tmp; ++ u32 dma_status = b3dfg_read32(fgdev, B3D_REG_EC220_DMA_STS); ++ ++ /* Check for DMA errors reported by the hardware. */ ++ if (unlikely(dma_status & 0x1)) { ++ dev_err(dev, "EC220 error: %08x\n", dma_status); + +- tmp = b3dfg_read32(fgdev, B3D_REG_EC220_DMA_STS); +- /* last DMA completed */ +- if (unlikely(tmp & 0x1)) { +- printk(KERN_ERR PFX "EC220 reports error (%08x)\n", tmp); + /* FIXME flesh out error handling */ + goto out_unlock; + } ++ ++ /* Sanity check, we should have a frame index at this point. */ + if (unlikely(fgdev->cur_dma_frame_idx == -1)) { +- printk("ERROR completed but no last idx?\n"); ++ dev_err(dev, "completed but no last idx?\n"); ++ + /* FIXME flesh out error handling */ + goto out_unlock; + } +- dma_unmap_single(dev, fgdev->cur_dma_frame_addr, frame_size, +- DMA_FROM_DEVICE); + +- buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list); +- if (likely(buf)) { +- dbg("handle frame completion\n"); +- if (fgdev->cur_dma_frame_idx == B3DFG_FRAMES_PER_BUFFER - 1) { +- /* last frame of that triplet completed */ +- dbg("triplet completed\n"); +- buf->state = B3DFG_BUFFER_POPULATED; +- list_del_init(&buf->list); +- wake_up_interruptible(&fgdev->buffer_waitqueue); +- } +- } else { +- printk("got frame but no buffer!\n"); +- } ++ transfer_complete(fgdev); + } + +- if (next_trf) { +- next_trf--; +- +- buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list); +- dbg("program DMA transfer for frame %d\n", next_trf + 1); +- if (likely(buf)) { +- if (next_trf != fgdev->cur_dma_frame_idx + 1) { +- printk("ERROR mismatch, next_trf %d vs cur_dma_frame_idx %d\n", +- next_trf, fgdev->cur_dma_frame_idx); +- /* FIXME this is where we should handle dropped triplets */ +- goto out_unlock; +- } +- setup_frame_transfer(fgdev, buf, next_trf, 1); +- need_ack = 0; +- } else { +- printk("cannot setup next DMA due to no buffer\n"); +- } +- } else { ++ /* Is there another frame transfer pending? */ ++ if (sts & 0x3) ++ need_ack = setup_next_frame_transfer(fgdev, sts & 0x3); ++ else + fgdev->cur_dma_frame_idx = -1; +- } + + out_unlock: + spin_unlock(&fgdev->buffer_lock); + out: + if (need_ack) { +- dbg("acknowledging interrupt\n"); ++ dev_dbg(dev, "acknowledging interrupt\n"); + b3dfg_write32(fgdev, B3D_REG_EC220_DMA_STS, 0x0b); + } +- return IRQ_HANDLED; ++ return res; + } + + static int b3dfg_open(struct inode *inode, struct file *filp) +@@ -714,7 +779,7 @@ static int b3dfg_open(struct inode *inod + struct b3dfg_dev *fgdev = + container_of(inode->i_cdev, struct b3dfg_dev, chardev); + +- printk(KERN_INFO PFX "open\n"); ++ dev_dbg(&fgdev->pdev->dev, "open\n"); + filp->private_data = fgdev; + return 0; + } +@@ -722,18 +787,14 @@ static int b3dfg_open(struct inode *inod + static int b3dfg_release(struct inode *inode, struct file *filp) + { + struct b3dfg_dev *fgdev = filp->private_data; +- printk(KERN_INFO PFX "release\n"); +- set_transmission(fgdev, 0); +- +- /* no buffer locking needed, this is serialized */ +- dequeue_all_buffers(fgdev); +- return set_num_buffers(fgdev, 0); ++ dev_dbg(&fgdev->pdev->dev, "release\n"); ++ disable_transmission(fgdev); ++ return 0; + } + + static long b3dfg_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + { + struct b3dfg_dev *fgdev = filp->private_data; +- int r; + + switch (cmd) { + case B3DFG_IOCGFRMSZ: +@@ -741,15 +802,11 @@ static long b3dfg_ioctl(struct file *fil + case B3DFG_IOCGWANDSTAT: + return get_wand_status(fgdev, (int __user *) arg); + case B3DFG_IOCTNUMBUFS: +- mutex_lock(&fgdev->ioctl_mutex); +- r = set_num_buffers(fgdev, (int) arg); +- mutex_unlock(&fgdev->ioctl_mutex); +- return r; ++ ++ /* TODO: Remove once userspace stops using this. */ ++ return 0; + case B3DFG_IOCTTRANS: +- mutex_lock(&fgdev->ioctl_mutex); +- r = set_transmission(fgdev, (int) arg); +- mutex_unlock(&fgdev->ioctl_mutex); +- return r; ++ return set_transmission(fgdev, (int) arg); + case B3DFG_IOCTQUEUEBUF: + return queue_buffer(fgdev, (int) arg); + case B3DFG_IOCTPOLLBUF: +@@ -757,7 +814,7 @@ static long b3dfg_ioctl(struct file *fil + case B3DFG_IOCTWAITBUF: + return wait_buffer(fgdev, (void __user *) arg); + default: +- printk(KERN_ERR PFX "unrecognised ioctl %x\n", cmd); ++ dev_dbg(&fgdev->pdev->dev, "unrecognised ioctl %x\n", cmd); + return -EINVAL; + } + } +@@ -765,32 +822,26 @@ static long b3dfg_ioctl(struct file *fil + static unsigned int b3dfg_poll(struct file *filp, poll_table *poll_table) + { + struct b3dfg_dev *fgdev = filp->private_data; +- unsigned long flags; ++ unsigned long flags, when; + int i; + int r = 0; + +- /* don't let the user mess with buffer allocations etc. while polling */ +- mutex_lock(&fgdev->ioctl_mutex); +- +- if (unlikely(!fgdev->transmission_enabled)) { +- printk(KERN_ERR PFX "cannot poll() when transmission is disabled\n"); +- r = POLLERR; +- goto out; +- } +- ++ when = get_cstate_change(fgdev); + poll_wait(filp, &fgdev->buffer_waitqueue, poll_table); ++ + spin_lock_irqsave(&fgdev->buffer_lock, flags); +- for (i = 0; i < fgdev->num_buffers; i++) { ++ for (i = 0; i < b3dfg_nbuf; i++) { + if (fgdev->buffers[i].state == B3DFG_BUFFER_POPULATED) { + r = POLLIN | POLLRDNORM; +- goto out_buffer_unlock; ++ break; + } + } +- +-out_buffer_unlock: + spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +-out: +- mutex_unlock(&fgdev->ioctl_mutex); ++ ++ /* TODO: Confirm this is how we want to communicate the change. */ ++ if (!fgdev->transmission_enabled || when != get_cstate_change(fgdev)) ++ r = POLLERR; ++ + return r; + } + +@@ -799,35 +850,18 @@ static int b3dfg_mmap(struct file *filp, + struct b3dfg_dev *fgdev = filp->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long vsize = vma->vm_end - vma->vm_start; +- unsigned long bufdatalen; +- unsigned long psize; +- unsigned long flags; ++ unsigned long bufdatalen = b3dfg_nbuf * fgdev->frame_size * 3; ++ unsigned long psize = bufdatalen - offset; + int r = 0; + +- /* don't let user mess with buffer allocations during mmap */ +- mutex_lock(&fgdev->ioctl_mutex); +- +- spin_lock_irqsave(&fgdev->buffer_lock, flags); +- bufdatalen = fgdev->num_buffers * fgdev->frame_size * 3; +- psize = bufdatalen - offset; +- +- if (fgdev->num_buffers == 0) { +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- r = -ENOENT; +- goto out; +- } +- spin_unlock_irqrestore(&fgdev->buffer_lock, flags); +- if (vsize > psize) { ++ if (vsize <= psize) { ++ vma->vm_flags |= VM_IO | VM_RESERVED | VM_CAN_NONLINEAR | ++ VM_PFNMAP; ++ vma->vm_ops = &b3dfg_vm_ops; ++ } else { + r = -EINVAL; +- goto out; + } + +- vma->vm_flags |= VM_IO | VM_RESERVED | VM_CAN_NONLINEAR | VM_PFNMAP; +- vma->vm_ops = &b3dfg_vm_ops; +- b3dfg_vma_open(vma); +- +-out: +- mutex_unlock(&fgdev->ioctl_mutex); + return r; + } + +@@ -842,43 +876,55 @@ static struct file_operations b3dfg_fops + + static void free_all_frame_buffers(struct b3dfg_dev *fgdev) + { +- int i; +- for (i = 0; i < B3DFG_NR_FRAME_BUFFERS; i++) +- kfree(fgdev->frame_buffer[i]); ++ int i, j; ++ for (i = 0; i < b3dfg_nbuf; i++) ++ for (j = 0; j < B3DFG_FRAMES_PER_BUFFER; j++) ++ kfree(fgdev->buffers[i].frame[j]); ++ kfree(fgdev->buffers); + } + + /* initialize device and any data structures. called before any interrupts + * are enabled. */ + static int b3dfg_init_dev(struct b3dfg_dev *fgdev) + { +- int i; ++ int i, j; + u32 frm_size = b3dfg_read32(fgdev, B3D_REG_FRM_SIZE); + +- /* disable interrupts. in abnormal circumstances (e.g. after a crash) the +- * board may still be transmitting from the previous session. if we ensure +- * that interrupts are disabled before we later enable them, we are sure +- * to capture a triplet from the start, rather than starting from frame +- * 2 or 3. disabling interrupts causes the FG to throw away all buffered +- * data and stop buffering more until interrupts are enabled again. */ ++ /* Disable interrupts. In abnormal circumstances (e.g. after a crash) ++ * the board may still be transmitting from the previous session. If we ++ * ensure that interrupts are disabled before we later enable them, we ++ * are sure to capture a triplet from the start, rather than starting ++ * from frame 2 or 3. Disabling interrupts causes the FG to throw away ++ * all buffered data and stop buffering more until interrupts are ++ * enabled again. ++ */ + b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0); + + fgdev->frame_size = frm_size * 4096; +- for (i = 0; i < B3DFG_NR_FRAME_BUFFERS; i++) { +- fgdev->frame_buffer[i] = kmalloc(fgdev->frame_size, GFP_KERNEL); +- if (!fgdev->frame_buffer[i]) +- goto err_no_mem; ++ fgdev->buffers = kzalloc(sizeof(struct b3dfg_buffer) * b3dfg_nbuf, ++ GFP_KERNEL); ++ if (!fgdev->buffers) ++ goto err_no_buf; ++ for (i = 0; i < b3dfg_nbuf; i++) { ++ struct b3dfg_buffer *buf = &fgdev->buffers[i]; ++ for (j = 0; j < B3DFG_FRAMES_PER_BUFFER; j++) { ++ buf->frame[j] = kmalloc(fgdev->frame_size, GFP_KERNEL); ++ if (!buf->frame[j]) ++ goto err_no_mem; ++ } ++ INIT_LIST_HEAD(&buf->list); + } + + INIT_LIST_HEAD(&fgdev->buffer_queue); + init_waitqueue_head(&fgdev->buffer_waitqueue); + spin_lock_init(&fgdev->buffer_lock); ++ spin_lock_init(&fgdev->cstate_lock); + spin_lock_init(&fgdev->triplets_dropped_lock); +- atomic_set(&fgdev->mapping_count, 0); +- mutex_init(&fgdev->ioctl_mutex); + return 0; + + err_no_mem: + free_all_frame_buffers(fgdev); ++err_no_buf: + return -ENOMEM; + } + +@@ -900,84 +946,112 @@ static int __devinit b3dfg_probe(struct + int r = 0; + int minor = get_free_minor(); + dev_t devno = MKDEV(MAJOR(b3dfg_devt), minor); ++ unsigned long res_len; ++ resource_size_t res_base; + + if (fgdev == NULL) + return -ENOMEM; + + if (minor < 0) { +- printk(KERN_ERR PFX "too many devices found!\n"); +- return -EIO; ++ dev_err(&pdev->dev, "too many devices found!\n"); ++ r = -EIO; ++ goto err_free; + } + + b3dfg_devices[minor] = 1; +- printk(KERN_INFO PFX "probe device at %s with IRQ %d\n", +- pci_name(pdev), pdev->irq); ++ dev_info(&pdev->dev, "probe device with IRQ %d\n", pdev->irq); + + cdev_init(&fgdev->chardev, &b3dfg_fops); + fgdev->chardev.owner = THIS_MODULE; + + r = cdev_add(&fgdev->chardev, devno, 1); +- if (r) +- goto err1; ++ if (r) { ++ dev_err(&pdev->dev, "cannot add char device\n"); ++ goto err_release_minor; ++ } + +- fgdev->classdev = class_device_create(b3dfg_class, NULL, devno, &pdev->dev, +- DRIVER_NAME "%d", minor); ++ fgdev->classdev = class_device_create(b3dfg_class, NULL, devno, ++ &pdev->dev, DRIVER_NAME "%d", ++ minor); + if (IS_ERR(fgdev->classdev)) { ++ dev_err(&pdev->dev, "cannot create class device\n"); + r = PTR_ERR(fgdev->classdev); +- goto err2; ++ goto err_del_cdev; + } + + r = pci_enable_device(pdev); +- if (r) +- goto err3; ++ if (r) { ++ dev_err(&pdev->dev, "cannot enable PCI device\n"); ++ goto err_dev_unreg; ++ } + +- if (pci_resource_len(pdev, B3DFG_BAR_REGS) != B3DFG_REGS_LENGTH) { +- printk(KERN_ERR PFX "invalid register resource size\n"); +- goto err4; ++ res_len = pci_resource_len(pdev, B3DFG_BAR_REGS); ++ if (res_len != B3DFG_REGS_LENGTH) { ++ dev_err(&pdev->dev, "invalid register resource size\n"); ++ r = -EIO; ++ goto err_disable; + } + + if (pci_resource_flags(pdev, B3DFG_BAR_REGS) != IORESOURCE_MEM) { +- printk(KERN_ERR PFX "invalid resource flags"); +- goto err4; ++ dev_err(&pdev->dev, "invalid resource flags\n"); ++ r = -EIO; ++ goto err_disable; + } + +- fgdev->regs = ioremap_nocache(pci_resource_start(pdev, B3DFG_BAR_REGS), +- B3DFG_REGS_LENGTH); ++ r = pci_request_regions(pdev, DRIVER_NAME); ++ if (r) { ++ dev_err(&pdev->dev, "cannot obtain PCI resources\n"); ++ goto err_disable; ++ } ++ ++ pci_set_master(pdev); ++ ++ r = pci_set_dma_mask(pdev, DMA_32BIT_MASK); ++ if (r) { ++ dev_err(&pdev->dev, "no usable DMA configuration\n"); ++ goto err_free_res; ++ } ++ ++ res_base = pci_resource_start(pdev, B3DFG_BAR_REGS); ++ fgdev->regs = ioremap_nocache(res_base, res_len); + if (!fgdev->regs) { +- printk(KERN_ERR PFX "regs ioremap failed\n"); +- goto err4; ++ dev_err(&pdev->dev, "regs ioremap failed\n"); ++ r = -EIO; ++ goto err_free_res; + } + + fgdev->pdev = pdev; + pci_set_drvdata(pdev, fgdev); + r = b3dfg_init_dev(fgdev); + if (r < 0) { +- printk(KERN_ERR PFX "failed to initalize device\n"); +- goto err5; ++ dev_err(&pdev->dev, "failed to initalize device\n"); ++ goto err_unmap; + } + + r = request_irq(pdev->irq, b3dfg_intr, IRQF_SHARED, DRIVER_NAME, fgdev); + if (r) { +- printk(KERN_ERR PFX "couldn't request irq %d\n", pdev->irq); +- goto err6; ++ dev_err(&pdev->dev, "couldn't request irq %d\n", pdev->irq); ++ goto err_free_bufs; + } + + return 0; + +-err6: ++err_free_bufs: + free_all_frame_buffers(fgdev); +-err5: ++err_unmap: + iounmap(fgdev->regs); +-err4: ++err_free_res: ++ pci_release_regions(pdev); ++err_disable: + pci_disable_device(pdev); +-err3: ++err_dev_unreg: + class_device_unregister(fgdev->classdev); +-err2: ++err_del_cdev: + cdev_del(&fgdev->chardev); +-err1: ++err_release_minor: ++ b3dfg_devices[minor] = 0; ++err_free: + kfree(fgdev); +- if (minor >= 0) +- b3dfg_devices[minor] = 0; + return r; + } + +@@ -986,10 +1060,11 @@ static void __devexit b3dfg_remove(struc + struct b3dfg_dev *fgdev = pci_get_drvdata(pdev); + unsigned int minor = MINOR(fgdev->chardev.dev); + +- printk(KERN_INFO PFX "remove\n"); ++ dev_dbg(&pdev->dev, "remove\n"); + + free_irq(pdev->irq, fgdev); + iounmap(fgdev->regs); ++ pci_release_regions(pdev); + pci_disable_device(pdev); + class_device_unregister(fgdev->classdev); + cdev_del(&fgdev->chardev); +@@ -1002,14 +1077,20 @@ static struct pci_driver b3dfg_driver = + .name = DRIVER_NAME, + .id_table = b3dfg_ids, + .probe = b3dfg_probe, +- .remove = b3dfg_remove, ++ .remove = __devexit_p(b3dfg_remove), + }; + + static int __init b3dfg_module_init(void) + { + int r; + +- printk(KERN_INFO PFX "loaded\n"); ++ if (b3dfg_nbuf < 2) { ++ printk(KERN_ERR DRIVER_NAME ++ ": buffer_count is out of range (must be >= 2)"); ++ return -EINVAL; ++ } ++ ++ printk(KERN_INFO DRIVER_NAME ": loaded\n"); + + b3dfg_class = class_create(THIS_MODULE, DRIVER_NAME); + if (IS_ERR(b3dfg_class)) +@@ -1034,7 +1115,7 @@ err1: + + static void __exit b3dfg_module_exit(void) + { +- printk(KERN_INFO PFX "unloaded\n"); ++ printk(KERN_INFO DRIVER_NAME ": unloaded\n"); + pci_unregister_driver(&b3dfg_driver); + unregister_chrdev_region(b3dfg_devt, B3DFG_MAX_DEVS); + class_destroy(b3dfg_class); +@@ -1042,8 +1123,3 @@ static void __exit b3dfg_module_exit(voi + + module_init(b3dfg_module_init); + module_exit(b3dfg_module_exit); +-MODULE_AUTHOR("Daniel Drake <ddrake@brontes3d.com>"); +-MODULE_DESCRIPTION("Brontes frame grabber driver"); +-MODULE_LICENSE("GPL"); +-MODULE_DEVICE_TABLE(pci, b3dfg_ids); +- diff --git a/staging/staging-b3dfg-prepare-b3dfg-for-submission-upstream.patch b/staging/staging-b3dfg-prepare-b3dfg-for-submission-upstream.patch new file mode 100644 index 00000000000000..aeccba7acb24f3 --- /dev/null +++ b/staging/staging-b3dfg-prepare-b3dfg-for-submission-upstream.patch @@ -0,0 +1,277 @@ +From 20e673d4a906066336a6a45036918fcb7833e611 Mon Sep 17 00:00:00 2001 +From: Justin Bronder <jsbronder@brontes3d.com> +Date: Wed, 28 Jan 2009 10:06:42 -0500 +Subject: Staging: b3dfg: Prepare b3dfg for submission upstream. + +From: Justin Bronder <jsbronder@brontes3d.com> + +- Basically, update driver to run with 2.6.28 + - Conversion from struct class_device to struct device. + - Conversion from .nopfn to .fault in vm_operations_struct. + - Update use of pci_resource_flags to check for IORESOURCE_SIZEALIGN. + - Update use of pci_dma_mapping_error. +- Minor code cleanup and integration with kernel build system. + +Signed-off-by: Justin Bronder <jsbronder@brontes3d.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/b3dfg/Kconfig | 9 ++++ + drivers/staging/b3dfg/Makefile | 1 + drivers/staging/b3dfg/TODO | 4 + + drivers/staging/b3dfg/b3dfg.c | 86 +++++++++++++++++++---------------------- + 6 files changed, 57 insertions(+), 46 deletions(-) + +--- a/drivers/staging/b3dfg/b3dfg.c ++++ b/drivers/staging/b3dfg/b3dfg.c +@@ -2,7 +2,9 @@ + * Brontes PCI frame grabber driver + * + * Copyright (C) 2008 3M Company +- * Contact: Daniel Drake <ddrake@brontes3d.com> ++ * Contact: Justin Bronder <jsbronder@brontes3d.com> ++ * Original Authors: Daniel Drake <ddrake@brontes3d.com> ++ * Duane Griffin <duaneg@dghda.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -34,14 +36,7 @@ + #include <linux/wait.h> + #include <linux/mm.h> + #include <linux/version.h> +- +-#include <asm/uaccess.h> +- +-/* TODO: +- * queue/wait buffer presents filltime results for each frame? +- * counting of dropped frames +- * review endianness +- */ ++#include <linux/uaccess.h> + + static unsigned int b3dfg_nbuf = 2; + +@@ -119,7 +114,7 @@ struct b3dfg_dev { + /* no protection needed: all finalized at initialization time */ + struct pci_dev *pdev; + struct cdev chardev; +- struct class_device *classdev; ++ struct device *dev; + void __iomem *regs; + unsigned int frame_size; + +@@ -164,10 +159,6 @@ static dev_t b3dfg_devt; + + static const struct pci_device_id b3dfg_ids[] __devinitdata = { + { PCI_DEVICE(0x0b3d, 0x0001) }, +- +- /* FIXME: remove this ID once all boards have been moved to 0xb3d. +- * Eureka's vendor ID that we borrowed before we bought our own. */ +- { PCI_DEVICE(0x1901, 0x0001) }, + { }, + }; + +@@ -213,15 +204,17 @@ static int setup_frame_transfer(struct b + + frm_addr = buf->frame[frame]; + frm_addr_dma = pci_map_single(fgdev->pdev, frm_addr, +- frm_size, PCI_DMA_FROMDEVICE); +- if (pci_dma_mapping_error(frm_addr_dma)) ++ frm_size, PCI_DMA_FROMDEVICE); ++ if (pci_dma_mapping_error(fgdev->pdev, frm_addr_dma)) + return -ENOMEM; + + fgdev->cur_dma_frame_addr = frm_addr_dma; + fgdev->cur_dma_frame_idx = frame; + +- b3dfg_write32(fgdev, B3D_REG_EC220_DMA_ADDR, cpu_to_le32(frm_addr_dma)); +- b3dfg_write32(fgdev, B3D_REG_EC220_TRF_SIZE, cpu_to_le32(frm_size >> 2)); ++ b3dfg_write32(fgdev, B3D_REG_EC220_DMA_ADDR, ++ cpu_to_le32(frm_addr_dma)); ++ b3dfg_write32(fgdev, B3D_REG_EC220_TRF_SIZE, ++ cpu_to_le32(frm_size >> 2)); + b3dfg_write32(fgdev, B3D_REG_EC220_DMA_STS, 0xf); + + return 0; +@@ -248,6 +241,7 @@ static int queue_buffer(struct b3dfg_dev + + spin_lock_irqsave(&fgdev->buffer_lock, flags); + if (bufidx < 0 || bufidx >= b3dfg_nbuf) { ++ dev_dbg(dev, "Invalid buffer index, %d\n", bufidx); + r = -ENOENT; + goto out; + } +@@ -432,11 +426,11 @@ out: + } + + /* mmap page fault handler */ +-static unsigned long b3dfg_vma_nopfn(struct vm_area_struct *vma, +- unsigned long address) ++static int b3dfg_vma_fault(struct vm_area_struct *vma, ++ struct vm_fault *vmf) + { + struct b3dfg_dev *fgdev = vma->vm_file->private_data; +- unsigned long off = address - vma->vm_start; ++ unsigned long off = vmf->pgoff << PAGE_SHIFT; + unsigned int frame_size = fgdev->frame_size; + unsigned int buf_size = frame_size * B3DFG_FRAMES_PER_BUFFER; + unsigned char *addr; +@@ -452,17 +446,17 @@ static unsigned long b3dfg_vma_nopfn(str + unsigned int frm_off = buf_off % frame_size; + + if (unlikely(buf_idx >= b3dfg_nbuf)) +- return NOPFN_SIGBUS; ++ return VM_FAULT_SIGBUS; + + addr = fgdev->buffers[buf_idx].frame[frm_idx] + frm_off; +- vm_insert_pfn(vma, vma->vm_start + off, +- virt_to_phys(addr) >> PAGE_SHIFT); ++ vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, ++ virt_to_phys(addr) >> PAGE_SHIFT); + +- return NOPFN_REFAULT; ++ return VM_FAULT_NOPAGE; + } + + static struct vm_operations_struct b3dfg_vm_ops = { +- .nopfn = b3dfg_vma_nopfn, ++ .fault = b3dfg_vma_fault, + }; + + static int get_wand_status(struct b3dfg_dev *fgdev, int __user *arg) +@@ -601,12 +595,12 @@ static void handle_cstate_change(struct + * broken wire and is momentarily losing contact. + * + * TODO: At the moment if you plug in the cable then enable transmission +- * the hardware will raise a couple of spurious interrupts, so +- * just ignore them for now. ++ * the hardware will raise a couple of spurious interrupts, so ++ * just ignore them for now. + * +- * Once the hardware is fixed we should complain and treat it as an +- * unplug. Or at least track how frequently it is happening and do +- * so if too many come in. ++ * Once the hardware is fixed we should complain and treat it as an ++ * unplug. Or at least track how frequently it is happening and do ++ * so if too many come in. + */ + if (cstate) { + dev_warn(dev, "ignoring unexpected plug event\n"); +@@ -801,10 +795,6 @@ static long b3dfg_ioctl(struct file *fil + return __put_user(fgdev->frame_size, (int __user *) arg); + case B3DFG_IOCGWANDSTAT: + return get_wand_status(fgdev, (int __user *) arg); +- case B3DFG_IOCTNUMBUFS: +- +- /* TODO: Remove once userspace stops using this. */ +- return 0; + case B3DFG_IOCTTRANS: + return set_transmission(fgdev, (int) arg); + case B3DFG_IOCTQUEUEBUF: +@@ -970,12 +960,16 @@ static int __devinit b3dfg_probe(struct + goto err_release_minor; + } + +- fgdev->classdev = class_device_create(b3dfg_class, NULL, devno, +- &pdev->dev, DRIVER_NAME "%d", +- minor); +- if (IS_ERR(fgdev->classdev)) { +- dev_err(&pdev->dev, "cannot create class device\n"); +- r = PTR_ERR(fgdev->classdev); ++ fgdev->dev = device_create( ++ b3dfg_class, ++ &pdev->dev, ++ devno, ++ dev_get_drvdata(&pdev->dev), ++ DRIVER_NAME "%d", minor); ++ ++ if (IS_ERR(fgdev->dev)) { ++ dev_err(&pdev->dev, "cannot create device\n"); ++ r = PTR_ERR(fgdev->dev); + goto err_del_cdev; + } + +@@ -992,12 +986,12 @@ static int __devinit b3dfg_probe(struct + goto err_disable; + } + +- if (pci_resource_flags(pdev, B3DFG_BAR_REGS) != IORESOURCE_MEM) { ++ if (pci_resource_flags(pdev, B3DFG_BAR_REGS) ++ != (IORESOURCE_MEM | IORESOURCE_SIZEALIGN)) { + dev_err(&pdev->dev, "invalid resource flags\n"); + r = -EIO; + goto err_disable; + } +- + r = pci_request_regions(pdev, DRIVER_NAME); + if (r) { + dev_err(&pdev->dev, "cannot obtain PCI resources\n"); +@@ -1045,7 +1039,7 @@ err_free_res: + err_disable: + pci_disable_device(pdev); + err_dev_unreg: +- class_device_unregister(fgdev->classdev); ++ device_destroy(b3dfg_class, devno); + err_del_cdev: + cdev_del(&fgdev->chardev); + err_release_minor: +@@ -1066,7 +1060,7 @@ static void __devexit b3dfg_remove(struc + iounmap(fgdev->regs); + pci_release_regions(pdev); + pci_disable_device(pdev); +- class_device_unregister(fgdev->classdev); ++ device_destroy(b3dfg_class, MKDEV(MAJOR(b3dfg_devt), minor)); + cdev_del(&fgdev->chardev); + free_all_frame_buffers(fgdev); + kfree(fgdev); +@@ -1086,7 +1080,7 @@ static int __init b3dfg_module_init(void + + if (b3dfg_nbuf < 2) { + printk(KERN_ERR DRIVER_NAME +- ": buffer_count is out of range (must be >= 2)"); ++ ": buffer_count is out of range (must be >= 2)"); + return -EINVAL; + } + +--- /dev/null ++++ b/drivers/staging/b3dfg/Kconfig +@@ -0,0 +1,9 @@ ++config B3DFG ++ tristate "Brontes 3d Frame Framegrabber" ++ default n ++ ---help--- ++ This driver provides support for the Brontes 3d Framegrabber ++ PCI card. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called b3dfg. +--- /dev/null ++++ b/drivers/staging/b3dfg/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_B3DFG) += b3dfg.o +--- /dev/null ++++ b/drivers/staging/b3dfg/TODO +@@ -0,0 +1,4 @@ ++ ++ - queue/wait buffer presents filltime results for each frame? ++ - counting of dropped frames ++ - review endianness +--- a/drivers/staging/Kconfig ++++ b/drivers/staging/Kconfig +@@ -101,5 +101,7 @@ source "drivers/staging/stlc45xx/Kconfig + + source "drivers/staging/uc2322/Kconfig" + ++source "drivers/staging/b3dfg/Kconfig" ++ + endif # !STAGING_EXCLUDE_BUILD + endif # STAGING +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -33,3 +33,4 @@ obj-$(CONFIG_ANDROID) += android/ + obj-$(CONFIG_DST) += dst/ + obj-$(CONFIG_STLC45XX) += stlc45xx/ + obj-$(CONFIG_USB_SERIAL_ATEN2011) += uc2322/ ++obj-$(CONFIG_B3DFG) += b3dfg/ diff --git a/staging/staging-poch-fix-verification-of-memory-area.patch b/staging/staging-poch-fix-verification-of-memory-area.patch new file mode 100644 index 00000000000000..07c9a8fa2f7264 --- /dev/null +++ b/staging/staging-poch-fix-verification-of-memory-area.patch @@ -0,0 +1,29 @@ +From roel.kluin@gmail.com Wed Jan 28 14:00:58 2009 +From: Roel Kluin <roel.kluin@gmail.com> +Date: Wed, 28 Jan 2009 22:14:17 +0100 +Subject: Staging: poch: fix verification of memory area +To: Greg KH <gregkh@suse.de> +Cc: lkml <linux-kernel@vger.kernel.org> +Message-ID: <4980CAA9.1060301@gmail.com> + + +fix verification of memory area + +Signed-off-by: Roel Kluin <roel.kluin@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/staging/poch/poch.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/staging/poch/poch.c ++++ b/drivers/staging/poch/poch.c +@@ -1026,7 +1026,7 @@ static int poch_ioctl(struct inode *inod + } + break; + case POCH_IOC_GET_COUNTERS: +- if (access_ok(VERIFY_WRITE, argp, sizeof(struct poch_counters))) ++ if (!access_ok(VERIFY_WRITE, argp, sizeof(struct poch_counters))) + return -EFAULT; + + spin_lock_irq(&channel->counters_lock); diff --git a/staging/staging-sxg-add-multicast-support-for-sahara-sxg-driver.patch b/staging/staging-sxg-add-multicast-support-for-sahara-sxg-driver.patch new file mode 100644 index 00000000000000..1f65a89233ea66 --- /dev/null +++ b/staging/staging-sxg-add-multicast-support-for-sahara-sxg-driver.patch @@ -0,0 +1,320 @@ +From mithlesh@linsyssoft.com Wed Jan 28 13:57:57 2009 +From: Mithlesh Thukral <mithlesh@linsyssoft.com> +Date: Wed, 28 Jan 2009 07:08:11 +0530 (IST) +Subject: Staging: sxg: Add multicast support for Sahara SXG driver +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Sahara Project <saharaproj@linsyssoft.com>, Michael Miles <mmiles@alacritech.com>, Christopher Harrer <charrer@alacritech.com> +Message-ID: <alpine.LFD.2.00.0901280706290.18077@localhost.localdomain> + +From: Mithlesh Thukral <mithlesh@linsyssoft.com> + +* Add multicast support for SXG driver for Alacritech's 10Gbe products. + +Signed-off-by: LinSysSoft Sahara Team <saharaproj@linsyssoft.com> +Signed-off-by: Mithlesh Thukral <mithlesh@linsyssoft.com> +Cc: Michael Miles <mmiles@alacritech.com> +Cc: Christopher Harrer <charrer@alacritech.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/staging/sxg/sxg.c | 127 ++++++++++++++------------------------ + drivers/staging/sxg/sxg_ethtool.c | 4 - + 2 files changed, 52 insertions(+), 79 deletions(-) + +--- a/drivers/staging/sxg/sxg.c ++++ b/drivers/staging/sxg/sxg.c +@@ -119,10 +119,8 @@ static void sxg_complete_slow_send(struc + static struct sk_buff *sxg_slow_receive(struct adapter_t *adapter, + struct sxg_event *Event); + static void sxg_process_rcv_error(struct adapter_t *adapter, u32 ErrorStatus); +-/* See if we need sxg_mac_filter() in future. If not remove it + static bool sxg_mac_filter(struct adapter_t *adapter, + struct ether_header *EtherHdr, ushort length); +-*/ + static struct net_device_stats *sxg_get_stats(struct net_device * dev); + void sxg_free_resources(struct adapter_t *adapter); + void sxg_free_rcvblocks(struct adapter_t *adapter); +@@ -155,6 +153,7 @@ static int sxg_write_mdio_reg(struct ada + u32 DevAddr, u32 RegAddr, u32 Value); + static int sxg_read_mdio_reg(struct adapter_t *adapter, + u32 DevAddr, u32 RegAddr, u32 *pValue); ++static void sxg_set_mcast_addr(struct adapter_t *adapter); + + static unsigned int sxg_first_init = 1; + static char *sxg_banner = +@@ -1609,16 +1608,14 @@ static struct sk_buff *sxg_slow_receive( + #endif + /* Dumb-nic frame. See if it passes our mac filter and update stats */ + +- /* +- * ASK if (!sxg_mac_filter(adapter, +- * SXG_RECEIVE_DATA_LOCATION(RcvDataBufferHdr), +- * Event->Length)) { +- * SXG_TRACE(TRACE_SXG, SxgTraceBuffer, TRACE_NOISY, "RcvFiltr", +- * Event, SXG_RECEIVE_DATA_LOCATION(RcvDataBufferHdr), +- * Event->Length, 0); +- * goto drop; +- * } +- */ ++ if (!sxg_mac_filter(adapter, ++ (struct ether_header *)(SXG_RECEIVE_DATA_LOCATION(RcvDataBufferHdr)), ++ Event->Length)) { ++ SXG_TRACE(TRACE_SXG, SxgTraceBuffer, TRACE_NOISY, "RcvFiltr", ++ Event, SXG_RECEIVE_DATA_LOCATION(RcvDataBufferHdr), ++ Event->Length, 0); ++ goto drop; ++ } + + Packet = RcvDataBufferHdr->SxgDumbRcvPacket; + SXG_ADJUST_RCV_PACKET(Packet, RcvDataBufferHdr, Event); +@@ -1728,7 +1725,6 @@ static void sxg_process_rcv_error(struct + } + } + +-#if 0 /* Find out if this code will be needed in future */ + /* + * sxg_mac_filter + * +@@ -1743,6 +1739,7 @@ static bool sxg_mac_filter(struct adapte + struct ether_header *EtherHdr, ushort length) + { + bool EqualAddr; ++ struct net_device *dev = adapter->netdev; + + if (SXG_MULTICAST_PACKET(EtherHdr)) { + if (SXG_BROADCAST_PACKET(EtherHdr)) { +@@ -1750,8 +1747,6 @@ static bool sxg_mac_filter(struct adapte + if (adapter->MacFilter & MAC_BCAST) { + adapter->Stats.DumbRcvBcastPkts++; + adapter->Stats.DumbRcvBcastBytes += length; +- adapter->Stats.DumbRcvPkts++; +- adapter->Stats.DumbRcvBytes += length; + return (TRUE); + } + } else { +@@ -1759,15 +1754,12 @@ static bool sxg_mac_filter(struct adapte + if (adapter->MacFilter & MAC_ALLMCAST) { + adapter->Stats.DumbRcvMcastPkts++; + adapter->Stats.DumbRcvMcastBytes += length; +- adapter->Stats.DumbRcvPkts++; +- adapter->Stats.DumbRcvBytes += length; + return (TRUE); + } + if (adapter->MacFilter & MAC_MCAST) { +- struct sxg_multicast_address *MulticastAddrs = +- adapter->MulticastAddrs; +- while (MulticastAddrs) { +- ETHER_EQ_ADDR(MulticastAddrs->Address, ++ struct dev_mc_list *mclist = dev->mc_list; ++ while (mclist) { ++ ETHER_EQ_ADDR(mclist->da_addr, + EtherHdr->ether_dhost, + EqualAddr); + if (EqualAddr) { +@@ -1775,12 +1767,9 @@ static bool sxg_mac_filter(struct adapte + DumbRcvMcastPkts++; + adapter->Stats. + DumbRcvMcastBytes += length; +- adapter->Stats.DumbRcvPkts++; +- adapter->Stats.DumbRcvBytes += +- length; + return (TRUE); + } +- MulticastAddrs = MulticastAddrs->Next; ++ mclist = mclist->next; + } + } + } +@@ -1792,20 +1781,15 @@ static bool sxg_mac_filter(struct adapte + */ + adapter->Stats.DumbRcvUcastPkts++; + adapter->Stats.DumbRcvUcastBytes += length; +- adapter->Stats.DumbRcvPkts++; +- adapter->Stats.DumbRcvBytes += length; + return (TRUE); + } + if (adapter->MacFilter & MAC_PROMISC) { + /* Whatever it is, keep it. */ +- adapter->Stats.DumbRcvPkts++; +- adapter->Stats.DumbRcvBytes += length; + return (TRUE); + } +- adapter->Stats.RcvDiscards++; + return (FALSE); + } +-#endif ++ + static int sxg_register_interrupt(struct adapter_t *adapter) + { + if (!adapter->intrregistered) { +@@ -1885,24 +1869,24 @@ static int sxg_if_init(struct adapter_t + ASSERT(adapter->linkstate == LINK_DOWN); + + adapter->devflags_prev = dev->flags; +- adapter->macopts = MAC_DIRECTED; ++ adapter->MacFilter = MAC_DIRECTED; + if (dev->flags) { + DBG_ERROR("sxg: %s (%s) Set MAC options: ", __func__, + adapter->netdev->name); + if (dev->flags & IFF_BROADCAST) { +- adapter->macopts |= MAC_BCAST; ++ adapter->MacFilter |= MAC_BCAST; + DBG_ERROR("BCAST "); + } + if (dev->flags & IFF_PROMISC) { +- adapter->macopts |= MAC_PROMISC; ++ adapter->MacFilter |= MAC_PROMISC; + DBG_ERROR("PROMISC "); + } + if (dev->flags & IFF_ALLMULTI) { +- adapter->macopts |= MAC_ALLMCAST; ++ adapter->MacFilter |= MAC_ALLMCAST; + DBG_ERROR("ALL_MCAST "); + } + if (dev->flags & IFF_MULTICAST) { +- adapter->macopts |= MAC_MCAST; ++ adapter->MacFilter |= MAC_MCAST; + DBG_ERROR("MCAST "); + } + DBG_ERROR("\n"); +@@ -3036,9 +3020,7 @@ static int sxg_read_mdio_reg(struct adap + * complemented), we must then transpose the value and return bits 30-23. + */ + static u32 sxg_crc_table[256];/* Table of CRC's for all possible byte values */ +-#if XXXTODO + static u32 sxg_crc_init; /* Is table initialized */ +-#endif + + /* Contruct the CRC32 table */ + static void sxg_mcast_init_crc32(void) +@@ -3063,7 +3045,6 @@ static void sxg_mcast_init_crc32(void) + } + } + +-#if XXXTODO + /* + * Return the MAC hast as described above. + */ +@@ -3091,13 +3072,12 @@ static unsigned char sxg_mcast_get_mac_h + + return (machash); + } +-#endif + + static void sxg_mcast_set_mask(struct adapter_t *adapter) + { + struct sxg_ucode_regs *sxg_regs = adapter->UcodeRegs; + +- DBG_ERROR("%s ENTER (%s) macopts[%x] mask[%llx]\n", __func__, ++ DBG_ERROR("%s ENTER (%s) MacFilter[%x] mask[%llx]\n", __FUNCTION__, + adapter->netdev->name, (unsigned int)adapter->MacFilter, + adapter->MulticastMask); + +@@ -3107,7 +3087,7 @@ static void sxg_mcast_set_mask(struct ad + * promiscuous mode as well as ALLMCAST mode. It saves the + * Microcode from having keep state about the MAC configuration + */ +- /* DBG_ERROR("sxg: %s macopts = MAC_ALLMCAST | MAC_PROMISC\n ++ /* DBG_ERROR("sxg: %s MacFilter = MAC_ALLMCAST | MAC_PROMISC\n \ + * SLUT MODE!!!\n",__func__); + */ + WRITE_REG(sxg_regs->McastLow, 0xFFFFFFFF, FLUSH); +@@ -3135,39 +3115,6 @@ static void sxg_mcast_set_mask(struct ad + } + } + +-#if XXXTODO +-/* +- * Allocate a mcast_address structure to hold the multicast address. +- * Link it in. +- */ +-static int sxg_mcast_add_list(struct adapter_t *adapter, char *address) +-{ +- struct mcast_address *mcaddr, *mlist; +- bool equaladdr; +- +- /* Check to see if it already exists */ +- mlist = adapter->mcastaddrs; +- while (mlist) { +- ETHER_EQ_ADDR(mlist->address, address, equaladdr); +- if (equaladdr) { +- return (STATUS_SUCCESS); +- } +- mlist = mlist->next; +- } +- +- /* Doesn't already exist. Allocate a structure to hold it */ +- mcaddr = kmalloc(sizeof(struct mcast_address), GFP_ATOMIC); +- if (mcaddr == NULL) +- return 1; +- +- memcpy(mcaddr->address, address, 6); +- +- mcaddr->next = adapter->mcastaddrs; +- adapter->mcastaddrs = mcaddr; +- +- return (STATUS_SUCCESS); +-} +- + static void sxg_mcast_set_bit(struct adapter_t *adapter, char *address) + { + unsigned char crcpoly; +@@ -3184,7 +3131,25 @@ static void sxg_mcast_set_bit(struct ada + /* OR in the new bit into our 64 bit mask. */ + adapter->MulticastMask |= (u64) 1 << crcpoly; + } +-#endif ++ ++/* ++ * Function takes MAC addresses from dev_mc_list and generates the Mask ++ */ ++ ++static void sxg_set_mcast_addr(struct adapter_t *adapter) ++{ ++ struct dev_mc_list *mclist; ++ struct net_device *dev = adapter->netdev; ++ int i; ++ ++ if (adapter->MacFilter & (MAC_ALLMCAST | MAC_MCAST)) { ++ for (i = 0, mclist = dev->mc_list; i < dev->mc_count; ++ i++, mclist = mclist->next) { ++ sxg_mcast_set_bit(adapter,mclist->da_addr); ++ } ++ } ++ sxg_mcast_set_mask(adapter); ++} + + static void sxg_mcast_set_list(struct net_device *dev) + { +@@ -3194,8 +3159,16 @@ static void sxg_mcast_set_list(struct ne + if (dev->flags & IFF_PROMISC) { + adapter->MacFilter |= MAC_PROMISC; + } ++ ++ if (dev->flags & IFF_MULTICAST) ++ adapter->MacFilter |= MAC_MCAST; ++ ++ if (dev->flags & IFF_ALLMULTI) { ++ adapter->MacFilter |= MAC_ALLMCAST; ++ } ++ + //XXX handle other flags as well +- sxg_mcast_set_mask(adapter); ++ sxg_set_mcast_addr(adapter); + } + + #if XXXTODO +--- a/drivers/staging/sxg/sxg_ethtool.c ++++ b/drivers/staging/sxg/sxg_ethtool.c +@@ -99,9 +99,9 @@ static struct sxg_nic_stats sxg_nic_gstr + /* May be need in future */ + /* {"dumb_rcv_broadcast_packets", SXG_NIC_STATS(Stats.DumbRcvBcastPkts)}, + {"dumb_rcv_broadcast_bytes", SXG_NIC_STATS(Stats.DumbRcvBcastBytes)}, +- {"dumb_rcv_multicast_packets", SXG_NIC_STATS(Stats.DumbRcvMcastPkts)}, ++*/ {"dumb_rcv_multicast_packets", SXG_NIC_STATS(Stats.DumbRcvMcastPkts)}, + {"dumb_rcv_multicast_bytes", SXG_NIC_STATS(Stats.DumbRcvMcastBytes)}, +- {"dumb_rcv_unicast_packets", SXG_NIC_STATS(Stats.DumbRcvUcastPkts)}, ++/* {"dumb_rcv_unicast_packets", SXG_NIC_STATS(Stats.DumbRcvUcastPkts)}, + {"dumb_rcv_unicast_bytes", SXG_NIC_STATS(Stats.DumbRcvUcastBytes)}, + */ + {"no_sgl_buffer", SXG_NIC_STATS(Stats.NoSglBuf)}, @@ -1 +1 @@ -2.6.29-rc2-git2 +2.6.29-rc2-git4 |
