[RFC] Patch to option HSO driver to the kernel

!MAILaRCHIVE_VOTE_RePLACE
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
To: <linux-usb@...>, <netdev@...>
Cc: Alan Cox <alan@...>, Filip Aben <f.aben@...>, Paulius Zaleckas <paulius.zaleckas@...>, <ajb@...>
Date: Monday, April 14, 2008 - 5:32 pm

Hi all,

Here's a patch that I have cleaned up for context only from Option that
is a USB serial / network device all in one.

I'd like to see this go into 2.6.26, so any review comments by anyone
who wishes to review any portion of this would be greatly apprecited.

Filip, can you send me some better information for the Kconfig text
talking about what devices this driver is for, and any other information
you wish to show there?

thanks,

greg k-h

-----------
Subject: USB: add option hso driver

This driver is for a number of different Option devices.

TODO:
	- review tty layer interface
	- review USB interfaces
	- remove proc files and move to debugfs
	- review network interfaces
	- add better changelog information

Cc: Andrew Bird <ajb@spheresystems.co.uk>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Filip Aben <f.aben@option.com>
Cc: Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/net/usb/Kconfig  |   10 
 drivers/net/usb/Makefile |    1 
 drivers/net/usb/hso.c    | 3349 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 3360 insertions(+)
 create mode 100644 drivers/net/usb/hso.c

--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -154,6 +154,16 @@ config USB_NET_AX8817X
 	  This driver creates an interface named "ethX", where X depends on
 	  what other networking devices you have in use.
 
+config USB_HSO
+	tristate "Option USB High Speed Mobile Devices"
+	depends on USB
+	default n
+	help
+	  Choose this option if you have an Option High Speed Mobile
+	  device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hso.
 
 config USB_NET_CDCETHER
 	tristate "CDC Ethernet support (smart devices such as cable modems)"
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_USB_CATC)		+= catc.o
 obj-$(CONFIG_USB_KAWETH)	+= kaweth.o
 obj-$(CONFIG_USB_PEGASUS)	+= pegasus.o
 obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
+obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
--- /dev/null
+++ b/drivers/net/usb/hso.c
@@ -0,0 +1,3349 @@
+/******************************************************************************
+ *
+ * Driver for Option High Speed Mobile Devices.
+ *
+ *  Copyright (C) 2008 Option International
+ *  Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd)
+ *  			<ajb@spheresystems.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ *  USA
+ *
+ *
+ *****************************************************************************/
+
+/******************************************************************************
+ *
+ * Description of the device:
+ *
+ * Interface 0:	Contains the IP network interface on the bulk end points.
+ *		The multiplexed serial ports are using the interrupt and
+ *		control endpoints.
+ *		Interrupt contains a bitmap telling which multiplexed
+ *		serialport needs servicing.
+ *
+ * Interface 1:	Diagnostics port, uses bulk only, do not submit urbs until the
+ *		port is opened, as this have a huge impact on the network port
+ *		throughput.
+ *
+ * Interface 2:	Standard modem interface - circuit switched interface, should
+ *		not be used.
+ *
+ *****************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/ethtool.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/kmod.h>
+#include <linux/ip.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include <net/arp.h>
+#include <asm/byteorder.h>
+
+
+#define DRIVER_VERSION			"1.2"
+#define MOD_AUTHOR			"Option Wireless"
+#define MOD_DESCRIPTION			"USB High Speed Option driver"
+#define MOD_LICENSE			"GPL"
+
+#define HSO_MAX_NET_DEVICES		10
+#define HSO__MAX_MTU			2048
+#define DEFAULT_MTU			1500
+#define DEFAULT_MRU			1500
+
+#define CTRL_URB_RX_SIZE		1024
+#define CTRL_URB_TX_SIZE		64
+
+#define BULK_URB_RX_SIZE		4096
+#define BULK_URB_TX_SIZE		8192
+
+#define MUX_INTR_BUF_SIZE		16
+#define MUX_BULK_RX_BUF_SIZE		HSO__MAX_MTU
+#define MUX_BULK_TX_BUF_SIZE		HSO__MAX_MTU
+#define MUX_BULK_RX_BUF_COUNT		4
+#define USB_TYPE_OPTION_VENDOR		0x20
+
+/* These definitions are used with the struct hso_net flags element */
+/* - use *_bit operations on it. (bit indices not values.) */
+#define HSO_NET_RUNNING			0
+#define HSO_NET_TX_BUSY			1
+#define HSO_TERMINATE			2
+
+#define	HSO_NET_TX_TIMEOUT		(HZ*10)
+
+#define SEND_ENCAPSULATED_COMMAND	0x00
+#define GET_ENCAPSULATED_RESPONSE	0x01
+
+/* Serial port defines and structs. */
+#define HSO_THRESHOLD_THROTTLE		(7*1024)
+#define HSO_THRESHOLD_UNTHROTTLE	(2*1024)
+
+/* These definitions used in the Ethernet Packet Filtering requests */
+/* See CDC Spec Table 62 */
+#define MODE_FLAG_PROMISCUOUS		(1<<0)
+#define MODE_FLAG_ALL_MULTICAST		(1<<1)
+#define MODE_FLAG_DIRECTED		(1<<2)
+#define MODE_FLAG_BROADCAST		(1<<3)
+#define MODE_FLAG_MULTICAST		(1<<4)
+
+/* CDC Spec class requests - CDC Spec Table 46 */
+#define SET_ETHERNET_MULTICAST_FILTER	0x40
+#define SET_ETHERNET_PACKET_FILTER	0x43
+
+#define HSO_SERIAL_FLAG_THROTTLED	0
+#define HSO_SERIAL_FLAG_TX_SENT		1
+#define HSO_SERIAL_FLAG_RX_SENT		2
+#define HSO_SERIAL_USB_GONE		3
+
+#define HSO_SERIAL_MAGIC		0x48534f31
+
+/* Number of ttys to handle */
+#define HSO_SERIAL_TTY_MINORS		256
+
+#define MAX_RX_URBS			2
+
+#define get_serial_by_tty(x)	\
+	(x ? (struct hso_serial *)x->driver_data : NULL)
+
+/*****************************************************************************/
+/* Debugging functions                                                       */
+/*****************************************************************************/
+#define D__(lvl_, fmt, arg...)				\
+	do {						\
+		printk(lvl_ "[%d:%s]: " fmt "\n",	\
+		       __LINE__, __func__, ## arg);	\
+	} while (0)
+
+#define D_(lvl, args...)				\
+	do {						\
+		if (lvl & debug)			\
+			D__(KERN_INFO, args);		\
+	} while (0)
+
+#define D1(args...)	D_(0x01, ##args)
+#define D2(args...)	D_(0x02, ##args)
+#define D3(args...)	D_(0x04, ##args)
+#define D4(args...)	D_(0x08, ##args)
+#define D5(args...)	D_(0x10, ##args)
+
+/*****************************************************************************/
+/* Enumerators                                                               */
+/*****************************************************************************/
+enum pkt_parse_state {
+	WAIT_IP,
+	WAIT_DATA,
+	WAIT_SYNC
+};
+
+/*****************************************************************************/
+/* Structs                                                                   */
+/*****************************************************************************/
+
+struct hso_shared_int {
+	struct usb_endpoint_descriptor *intr_endp;
+	void *shared_intr_buf;
+	struct urb *shared_intr_urb;
+	struct usb_device *usb;
+	int use_count;
+	int ref_count;
+	spinlock_t shared_int_lock;
+};
+
+struct hso_net {
+	struct hso_device *parent;
+	struct net_device_stats stats;
+	struct net_device *net;
+
+	struct usb_endpoint_descriptor *in_endp;
+	struct usb_endpoint_descriptor *out_endp;
+
+	struct urb *mux_bulk_rx_urb_pool[MUX_BULK_RX_BUF_COUNT];
+	struct urb *mux_bulk_tx_urb;
+	void *mux_bulk_rx_buf_pool[MUX_BULK_RX_BUF_COUNT];
+	void *mux_bulk_tx_buf;
+
+	struct sk_buff *skb_rx_buf;
+	struct sk_buff *skb_tx_buf;
+
+	enum pkt_parse_state rx_parse_state;
+	spinlock_t net_lock;
+
+	unsigned short rx_buf_size;
+	unsigned short rx_buf_missing;
+	struct iphdr rx_ip_hdr;
+	struct ethhdr dummy_eth_head;
+
+	__u16 bcdCDC;
+	__u16 wMaxSegmentSize;
+	__u16 wNumberMCFilters;
+	__u16 mode_flags;
+	unsigned long flags;
+
+};
+
+struct hso_serial {
+	struct hso_device *parent;
+	int magic;
+	u8 minor;
+
+	struct hso_shared_int *shared_int;
+
+	/* rx/tx urb could be either a bulk urb or a control urb depending
+	   on which serial port it is used on. */
+	struct urb *rx_urb[MAX_RX_URBS];
+	u8 num_rx_urbs;
+	u8 *rx_data[MAX_RX_URBS];
+	u16 rx_data_length;	/* should contain allocated length */
+
+	struct urb *tx_urb;
+	u8 *tx_data;
+	u8 *tx_buffer;
+	u16 tx_data_length;	/* should contain allocated length */
+	u16 tx_data_count;
+	u16 tx_buffer_count;
+	spinlock_t buffer_lock;
+	struct usb_ctrlrequest ctrl_req_tx;
+	struct usb_ctrlrequest ctrl_req_rx;
+
+	struct usb_endpoint_descriptor *in_endp;
+	struct usb_endpoint_descriptor *out_endp;
+
+	unsigned long flags;
+	u8 rts_state;
+	u8 dtr_state;
+
+	/* from usb_serial_port */
+	struct tty_struct *tty;
+	int open_count;
+	spinlock_t serial_lock;
+	void *private;
+
+	int (*write_data) (struct hso_serial *serial);
+};
+
+struct hso_device {
+	union {
+		struct hso_serial *dev_serial;
+		struct hso_net *dev_net;
+	} port_data;
+
+	u32 port_spec;
+
+	u8 is_active;
+	u8 suspend_disabled;
+	struct work_struct async_get_intf;
+	struct work_struct async_put_intf;
+
+	struct usb_device *usb;
+	struct usb_interface *interface;
+
+	struct device *dev;
+	struct kref ref;
+
+	/* TODO: Not sure about the ones below */
+	struct proc_dir_entry *ourproc;
+};
+
+/* Type of interface */
+#define HSO_INTF_MASK		0xFF00
+#define	HSO_INTF_MUX		0x0100
+#define	HSO_INTF_BULK   	0x0200
+
+/* Type of port */
+#define HSO_PORT_MASK		0xFF
+#define HSO_PORT_NO_PORT	0x0
+#define	HSO_PORT_CONTROL	0x1
+#define	HSO_PORT_APP		0x2
+#define	HSO_PORT_GPS		0x3
+#define	HSO_PORT_PCSC		0x4
+#define	HSO_PORT_APP2		0x5
+#define HSO_PORT_GPS_CONTROL	0x6
+#define HSO_PORT_MSD		0x7
+#define HSO_PORT_VOICE		0x8
+#define HSO_PORT_DIAG2		0x9
+#define	HSO_PORT_DIAG		0x10
+#define	HSO_PORT_MODEM		0x11
+#define	HSO_PORT_NETWORK	0x12
+
+/* Additional device info */
+#define HSO_INFO_MASK		0xFF000000
+#define HSO_INFO_CRC_BUG	0x01000000
+
+/*****************************************************************************/
+/* Prototypes                                                                */
+/*****************************************************************************/
+/* Network interface functions */
+static int hso_net_open(struct net_device *net);
+static int hso_net_close(struct net_device *net);
+static int hso_net_start_xmit(struct sk_buff *skb, struct net_device *net);
+static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd);
+static struct net_device_stats *hso_net_get_stats(struct net_device *net);
+static void hso_net_tx_timeout(struct net_device *net);
+static void hso_net_set_multicast(struct net_device *net);
+static void read_bulk_callback(struct urb *urb);
+static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,
+			unsigned int count, unsigned char is_eop);
+static void write_bulk_callback(struct urb *urb);
+/* Serial driver functions */
+static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
+			       unsigned int set, unsigned int clear);
+static void ctrl_callback(struct urb *urb);
+static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial);
+static void hso_std_serial_read_bulk_callback(struct urb *urb);
+static void hso_std_serial_write_bulk_callback(struct urb *urb);
+static void _hso_serial_set_termios(struct tty_struct *tty,
+				    struct ktermios *old);
+static void hso_kick_transmit(struct hso_serial *serial);
+/* Base driver functions */
+static int hso_probe(struct usb_interface *interface,
+		     const struct usb_device_id *id);
+static void hso_disconnect(struct usb_interface *interface);
+/* Helper functions */
+static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int,
+				   struct usb_device *usb, gfp_t gfp);
+static void hso_net_init(struct net_device *net);
+static void set_ethernet_addr(struct hso_net *odev);
+static struct hso_serial *get_serial_by_index(unsigned index);
+static struct hso_serial *get_serial_by_shared_int_and_type(
+					struct hso_shared_int *shared_int,
+					int mux);
+static int get_free_serial_index(void);
+static void set_serial_by_index(unsigned index, struct hso_serial *serial);
+static int remove_net_device(struct hso_device *hso_dev);
+static int add_net_device(struct hso_device *hso_dev);
+static void log_usb_status(int status, const char *function);
+static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
+						  int type, int dir);
+static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports);
+static void hso_free_interface(struct usb_interface *intf);
+static int hso_start_serial_device(struct hso_device *hso_dev);
+static int hso_stop_serial_device(struct hso_device *hso_dev);
+static int hso_start_net_device(struct hso_device *hso_dev);
+static void hso_free_shared_int(struct hso_shared_int *shared_int);
+static int hso_stop_net_device(struct hso_device *hso_dev);
+static void hso_serial_ref_free(struct kref *ref);
+static int hso_suspend(struct usb_interface *iface, pm_message_t message);
+static int hso_resume(struct usb_interface *iface);
+static void async_get_intf(struct work_struct *data);
+static void async_put_intf(struct work_struct *data);
+static int hso_put_activity(struct hso_device *hso_dev);
+static int hso_get_activity(struct hso_device *hso_dev);
+
+/*****************************************************************************/
+/* Helping functions                                                         */
+/*****************************************************************************/
+
+/* convert a character representing a hex value to a number */
+static unsigned char hex2dec(unsigned char digit)
+{
+
+	if ((digit >= '0') && (digit <= '9'))
+		return (digit - '0');
+	/* Map all characters to 0-15 */
+	if ((digit >= 'a') && (digit <= 'z'))
+		return (digit - 'a' + 10) % 16;
+	if ((digit >= 'A') && (digit <= 'Z'))
+		return (digit - 'A' + 10) % 16;
+	return 16;
+}
+
+#define SIOCSETSUSPEND (SIOCDEVPRIVATE+3)
+#define SIOCSETRADIO   (SIOCDEVPRIVATE+4)
+
+/* #define DEBUG */
+
+#define dev2net(x) (x->port_data.dev_net)
+#define dev2ser(x) (x->port_data.dev_serial)
+
+/* Debugging functions */
+#ifdef DEBUG
+static void dbg_dump(int line_count, const char *func_name, unsigned char *buf,
+		     unsigned int len)
+{
+	u8 i = 0;
+
+	printk(KERN_DEBUG "[%d:%s]: len %d", line_count, func_name, len);
+
+	for (i = 0; i < len; i++) {
+		if (!(i % 16))
+			printk("\n    0x%03x:  ", i);
+		printk("%02x ", (unsigned char)buf[i]);
+	}
+	printk("\n");
+}
+
+#define DUMP(buf_, len_)	\
+	dbg_dump(__LINE__, __func__, buf_, len_)
+
+#define DUMP1(buf_, len_)			\
+	do {					\
+		if (0x01 & debug)		\
+			DUMP(buf_, len_);	\
+	} while (0)
+#else
+#define DUMP(buf_, len_)
+#define DUMP1(buf_, len_)
+#endif
+
+/* module parameters */
+static int debug;
+static int procfs = 1;
+static int tty_major;
+static int disable_net;
+static int enable_autopm;
+
+/* driver info */
+static const char driver_name[] = "hso";
+static const char tty_filename[] = "ttyHS";
+static const char *version = __FILE__ ": " DRIVER_VERSION " " MOD_AUTHOR;
+/* the usb driver itself (registered in hso_init) */
+static struct usb_driver hso_driver;
+/* the procfs vars (if procfs enabled as parameter) */
+static struct proc_dir_entry *hso_proc_dir;
+static struct proc_dir_entry *hso_proc_dir_devices;
+/* serial structures */
+static struct tty_driver *tty_drv;
+static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS];
+static struct hso_device *network_table[HSO_MAX_NET_DEVICES];
+static spinlock_t serial_table_lock;
+static struct ktermios *hso_serial_termios[HSO_SERIAL_TTY_MINORS];
+static struct ktermios *hso_serial_termios_locked[HSO_SERIAL_TTY_MINORS];
+
+static const s32 default_port_spec[] = {
+	HSO_INTF_MUX | HSO_PORT_NETWORK,
+	HSO_INTF_BULK | HSO_PORT_DIAG,
+	HSO_INTF_BULK | HSO_PORT_MODEM,
+	0
+};
+
+static const s32 icon321_port_spec[] = {
+	HSO_INTF_MUX | HSO_PORT_NETWORK,
+	HSO_INTF_BULK | HSO_PORT_DIAG2,
+	HSO_INTF_BULK | HSO_PORT_MODEM,
+	HSO_INTF_BULK | HSO_PORT_DIAG,
+	0
+};
+
+#define default_port_device(vendor, product)	\
+	USB_DEVICE(vendor, product),	\
+		.driver_info = (kernel_ulong_t)default_port_spec
+
+#define icon321_port_device(vendor, product)	\
+	USB_DEVICE(vendor, product),	\
+		.driver_info = (kernel_ulong_t)icon321_port_spec
+
+/* list of devices we support */
+static struct usb_device_id hso_ids[] = {
+	{default_port_device(0x0af0, 0x6711)},
+	{default_port_device(0x0af0, 0x6731)},
+	{default_port_device(0x0af0, 0x6751)},
+	{default_port_device(0x0af0, 0x6771)},
+	{default_port_device(0x0af0, 0x6791)},
+	{default_port_device(0x0af0, 0x6811)},
+	{default_port_device(0x0af0, 0x6911)},
+	{default_port_device(0x0af0, 0x6951)},
+	{default_port_device(0x0af0, 0x6971)},
+	{default_port_device(0x0af0, 0x7011)},
+	{default_port_device(0x0af0, 0x7031)},
+	{default_port_device(0x0af0, 0x7051)},
+	{default_port_device(0x0af0, 0x7071)},
+	{default_port_device(0x0af0, 0x7111)},
+	{default_port_device(0x0af0, 0x7211)},
+	{default_port_device(0x0af0, 0x7251)},
+	{default_port_device(0x0af0, 0x7271)},
+	{default_port_device(0x0af0, 0x7311)},
+	{default_port_device(0x0af0, 0xc031)},	/* Icon-Edge */
+	{icon321_port_device(0x0af0, 0xd013)},	/* Module HSxPA */
+	{icon321_port_device(0x0af0, 0xd031)},	/* Icon-321 */
+	{default_port_device(0x0af0, 0xd033)},	/* Icon-322 */
+	{USB_DEVICE(0x0af0, 0x7301)},		/* GE40x */
+	{USB_DEVICE(0x0af0, 0x7361)},		/* GE40x */
+	{USB_DEVICE(0x0af0, 0x7401)},		/* GI 0401 */
+	{USB_DEVICE(0x0af0, 0x7501)},		/* GTM 382 */
+	{USB_DEVICE(0x0af0, 0x7601)},		/* GE40x */
+	{}
+};
+MODULE_DEVICE_TABLE(usb, hso_ids);
+
+/* driver setup */
+static struct usb_driver hso_driver = {
+	.name = driver_name,
+	.probe = hso_probe,
+	.disconnect = hso_disconnect,
+	.id_table = hso_ids,
+	.suspend = hso_suspend,
+	.resume = hso_resume,
+	.supports_autosuspend = 1,
+};
+
+/* Sysfs attribute & function declaration */
+static ssize_t hso_sysfs_show_porttype(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL);
+
+static ssize_t hso_sysfs_show_porttype(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct hso_device *hso_dev = dev->driver_data;
+	char *port_name;
+
+	if (!hso_dev)
+		return 0;
+
+	switch (hso_dev->port_spec & HSO_PORT_MASK) {
+	case HSO_PORT_CONTROL:
+		port_name = "Control";
+		break;
+	case HSO_PORT_APP:
+		port_name = "Application";
+		break;
+	case HSO_PORT_APP2:
+		port_name = "Application2";
+		break;
+	case HSO_PORT_GPS:
+		port_name = "GPS";
+		break;
+	case HSO_PORT_GPS_CONTROL:
+		port_name = "GPS Control";
+		break;
+	case HSO_PORT_PCSC:
+		port_name = "PCSC";
+		break;
+	case HSO_PORT_DIAG:
+		port_name = "Diagnostic";
+		break;
+	case HSO_PORT_DIAG2:
+		port_name = "Diagnostic2";
+		break;
+	case HSO_PORT_MODEM:
+		port_name = "Modem";
+		break;
+	case HSO_PORT_NETWORK:
+		port_name = "Network";
+		break;
+	default:
+		port_name = "Unknown";
+		break;
+	}
+
+	return sprintf(buf, "%s\n", port_name);
+}
+
+/* Procfs functions */
+
+static int hso_proc_options(char *buf, char **start, off_t offset, int count,
+			    int *eof, void *data)
+{
+	int len = 0;
+	/* get the module parameters */
+	len +=
+	    snprintf(buf + len, count - len, "Version: %s\n", DRIVER_VERSION);
+	len += snprintf(buf + len, count - len, "debug: 0x%02x\n", debug);
+	len += snprintf(buf + len, count - len, "procfs: 0x%02x\n", procfs);
+	len +=
+	    snprintf(buf + len, count - len, "tty_major: 0x%02x\n", tty_major);
+	len +=
+	    snprintf(buf + len, count - len, "enable_autopm: 0x%02x\n",
+		     enable_autopm);
+	len +=
+	    snprintf(buf + len, count - len, "disable_net: 0x%02x\n",
+		     disable_net);
+	return len;
+}
+
+static int hso_proc_port_info(char *buf, char **start, off_t offset, int count,
+			      int *eof, void *data)
+{
+	int len = 0;
+	struct hso_device *hso_dev = (struct hso_device *)data;
+	char *port_name = NULL;
+
+	D1("count: %d", count);
+
+	switch (hso_dev->port_spec & HSO_PORT_MASK) {
+	case HSO_PORT_CONTROL:
+		port_name = "Control";
+		break;
+	case HSO_PORT_APP:
+		port_name = "Application";
+		break;
+	case HSO_PORT_GPS:
+		port_name = "GPS";
+		break;
+	case HSO_PORT_GPS_CONTROL:
+		port_name = "GPS Control";
+		break;
+	case HSO_PORT_APP2:
+		port_name = "Application2";
+		break;
+	case HSO_PORT_PCSC:
+		port_name = "PCSC";
+		break;
+	case HSO_PORT_DIAG:
+		port_name = "Diagnostic";
+		break;
+	case HSO_PORT_DIAG2:
+		port_name = "Diagnostic2";
+		break;
+	case HSO_PORT_MODEM:
+		port_name = "Modem";
+		break;
+	case HSO_PORT_NETWORK:
+		port_name = "Network";
+		break;
+	default:
+		port_name = "Unknown";
+		break;
+	}
+
+	len += snprintf(buf + len, count - len, "%s\n", port_name);
+
+	/* return the device id to the user, for use in scripts for autopm */
+	len +=
+	    snprintf(buf + len, count - len, "USB bus ID:\t%s\n",
+		     hso_dev->usb->dev.bus_id);
+	return len;
+}
+
+/* Network interface functions */
+
+/* called when net interface is brought up by ifconfig */
+static int hso_net_open(struct net_device *net)
+{
+	struct hso_net *odev = netdev_priv(net);
+	unsigned long flags = 0;
+
+	if (!odev) {
+		dev_err(&net->dev, "No net device !\n");
+		return -ENODEV;
+	}
+	/* reset read counter. */
+	odev->stats.rx_packets = 0;
+	odev->skb_tx_buf = NULL;
+
+	/* setup environment */
+	spin_lock_irqsave(&odev->net_lock, flags);
+	odev->rx_parse_state = WAIT_IP;
+	odev->rx_buf_size = 0;
+	odev->rx_buf_missing = sizeof(struct iphdr);
+	spin_unlock_irqrestore(&odev->net_lock, flags);
+
+	hso_start_net_device(odev->parent);
+
+	/* Tell the kernel we are ready to start receiving from it */
+	netif_start_queue(net);
+
+	/* We are up and running. */
+	set_bit(HSO_NET_RUNNING, &odev->flags);
+
+	return 0;
+}
+
+/* called when interface is brought down by ifconfig */
+static int hso_net_close(struct net_device *net)
+{
+	struct hso_net *odev = netdev_priv(net);
+
+	/* no longer running */
+	clear_bit(HSO_NET_RUNNING, &odev->flags);
+	/* we don't need the queue anymore */
+	netif_stop_queue(net);
+
+	hso_stop_net_device(odev->parent);
+
+	/* done */
+	return 0;
+}
+
+/* called by kernel when we need to transmit a packet */
+static int hso_net_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	struct hso_net *odev = netdev_priv(net);
+	int result = 0;
+
+	/* Tell the kernel, "No more frames 'til we are done with this one." */
+	netif_stop_queue(net);
+	if (hso_get_activity(odev->parent) == -EAGAIN) {
+		odev->skb_tx_buf = skb;
+		return 0;
+	}
+
+	/* fetch the packet */
+	skb_pull(skb, ETH_HLEN);
+	/* log if asked */
+	DUMP1(skb->data, skb->len);
+	/* Copy it from kernel memory to OUR memory */
+	memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len);
+	D1("len: %d/%d", skb->len, MUX_BULK_TX_BUF_SIZE);
+
+	/* Fill in the URB for shipping it out. */
+	usb_fill_bulk_urb(odev->mux_bulk_tx_urb,
+			  odev->parent->usb,
+			  usb_sndbulkpipe(odev->parent->usb,
+					  odev->out_endp->
+					  bEndpointAddress & 0x7F),
+			  odev->mux_bulk_tx_buf, skb->len, write_bulk_callback,
+			  odev);
+
+	/* Deal with the Zero Length packet problem, I hope */
+	odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+	/* Send the URB on its merry way. */
+	result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC);
+	if (result) {
+		dev_warn(&odev->parent->interface->dev,
+			"failed mux_bulk_tx_urb %d", result);
+		odev->stats.tx_errors++;
+		netif_start_queue(net);
+	} else {
+		odev->stats.tx_packets++;
+		odev->stats.tx_bytes += skb->len;
+		/* And tell the kernel when the last transmit started. */
+		net->trans_start = jiffies;
+	}
+	dev_kfree_skb(skb);
+	/* we're done */
+	return result;
+}
+
+static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct hso_net *odev = netdev_priv(net);
+	u32 usercmd = 0;
+	char tmp[40];
+
+	switch (cmd) {
+	case SIOCDEVPRIVATE + 1:
+		/* Chose this one because SIOCDEVPRIVATE used somewhere else in
+		 * this code */
+		/* return the number of sent bytes */
+		D5("Transmitted: %lu", odev->stats.tx_bytes);
+		rq->ifr_ifru.ifru_ivalue = odev->stats.tx_bytes;
+		return 0;
+
+	case SIOCETHTOOL:
+		/* net specific */
+		if (copy_from_user(&usercmd, rq->ifr_data, sizeof(usercmd)))
+			return -EFAULT;
+
+		switch (usercmd) {
+		case ETHTOOL_GDRVINFO:
+			{
+			/* get driver info */
+			struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+			strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN);
+			strncpy(info.version, DRIVER_VERSION,
+				ETHTOOL_BUSINFO_LEN);
+			sprintf(tmp, "usb%d:%d",
+				odev->parent->usb->bus->busnum,
+				odev->parent->usb->devnum);
+			strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN);
+			sprintf(tmp, "%s %x.%x", driver_name,
+				((odev->bcdCDC & 0xff00) >> 8),
+				(odev->bcdCDC & 0x00ff));
+			strncpy(info.fw_version, tmp, ETHTOOL_BUSINFO_LEN);
+			if (copy_to_user(rq->ifr_data, &info, sizeof(info)))
+				return -EFAULT;
+
+			return 0;
+			}
+		case ETHTOOL_GLINK:
+			{
+			/* get link status */
+			struct ethtool_value edata = { ETHTOOL_GLINK };
+
+			edata.data = netif_carrier_ok(net);
+			if (copy_to_user(rq->ifr_data, &edata, sizeof(edata)))
+				return -EFAULT;
+
+			return 0;
+			}
+		default:
+			dev_warn(&net->dev, "Got unsupported ioctl: %x\n",
+				 usercmd);
+			/* the ethtool user space tool relies on this */
+			return -EOPNOTSUPP;
+		}
+	default:
+		return -ENOTTY;	/* per ioctl man page */
+	}
+}
+
+static struct net_device_stats *hso_net_get_stats(struct net_device *net)
+{
+	return &((struct hso_net *)netdev_priv(net))->stats;
+}
+
+/* called when a packet did not ack after watchdogtimeout */
+static void hso_net_tx_timeout(struct net_device *net)
+{
+	struct hso_net *odev = netdev_priv(net);
+
+	if (!odev)
+		return;
+
+	/* Tell syslog we are hosed. */
+	dev_warn(&net->dev, "Tx timed out.\n");
+
+	/* Tear the waiting frame off the list */
+	if (odev->mux_bulk_tx_urb
+	    && (odev->mux_bulk_tx_urb->status == -EINPROGRESS))
+		usb_unlink_urb(odev->mux_bulk_tx_urb);
+
+	/* Update statistics */
+	odev->stats.tx_errors++;
+}
+
+/* setup the multicast filters */
+static void hso_net_set_multicast(struct net_device *net)
+{
+	struct hso_net *odev = netdev_priv(net);
+	int i = 0;
+	__u8 *buf = NULL;
+
+	/* Tell the kernel to stop sending us frames while we get this all set
+	 * up. */
+	netif_stop_queue(net);
+
+	/* Note: do not reorder, GCC is clever about common statements. */
+	if (net->flags & IFF_PROMISC) {
+		/* Unconditionally log net taps. */
+		D1("%s: Promiscuous mode enabled", net->name);
+		odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+				   MODE_FLAG_DIRECTED |
+				   MODE_FLAG_BROADCAST |
+				   MODE_FLAG_MULTICAST |
+				   MODE_FLAG_PROMISCUOUS;
+	} else if (net->mc_count > odev->wNumberMCFilters) {
+		/* Too many to filter perfectly -- accept all multicasts. */
+		D1("%s: too many MC filters for hardware, using allmulti",
+		   net->name);
+		odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+				   MODE_FLAG_DIRECTED |
+				   MODE_FLAG_BROADCAST |
+				   MODE_FLAG_MULTICAST;
+	} else if (net->flags & IFF_ALLMULTI) {
+		/* Filter in software */
+		D1("%s: using allmulti", net->name);
+		odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+				   MODE_FLAG_DIRECTED |
+				   MODE_FLAG_BROADCAST |
+				   MODE_FLAG_MULTICAST;
+	} else {
+		/* do multicast filtering in hardware */
+		struct dev_mc_list *mclist;
+		D1("%s: set multicast filters", net->name);
+		odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+				   MODE_FLAG_DIRECTED |
+				   MODE_FLAG_BROADCAST |
+				   MODE_FLAG_MULTICAST;
+		buf = kmalloc(6 * net->mc_count, GFP_ATOMIC);
+		if (!buf) {
+			dev_err(&net->dev, "No memory to allocate?");
+			goto exit;
+		}
+		for (i = 0, mclist = net->mc_list; mclist && i < net->mc_count;
+		     i++, mclist = mclist->next) {
+			memcpy(&mclist->dmi_addr, &buf[i * 6], 6);
+		}
+		kfree(buf);
+	}
+
+exit:
+	/* Tell the kernel to start giving frames to us again. */
+	netif_wake_queue(net);
+}
+
+/* Moving data from usb to kernel (in interrupt state) */
+static void read_bulk_callback(struct urb *urb)
+{
+	struct hso_net *odev = urb->context;
+	struct net_device *net = NULL;
+	int result = 0;
+	unsigned long flags = 0;
+	int status = urb->status;
+
+	/* is al ok?  (Filip: Who's Al ?) */
+	if (status) {
+		log_usb_status(status, __func__);
+		return;
+	}
+
+	/* Sanity check */
+	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
+		D1("BULK IN callback but driver is not active!");
+		return;
+	}
+	usb_mark_last_busy(urb->dev);
+
+	net = odev->net;
+
+	if (!netif_device_present(net)) {
+		/* Somebody killed our network interface... */
+		return;
+	}
+
+	if (odev->parent->port_spec & HSO_INFO_CRC_BUG) {
+		u32 rest;
+		u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
+		rest = urb->actual_length % odev->in_endp->wMaxPacketSize;
+		if (((rest == 5) || (rest == 6))
+		    && !memcmp(((u8 *) urb->transfer_buffer) +
+			       urb->actual_length - 4, crc_check, 4)) {
+			urb->actual_length -= 4;
+		}
+	}
+
+	/* do we even have a packet? */
+	if (urb->actual_length) {
+		/* Handle the IP stream, add header and push it onto network
+		 * stack if the packet is complete. */
+		spin_lock_irqsave(&odev->net_lock, flags);
+		packetizeRx(odev, urb->transfer_buffer, urb->actual_length,
+			    (urb->transfer_buffer_length >
+			     urb->actual_length) ? 1 : 0);
+		spin_unlock_irqrestore(&odev->net_lock, flags);
+	}
+
+	/* We are done with this URB, resubmit it. Prep the USB to wait for
+	 * another frame. Reuse same as received. */
+	usb_fill_bulk_urb(urb,
+			  odev->parent->usb,
+			  usb_rcvbulkpipe(odev->parent->usb,
+					  odev->in_endp->
+					  bEndpointAddress & 0x7F),
+			  urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE,
+			  read_bulk_callback, odev);
+
+	/* Give this to the USB subsystem so it can tell us when more data
+	 * arrives. */
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_warn(&odev->parent->interface->dev,
+			 "%s failed submit mux_bulk_rx_urb %d", __func__,
+			 result);
+}
+
+/* make a real packet from the received USB buffer */
+static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,
+			unsigned int count, unsigned char is_eop)
+{
+	unsigned short temp_bytes = 0;
+	unsigned short buffer_offset = 0;
+	unsigned short frame_len = 0;
+	unsigned char *tmp_rx_buf = NULL;
+	struct ethhdr *eth_head = NULL;
+
+	/* log if needed */
+	D1("Rx %d bytes", count);
+	DUMP(ip_pkt, min(128, (int)count));
+
+	while (count) {
+		switch (odev->rx_parse_state) {
+		case WAIT_IP:
+			/* waiting for IP header. */
+			/* wanted bytes - size of ip header */
+			temp_bytes =
+			    (count <
+			     odev->rx_buf_missing) ? count : odev->
+			    rx_buf_missing;
+
+			memcpy(((unsigned char *)(&odev->rx_ip_hdr)) +
+			       odev->rx_buf_size, ip_pkt + buffer_offset,
+			       temp_bytes);
+
+			odev->rx_buf_size += temp_bytes;
+			buffer_offset += temp_bytes;
+			odev->rx_buf_missing -= temp_bytes;
+			count -= temp_bytes;
+
+			if (!odev->rx_buf_missing) {
+				/* header is complete allocate an sk_buffer and
+				 * continue to WAIT_DATA */
+				frame_len = ntohs(odev->rx_ip_hdr.tot_len);
+
+				if ((frame_len > DEFAULT_MRU) ||
+				    (frame_len < sizeof(struct iphdr))) {
+					dev_err(&odev->net->dev,
+						"Invalid frame (%d) length\n",
+						frame_len);
+					odev->rx_parse_state = WAIT_SYNC;
+					continue;
+				}
+				/* Allocate an sk_buff */
+				odev->skb_rx_buf = dev_alloc_skb(
+					frame_len + 2 +
+					odev->net->hard_header_len);
+				if (!odev->skb_rx_buf) {
+					/* We got no receive buffer. */
+					D1("could not allocate memory");
+					odev->rx_parse_state = WAIT_SYNC;
+					return;
+				}
+				/* Here's where it came from */
+				odev->skb_rx_buf->dev = odev->net;
+
+				/* Make some headroom: standard alignment + the
+				 * ethernet header. */
+				skb_reserve(odev->skb_rx_buf,
+					    2 + odev->net->hard_header_len);
+
+				/* Copy what we got so far. make room for iphdr
+				 * after tail. */
+				tmp_rx_buf =
+				    skb_put(odev->skb_rx_buf,
+					    sizeof(struct iphdr));
+				memcpy(tmp_rx_buf, (char *)&(odev->rx_ip_hdr),
+				       sizeof(struct iphdr));
+
+				/* ETH_HLEN */
+				odev->rx_buf_size =
+				    odev->net->hard_header_len +
+				    sizeof(struct iphdr);
+
+				/* Filip actually use .tot_len */
+				odev->rx_buf_missing =
+				    frame_len - sizeof(struct iphdr);
+				odev->rx_parse_state = WAIT_DATA;
+			}
+			break;
+
+		case WAIT_DATA:
+			temp_bytes = (count < odev->rx_buf_missing)
+					? count : odev->rx_buf_missing;
+
+			/* Copy the rest of the bytes that are left in the
+			 * buffer into the waiting sk_buf. */
+			/* Make room for temp_bytes after tail. */
+			tmp_rx_buf = skb_put(odev->skb_rx_buf, temp_bytes);
+			memcpy(tmp_rx_buf, ip_pkt + buffer_offset, temp_bytes);
+
+			odev->rx_buf_missing -= temp_bytes;
+			count -= temp_bytes;
+			buffer_offset += temp_bytes;
+			odev->rx_buf_size += temp_bytes;
+			if (!odev->rx_buf_missing) {
+				/* Packet is complete. Inject into stack. */
+				/* Add fake ethernet header. */
+				eth_head = (struct ethhdr *)skb_push(odev->
+							      skb_rx_buf,
+							      odev->
+							      net->
+							      hard_header_len);
+				memcpy(eth_head, &odev->dummy_eth_head,
+				       sizeof(struct ethhdr));
+
+				/* Not sure here either */
+				odev->skb_rx_buf->protocol = eth_type_trans(
+							odev->skb_rx_buf,
+							odev->net);
+				/* don't check it */
+				odev->skb_rx_buf->ip_summed =
+					CHECKSUM_UNNECESSARY;
+				/* Ship it off to the kernel */
+				netif_rx(odev->skb_rx_buf);
+				/* No longer our buffer. */
+				odev->skb_rx_buf = NULL;
+
+				/* update out statistics */
+				odev->stats.rx_packets++;
+
+				/* Hmmm, wonder if we have received the IP len
+				 * or the ETH len. */
+				odev->stats.rx_bytes += odev->rx_buf_size;
+
+				odev->rx_buf_size = 0;
+				odev->rx_buf_missing = sizeof(struct iphdr);
+				odev->rx_parse_state = WAIT_IP;
+			}
+			break;
+
+		case WAIT_SYNC:
+			D1(" W_S");
+			count = 0;
+			break;
+		default:
+			D1(" ");
+			count--;
+			break;
+		}
+	}
+
+	/* Recovery mechanism for WAIT_SYNC state. */
+	if (is_eop) {
+		if (odev->rx_parse_state == WAIT_SYNC) {
+			odev->rx_parse_state = WAIT_IP;
+			odev->rx_buf_size = 0;
+			odev->rx_buf_missing = sizeof(struct iphdr);
+		}
+	}
+}
+
+/* USB tells is xmit done, we should start the netqueue again */
+static void write_bulk_callback(struct urb *urb)
+{
+	struct hso_net *odev = urb->context;
+	int status = urb->status;
+
+	/* Sanity check */
+	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
+		dev_err(&urb->dev->dev, "%s: device not running\n", __func__);
+		return;
+	}
+
+	/* Do we still have a valid kernel network device? */
+	if (!netif_device_present(odev->net)) {
+		dev_err(&urb->dev->dev, "%s: net device not present\n",
+			__func__);
+		return;
+	}
+
+	/* log status, but don't act on it, we don't need to resubmit anything
+	 * anyhow */
+	if (status)
+		log_usb_status(status, __func__);
+
+	hso_put_activity(odev->parent);
+
+	/* Tell the network interface we are ready for another frame */
+	netif_wake_queue(odev->net);
+}
+
+/* Serial driver functions */
+
+static void _hso_serial_set_termios(struct tty_struct *tty,
+				    struct ktermios *old)
+{
+	struct hso_serial *serial = get_serial_by_tty(tty);
+
+	if ((!tty) || (!tty->termios) || (!serial)) {
+		printk(KERN_ERR "%s: no tty structures", __func__);
+		return;
+	}
+
+	D4("port %d", serial->minor);
+
+	/*
+	 * The default requirements for this device are:
+	 */
+	serial->tty->termios->c_iflag &=
+		~(IGNBRK	/* disable ignore break */
+		| BRKINT	/* disable break causes interrupt */
+		| PARMRK	/* disable mark parity errors */
+		| ISTRIP	/* disable clear high bit of input characters */
+		| INLCR		/* disable translate NL to CR */
+		| IGNCR		/* disable ignore CR */
+		| ICRNL		/* disable translate CR to NL */
+		| IXON);	/* disable enable XON/XOFF flow control */
+
+	/* disable postprocess output characters */
+	serial->tty->termios->c_oflag &= ~OPOST;
+
+	serial->tty->termios->c_lflag &=
+		~(ECHO		/* disable echo input characters */
+		| ECHONL	/* disable echo new line */
+		| ICANON	/* disable erase, kill, werase, and rprnt
+				   special characters */
+		| ISIG		/* disable interrupt, quit, and suspend special
+				   characters */
+		| IEXTEN);	/* disable non-POSIX special characters */
+
+	serial->tty->termios->c_cflag &=
+		~(CSIZE		/* no size */
+		| PARENB	/* disable parity bit */
+		| CBAUD);	/* clear current baud rate */
+
+	serial->tty->termios->c_cflag |=
+		(CS8		/* character size 8 bits */
+		| B115200);	/* baud rate 115200 */
+
+	/*
+	 * Force low_latency on; otherwise the pushes are scheduled;
+	 * this is bad as it opens up the possibility of dropping bytes
+	 * on the floor.  We don't want to drop bytes on the floor. :)
+	 */
+	serial->tty->low_latency = 1;
+
+	/* Notify the tty driver that the termios have changed. */
+	serial->tty->ldisc.set_termios(serial->tty, NULL);
+	return;
+}
+
+/* open the requested serial port */
+static int hso_serial_open(struct tty_struct *tty, struct file *filp)
+{
+	struct hso_serial *serial = get_serial_by_index(tty->index);
+	int result = 0;
+	unsigned long flags;
+
+	/* sanity check */
+	if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) {
+		tty->driver_data = NULL;
+		D1("Failed to open port");
+		return -ENODEV;
+	}
+
+	usb_autopm_get_interface(serial->parent->interface);
+
+	D1("Opening %d", serial->minor);
+	/* lock it down */
+	spin_lock_irqsave(&serial->serial_lock, flags);
+
+	kref_get(&serial->parent->ref);
+
+	/* setup */
+	tty->driver_data = serial;
+	serial->tty = tty;
+
+	/* check for port allready opened, if not set the termios */
+	serial->open_count++;
+	if (serial->open_count == 1) {
+		tty->low_latency = 1;
+		serial->flags = 0;
+		/* Force default termio settings */
+		_hso_serial_set_termios(tty, NULL);
+		result = hso_start_serial_device(serial->parent);
+		if (result) {
+			hso_stop_serial_device(serial->parent);
+			serial->open_count--;
+			kref_put(&serial->parent->ref, hso_serial_ref_free);
+		}
+	} else {
+		D1("Port was already open");
+	}
+
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+	usb_autopm_put_interface(serial->parent->interface);
+
+	/* done */
+	if (result)
+		hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0);
+	return result;
+}
+
+/* close the requested serial port */
+static void hso_serial_close(struct tty_struct *tty, struct file *filp)
+{
+	struct hso_serial *serial = tty->driver_data;
+	unsigned long flags;
+	u8 usb_gone;
+
+	D1("Closing serial port");
+
+	/* sanity check */
+	if (tty == NULL || serial == NULL) {
+		D1("(tty == NULL || tty->driver_data == NULL)");
+		return;
+	}
+
+	usb_gone = test_bit(HSO_SERIAL_USB_GONE, &serial->flags);
+
+	if (!usb_gone)
+		usb_autopm_get_interface(serial->parent->interface);
+
+	/* reset the rts and dtr */
+	/* do the actual close */
+	spin_lock_irqsave(&serial->serial_lock, flags);
+	serial->open_count--;
+	if (serial->open_count <= 0) {
+		kref_put(&serial->parent->ref, hso_serial_ref_free);
+		serial->open_count = 0;
+		if (serial->tty) {
+			serial->tty->driver_data = NULL;
+			serial->tty = NULL;
+		}
+		if (!usb_gone)
+			hso_stop_serial_device(serial->parent);
+	}
+	if (serial->open_count < 0)
+		serial->open_count = 0;
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+	if (!usb_gone)
+		usb_autopm_put_interface(serial->parent->interface);
+}
+
+/* close the requested serial port */
+static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf,
+			    int count)
+{
+	struct hso_serial *serial = get_serial_by_tty(tty);
+	int space = 0, tx_bytes = 0;
+	unsigned long flags;
+
+	/* sanity check */
+	if (serial == NULL) {
+		printk(KERN_ERR "%s: tty or tty->driver_data is NULL\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&serial->serial_lock, flags);
+
+	space = serial->tx_data_length - serial->tx_buffer_count;
+	tx_bytes = (count < space) ? count : space;
+
+	if (!tx_bytes)
+		goto out;
+
+	memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes);
+	serial->tx_buffer_count += tx_bytes;
+
+out:
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+	hso_kick_transmit(serial);
+	/* done */
+	return tx_bytes;
+}
+
+/* how much room is there for writing */
+static int hso_serial_write_room(struct tty_struct *tty)
+{
+	struct hso_serial *serial = get_serial_by_tty(tty);
+	int room = 0;
+	unsigned long flags;
+
+	/* sanity check */
+	if (serial == NULL)
+		return 0;
+
+	spin_lock_irqsave(&serial->serial_lock, flags);
+	room = serial->tx_data_length - serial->tx_buffer_count;
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+	/* return free room */
+	return room;
+}
+
+/* setup the term */
+static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	struct hso_serial *serial = get_serial_by_tty(tty);
+	unsigned long flags;
+
+	/* sanity check */
+	if ((!serial) || (!tty->termios)) {
+		D1("no tty structures");
+		return;
+	}
+
+	if (old)
+		D5("Termios called with: cflags new[%d] - old[%d]",
+		   tty->termios->c_cflag, old->c_cflag);
+
+	/* the actual setup */
+	spin_lock_irqsave(&serial->serial_lock, flags);
+	if (serial->open_count)
+		_hso_serial_set_termios(tty, old);
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+	/* done */
+	return;
+}
+
+/* how many characters in the buffer */
+static int hso_serial_chars_in_buffer(struct tty_struct *tty)
+{
+	struct hso_serial *serial = get_serial_by_tty(tty);
+	int chars = 0;
+	unsigned long flags;
+
+	/* sanity check */
+	if (serial == NULL)
+		return 0;
+
+	spin_lock_irqsave(&serial->serial_lock, flags);
+	chars = serial->tx_buffer_count;
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+	return chars;
+}
+
+static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	unsigned int value = 0;
+	struct hso_serial *serial = get_serial_by_tty(tty);
+	unsigned long flags;
+
+	/* sanity check */
+	if (!serial) {
+		D1("no tty structures");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&serial->serial_lock, flags);
+	value = ((serial->rts_state) ? TIOCM_RTS : 0) |
+	    ((serial->dtr_state) ? TIOCM_DTR : 0);
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+	return value;
+}
+
+static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
+			       unsigned int set, unsigned int clear)
+{
+	int val = 0;
+	unsigned long flags;
+	int if_num;
+	struct hso_serial *serial = get_serial_by_tty(tty);
+
+	/* sanity check */
+	if (!serial) {
+		D1("no tty structures");
+		return -EINVAL;
+	}
+	if_num = serial->parent->interface->altsetting->desc.bInterfaceNumber;
+
+	spin_lock_irqsave(&serial->serial_lock, flags);
+	if (set & TIOCM_RTS)
+		serial->rts_state = 1;
+	if (set & TIOCM_DTR)
+		serial->dtr_state = 1;
+
+	if (clear & TIOCM_RTS)
+		serial->rts_state = 0;
+	if (clear & TIOCM_DTR)
+		serial->dtr_state = 0;
+
+	if (serial->dtr_state)
+		val |= 0x01;
+	if (serial->rts_state)
+		val |= 0x02;
+
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+	return usb_control_msg(serial->parent->usb,
+			       usb_rcvctrlpipe(serial->parent->usb, 0), 0x22,
+			       0x21, val, if_num, NULL, 0,
+			       USB_CTRL_SET_TIMEOUT);
+}
+
+/* Toggles suspend on or off ( used by ioctl ) */
+static int hso_set_suspend(struct hso_device *hso_dev, int enabled)
+{
+	if (!hso_dev)
+		return -ENODEV;
+
+	if (enabled) {
+		if (hso_dev->suspend_disabled) {
+			usb_autopm_put_interface(hso_dev->interface);
+			hso_dev->suspend_disabled = 0;
+		}
+	} else {
+		if (!hso_dev->suspend_disabled) {
+			usb_autopm_get_interface(hso_dev->interface);
+			hso_dev->suspend_disabled = 1;
+		}
+	}
+	return 0;
+}
+
+/* Toggles radioon or off ( used by ioctl ) */
+static int hso_set_radio(struct hso_device *hso_dev, int enabled)
+{
+	if (!hso_dev)
+		return -ENODEV;
+
+	return usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0),
+			       enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0,
+			       USB_CTRL_SET_TIMEOUT);
+}
+
+/* ioctl not supported */
+static int hso_serial_ioctl(struct tty_struct *tty, struct file *file,
+			    unsigned int cmd, unsigned long arg)
+{
+	struct hso_serial *serial = get_serial_by_tty(tty);
+	int ret = 0;
+	D4("IOCTL cmd: %d, arg: %ld", cmd, arg);
+
+	if (!serial)
+		return -ENODEV;
+
+	switch (cmd) {
+	case SIOCSETSUSPEND:
+		if (arg)
+			hso_set_suspend(serial->parent, 1);
+		else
+			hso_set_suspend(serial->parent, 0);
+		ret = 0;
+		break;
+	case SIOCSETRADIO:
+		if (arg)
+			hso_set_radio(serial->parent, 1);
+		else
+			hso_set_radio(serial->parent, 0);
+		ret = 0;
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	return ret;
+}
+
+static void hso_serial_throttle(struct tty_struct *tty)
+{
+	D1(" ");
+}
+
+static void hso_serial_unthrottle(struct tty_struct *tty)
+{
+	D1(" ");
+}
+
+static void hso_serial_break(struct tty_struct *tty, int break_state)
+{
+	D1(" ");
+}
+
+static int hso_serial_read_proc(char *page, char **start, off_t off, int count,
+				int *eof, void *data)
+{
+	return 0;
+}
+
+static void hso_serial_hangup(struct tty_struct *tty)
+{
+	D1("hang up");
+}
+
+/* starts a transmit */
+static void hso_kick_transmit(struct hso_serial *serial)
+{
+	u8 *temp = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serial->serial_lock, flags);
+	if (!serial->tx_buffer_count)
+		goto out;
+
+	if (serial->tx_urb->status == -EINPROGRESS)
+		goto out;
+
+	/* Wakeup USB interface if necessary */
+	if (hso_get_activity(serial->parent) == -EAGAIN)
+		goto out;
+
+	/* Switch pointers around to avoid memcpy */
+	temp = serial->tx_buffer;
+	serial->tx_buffer = serial->tx_data;
+	serial->tx_data = temp;
+	serial->tx_data_count = serial->tx_buffer_count;
+	serial->tx_buffer_count = 0;
+
+out:
+	/* If temp is set, it means we switched buffers */
+	if (temp && serial->write_data)
+		serial->write_data(serial);
+	spin_unlock_irqrestore(&serial->serial_lock, flags);
+}
+
+/* converts mux value to a port spec value */
+static u32 hso_mux_to_port(int mux)
+{
+	u32 result;
+
+	switch (mux) {
+	case 0x1:
+		result = HSO_PORT_CONTROL;
+		break;
+	case 0x2:
+		result = HSO_PORT_APP;
+		break;
+	case 0x4:
+		result = HSO_PORT_PCSC;
+		break;
+	case 0x8:
+		result = HSO_PORT_GPS;
+		break;
+	case 0x10:
+		result = HSO_PORT_APP2;
+		break;
+	default:
+		result = HSO_PORT_NO_PORT;
+	}
+	return result;
+}
+
+/* converts port spec value to a mux value */
+static u32 hso_port_to_mux(int port)
+{
+	u32 result;
+
+	switch (port & HSO_PORT_MASK) {
+	case HSO_PORT_CONTROL:
+		result = 0x0;
+		break;
+	case HSO_PORT_APP:
+		result = 0x1;
+		break;
+	case HSO_PORT_PCSC:
+		result = 0x2;
+		break;
+	case HSO_PORT_GPS:
+		result = 0x3;
+		break;
+	case HSO_PORT_APP2:
+		result = 0x4;
+		break;
+	default:
+		result = 0x0;
+	}
+	return result;
+}
+
+/* make a request (for reading and writing data to muxed serial port) */
+static int mux_device_request(struct hso_serial *serial, u8 type, u16 port,
+			      struct urb *ctrl_urb,
+			      struct usb_ctrlrequest *ctrl_req,
+			      u8 *ctrl_urb_data, u32 size)
+{
+	int result = 0;
+	int pipe = -1;
+
+	/* Sanity check */
+	if (!serial || !ctrl_urb || !ctrl_req) {
+		printk(KERN_ERR "%s: Wrong arguments\n", __func__);
+		return -EINVAL;
+	}
+
+	/* initialize */
+	ctrl_req->wValue = 0;
+	ctrl_req->wIndex = hso_port_to_mux(port);
+	ctrl_req->wLength = size;
+
+	if (type == GET_ENCAPSULATED_RESPONSE) {
+		/* Reading command */
+		ctrl_req->bRequestType = USB_DIR_IN |
+					 USB_TYPE_OPTION_VENDOR |
+					 USB_RECIP_INTERFACE;
+		ctrl_req->bRequest = GET_ENCAPSULATED_RESPONSE;
+		pipe = usb_rcvctrlpipe(serial->parent->usb, 0);
+	} else {
+		/* Writing command */
+		ctrl_req->bRequestType = USB_DIR_OUT |
+					 USB_TYPE_OPTION_VENDOR |
+					 USB_RECIP_INTERFACE;
+		ctrl_req->bRequest = SEND_ENCAPSULATED_COMMAND;
+		pipe = usb_sndctrlpipe(serial->parent->usb, 0);
+	}
+	/* syslog */
+	D2("%s command (%02x) len: %d, port: %d",
+	   type == GET_ENCAPSULATED_RESPONSE ? "Read" : "Write",
+	   ctrl_req->bRequestType, ctrl_req->wLength, port);
+
+	/* Load ctrl urb */
+	ctrl_urb->transfer_flags = 0;
+	usb_fill_control_urb(ctrl_urb,
+			     serial->parent->usb,
+			     pipe,
+			     (u8 *) ctrl_req,
+			     ctrl_urb_data, size, ctrl_callback, serial);
+	/* Send it on merry way */
+	result = usb_submit_urb(ctrl_urb, GFP_ATOMIC);
+	if (result) {
+		dev_err(&ctrl_urb->dev->dev,
+			"%s failed submit ctrl_urb %d type %d", __func__,
+			result, type);
+		return result;
+	}
+
+	/* done */
+	return size;
+}
+
+/* called by intr_callback when read occurs */
+static int hso_mux_serial_read(struct hso_serial *serial)
+{
+	if (!serial)
+		return -EINVAL;
+
+	/* clean data */
+	memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE);
+	/* make the request */
+
+	if (serial->num_rx_urbs != 1) {
+		dev_err(&serial->parent->interface->dev,
+			"ERROR: mux'd reads with multiple buffers "
+			"not possible\n");
+		return 0;
+	}
+	return mux_device_request(serial,
+				  GET_ENCAPSULATED_RESPONSE,
+				  serial->parent->port_spec & HSO_PORT_MASK,
+				  serial->rx_urb[0],
+				  &serial->ctrl_req_rx,
+				  serial->rx_data[0], serial->rx_data_length);
+}
+
+/* used for muxed serial port callback (muxed serial read) */
+static void intr_callback(struct urb *urb)
+{
+	struct hso_shared_int *shared_int = urb->context;
+	struct hso_serial *serial = NULL;
+	unsigned char *port_req = NULL;
+	int status = urb->status;
+	int i = 0;
+
+	usb_mark_last_busy(urb->dev);
+
+	/* sanity check */
+	if (!shared_int)
+		return;
+
+	/* status check */
+	if (status) {
+		log_usb_status(status, __func__);
+		return;
+	}
+	D4("\n--- Got intr callback 0x%02X ---", status);
+
+	/* what request? */
+	port_req = urb->transfer_buffer;
+	D4(" port_req = 0x%.2X\n", *port_req);
+	/* loop over all muxed ports to find the one sending this */
+	for (i = 0; i < 8; i++) {
+		/* max 8 channels on MUX */
+		if (*port_req & (1 << i)) {
+			serial = get_serial_by_shared_int_and_type(shared_int,
+								   (1 << i));
+			if (serial != NULL) {
+				D1("Pending read interrupt on port %d\n", i);
+				if (!test_and_set_bit(HSO_SERIAL_FLAG_RX_SENT,
+						      &serial->flags)) {
+					/* Setup and send a ctrl req read on
+					 * port i */
+					hso_mux_serial_read(serial);
+				} else {
+					D1("Already pending a read on "
+					   "port %d\n", i);
+				}
+			}
+		}
+	}
+	/* Resubmit interrupt urb */
+	hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_KERNEL);
+}
+
+/* called for writing to muxed serial port */
+static int hso_mux_serial_write_data(struct hso_serial *serial)
+{
+	if (NULL == serial)
+		return -EINVAL;
+
+	return mux_device_request(serial,
+				  SEND_ENCAPSULATED_COMMAND,
+				  serial->parent->port_spec & HSO_PORT_MASK,
+				  serial->tx_urb,
+				  &serial->ctrl_req_tx,
+				  serial->tx_data, serial->tx_data_count);
+}
+
+/* called for writing diag or CS serial port */
+static int hso_std_serial_write_data(struct hso_serial *serial)
+{
+	int count = serial->tx_data_count;
+	int result = 0;
+
+	usb_fill_bulk_urb(serial->tx_urb,
+			  serial->parent->usb,
+			  usb_sndbulkpipe(serial->parent->usb,
+					  serial->out_endp->
+					  bEndpointAddress & 0x7F),
+			  serial->tx_data, serial->tx_data_count,
+			  hso_std_serial_write_bulk_callback, serial);
+
+	result = usb_submit_urb(serial->tx_urb, GFP_KERNEL);
+	if (result) {
+		dev_warn(&serial->parent->usb->dev,
+			 "Failed to submit urb - res %d\n", result);
+		return result;
+	}
+
+	return count;
+}
+
+/* callback after read or write on muxed serial port */
+static void ctrl_callback(struct urb *urb)
+{
+	struct hso_serial *serial = urb->context;
+	struct usb_ctrlrequest *req = NULL;
+	int status = urb->status;
+
+	/* sanity check */
+	if (!serial)
+		return;
+
+	if (status) {
+		log_usb_status(status, __func__);
+		return;
+	}
+
+	/* what request? */
+	req = (struct usb_ctrlrequest *)(urb->setup_packet);
+	D4("\n--- Got muxed ctrl callback 0x%02X ---", status);
+	D4("Actual length of urb = %d\n", urb->actual_length);
+	DUMP1(urb->transfer_buffer, urb->actual_length);
+
+	if (req->bRequestType ==
+	    (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) {
+		/* response to a read command */
+		if (serial->open_count > 0) {
+			/* handle RX data the normal way */
+			put_rxbuf_data(urb, serial);
+		}
+
+		/* Re issue a read as long as we receive data. */
+		if (urb->actual_length != 0)
+			hso_mux_serial_read(serial);
+		else
+			clear_bit(HSO_SERIAL_FLAG_RX_SENT, &serial->flags);
+	} else {
+		hso_put_activity(serial->parent);
+		tty_wakeup(serial->tty);
+		/* response to a write command */
+		hso_kick_transmit(serial);
+	}
+}
+
+/* handle RX data for serial port */
+static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
+{
+	struct tty_struct *tty = serial->tty;
+
+	/* Sanity check */
+	if (urb == NULL || serial == NULL) {
+		D1("serial = NULL");
+		return;
+	}
+
+	/* Push data to tty */
+	if (tty && urb->actual_length) {
+		D1("data to push to tty");
+		tty_buffer_request_room(tty, urb->actual_length);
+		tty_insert_flip_string(tty, urb->transfer_buffer,
+				       urb->actual_length);
+		tty_flip_buffer_push(tty);
+	}
+}
+
+/* read callback for Diag and CS port */
+static void hso_std_serial_read_bulk_callback(struct urb *urb)
+{
+	struct hso_serial *serial = urb->context;
+	int result = 0;
+	int status = urb->status;
+
+	/* sanity check */
+	if (!serial) {
+		D1("serial == NULL");
+		return;
+	} else if (status) {
+		log_usb_status(status, __func__);
+		return;
+	}
+
+	D4("\n--- Got serial_read_bulk callback %02x ---", status);
+	D1("Actual length = %d\n", urb->actual_length);
+	DUMP1(urb->transfer_buffer, urb->actual_length);
+
+	/* Anyone listening? */
+	if (serial->open_count == 0)
+		return;
+
+	if (status == 0) {
+		if (serial->parent->port_spec & HSO_INFO_CRC_BUG) {
+			u32 rest;
+			u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
+			rest =
+			    urb->actual_length %
+			    serial->in_endp->wMaxPacketSize;
+			if (((rest == 5) || (rest == 6))
+			    && !memcmp(((u8 *) urb->transfer_buffer) +
+				       urb->actual_length - 4, crc_check, 4)) {
+				urb->actual_length -= 4;
+			}
+		}
+		/* Valid data, handle RX data */
+		put_rxbuf_data(urb, serial);
+	} else if (status == -ENOENT || status == -ECONNRESET) {
+		/* Unlinked - check for throttled port. */
+		D2("Port %d, successfully unlinked urb", serial->minor);
+	} else {
+		D2("Port %d, status = %d for read urb", serial->minor, status);
+		return;
+	}
+
+	usb_mark_last_busy(urb->dev);
+
+	/* We are done with this URB, resubmit it. Prep the USB to wait for
+	 * another frame */
+	usb_fill_bulk_urb(urb, serial->parent->usb,
+			  usb_rcvbulkpipe(serial->parent->usb,
+					  serial->in_endp->
+					  bEndpointAddress & 0x7F),
+			  urb->transfer_buffer, serial->rx_data_length,
+			  hso_std_serial_read_bulk_callback, serial);
+	/* Give this to the USB subsystem so it can tell us when more data
+	 * arrives. */
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result) {
+		dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d",
+			__func__, result);
+	}
+}
+
+/* write callback for Diag and CS port */
+static void hso_std_serial_write_bulk_callback(struct urb *urb)
+{
+	struct hso_serial *serial = urb->context;
+	int status = urb->status;
+
+	/* sanity check */
+	if (!serial) {
+		D1("serial == NULL");
+		return;
+	}
+
+	if (status) {
+		log_usb_status(status, __func__);
+		return;
+	}
+	hso_put_activity(serial->parent);
+	tty_wakeup(serial->tty);
+	hso_kick_transmit(serial);
+
+	D1(" ");
+	return;
+}
+
+/* Base driver functions */
+
+/* operations setup of the serial interface */
+static struct tty_operations hso_serial_ops = {
+	.open = hso_serial_open,
+	.close = hso_serial_close,
+	.write = hso_serial_write,
+	.write_room = hso_serial_write_room,
+	.ioctl = hso_serial_ioctl,
+	.set_termios = hso_serial_set_termios,
+	.throttle = hso_serial_throttle,
+	.unthrottle = hso_serial_unthrottle,
+	.break_ctl = hso_serial_break,
+	.chars_in_buffer = hso_serial_chars_in_buffer,
+	.read_proc = hso_serial_read_proc,
+	.hangup = hso_serial_hangup,
+	.tiocmget = hso_serial_tiocmget,
+	.tiocmset = hso_serial_tiocmset,
+};
+
+static int __init hso_init(void)
+{
+	int i = 0;
+	int result = 0;
+
+	/* put it in the log */
+	printk(KERN_INFO "hso: %s\n", version);
+
+	/* if procfs is enabled, initialize devices */
+	if (procfs) {
+		hso_proc_dir = proc_mkdir(driver_name, NULL);
+		if (hso_proc_dir) {
+			hso_proc_dir_devices =
+			    proc_mkdir("devices", hso_proc_dir);
+			create_proc_read_entry("options", 0, hso_proc_dir,
+					       hso_proc_options, NULL);
+		}
+	}
+
+	/* Initialise the serial table semaphore and table */
+	spin_lock_init(&serial_table_lock);
+	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++)
+		serial_table[i] = NULL;
+
+	/* allocate our driver using the proper amount of supported minors */
+	tty_drv = alloc_tty_driver(HSO_SERIAL_TTY_MINORS);
+	if (!tty_drv)
+		return -ENOMEM;
+
+	/* fill in all needed values */
+	tty_drv->magic = TTY_DRIVER_MAGIC;
+	tty_drv->owner = THIS_MODULE;
+	tty_drv->driver_name = driver_name;
+	tty_drv->name = tty_filename;
+
+	/* if major number is provided as parameter, use that one */
+	if (tty_major)
+		tty_drv->major = tty_major;
+
+	tty_drv->minor_start = 0;
+	tty_drv->num = HSO_SERIAL_TTY_MINORS;
+	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
+	tty_drv->subtype = SERIAL_TYPE_NORMAL;
+	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	tty_drv->init_termios = tty_std_termios;
+	tty_drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	tty_drv->termios = hso_serial_termios;
+	tty_drv->termios_locked = hso_serial_termios_locked;
+	tty_set_operations(tty_drv, &hso_serial_ops);
+
+	/* register the tty driver */
+	result = tty_register_driver(tty_drv);
+	if (result) {
+		printk(KERN_ERR "%s - tty_register_driver failed(%d)\n",
+			__func__, result);
+		return result;
+	}
+
+	/* register this module as an usb driver */
+	result = usb_register(&hso_driver);
+	if (result) {
+		printk(KERN_ERR "Could not register hso driver? error: %d\n",
+			result);
+		/* cleanup serial interface */
+		tty_unregister_driver(tty_drv);
+		return result;
+	}
+
+	/* done */
+	return 0;
+}
+
+static void __exit hso_exit(void)
+{
+	printk(KERN_INFO "hso: unloaded\n");
+
+	tty_unregister_driver(tty_drv);
+	/* deregister the usb driver */
+	usb_deregister(&hso_driver);
+	/* if procfs is enabled, remove the entries */
+
+	if (procfs) {
+		remove_proc_entry("options", hso_proc_dir);
+		remove_proc_entry("devices", hso_proc_dir);
+		remove_proc_entry(driver_name, NULL);
+	}
+
+}
+
+static void hso_log_port(struct hso_device *hso_dev)
+{
+	char *port_type = NULL;
+	char port_dev[20];
+
+	switch (hso_dev->port_spec & HSO_PORT_MASK) {
+	case HSO_PORT_CONTROL:
+		port_type = "Control";
+		break;
+	case HSO_PORT_APP:
+		port_type = "Application";
+		break;
+	case HSO_PORT_GPS:
+		port_type = "GPS";
+		break;
+	case HSO_PORT_GPS_CONTROL:
+		port_type = "GPS control";
+		break;
+	case HSO_PORT_APP2:
+		port_type = "Application2";
+		break;
+	case HSO_PORT_PCSC:
+		port_type = "PCSC";
+		break;
+	case HSO_PORT_DIAG:
+		port_type = "Diagnostic";
+		break;
+	case HSO_PORT_DIAG2:
+		port_type = "Diagnostic2";
+		break;
+	case HSO_PORT_MODEM:
+		port_type = "Modem";
+		break;
+	case HSO_PORT_NETWORK:
+		port_type = "Network";
+		break;
+	default:
+		port_type = "Unknown";
+		break;
+	}
+	if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
+		sprintf(port_dev, "%s", dev2net(hso_dev)->net->name);
+	} else
+		sprintf(port_dev, "/dev/%s%d", tty_filename,
+			dev2ser(hso_dev)->minor);
+
+	dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n",
+		port_type, port_dev);
+}
+
+static int hso_start_net_device(struct hso_device *hso_dev)
+{
+	int i, result = 0;
+	struct hso_net *hso_net = dev2net(hso_dev);
+
+	if (!hso_net)
+		return -ENODEV;
+
+	/* send URBs for all read buffers */
+	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
+
+		/* Prep a receive URB */
+		usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i],
+				  hso_dev->usb,
+				  usb_rcvbulkpipe(hso_dev->usb,
+						  hso_net->in_endp->
+						  bEndpointAddress & 0x7F),
+				  hso_net->mux_bulk_rx_buf_pool[i],
+				  MUX_BULK_RX_BUF_SIZE, read_bulk_callback,
+				  hso_net);
+
+		/* Put it out there so the device can send us stuff */
+		result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i],
+					GFP_KERNEL);
+		if (result)
+			dev_warn(&hso_dev->usb->dev,
+				"%s failed mux_bulk_rx_urb[%d] %d\n", __func__,
+				i, result);
+	}
+
+	return result;
+}
+
+static int hso_stop_net_device(struct hso_device *hso_dev)
+{
+	int i;
+	struct hso_net *hso_net = dev2net(hso_dev);
+
+	if (!hso_net)
+		return -ENODEV;
+
+	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
+		if (hso_net->mux_bulk_rx_urb_pool[i]
+		    && (hso_net->mux_bulk_rx_urb_pool[i]->status ==
+			-EINPROGRESS))
+			usb_unlink_urb(hso_net->mux_bulk_rx_urb_pool[i]);
+
+	}
+	if (hso_net->mux_bulk_tx_urb
+	    && (hso_net->mux_bulk_tx_urb->status == -EINPROGRESS))
+		usb_unlink_urb(hso_net->mux_bulk_tx_urb);
+
+	return 0;
+}
+
+static int hso_start_serial_device(struct hso_device *hso_dev)
+{
+	int i, result = 0;
+	struct hso_serial *serial = dev2ser(hso_dev);
+
+	if (!serial)
+		return -ENODEV;
+
+	/* If it is not the MUX port fill in and submit a bulk urb (already
+	 * allocated in hso_serial_start) */
+	if (!(serial->parent->port_spec & HSO_INTF_MUX)) {
+		for (i = 0; i < serial->num_rx_urbs; i++) {
+			usb_fill_bulk_urb(serial->rx_urb[i],
+					  serial->parent->usb,
+					  usb_rcvbulkpipe(serial->parent->usb,
+							  serial->in_endp->
+							  bEndpointAddress &
+							  0x7F),
+					  serial->rx_data[i],
+					  serial->rx_data_length,
+					  hso_std_serial_read_bulk_callback,
+					  serial);
+			result = usb_submit_urb(serial->rx_urb[i], GFP_KERNEL);
+			if (result) {
+				dev_warn(&serial->parent->usb->dev,
+					 "Failed to submit urb - res %d\n",
+					 result);
+				break;
+			}
+		}
+	} else {
+		spin_lock_bh(&serial->shared_int->shared_int_lock);
+		if (!serial->shared_int->use_count) {
+			result =
+			    hso_mux_submit_intr_urb(serial->shared_int,
+						    hso_dev->usb, GFP_KERNEL);
+		}
+		serial->shared_int->use_count++;
+		spin_unlock_bh(&serial->shared_int->sh
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
[RFC] Patch to option HSO driver to the kernel, Greg KH, (Mon Apr 14, 5:32 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Wed Apr 16, 9:11 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Tue Apr 15, 6:49 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Wed Apr 16, 5:18 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Wed Apr 16, 7:54 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Wed Apr 16, 11:15 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Wed Apr 16, 8:03 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Wed Apr 16, 8:12 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Wed Apr 16, 9:43 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Wed Apr 16, 9:55 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Thu Apr 17, 10:32 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Fri Apr 18, 11:18 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Mon Apr 21, 7:45 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Mon Apr 21, 8:38 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Mon Apr 21, 8:50 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Mon Apr 21, 9:00 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Thu Apr 17, 5:47 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Fri Apr 18, 2:51 am)
RE: Re: [RFC] Patch to option HSO driver to the kernel, Chetty, Jay, (Thu Apr 17, 6:31 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 9:55 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 9:25 am)
Re: [RFC] Patch to option HSO driver to the kernel, Filip Aben, (Tue Apr 15, 10:12 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 10:14 am)
Re: [RFC] Patch to option HSO driver to the kernel, Filip Aben, (Tue Apr 15, 11:03 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 2:46 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Thu Apr 17, 8:15 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 4:17 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Stephen Hemminger, (Tue Apr 15, 3:14 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Filip Aben, (Tue Apr 15, 12:24 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 1:58 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 9:06 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 7:44 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Mon Apr 14, 7:20 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Neukum, (Tue Apr 15, 4:10 am)
Re: [RFC] Patch to option HSO driver to the kernel, Paulius Zaleckas, (Tue Apr 15, 4:58 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Hartkopp, (Tue Apr 15, 12:30 am)
Re: [RFC] Patch to option HSO driver to the kernel, Oliver Hartkopp, (Tue Apr 15, 1:53 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Matthew Dharm, (Mon Apr 14, 5:59 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Andrew Bird (Sphere Systems)..., (Mon Apr 14, 6:42 pm)
Re: [RFC] Patch to option HSO driver to the kernel, Filip Aben, (Tue Apr 15, 4:01 am)