172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/******************************************************************************
272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman * Driver for Option High Speed Mobile Devices.
472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  Copyright (C) 2008 Option International
611cd29b028be88b13717401496fe4953fb96be03Denis Joseph Barrow *                     Filip Aben <f.aben@option.com>
711cd29b028be88b13717401496fe4953fb96be03Denis Joseph Barrow *                     Denis Joseph Barrow <d.barow@option.com>
83b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon *                     Jan Dumon <j.dumon@option.com>
972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd)
1072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  			<ajb@spheresystems.co.uk>
1172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
1272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  Copyright (C) 2008 Novell, Inc.
1372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
1472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  This program is free software; you can redistribute it and/or modify
1572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  it under the terms of the GNU General Public License version 2 as
1672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  published by the Free Software Foundation.
1772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
1872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  This program is distributed in the hope that it will be useful,
1972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  but WITHOUT ANY WARRANTY; without even the implied warranty of
2072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  GNU General Public License for more details.
2272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
2372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  You should have received a copy of the GNU General Public License
2472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  along with this program; if not, write to the Free Software
2572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
2672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *  USA
2772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
2872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
2972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *****************************************************************************/
3072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
3172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/******************************************************************************
3272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
3372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman * Description of the device:
3472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
3572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman * Interface 0:	Contains the IP network interface on the bulk end points.
3672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *		The multiplexed serial ports are using the interrupt and
3772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *		control endpoints.
3872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *		Interrupt contains a bitmap telling which multiplexed
3972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *		serialport needs servicing.
4072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
4172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman * Interface 1:	Diagnostics port, uses bulk only, do not submit urbs until the
4272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *		port is opened, as this have a huge impact on the network port
4372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *		throughput.
4472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
45542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow * Interface 2:	Standard modem interface - circuit switched interface, this
46542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow *		can be used to make a standard ppp connection however it
47542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow *              should not be used in conjunction with the IP network interface
48542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow *              enabled for USB performance reasons i.e. if using this set
49542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow *              ideally disable_net=1.
5072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *
5172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman *****************************************************************************/
5272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
5372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/sched.h>
5472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/slab.h>
5572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/init.h>
5672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/delay.h>
5772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/netdevice.h>
5872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/module.h>
5972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/ethtool.h>
6072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/usb.h>
6172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/timer.h>
6272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/tty.h>
6372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/tty_driver.h>
6472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/tty_flip.h>
6572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/kmod.h>
6672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/rfkill.h>
6772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/ip.h>
6872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/uaccess.h>
6972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <linux/usb/cdc.h>
7072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <net/arp.h>
7172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#include <asm/byteorder.h>
72542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#include <linux/serial_core.h>
73542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#include <linux/serial.h>
7472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
7572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
7672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define MOD_AUTHOR			"Option Wireless"
7772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define MOD_DESCRIPTION			"USB High Speed Option driver"
7872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define MOD_LICENSE			"GPL"
7972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
8072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_MAX_NET_DEVICES		10
8172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO__MAX_MTU			2048
8272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define DEFAULT_MTU			1500
8372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define DEFAULT_MRU			1500
8472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
8572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define CTRL_URB_RX_SIZE		1024
8672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define CTRL_URB_TX_SIZE		64
8772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
8872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define BULK_URB_RX_SIZE		4096
8972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define BULK_URB_TX_SIZE		8192
9072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
9172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define MUX_BULK_RX_BUF_SIZE		HSO__MAX_MTU
9272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define MUX_BULK_TX_BUF_SIZE		HSO__MAX_MTU
9372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define MUX_BULK_RX_BUF_COUNT		4
9472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define USB_TYPE_OPTION_VENDOR		0x20
9572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
9672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* These definitions are used with the struct hso_net flags element */
9772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* - use *_bit operations on it. (bit indices not values.) */
9872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_NET_RUNNING			0
9972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
10072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_NET_TX_TIMEOUT		(HZ*10)
10172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
10272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_SERIAL_MAGIC		0x48534f31
10372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
10472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Number of ttys to handle */
10572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_SERIAL_TTY_MINORS		256
10672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
10772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define MAX_RX_URBS			2
10872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
1090235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartmanstatic inline struct hso_serial *get_serial_by_tty(struct tty_struct *tty)
1100235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman{
1110235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	if (tty)
1120235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman		return tty->driver_data;
1130235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	return NULL;
1140235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman}
11572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
11672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
11772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Debugging functions                                                       */
11872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
11972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define D__(lvl_, fmt, arg...)				\
12072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	do {						\
12172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		printk(lvl_ "[%d:%s]: " fmt "\n",	\
12272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		       __LINE__, __func__, ## arg);	\
12372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} while (0)
12472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
12572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define D_(lvl, args...)				\
12672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	do {						\
12772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (lvl & debug)			\
12872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			D__(KERN_INFO, args);		\
12972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} while (0)
13072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
13172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define D1(args...)	D_(0x01, ##args)
13272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define D2(args...)	D_(0x02, ##args)
13372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define D3(args...)	D_(0x04, ##args)
13472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define D4(args...)	D_(0x08, ##args)
13572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define D5(args...)	D_(0x10, ##args)
13672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
13772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
13872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Enumerators                                                               */
13972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
14072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanenum pkt_parse_state {
14172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	WAIT_IP,
14272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	WAIT_DATA,
14372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	WAIT_SYNC
14472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
14572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
14672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
14772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Structs                                                                   */
14872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
14972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
15072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstruct hso_shared_int {
15172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_endpoint_descriptor *intr_endp;
15272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	void *shared_intr_buf;
15372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct urb *shared_intr_urb;
15472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_device *usb;
15572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int use_count;
15672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int ref_count;
15772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct mutex shared_int_lock;
15872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
15972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
16072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstruct hso_net {
16172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *parent;
16272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct net_device *net;
16372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct rfkill *rfkill;
16472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
16572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_endpoint_descriptor *in_endp;
16672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_endpoint_descriptor *out_endp;
16772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
16872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct urb *mux_bulk_rx_urb_pool[MUX_BULK_RX_BUF_COUNT];
16972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct urb *mux_bulk_tx_urb;
17072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	void *mux_bulk_rx_buf_pool[MUX_BULK_RX_BUF_COUNT];
17172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	void *mux_bulk_tx_buf;
17272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
17372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct sk_buff *skb_rx_buf;
17472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct sk_buff *skb_tx_buf;
17572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
17672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	enum pkt_parse_state rx_parse_state;
17772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spinlock_t net_lock;
17872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
17972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned short rx_buf_size;
18072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned short rx_buf_missing;
18172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct iphdr rx_ip_hdr;
18272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
18372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
18472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
18572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
1868ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowenum rx_ctrl_state{
1878ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	RX_IDLE,
1888ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	RX_SENT,
1898ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	RX_PENDING
1908ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow};
1918ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
192542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define BM_REQUEST_TYPE (0xa1)
193542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_NOTIFICATION  (0x20)
194542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define W_VALUE         (0x0)
195542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define W_INDEX         (0x2)
196542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define W_LENGTH        (0x2)
197542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
198542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_OVERRUN       (0x1<<6)
199542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_PARITY        (0x1<<5)
200542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_FRAMING       (0x1<<4)
201542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_RING_SIGNAL   (0x1<<3)
202542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_BREAK         (0x1<<2)
203542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_TX_CARRIER    (0x1<<1)
204542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow#define B_RX_CARRIER    (0x1<<0)
205542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
206542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrowstruct hso_serial_state_notification {
207542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u8 bmRequestType;
208542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u8 bNotification;
209542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u16 wValue;
210542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u16 wIndex;
211542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u16 wLength;
212542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u16 UART_state_bitmap;
213ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
214542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
215542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrowstruct hso_tiocmget {
216542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct mutex mutex;
217542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	wait_queue_head_t waitq;
218542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	int    intr_completed;
219542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct usb_endpoint_descriptor *endp;
220542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct urb *urb;
221542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_serial_state_notification serial_state_notification;
222542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u16    prev_UART_state_bitmap;
223542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct uart_icount icount;
224542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow};
225542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
226542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
22772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstruct hso_serial {
22872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *parent;
22972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int magic;
23072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 minor;
23172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
23272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_shared_int *shared_int;
23372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
23472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* rx/tx urb could be either a bulk urb or a control urb depending
23572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	   on which serial port it is used on. */
23672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct urb *rx_urb[MAX_RX_URBS];
23772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 num_rx_urbs;
23872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 *rx_data[MAX_RX_URBS];
23972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u16 rx_data_length;	/* should contain allocated length */
24072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
24172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct urb *tx_urb;
24272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 *tx_data;
24372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 *tx_buffer;
24472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u16 tx_data_length;	/* should contain allocated length */
24572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u16 tx_data_count;
24672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u16 tx_buffer_count;
24772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_ctrlrequest ctrl_req_tx;
24872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_ctrlrequest ctrl_req_rx;
24972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
25072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_endpoint_descriptor *in_endp;
25172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_endpoint_descriptor *out_endp;
25272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
2538ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	enum rx_ctrl_state rx_state;
25472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 rts_state;
25572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 dtr_state;
25672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned tx_urb_used:1;
25772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
25872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* from usb_serial_port */
25972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct tty_struct *tty;
26072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int open_count;
26172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spinlock_t serial_lock;
26272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
26372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int (*write_data) (struct hso_serial *serial);
264542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_tiocmget  *tiocmget;
2658ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	/* Hacks required to get flow control
2668ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	 * working on the serial receive buffers
2678ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	 * so as not to drop characters on the floor.
2688ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	 */
2698ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int  curr_rx_urb_idx;
2708ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	u16  curr_rx_urb_offset;
2718ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	u8   rx_urb_filled[MAX_RX_URBS];
2728ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	struct tasklet_struct unthrottle_tasklet;
2738ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	struct work_struct    retry_unthrottle_workqueue;
27472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
27572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
27672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstruct hso_device {
27772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	union {
27872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		struct hso_serial *dev_serial;
27972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		struct hso_net *dev_net;
28072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} port_data;
28172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
28272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u32 port_spec;
28372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
28472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 is_active;
28572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 usb_gone;
28672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct work_struct async_get_intf;
28772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct work_struct async_put_intf;
28868a351c501ad22077a969df157cd13367cb43a40Jan Dumon	struct work_struct reset_device;
28972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
29072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_device *usb;
29172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_interface *interface;
29272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
29372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct device *dev;
29472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct kref ref;
295ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	struct mutex mutex;
29672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
29772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
29872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Type of interface */
29972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_INTF_MASK		0xFF00
30072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_INTF_MUX		0x0100
30172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_INTF_BULK   	0x0200
30272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
30372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Type of port */
30472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_PORT_MASK		0xFF
30572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_PORT_NO_PORT	0x0
30672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_CONTROL	0x1
30772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_APP		0x2
30872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_GPS		0x3
30972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_PCSC		0x4
31072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_APP2		0x5
31172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_PORT_GPS_CONTROL	0x6
31272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_PORT_MSD		0x7
31372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_PORT_VOICE		0x8
31472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_PORT_DIAG2		0x9
31572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_DIAG		0x10
31672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_MODEM		0x11
31772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define	HSO_PORT_NETWORK	0x12
31872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
31972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Additional device info */
32072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_INFO_MASK		0xFF000000
32172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define HSO_INFO_CRC_BUG	0x01000000
32272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
32372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
32472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Prototypes                                                                */
32572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
32672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Serial driver functions */
32720b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int hso_serial_tiocmset(struct tty_struct *tty,
32872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			       unsigned int set, unsigned int clear);
32972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void ctrl_callback(struct urb *urb);
3308ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic int put_rxbuf_data(struct urb *urb, struct hso_serial *serial);
33172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_kick_transmit(struct hso_serial *serial);
33272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Helper functions */
33372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int,
33472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				   struct usb_device *usb, gfp_t gfp);
33568a351c501ad22077a969df157cd13367cb43a40Jan Dumonstatic void handle_usb_error(int status, const char *function,
33668a351c501ad22077a969df157cd13367cb43a40Jan Dumon			     struct hso_device *hso_dev);
33772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
33872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						  int type, int dir);
33972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports);
34072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_free_interface(struct usb_interface *intf);
34172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags);
34272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_stop_serial_device(struct hso_device *hso_dev);
34372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_start_net_device(struct hso_device *hso_dev);
34472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_free_shared_int(struct hso_shared_int *shared_int);
34572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_stop_net_device(struct hso_device *hso_dev);
34672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_serial_ref_free(struct kref *ref);
3478ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic void hso_std_serial_read_bulk_callback(struct urb *urb);
3488ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic int hso_mux_serial_read(struct hso_serial *serial);
34972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void async_get_intf(struct work_struct *data);
35072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void async_put_intf(struct work_struct *data);
35172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_put_activity(struct hso_device *hso_dev);
35272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_get_activity(struct hso_device *hso_dev);
353542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrowstatic void tiocmget_intr_callback(struct urb *urb);
35468a351c501ad22077a969df157cd13367cb43a40Jan Dumonstatic void reset_device(struct work_struct *data);
35572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
35672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Helping functions                                                         */
35772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/*****************************************************************************/
35872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
35972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* #define DEBUG */
36072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
3610235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartmanstatic inline struct hso_net *dev2net(struct hso_device *hso_dev)
3620235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman{
3630235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	return hso_dev->port_data.dev_net;
3640235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman}
3650235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman
3660235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartmanstatic inline struct hso_serial *dev2ser(struct hso_device *hso_dev)
3670235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman{
3680235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	return hso_dev->port_data.dev_serial;
3690235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman}
37072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
37172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Debugging functions */
37272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#ifdef DEBUG
37372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void dbg_dump(int line_count, const char *func_name, unsigned char *buf,
37472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		     unsigned int len)
37572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
3760235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	static char name[255];
37772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
3780235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	sprintf(name, "hso[%d:%s]", line_count, func_name);
3790235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	print_hex_dump_bytes(name, DUMP_PREFIX_NONE, buf, len);
38072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
38172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
38272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define DUMP(buf_, len_)	\
3839ce673d5e919966efc1ef5adf20248e6ecc62724Antti Kaijanmäki	dbg_dump(__LINE__, __func__, (unsigned char *)buf_, len_)
38472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
38572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define DUMP1(buf_, len_)			\
38672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	do {					\
38772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (0x01 & debug)		\
38872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			DUMP(buf_, len_);	\
38972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} while (0)
39072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#else
39172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define DUMP(buf_, len_)
39272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define DUMP1(buf_, len_)
39372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#endif
39472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
39572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* module parameters */
39672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int debug;
39772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int tty_major;
39872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int disable_net;
39972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
40072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* driver info */
40172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic const char driver_name[] = "hso";
40272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic const char tty_filename[] = "ttyHS";
403242647bcf8464860f173f3d4d4ab3490d3558518Filip Abenstatic const char *version = __FILE__ ": " MOD_AUTHOR;
40472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* the usb driver itself (registered in hso_init) */
40572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct usb_driver hso_driver;
40672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* serial structures */
40772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct tty_driver *tty_drv;
40872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS];
40972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct hso_device *network_table[HSO_MAX_NET_DEVICES];
41072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic spinlock_t serial_table_lock;
41172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
41272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic const s32 default_port_spec[] = {
41372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	HSO_INTF_MUX | HSO_PORT_NETWORK,
41472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	HSO_INTF_BULK | HSO_PORT_DIAG,
41572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	HSO_INTF_BULK | HSO_PORT_MODEM,
41672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	0
41772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
41872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
41972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic const s32 icon321_port_spec[] = {
42072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	HSO_INTF_MUX | HSO_PORT_NETWORK,
42172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	HSO_INTF_BULK | HSO_PORT_DIAG2,
42272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	HSO_INTF_BULK | HSO_PORT_MODEM,
42372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	HSO_INTF_BULK | HSO_PORT_DIAG,
42472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	0
42572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
42672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
42772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define default_port_device(vendor, product)	\
42872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	USB_DEVICE(vendor, product),	\
42972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		.driver_info = (kernel_ulong_t)default_port_spec
43072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
43172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman#define icon321_port_device(vendor, product)	\
43272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	USB_DEVICE(vendor, product),	\
43372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		.driver_info = (kernel_ulong_t)icon321_port_spec
43472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
43572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* list of devices we support */
43672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic const struct usb_device_id hso_ids[] = {
43772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6711)},
43872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6731)},
43972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6751)},
44072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6771)},
44172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6791)},
44272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6811)},
44372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6911)},
44472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6951)},
44572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x6971)},
44672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7011)},
44772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7031)},
44872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7051)},
44972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7071)},
45072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7111)},
45172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7211)},
45272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7251)},
45372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7271)},
45472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0x7311)},
45572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{default_port_device(0x0af0, 0xc031)},	/* Icon-Edge */
45672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{icon321_port_device(0x0af0, 0xd013)},	/* Module HSxPA */
45772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{icon321_port_device(0x0af0, 0xd031)},	/* Icon-321 */
45895eacee870a521d2647f42c4f670cd65a145a6bdDenis Joseph Barrow	{icon321_port_device(0x0af0, 0xd033)},	/* Icon-322 */
45972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{USB_DEVICE(0x0af0, 0x7301)},		/* GE40x */
46072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{USB_DEVICE(0x0af0, 0x7361)},		/* GE40x */
46167dd82462d553c35bef14de1bf8afcb1095e041dFilip Aben	{USB_DEVICE(0x0af0, 0x7381)},		/* GE40x */
46272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{USB_DEVICE(0x0af0, 0x7401)},		/* GI 0401 */
46372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{USB_DEVICE(0x0af0, 0x7501)},		/* GTM 382 */
46472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{USB_DEVICE(0x0af0, 0x7601)},		/* GE40x */
465bab04c3adbb55aeb5e8db60522f14ce0bb0d4179Denis Joseph Barrow	{USB_DEVICE(0x0af0, 0x7701)},
466ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0x7706)},
467bab04c3adbb55aeb5e8db60522f14ce0bb0d4179Denis Joseph Barrow	{USB_DEVICE(0x0af0, 0x7801)},
468bab04c3adbb55aeb5e8db60522f14ce0bb0d4179Denis Joseph Barrow	{USB_DEVICE(0x0af0, 0x7901)},
469ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0x7A01)},
470ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0x7A05)},
4719961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0x8200)},
4729961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0x8201)},
473ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0x8300)},
474ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0x8302)},
475ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0x8304)},
476ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0x8400)},
477dd7496f217462a23a9a8a15b9925866eaad76e22Filip Aben	{USB_DEVICE(0x0af0, 0x8600)},
478dd7496f217462a23a9a8a15b9925866eaad76e22Filip Aben	{USB_DEVICE(0x0af0, 0x8800)},
479dd7496f217462a23a9a8a15b9925866eaad76e22Filip Aben	{USB_DEVICE(0x0af0, 0x8900)},
4805c7bf2f4d6304ab4741f38365ca0c0223147263dFilip Aben	{USB_DEVICE(0x0af0, 0x9000)},
4819961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0xd035)},
48267dd82462d553c35bef14de1bf8afcb1095e041dFilip Aben	{USB_DEVICE(0x0af0, 0xd055)},
4839961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0xd155)},
4849961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0xd255)},
4859961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0xd057)},
4869961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0xd157)},
4879961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0xd257)},
4889961d84270ce1d5f4c662382acf0b418a3ff6a0fJan Dumon	{USB_DEVICE(0x0af0, 0xd357)},
489ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0xd058)},
490ec157937d9799cf30c9a19bd18be33721242c64fJan Dumon	{USB_DEVICE(0x0af0, 0xc100)},
49172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	{}
49272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
49372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-HartmanMODULE_DEVICE_TABLE(usb, hso_ids);
49472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
49572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Sysfs attribute */
49672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic ssize_t hso_sysfs_show_porttype(struct device *dev,
49772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				       struct device_attribute *attr,
49872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				       char *buf)
49972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
5001aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman	struct hso_device *hso_dev = dev_get_drvdata(dev);
50172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	char *port_name;
50272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
50372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_dev)
50472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return 0;
50572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
50672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	switch (hso_dev->port_spec & HSO_PORT_MASK) {
50772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_CONTROL:
50872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Control";
50972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
51072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_APP:
51172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Application";
51272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
51372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_APP2:
51472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Application2";
51572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
51672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_GPS:
51772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "GPS";
51872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
51972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_GPS_CONTROL:
52072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "GPS Control";
52172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
52272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_PCSC:
52372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "PCSC";
52472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
52572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_DIAG:
52672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Diagnostic";
52772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
52872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_DIAG2:
52972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Diagnostic2";
53072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
53172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_MODEM:
53272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Modem";
53372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
53472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_NETWORK:
53572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Network";
53672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
53772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	default:
53872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_name = "Unknown";
53972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
54072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
54172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
54272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return sprintf(buf, "%s\n", port_name);
54372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
54472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL);
54572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
5468ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic int hso_urb_to_index(struct hso_serial *serial, struct urb *urb)
5478ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
5488ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int idx;
5498ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
5508ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	for (idx = 0; idx < serial->num_rx_urbs; idx++)
5518ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		if (serial->rx_urb[idx] == urb)
5528ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			return idx;
5538ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	dev_err(serial->parent->dev, "hso_urb_to_index failed\n");
5548ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	return -1;
5558ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
5568ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
55772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* converts mux value to a port spec value */
55872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic u32 hso_mux_to_port(int mux)
55972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
56072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u32 result;
56172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
56272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	switch (mux) {
56372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x1:
56472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_CONTROL;
56572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
56672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x2:
56772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_APP;
56872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
56972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x4:
57072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_PCSC;
57172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
57272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x8:
57372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_GPS;
57472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
57572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x10:
57672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_APP2;
57772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
57872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	default:
57972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_NO_PORT;
58072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
58172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return result;
58272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
58372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
58472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* converts port spec value to a mux value */
58572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic u32 hso_port_to_mux(int port)
58672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
58772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u32 result;
58872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
58972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	switch (port & HSO_PORT_MASK) {
59072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_CONTROL:
59172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0x0;
59272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
59372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_APP:
59472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0x1;
59572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
59672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_PCSC:
59772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0x2;
59872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
59972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_GPS:
60072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0x3;
60172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
60272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_APP2:
60372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0x4;
60472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
60572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	default:
60672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0x0;
60772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
60872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return result;
60972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
61072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
61172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct hso_serial *get_serial_by_shared_int_and_type(
61272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					struct hso_shared_int *shared_int,
61372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					int mux)
61472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
61572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i, port;
61672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
61772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	port = hso_mux_to_port(mux);
61872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
61972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
6208e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		if (serial_table[i] &&
6218e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		    (dev2ser(serial_table[i])->shared_int == shared_int) &&
6228e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		    ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) {
62372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			return dev2ser(serial_table[i]);
62472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
62572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
62672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
62772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return NULL;
62872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
62972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
63072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct hso_serial *get_serial_by_index(unsigned index)
63172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
6320235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	struct hso_serial *serial = NULL;
63372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
63472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
63572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial_table_lock, flags);
6360235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman	if (serial_table[index])
6370235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman		serial = dev2ser(serial_table[index]);
63872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial_table_lock, flags);
63972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
64072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return serial;
64172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
64272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
64372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int get_free_serial_index(void)
64472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
64572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int index;
64672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
64772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
64872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial_table_lock, flags);
64972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (index = 0; index < HSO_SERIAL_TTY_MINORS; index++) {
65072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (serial_table[index] == NULL) {
65172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			spin_unlock_irqrestore(&serial_table_lock, flags);
65272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			return index;
65372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
65472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
65572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial_table_lock, flags);
65672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
65772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	printk(KERN_ERR "%s: no free serial devices in table\n", __func__);
65872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return -1;
65972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
66072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
66172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void set_serial_by_index(unsigned index, struct hso_serial *serial)
66272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
66372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
6640235f64175db41fa17a6ce5c9b58fd3550986eb4Greg Kroah-Hartman
66572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial_table_lock, flags);
66672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial)
66772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial_table[index] = serial->parent;
66872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	else
66972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial_table[index] = NULL;
67072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial_table_lock, flags);
67172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
67272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
67368a351c501ad22077a969df157cd13367cb43a40Jan Dumonstatic void handle_usb_error(int status, const char *function,
67468a351c501ad22077a969df157cd13367cb43a40Jan Dumon			     struct hso_device *hso_dev)
67572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
67672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	char *explanation;
67772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
67872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	switch (status) {
67972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -ENODEV:
68072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "no device";
68172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
68272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -ENOENT:
68372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "endpoint not enabled";
68472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
68572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -EPIPE:
68672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "endpoint stalled";
68772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
68872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -ENOSPC:
68972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "not enough bandwidth";
69072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
69172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -ESHUTDOWN:
69272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "device disabled";
69372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
69472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -EHOSTUNREACH:
69572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "device suspended";
69672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
69772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -EINVAL:
69872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -EAGAIN:
69972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -EFBIG:
70072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case -EMSGSIZE:
70172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "internal error";
70272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
70368a351c501ad22077a969df157cd13367cb43a40Jan Dumon	case -EILSEQ:
70468a351c501ad22077a969df157cd13367cb43a40Jan Dumon	case -EPROTO:
70568a351c501ad22077a969df157cd13367cb43a40Jan Dumon	case -ETIME:
70668a351c501ad22077a969df157cd13367cb43a40Jan Dumon	case -ETIMEDOUT:
70768a351c501ad22077a969df157cd13367cb43a40Jan Dumon		explanation = "protocol error";
70868a351c501ad22077a969df157cd13367cb43a40Jan Dumon		if (hso_dev)
70968a351c501ad22077a969df157cd13367cb43a40Jan Dumon			schedule_work(&hso_dev->reset_device);
71068a351c501ad22077a969df157cd13367cb43a40Jan Dumon		break;
71172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	default:
71272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		explanation = "unknown status";
71372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
71472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
71568a351c501ad22077a969df157cd13367cb43a40Jan Dumon
71668a351c501ad22077a969df157cd13367cb43a40Jan Dumon	/* log a meaningful explanation of an USB status */
71772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D1("%s: received USB status - %s (%d)", function, explanation, status);
71872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
71972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
72072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Network interface functions */
72172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
72272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called when net interface is brought up by ifconfig */
72372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_net_open(struct net_device *net)
72472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
72572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *odev = netdev_priv(net);
72672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags = 0;
72772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
72872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!odev) {
72972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&net->dev, "No net device !\n");
73072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
73172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
73272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
73372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	odev->skb_tx_buf = NULL;
73472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
73572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* setup environment */
73672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&odev->net_lock, flags);
73772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	odev->rx_parse_state = WAIT_IP;
73872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	odev->rx_buf_size = 0;
73972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	odev->rx_buf_missing = sizeof(struct iphdr);
74072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&odev->net_lock, flags);
74172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
74272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* We are up and running. */
74372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	set_bit(HSO_NET_RUNNING, &odev->flags);
744889bd9b6dbcd426b8698c4a779dd7dbf247f57b8Oliver Neukum	hso_start_net_device(odev->parent);
74572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
74672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Tell the kernel we are ready to start receiving from it */
74772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	netif_start_queue(net);
74872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
74972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
75072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
75172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
75272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called when interface is brought down by ifconfig */
75372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_net_close(struct net_device *net)
75472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
75572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *odev = netdev_priv(net);
75672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
75772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* we don't need the queue anymore */
75872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	netif_stop_queue(net);
75972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* no longer running */
76072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	clear_bit(HSO_NET_RUNNING, &odev->flags);
76172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
76272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_stop_net_device(odev->parent);
76372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
76472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done */
76572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
76672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
76772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
76872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* USB tells is xmit done, we should start the netqueue again */
76972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void write_bulk_callback(struct urb *urb)
77072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
77172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *odev = urb->context;
77272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int status = urb->status;
77372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
77472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Sanity check */
77572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
77672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&urb->dev->dev, "%s: device not running\n", __func__);
77772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
77872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
77972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
78072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Do we still have a valid kernel network device? */
78172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!netif_device_present(odev->net)) {
78272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&urb->dev->dev, "%s: net device not present\n",
78372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			__func__);
78472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
78572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
78672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
78772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* log status, but don't act on it, we don't need to resubmit anything
78872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 * anyhow */
78972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (status)
79068a351c501ad22077a969df157cd13367cb43a40Jan Dumon		handle_usb_error(status, __func__, odev->parent);
79172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
79272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_put_activity(odev->parent);
79372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
79472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Tell the network interface we are ready for another frame */
79572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	netif_wake_queue(odev->net);
79672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
79772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
79872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called by kernel when we need to transmit a packet */
79925a79c41ce0ce88a4288adf278e9b0e00f228383Stephen Hemmingerstatic netdev_tx_t hso_net_start_xmit(struct sk_buff *skb,
80025a79c41ce0ce88a4288adf278e9b0e00f228383Stephen Hemminger					    struct net_device *net)
80172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
80272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *odev = netdev_priv(net);
80372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int result;
80472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
80572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Tell the kernel, "No more frames 'til we are done with this one." */
80672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	netif_stop_queue(net);
80772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_get_activity(odev->parent) == -EAGAIN) {
80872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		odev->skb_tx_buf = skb;
8096ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy		return NETDEV_TX_OK;
81072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
81172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
81272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* log if asked */
81372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	DUMP1(skb->data, skb->len);
81472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Copy it from kernel memory to OUR memory */
81572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len);
81672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D1("len: %d/%d", skb->len, MUX_BULK_TX_BUF_SIZE);
81772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
81872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Fill in the URB for shipping it out. */
81972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_fill_bulk_urb(odev->mux_bulk_tx_urb,
82072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  odev->parent->usb,
82172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  usb_sndbulkpipe(odev->parent->usb,
82272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  odev->out_endp->
82372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  bEndpointAddress & 0x7F),
82472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  odev->mux_bulk_tx_buf, skb->len, write_bulk_callback,
82572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  odev);
82672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
82772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Deal with the Zero Length packet problem, I hope */
82872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET;
82972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
83072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Send the URB on its merry way. */
83172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC);
83272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result) {
83372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_warn(&odev->parent->interface->dev,
8348a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon			"failed mux_bulk_tx_urb %d\n", result);
83572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		net->stats.tx_errors++;
83672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		netif_start_queue(net);
83772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} else {
83872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		net->stats.tx_packets++;
83972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		net->stats.tx_bytes += skb->len;
84072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
84172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	dev_kfree_skb(skb);
84272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* we're done */
8435b2c4b972c0226406361f83b747eb5cdab51e68ePatrick McHardy	return NETDEV_TX_OK;
84472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
84572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
8460fc0b732eaa38beb93a6fb62f77c7bd9622c76ecStephen Hemmingerstatic const struct ethtool_ops ops = {
84772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.get_link = ethtool_op_get_link
84872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
84972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
85072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called when a packet did not ack after watchdogtimeout */
85172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_net_tx_timeout(struct net_device *net)
85272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
85372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *odev = netdev_priv(net);
85472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
85572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!odev)
85672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
85772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
85872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Tell syslog we are hosed. */
85972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	dev_warn(&net->dev, "Tx timed out.\n");
86072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
86172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Tear the waiting frame off the list */
8628e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches	if (odev->mux_bulk_tx_urb &&
8638e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches	    (odev->mux_bulk_tx_urb->status == -EINPROGRESS))
86472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_unlink_urb(odev->mux_bulk_tx_urb);
86572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
86672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Update statistics */
86772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net->stats.tx_errors++;
86872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
86972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
87072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* make a real packet from the received USB buffer */
87172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,
87272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			unsigned int count, unsigned char is_eop)
87372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
87472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned short temp_bytes;
87572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned short buffer_offset = 0;
87672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned short frame_len;
87772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned char *tmp_rx_buf;
87872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
87972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* log if needed */
88072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D1("Rx %d bytes", count);
88172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	DUMP(ip_pkt, min(128, (int)count));
88272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
88372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	while (count) {
88472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		switch (odev->rx_parse_state) {
88572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		case WAIT_IP:
88672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			/* waiting for IP header. */
88772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			/* wanted bytes - size of ip header */
88872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			temp_bytes =
88972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    (count <
89072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			     odev->rx_buf_missing) ? count : odev->
89172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    rx_buf_missing;
89272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
89372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			memcpy(((unsigned char *)(&odev->rx_ip_hdr)) +
89472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			       odev->rx_buf_size, ip_pkt + buffer_offset,
89572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			       temp_bytes);
89672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
89772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			odev->rx_buf_size += temp_bytes;
89872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			buffer_offset += temp_bytes;
89972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			odev->rx_buf_missing -= temp_bytes;
90072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			count -= temp_bytes;
90172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
90272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (!odev->rx_buf_missing) {
90372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* header is complete allocate an sk_buffer and
90472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				 * continue to WAIT_DATA */
90572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				frame_len = ntohs(odev->rx_ip_hdr.tot_len);
90672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
90772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				if ((frame_len > DEFAULT_MRU) ||
90872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				    (frame_len < sizeof(struct iphdr))) {
90972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					dev_err(&odev->net->dev,
91072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						"Invalid frame (%d) length\n",
91172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						frame_len);
91272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					odev->rx_parse_state = WAIT_SYNC;
91372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					continue;
91472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				}
91572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* Allocate an sk_buff */
916d65a68a8da5c9d1041ebc5e018e677d641910668Paulius Zaleckas				odev->skb_rx_buf = netdev_alloc_skb(odev->net,
917d65a68a8da5c9d1041ebc5e018e677d641910668Paulius Zaleckas								    frame_len);
91872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				if (!odev->skb_rx_buf) {
91972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					/* We got no receive buffer. */
92072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					D1("could not allocate memory");
92172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					odev->rx_parse_state = WAIT_SYNC;
92272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					return;
92372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				}
92472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
92572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* Copy what we got so far. make room for iphdr
92672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				 * after tail. */
92772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				tmp_rx_buf =
92872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				    skb_put(odev->skb_rx_buf,
92972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					    sizeof(struct iphdr));
93072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				memcpy(tmp_rx_buf, (char *)&(odev->rx_ip_hdr),
93172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				       sizeof(struct iphdr));
93272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
93372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* ETH_HLEN */
93472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->rx_buf_size = sizeof(struct iphdr);
93572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
93672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* Filip actually use .tot_len */
93772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->rx_buf_missing =
93872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				    frame_len - sizeof(struct iphdr);
93972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->rx_parse_state = WAIT_DATA;
94072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
94172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			break;
94272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
94372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		case WAIT_DATA:
94472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			temp_bytes = (count < odev->rx_buf_missing)
94572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					? count : odev->rx_buf_missing;
94672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
94772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			/* Copy the rest of the bytes that are left in the
94872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 * buffer into the waiting sk_buf. */
94972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			/* Make room for temp_bytes after tail. */
95072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			tmp_rx_buf = skb_put(odev->skb_rx_buf, temp_bytes);
95172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			memcpy(tmp_rx_buf, ip_pkt + buffer_offset, temp_bytes);
95272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
95372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			odev->rx_buf_missing -= temp_bytes;
95472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			count -= temp_bytes;
95572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			buffer_offset += temp_bytes;
95672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			odev->rx_buf_size += temp_bytes;
95772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (!odev->rx_buf_missing) {
95872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* Packet is complete. Inject into stack. */
95972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* We have IP packet here */
96009640e6365c679b5642b1c41b6d7078f51689ddfHarvey Harrison				odev->skb_rx_buf->protocol = cpu_to_be16(ETH_P_IP);
96172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				skb_reset_mac_header(odev->skb_rx_buf);
96272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
96372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* Ship it off to the kernel */
96472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				netif_rx(odev->skb_rx_buf);
96572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* No longer our buffer. */
96672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->skb_rx_buf = NULL;
96772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
96872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				/* update out statistics */
96972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->net->stats.rx_packets++;
97072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
97172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->net->stats.rx_bytes += odev->rx_buf_size;
97272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
97372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->rx_buf_size = 0;
97472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->rx_buf_missing = sizeof(struct iphdr);
97572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				odev->rx_parse_state = WAIT_IP;
97672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
97772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			break;
97872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
97972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		case WAIT_SYNC:
98072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			D1(" W_S");
98172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			count = 0;
98272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			break;
98372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		default:
98472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			D1(" ");
98572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			count--;
98672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			break;
98772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
98872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
98972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
99072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Recovery mechanism for WAIT_SYNC state. */
99172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (is_eop) {
99272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (odev->rx_parse_state == WAIT_SYNC) {
99372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			odev->rx_parse_state = WAIT_IP;
99472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			odev->rx_buf_size = 0;
99572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			odev->rx_buf_missing = sizeof(struct iphdr);
99672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
99772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
99872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
99972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
10005591c75dc345d93d353d2ab2962824648a73efe4Joe Perchesstatic void fix_crc_bug(struct urb *urb, __le16 max_packet_size)
10015591c75dc345d93d353d2ab2962824648a73efe4Joe Perches{
10025591c75dc345d93d353d2ab2962824648a73efe4Joe Perches	static const u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
10035591c75dc345d93d353d2ab2962824648a73efe4Joe Perches	u32 rest = urb->actual_length % le16_to_cpu(max_packet_size);
10045591c75dc345d93d353d2ab2962824648a73efe4Joe Perches
10055591c75dc345d93d353d2ab2962824648a73efe4Joe Perches	if (((rest == 5) || (rest == 6)) &&
10065591c75dc345d93d353d2ab2962824648a73efe4Joe Perches	    !memcmp(((u8 *)urb->transfer_buffer) + urb->actual_length - 4,
10075591c75dc345d93d353d2ab2962824648a73efe4Joe Perches		    crc_check, 4)) {
10085591c75dc345d93d353d2ab2962824648a73efe4Joe Perches		urb->actual_length -= 4;
10095591c75dc345d93d353d2ab2962824648a73efe4Joe Perches	}
10105591c75dc345d93d353d2ab2962824648a73efe4Joe Perches}
10115591c75dc345d93d353d2ab2962824648a73efe4Joe Perches
101272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Moving data from usb to kernel (in interrupt state) */
101372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void read_bulk_callback(struct urb *urb)
101472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
101572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *odev = urb->context;
101672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct net_device *net;
101772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int result;
101872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int status = urb->status;
101972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
102072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* is al ok?  (Filip: Who's Al ?) */
102172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (status) {
102268a351c501ad22077a969df157cd13367cb43a40Jan Dumon		handle_usb_error(status, __func__, odev->parent);
102372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
102472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
102572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
102672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Sanity check */
102772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
102872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("BULK IN callback but driver is not active!");
102972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
103072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
103172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_mark_last_busy(urb->dev);
103272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
103372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net = odev->net;
103472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
103572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!netif_device_present(net)) {
103672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* Somebody killed our network interface... */
103772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
103872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
103972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
10405591c75dc345d93d353d2ab2962824648a73efe4Joe Perches	if (odev->parent->port_spec & HSO_INFO_CRC_BUG)
10415591c75dc345d93d353d2ab2962824648a73efe4Joe Perches		fix_crc_bug(urb, odev->in_endp->wMaxPacketSize);
104272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
104372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* do we even have a packet? */
104472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (urb->actual_length) {
104572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* Handle the IP stream, add header and push it onto network
104672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		 * stack if the packet is complete. */
104772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		spin_lock(&odev->net_lock);
104872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		packetizeRx(odev, urb->transfer_buffer, urb->actual_length,
104972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    (urb->transfer_buffer_length >
105072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			     urb->actual_length) ? 1 : 0);
105172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		spin_unlock(&odev->net_lock);
105272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
105372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
105472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* We are done with this URB, resubmit it. Prep the USB to wait for
105572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 * another frame. Reuse same as received. */
105672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_fill_bulk_urb(urb,
105772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  odev->parent->usb,
105872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  usb_rcvbulkpipe(odev->parent->usb,
105972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  odev->in_endp->
106072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  bEndpointAddress & 0x7F),
106172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE,
106272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  read_bulk_callback, odev);
106372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
106472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Give this to the USB subsystem so it can tell us when more data
106572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 * arrives. */
106672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = usb_submit_urb(urb, GFP_ATOMIC);
106772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result)
106872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_warn(&odev->parent->interface->dev,
10698a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon			 "%s failed submit mux_bulk_rx_urb %d\n", __func__,
107072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 result);
107172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
107272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
107372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Serial driver functions */
107472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
1075ac9720c37e8795317e8be3adad63cb0d5522a640Alan Coxstatic void hso_init_termios(struct ktermios *termios)
107672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
107772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/*
107872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 * The default requirements for this device are:
107972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 */
108072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	termios->c_iflag &=
108172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		~(IGNBRK	/* disable ignore break */
108272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| BRKINT	/* disable break causes interrupt */
108372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| PARMRK	/* disable mark parity errors */
108472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| ISTRIP	/* disable clear high bit of input characters */
108572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| INLCR		/* disable translate NL to CR */
108672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| IGNCR		/* disable ignore CR */
108772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| ICRNL		/* disable translate CR to NL */
108872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| IXON);	/* disable enable XON/XOFF flow control */
108972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
109072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* disable postprocess output characters */
109172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	termios->c_oflag &= ~OPOST;
109272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
109372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	termios->c_lflag &=
109472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		~(ECHO		/* disable echo input characters */
109572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| ECHONL	/* disable echo new line */
109672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| ICANON	/* disable erase, kill, werase, and rprnt
109772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				   special characters */
109872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| ISIG		/* disable interrupt, quit, and suspend special
109972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				   characters */
110072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| IEXTEN);	/* disable non-POSIX special characters */
110172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
110272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	termios->c_cflag &=
110372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		~(CSIZE		/* no size */
110472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| PARENB	/* disable parity bit */
110572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| CBAUD		/* clear current baud rate */
110672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		| CBAUDEX);	/* clear current buad rate */
110772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
110872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	termios->c_cflag |= CS8;	/* character size 8 bits */
110972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
111072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* baud rate 115200 */
1111ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	tty_termios_encode_baud_rate(termios, 115200, 115200);
1112ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox}
1113ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox
1114ac9720c37e8795317e8be3adad63cb0d5522a640Alan Coxstatic void _hso_serial_set_termios(struct tty_struct *tty,
1115ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox				    struct ktermios *old)
1116ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox{
1117ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	struct hso_serial *serial = get_serial_by_tty(tty);
1118ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	struct ktermios *termios;
1119ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox
1120ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	if (!serial) {
1121ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox		printk(KERN_ERR "%s: no tty structures", __func__);
1122ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox		return;
1123ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	}
1124ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox
1125ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	D4("port %d", serial->minor);
112672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
112772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/*
1128ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	 *	Fix up unsupported bits
112972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 */
1130ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	termios = tty->termios;
1131ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	termios->c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */
1132ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox
1133ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	termios->c_cflag &=
1134ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox		~(CSIZE		/* no size */
1135ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox		| PARENB	/* disable parity bit */
1136ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox		| CBAUD		/* clear current baud rate */
1137ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox		| CBAUDEX);	/* clear current buad rate */
1138ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox
1139ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	termios->c_cflag |= CS8;	/* character size 8 bits */
1140ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox
1141ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	/* baud rate 115200 */
1142ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	tty_encode_baud_rate(tty, 115200, 115200);
114372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
114472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
11458ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic void hso_resubmit_rx_bulk_urb(struct hso_serial *serial, struct urb *urb)
11468ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
11478ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int result;
11488ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	/* We are done with this URB, resubmit it. Prep the USB to wait for
11498ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	 * another frame */
11508ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	usb_fill_bulk_urb(urb, serial->parent->usb,
11518ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			  usb_rcvbulkpipe(serial->parent->usb,
11528ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow					  serial->in_endp->
11538ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow					  bEndpointAddress & 0x7F),
11548ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			  urb->transfer_buffer, serial->rx_data_length,
11558ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			  hso_std_serial_read_bulk_callback, serial);
11568ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	/* Give this to the USB subsystem so it can tell us when more data
11578ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	 * arrives. */
11588ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	result = usb_submit_urb(urb, GFP_ATOMIC);
11598ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (result) {
11608ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d\n",
11618ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			__func__, result);
11628ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	}
11638ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
11648ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
11658ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
11668ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
11678ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
11688ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial)
11698ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
11708ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int count;
11718ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	struct urb *curr_urb;
11728ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
11738ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) {
11748ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		curr_urb = serial->rx_urb[serial->curr_rx_urb_idx];
11758ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		count = put_rxbuf_data(curr_urb, serial);
11768ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		if (count == -1)
11778ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			return;
11788ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		if (count == 0) {
11798ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			serial->curr_rx_urb_idx++;
11808ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			if (serial->curr_rx_urb_idx >= serial->num_rx_urbs)
11818ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				serial->curr_rx_urb_idx = 0;
11828ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			hso_resubmit_rx_bulk_urb(serial, curr_urb);
11838ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		}
11848ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	}
11858ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
11868ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
11878ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial)
11888ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
11898ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int count = 0;
11908ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	struct urb *urb;
11918ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
11928ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	urb = serial->rx_urb[0];
11938ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (serial->open_count > 0) {
11948ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		count = put_rxbuf_data(urb, serial);
11958ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		if (count == -1)
11968ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			return;
11978ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	}
11988ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	/* Re issue a read as long as we receive data. */
11998ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12008ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (count == 0 && ((urb->actual_length != 0) ||
12018ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			   (serial->rx_state == RX_PENDING))) {
12028ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->rx_state = RX_SENT;
12038ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		hso_mux_serial_read(serial);
12048ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	} else
12058ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->rx_state = RX_IDLE;
12068ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
12078ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12088ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12098ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow/* read callback for Diag and CS port */
12108ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic void hso_std_serial_read_bulk_callback(struct urb *urb)
12118ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
12128ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	struct hso_serial *serial = urb->context;
12138ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int status = urb->status;
12148ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12158ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	/* sanity check */
12168ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (!serial) {
12178ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		D1("serial == NULL");
12188ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		return;
12198ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	} else if (status) {
122068a351c501ad22077a969df157cd13367cb43a40Jan Dumon		handle_usb_error(status, __func__, serial->parent);
12218ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		return;
12228ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	}
12238ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12248ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	D4("\n--- Got serial_read_bulk callback %02x ---", status);
12258ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	D1("Actual length = %d\n", urb->actual_length);
12268ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	DUMP1(urb->transfer_buffer, urb->actual_length);
12278ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12288ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	/* Anyone listening? */
12298ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (serial->open_count == 0)
12308ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		return;
12318ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12328ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (status == 0) {
12335591c75dc345d93d353d2ab2962824648a73efe4Joe Perches		if (serial->parent->port_spec & HSO_INFO_CRC_BUG)
12345591c75dc345d93d353d2ab2962824648a73efe4Joe Perches			fix_crc_bug(urb, serial->in_endp->wMaxPacketSize);
12358ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		/* Valid data, handle RX data */
12368ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		spin_lock(&serial->serial_lock);
12378ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 1;
12388ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		put_rxbuf_data_and_resubmit_bulk_urb(serial);
12398ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		spin_unlock(&serial->serial_lock);
12408ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	} else if (status == -ENOENT || status == -ECONNRESET) {
12418ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		/* Unlinked - check for throttled port. */
12428ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		D2("Port %d, successfully unlinked urb", serial->minor);
12438ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		spin_lock(&serial->serial_lock);
12448ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0;
12458ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		hso_resubmit_rx_bulk_urb(serial, urb);
12468ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		spin_unlock(&serial->serial_lock);
12478ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	} else {
12488ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		D2("Port %d, status = %d for read urb", serial->minor, status);
12498ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		return;
12508ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	}
12518ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
12528ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12538ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow/*
12548ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow * This needs to be a tasklet otherwise we will
12558ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow * end up recursively calling this function.
12568ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow */
12570227abc9d011892fd13f360b56a7b276ebea8b07Hannes Ederstatic void hso_unthrottle_tasklet(struct hso_serial *serial)
12588ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
12598ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	unsigned long flags;
12608ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12618ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	spin_lock_irqsave(&serial->serial_lock, flags);
12628ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if ((serial->parent->port_spec & HSO_INTF_MUX))
12638ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		put_rxbuf_data_and_resubmit_ctrl_urb(serial);
12648ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	else
12658ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		put_rxbuf_data_and_resubmit_bulk_urb(serial);
12668ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	spin_unlock_irqrestore(&serial->serial_lock, flags);
12678ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
12688ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12698ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic	void hso_unthrottle(struct tty_struct *tty)
12708ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
12718ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	struct hso_serial *serial = get_serial_by_tty(tty);
12728ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12738ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	tasklet_hi_schedule(&serial->unthrottle_tasklet);
12748ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
12758ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
12760227abc9d011892fd13f360b56a7b276ebea8b07Hannes Ederstatic void hso_unthrottle_workfunc(struct work_struct *work)
12778ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow{
12788ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	struct hso_serial *serial =
12798ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	    container_of(work, struct hso_serial,
12808ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			 retry_unthrottle_workqueue);
12818ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	hso_unthrottle_tasklet(serial);
12828ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow}
12838ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
128472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* open the requested serial port */
128572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_serial_open(struct tty_struct *tty, struct file *filp)
128672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
128772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = get_serial_by_index(tty->index);
1288ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	int result;
128972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
129072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
129172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) {
1292e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		WARN_ON(1);
129372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		tty->driver_data = NULL;
129472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("Failed to open port");
129572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
129672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
129772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
1298ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	mutex_lock(&serial->parent->mutex);
1299ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	result = usb_autopm_get_interface(serial->parent->interface);
1300ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	if (result < 0)
130172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto err_out;
130272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
130372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D1("Opening %d", serial->minor);
130472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kref_get(&serial->parent->ref);
130572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
130672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* setup */
1307e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	spin_lock_irq(&serial->serial_lock);
130872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty->driver_data = serial;
1309fe41cbb164a0dc55f3914a0e4cabe8240410157cAlan Cox	tty_kref_put(serial->tty);
1310e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	serial->tty = tty_kref_get(tty);
1311e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	spin_unlock_irq(&serial->serial_lock);
131272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
13132f9889a20cd2854bc6305198255c617b0b4eb719David S. Miller	/* check for port already opened, if not set the termios */
13142f9889a20cd2854bc6305198255c617b0b4eb719David S. Miller	serial->open_count++;
131572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->open_count == 1) {
13168ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->rx_state = RX_IDLE;
131772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* Force default termio settings */
131872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		_hso_serial_set_termios(tty, NULL);
13198ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		tasklet_init(&serial->unthrottle_tasklet,
13208ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			     (void (*)(unsigned long))hso_unthrottle_tasklet,
13218ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			     (unsigned long)serial);
13228ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		INIT_WORK(&serial->retry_unthrottle_workqueue,
13238ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			  hso_unthrottle_workfunc);
1324ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller		result = hso_start_serial_device(serial->parent, GFP_KERNEL);
1325ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller		if (result) {
132672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_stop_serial_device(serial->parent);
132772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			serial->open_count--;
1328ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller			kref_put(&serial->parent->ref, hso_serial_ref_free);
132972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
133072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} else {
133172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("Port was already open");
133272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
133372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
133472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_autopm_put_interface(serial->parent->interface);
133572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
133672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done */
1337ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	if (result)
133820b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Cox		hso_serial_tiocmset(tty, TIOCM_RTS | TIOCM_DTR, 0);
133972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanerr_out:
1340ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	mutex_unlock(&serial->parent->mutex);
1341ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	return result;
134272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
134372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
134472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* close the requested serial port */
134572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_serial_close(struct tty_struct *tty, struct file *filp)
134672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
134772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = tty->driver_data;
134872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 usb_gone;
134972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
135072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D1("Closing serial port");
135172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
1352e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	/* Open failed, no close cleanup required */
1353e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	if (serial == NULL)
1354e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		return;
1355e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox
1356ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	mutex_lock(&serial->parent->mutex);
135772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_gone = serial->parent->usb_gone;
135872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
135972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!usb_gone)
136072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_autopm_get_interface(serial->parent->interface);
136172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
136272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* reset the rts and dtr */
136372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* do the actual close */
136472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->open_count--;
1365dcfcb256cc23c4436691b0fe677275306699d6a1Antti Kaijanmäki
136672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->open_count <= 0) {
136772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->open_count = 0;
1368e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		spin_lock_irq(&serial->serial_lock);
1369e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		if (serial->tty == tty) {
137072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			serial->tty->driver_data = NULL;
137172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			serial->tty = NULL;
1372e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox			tty_kref_put(tty);
137372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
1374e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		spin_unlock_irq(&serial->serial_lock);
137572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!usb_gone)
137672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_stop_serial_device(serial->parent);
13778ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		tasklet_kill(&serial->unthrottle_tasklet);
13788ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		cancel_work_sync(&serial->retry_unthrottle_workqueue);
137972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
13808ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
138172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!usb_gone)
138272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_autopm_put_interface(serial->parent->interface);
1383ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller
1384ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	mutex_unlock(&serial->parent->mutex);
1385dcfcb256cc23c4436691b0fe677275306699d6a1Antti Kaijanmäki
1386dcfcb256cc23c4436691b0fe677275306699d6a1Antti Kaijanmäki	kref_put(&serial->parent->ref, hso_serial_ref_free);
138772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
138872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
138972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* close the requested serial port */
139072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_serial_write(struct tty_struct *tty, const unsigned char *buf,
139172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    int count)
139272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
139372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = get_serial_by_tty(tty);
139472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int space, tx_bytes;
139572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
139672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
139772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
139872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial == NULL) {
139972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		printk(KERN_ERR "%s: serial is NULL\n", __func__);
140072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
140172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
140272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
140372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial->serial_lock, flags);
140472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
140572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	space = serial->tx_data_length - serial->tx_buffer_count;
140672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tx_bytes = (count < space) ? count : space;
140772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
140872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!tx_bytes)
140972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto out;
141072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
141172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes);
141272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_buffer_count += tx_bytes;
141372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
141472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanout:
141572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial->serial_lock, flags);
141672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
141772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_kick_transmit(serial);
141872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done */
141972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return tx_bytes;
142072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
142172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
142272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* how much room is there for writing */
142372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_serial_write_room(struct tty_struct *tty)
142472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
142572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = get_serial_by_tty(tty);
142672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int room;
142772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
142872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
142972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial->serial_lock, flags);
143072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	room = serial->tx_data_length - serial->tx_buffer_count;
143172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial->serial_lock, flags);
143272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
143372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* return free room */
143472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return room;
143572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
143672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
143772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* setup the term */
143872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
143972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
144072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = get_serial_by_tty(tty);
144172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
144272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
144372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (old)
144472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D5("Termios called with: cflags new[%d] - old[%d]",
144572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		   tty->termios->c_cflag, old->c_cflag);
144672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
144772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* the actual setup */
144872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial->serial_lock, flags);
144972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->open_count)
145072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		_hso_serial_set_termios(tty, old);
145172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	else
145272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		tty->termios = old;
145372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial->serial_lock, flags);
145472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
145572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done */
145672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
145772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
145872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* how many characters in the buffer */
145972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_serial_chars_in_buffer(struct tty_struct *tty)
146072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
146172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = get_serial_by_tty(tty);
146272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int chars;
146372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
146472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
146572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
146672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial == NULL)
146772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return 0;
146872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
146972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial->serial_lock, flags);
147072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	chars = serial->tx_buffer_count;
147172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial->serial_lock, flags);
147272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
147372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return chars;
147472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
14750227abc9d011892fd13f360b56a7b276ebea8b07Hannes Ederstatic int tiocmget_submit_urb(struct hso_serial *serial,
14760227abc9d011892fd13f360b56a7b276ebea8b07Hannes Eder			       struct hso_tiocmget *tiocmget,
14770227abc9d011892fd13f360b56a7b276ebea8b07Hannes Eder			       struct usb_device *usb)
1478542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow{
1479542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	int result;
1480542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1481542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (serial->parent->usb_gone)
1482542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		return -ENODEV;
1483542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	usb_fill_int_urb(tiocmget->urb, usb,
1484542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			 usb_rcvintpipe(usb,
1485542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow					tiocmget->endp->
1486542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow					bEndpointAddress & 0x7F),
1487542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			 &tiocmget->serial_state_notification,
1488542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			 sizeof(struct hso_serial_state_notification),
1489542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			 tiocmget_intr_callback, serial,
1490542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			 tiocmget->endp->bInterval);
1491542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC);
1492542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (result) {
1493542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__,
1494542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			 result);
1495542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
1496542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	return result;
1497542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1498542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow}
1499542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1500542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrowstatic void tiocmget_intr_callback(struct urb *urb)
1501542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow{
1502542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_serial *serial = urb->context;
1503542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_tiocmget *tiocmget;
1504542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	int status = urb->status;
1505542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u16 UART_state_bitmap, prev_UART_state_bitmap;
1506542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct uart_icount *icount;
1507542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_serial_state_notification *serial_state_notification;
1508542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct usb_device *usb;
1509542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1510542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	/* Sanity checks */
1511542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (!serial)
1512542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		return;
1513542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (status) {
151468a351c501ad22077a969df157cd13367cb43a40Jan Dumon		handle_usb_error(status, __func__, serial->parent);
1515542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		return;
1516542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
1517542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	tiocmget = serial->tiocmget;
1518542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (!tiocmget)
1519542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		return;
1520542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	usb = serial->parent->usb;
1521542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	serial_state_notification = &tiocmget->serial_state_notification;
1522542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE ||
1523542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	    serial_state_notification->bNotification != B_NOTIFICATION ||
1524542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	    le16_to_cpu(serial_state_notification->wValue) != W_VALUE ||
1525542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	    le16_to_cpu(serial_state_notification->wIndex) != W_INDEX ||
1526542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	    le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) {
1527542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		dev_warn(&usb->dev,
1528542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			 "hso received invalid serial state notification\n");
1529542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		DUMP(serial_state_notification,
15309ce673d5e919966efc1ef5adf20248e6ecc62724Antti Kaijanmäki		     sizeof(struct hso_serial_state_notification));
1531542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	} else {
1532542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1533542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		UART_state_bitmap = le16_to_cpu(serial_state_notification->
1534542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow						UART_state_bitmap);
1535542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap;
1536542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		icount = &tiocmget->icount;
1537542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		spin_lock(&serial->serial_lock);
1538542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if ((UART_state_bitmap & B_OVERRUN) !=
1539542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		   (prev_UART_state_bitmap & B_OVERRUN))
1540542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			icount->parity++;
1541542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if ((UART_state_bitmap & B_PARITY) !=
1542542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		   (prev_UART_state_bitmap & B_PARITY))
1543542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			icount->parity++;
1544542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if ((UART_state_bitmap & B_FRAMING) !=
1545542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		   (prev_UART_state_bitmap & B_FRAMING))
1546542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			icount->frame++;
1547542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if ((UART_state_bitmap & B_RING_SIGNAL) &&
1548542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		   !(prev_UART_state_bitmap & B_RING_SIGNAL))
1549542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			icount->rng++;
1550542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if ((UART_state_bitmap & B_BREAK) !=
1551542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		   (prev_UART_state_bitmap & B_BREAK))
1552542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			icount->brk++;
1553542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if ((UART_state_bitmap & B_TX_CARRIER) !=
1554542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		   (prev_UART_state_bitmap & B_TX_CARRIER))
1555542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			icount->dsr++;
1556542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if ((UART_state_bitmap & B_RX_CARRIER) !=
1557542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		   (prev_UART_state_bitmap & B_RX_CARRIER))
1558542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			icount->dcd++;
1559542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		tiocmget->prev_UART_state_bitmap = UART_state_bitmap;
1560542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		spin_unlock(&serial->serial_lock);
1561542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		tiocmget->intr_completed = 1;
1562542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		wake_up_interruptible(&tiocmget->waitq);
1563542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
1564542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	memset(serial_state_notification, 0,
1565542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	       sizeof(struct hso_serial_state_notification));
1566542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	tiocmget_submit_urb(serial,
1567542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			    tiocmget,
1568542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			    serial->parent->usb);
1569542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow}
1570542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1571542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow/*
1572542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow * next few functions largely stolen from drivers/serial/serial_core.c
1573542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow */
1574542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
1575542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow * - mask passed in arg for lines of interest
1576542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
1577542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow * Caller should use TIOCGICOUNT to see which one it was
1578542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow */
1579542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrowstatic int
1580542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrowhso_wait_modem_status(struct hso_serial *serial, unsigned long arg)
1581542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow{
1582542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	DECLARE_WAITQUEUE(wait, current);
1583542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct uart_icount cprev, cnow;
1584542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_tiocmget  *tiocmget;
1585542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	int ret;
1586542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1587542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	tiocmget = serial->tiocmget;
1588542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (!tiocmget)
1589542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		return -ENOENT;
1590542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	/*
1591542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	 * note the counters on entry
1592542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	 */
1593542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	spin_lock_irq(&serial->serial_lock);
1594542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount));
1595542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	spin_unlock_irq(&serial->serial_lock);
1596542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	add_wait_queue(&tiocmget->waitq, &wait);
1597542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	for (;;) {
1598542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		spin_lock_irq(&serial->serial_lock);
1599542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
1600542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		spin_unlock_irq(&serial->serial_lock);
1601542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		set_current_state(TASK_INTERRUPTIBLE);
1602542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
1603542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
1604542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd))) {
1605542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			ret = 0;
1606542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			break;
1607542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		}
1608542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		schedule();
1609542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		/* see if a signal did it */
1610542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if (signal_pending(current)) {
1611542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			ret = -ERESTARTSYS;
1612542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			break;
1613542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		}
1614542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		cprev = cnow;
1615542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
1616542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	current->state = TASK_RUNNING;
1617542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	remove_wait_queue(&tiocmget->waitq, &wait);
1618542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1619542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	return ret;
1620542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow}
1621542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1622542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow/*
1623542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
1624542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow * Return: write counters to the user passed counter struct
1625542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow * NB: both 1->0 and 0->1 transitions are counted except for
1626542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow *     RI where only 0->1 is counted.
1627542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow */
16280bca1b913affbd7e2fdaffee62a499659a466eb5Alan Coxstatic int hso_get_count(struct tty_struct *tty,
16290bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox		  struct serial_icounter_struct *icount)
1630542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow{
1631542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct uart_icount cnow;
16320bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	struct hso_serial *serial = get_serial_by_tty(tty);
1633542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_tiocmget  *tiocmget = serial->tiocmget;
1634542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
163522ad7499bc9297e47c8779bf5523694f28338499Dan Carpenter	memset(icount, 0, sizeof(struct serial_icounter_struct));
16367011e660938fc44ed86319c18a5954e95a82ab3eDan Rosenberg
1637542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (!tiocmget)
1638542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		 return -ENOENT;
1639542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	spin_lock_irq(&serial->serial_lock);
1640542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
1641542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	spin_unlock_irq(&serial->serial_lock);
1642542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
16430bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->cts         = cnow.cts;
16440bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->dsr         = cnow.dsr;
16450bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->rng         = cnow.rng;
16460bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->dcd         = cnow.dcd;
16470bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->rx          = cnow.rx;
16480bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->tx          = cnow.tx;
16490bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->frame       = cnow.frame;
16500bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->overrun     = cnow.overrun;
16510bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->parity      = cnow.parity;
16520bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->brk         = cnow.brk;
16530bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->buf_overrun = cnow.buf_overrun;
1654542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
16550bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	return 0;
1656542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow}
1657542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
165872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
165960b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int hso_serial_tiocmget(struct tty_struct *tty)
166072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
1661542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	int retval;
166272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = get_serial_by_tty(tty);
1663542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_tiocmget  *tiocmget;
1664542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	u16 UART_state_bitmap;
166572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
166672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
166772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial) {
166872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("no tty structures");
166972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -EINVAL;
167072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
1671542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	spin_lock_irq(&serial->serial_lock);
1672542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	retval = ((serial->rts_state) ? TIOCM_RTS : 0) |
167372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	    ((serial->dtr_state) ? TIOCM_DTR : 0);
1674542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	tiocmget = serial->tiocmget;
1675542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (tiocmget) {
1676542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1677542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		UART_state_bitmap = le16_to_cpu(
1678542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			tiocmget->prev_UART_state_bitmap);
1679542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if (UART_state_bitmap & B_RING_SIGNAL)
1680542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			retval |=  TIOCM_RNG;
1681542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if (UART_state_bitmap & B_RX_CARRIER)
1682542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			retval |=  TIOCM_CD;
1683542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if (UART_state_bitmap & B_TX_CARRIER)
1684542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			retval |=  TIOCM_DSR;
1685542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
1686542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	spin_unlock_irq(&serial->serial_lock);
1687542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	return retval;
168872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
168972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
169020b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int hso_serial_tiocmset(struct tty_struct *tty,
169172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			       unsigned int set, unsigned int clear)
169272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
169372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int val = 0;
169472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
169572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int if_num;
169672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = get_serial_by_tty(tty);
169772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
169872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
169972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial) {
170072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("no tty structures");
170172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -EINVAL;
170272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
17030e0367e980b55629917f3dd5f5f0ccbf3d0dab62Jan Dumon
17040e0367e980b55629917f3dd5f5f0ccbf3d0dab62Jan Dumon	if ((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM)
17050e0367e980b55629917f3dd5f5f0ccbf3d0dab62Jan Dumon		return -EINVAL;
17060e0367e980b55629917f3dd5f5f0ccbf3d0dab62Jan Dumon
170772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if_num = serial->parent->interface->altsetting->desc.bInterfaceNumber;
170872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
170972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial->serial_lock, flags);
171072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (set & TIOCM_RTS)
171172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->rts_state = 1;
171272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (set & TIOCM_DTR)
171372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->dtr_state = 1;
171472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
171572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (clear & TIOCM_RTS)
171672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->rts_state = 0;
171772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (clear & TIOCM_DTR)
171872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->dtr_state = 0;
171972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
172072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->dtr_state)
172172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		val |= 0x01;
172272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->rts_state)
172372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		val |= 0x02;
172472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
172572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial->serial_lock, flags);
172672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
172772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return usb_control_msg(serial->parent->usb,
172872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			       usb_rcvctrlpipe(serial->parent->usb, 0), 0x22,
172972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			       0x21, val, if_num, NULL, 0,
173072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			       USB_CTRL_SET_TIMEOUT);
173172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
173272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
17336caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int hso_serial_ioctl(struct tty_struct *tty,
1734542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			    unsigned int cmd, unsigned long arg)
1735542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow{
1736542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_serial *serial =  get_serial_by_tty(tty);
1737542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	int ret = 0;
1738542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	D4("IOCTL cmd: %d, arg: %ld", cmd, arg);
1739542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1740542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (!serial)
1741542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		return -ENODEV;
1742542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	switch (cmd) {
1743542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	case TIOCMIWAIT:
1744542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		ret = hso_wait_modem_status(serial, arg);
1745542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		break;
1746542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	default:
1747542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		ret = -ENOIOCTLCMD;
1748542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		break;
1749542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
1750542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	return ret;
1751542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow}
1752542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
1753542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
175472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* starts a transmit */
175572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_kick_transmit(struct hso_serial *serial)
175672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
175772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 *temp;
175872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned long flags;
175972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int res;
176072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
176172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_irqsave(&serial->serial_lock, flags);
176272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial->tx_buffer_count)
176372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto out;
176472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
176572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->tx_urb_used)
176672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto out;
176772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
176872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Wakeup USB interface if necessary */
176972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_get_activity(serial->parent) == -EAGAIN)
177072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto out;
177172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
177272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Switch pointers around to avoid memcpy */
177372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	temp = serial->tx_buffer;
177472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_buffer = serial->tx_data;
177572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_data = temp;
177672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_data_count = serial->tx_buffer_count;
177772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_buffer_count = 0;
177872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
177972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* If temp is set, it means we switched buffers */
178072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (temp && serial->write_data) {
178172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		res = serial->write_data(serial);
178272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (res >= 0)
178372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			serial->tx_urb_used = 1;
178472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
178572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanout:
178672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock_irqrestore(&serial->serial_lock, flags);
178772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
178872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
178972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* make a request (for reading and writing data to muxed serial port) */
179072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int mux_device_request(struct hso_serial *serial, u8 type, u16 port,
179172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			      struct urb *ctrl_urb,
179272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			      struct usb_ctrlrequest *ctrl_req,
179372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			      u8 *ctrl_urb_data, u32 size)
179472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
179572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int result;
179672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int pipe;
179772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
179872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Sanity check */
179972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial || !ctrl_urb || !ctrl_req) {
180072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		printk(KERN_ERR "%s: Wrong arguments\n", __func__);
180172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -EINVAL;
180272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
180372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
180472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* initialize */
180572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	ctrl_req->wValue = 0;
1806b74f62c1e736ea01c660355526dd54132d241ca9Denis Joseph Barrow	ctrl_req->wIndex = cpu_to_le16(hso_port_to_mux(port));
1807b74f62c1e736ea01c660355526dd54132d241ca9Denis Joseph Barrow	ctrl_req->wLength = cpu_to_le16(size);
180872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
180972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (type == USB_CDC_GET_ENCAPSULATED_RESPONSE) {
181072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* Reading command */
181172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		ctrl_req->bRequestType = USB_DIR_IN |
181272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					 USB_TYPE_OPTION_VENDOR |
181372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					 USB_RECIP_INTERFACE;
181472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		ctrl_req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
181572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		pipe = usb_rcvctrlpipe(serial->parent->usb, 0);
181672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} else {
181772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* Writing command */
181872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		ctrl_req->bRequestType = USB_DIR_OUT |
181972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					 USB_TYPE_OPTION_VENDOR |
182072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					 USB_RECIP_INTERFACE;
182172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		ctrl_req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
182272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		pipe = usb_sndctrlpipe(serial->parent->usb, 0);
182372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
182472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* syslog */
182572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D2("%s command (%02x) len: %d, port: %d",
182672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	   type == USB_CDC_GET_ENCAPSULATED_RESPONSE ? "Read" : "Write",
182772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	   ctrl_req->bRequestType, ctrl_req->wLength, port);
182872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
182972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Load ctrl urb */
183072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	ctrl_urb->transfer_flags = 0;
183172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_fill_control_urb(ctrl_urb,
183272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			     serial->parent->usb,
183372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			     pipe,
183472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			     (u8 *) ctrl_req,
183572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			     ctrl_urb_data, size, ctrl_callback, serial);
183672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Send it on merry way */
183772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = usb_submit_urb(ctrl_urb, GFP_ATOMIC);
183872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result) {
183972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&ctrl_urb->dev->dev,
18408a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon			"%s failed submit ctrl_urb %d type %d\n", __func__,
184172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			result, type);
184272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return result;
184372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
184472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
184572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done */
184672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return size;
184772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
184872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
184972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called by intr_callback when read occurs */
185072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_mux_serial_read(struct hso_serial *serial)
185172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
185272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial)
185372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -EINVAL;
185472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
185572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* clean data */
185672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE);
185772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* make the request */
185872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
185972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->num_rx_urbs != 1) {
186072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&serial->parent->interface->dev,
186172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			"ERROR: mux'd reads with multiple buffers "
186272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			"not possible\n");
186372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return 0;
186472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
186572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return mux_device_request(serial,
186672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  USB_CDC_GET_ENCAPSULATED_RESPONSE,
186772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  serial->parent->port_spec & HSO_PORT_MASK,
186872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  serial->rx_urb[0],
186972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  &serial->ctrl_req_rx,
187072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  serial->rx_data[0], serial->rx_data_length);
187172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
187272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
187372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* used for muxed serial port callback (muxed serial read) */
187472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void intr_callback(struct urb *urb)
187572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
187672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_shared_int *shared_int = urb->context;
187772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial;
187872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned char *port_req;
187972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int status = urb->status;
188072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
188172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
188272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_mark_last_busy(urb->dev);
188372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
188472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
188572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!shared_int)
188672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
188772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
188872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* status check */
188972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (status) {
189068a351c501ad22077a969df157cd13367cb43a40Jan Dumon		handle_usb_error(status, __func__, NULL);
189172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
189272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
189372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D4("\n--- Got intr callback 0x%02X ---", status);
189472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
189572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* what request? */
189672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	port_req = urb->transfer_buffer;
189772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D4(" port_req = 0x%.2X\n", *port_req);
189872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* loop over all muxed ports to find the one sending this */
189972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < 8; i++) {
190072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* max 8 channels on MUX */
190172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (*port_req & (1 << i)) {
190272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			serial = get_serial_by_shared_int_and_type(shared_int,
190372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman								   (1 << i));
190472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (serial != NULL) {
190572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				D1("Pending read interrupt on port %d\n", i);
19068ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				spin_lock(&serial->serial_lock);
1907f4763e96c08ea0790750603999e5b3158c3b50d4Jan Dumon				if (serial->rx_state == RX_IDLE &&
1908f4763e96c08ea0790750603999e5b3158c3b50d4Jan Dumon					serial->open_count > 0) {
190972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					/* Setup and send a ctrl req read on
191072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					 * port i */
1911f4763e96c08ea0790750603999e5b3158c3b50d4Jan Dumon					if (!serial->rx_urb_filled[0]) {
19128ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow						serial->rx_state = RX_SENT;
19138ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow						hso_mux_serial_read(serial);
19148ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow					} else
19158ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow						serial->rx_state = RX_PENDING;
191672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				} else {
1917f4763e96c08ea0790750603999e5b3158c3b50d4Jan Dumon					D1("Already a read pending on "
1918f4763e96c08ea0790750603999e5b3158c3b50d4Jan Dumon					   "port %d or port not open\n", i);
191972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				}
19208ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				spin_unlock(&serial->serial_lock);
192172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
192272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
192372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
192472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Resubmit interrupt urb */
192572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_ATOMIC);
192672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
192772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
192872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called for writing to muxed serial port */
192972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_mux_serial_write_data(struct hso_serial *serial)
193072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
193172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (NULL == serial)
193272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -EINVAL;
193372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
193472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return mux_device_request(serial,
193572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  USB_CDC_SEND_ENCAPSULATED_COMMAND,
193672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  serial->parent->port_spec & HSO_PORT_MASK,
193772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  serial->tx_urb,
193872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  &serial->ctrl_req_tx,
193972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  serial->tx_data, serial->tx_data_count);
194072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
194172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
194272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* write callback for Diag and CS port */
194372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_std_serial_write_bulk_callback(struct urb *urb)
194472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
194572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = urb->context;
194672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int status = urb->status;
1947e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	struct tty_struct *tty;
194872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
194972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
195072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial) {
195172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("serial == NULL");
195272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
195372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
195472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
195572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock(&serial->serial_lock);
195672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_urb_used = 0;
1957e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	tty = tty_kref_get(serial->tty);
195872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock(&serial->serial_lock);
195972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (status) {
196068a351c501ad22077a969df157cd13367cb43a40Jan Dumon		handle_usb_error(status, __func__, serial->parent);
1961e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		tty_kref_put(tty);
196272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
196372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
196472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_put_activity(serial->parent);
1965e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	if (tty) {
1966e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		tty_wakeup(tty);
1967e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		tty_kref_put(tty);
1968e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	}
196972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_kick_transmit(serial);
197072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
197172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D1(" ");
197272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
197372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
197472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called for writing diag or CS serial port */
197572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_std_serial_write_data(struct hso_serial *serial)
197672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
197772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int count = serial->tx_data_count;
197872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int result;
197972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
198072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_fill_bulk_urb(serial->tx_urb,
198172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  serial->parent->usb,
198272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  usb_sndbulkpipe(serial->parent->usb,
198372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  serial->out_endp->
198472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  bEndpointAddress & 0x7F),
198572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  serial->tx_data, serial->tx_data_count,
198672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			  hso_std_serial_write_bulk_callback, serial);
198772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
198872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = usb_submit_urb(serial->tx_urb, GFP_ATOMIC);
198972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result) {
199072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_warn(&serial->parent->usb->dev,
199172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 "Failed to submit urb - res %d\n", result);
199272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return result;
199372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
199472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
199572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return count;
199672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
199772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
199872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* callback after read or write on muxed serial port */
199972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void ctrl_callback(struct urb *urb)
200072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
200172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = urb->context;
200272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_ctrlrequest *req;
200372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int status = urb->status;
2004e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	struct tty_struct *tty;
200572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
200672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* sanity check */
200772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial)
200872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
200972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
201072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock(&serial->serial_lock);
201172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_urb_used = 0;
2012e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	tty = tty_kref_get(serial->tty);
201372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_unlock(&serial->serial_lock);
201472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (status) {
201568a351c501ad22077a969df157cd13367cb43a40Jan Dumon		handle_usb_error(status, __func__, serial->parent);
2016e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		tty_kref_put(tty);
201772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
201872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
201972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
202072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* what request? */
202172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	req = (struct usb_ctrlrequest *)(urb->setup_packet);
202272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D4("\n--- Got muxed ctrl callback 0x%02X ---", status);
202372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D4("Actual length of urb = %d\n", urb->actual_length);
202472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	DUMP1(urb->transfer_buffer, urb->actual_length);
202572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
202672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (req->bRequestType ==
202772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	    (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) {
202872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* response to a read command */
20298ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->rx_urb_filled[0] = 1;
20308ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		spin_lock(&serial->serial_lock);
20318ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		put_rxbuf_data_and_resubmit_ctrl_urb(serial);
20328ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		spin_unlock(&serial->serial_lock);
203372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} else {
203472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		hso_put_activity(serial->parent);
2035e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox		if (tty)
2036e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox			tty_wakeup(tty);
203772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* response to a write command */
203872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		hso_kick_transmit(serial);
203972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
2040e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	tty_kref_put(tty);
204172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
204272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
204372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* handle RX data for serial port */
20448ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrowstatic int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
204572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
2046e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	struct tty_struct *tty;
20478ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int write_length_remaining = 0;
20488ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	int curr_write_len;
2049e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox
205072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Sanity check */
205172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (urb == NULL || serial == NULL) {
205272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("serial = NULL");
20538ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		return -2;
205472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
205572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
2056d45eb81c3e345fabaf27ef3ab437b85c0bf9fafaDenis Joseph Barrow	/* All callers to put_rxbuf_data hold serial_lock */
2057e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	tty = tty_kref_get(serial->tty);
2058e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox
205972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Push data to tty */
20608ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (tty) {
20618ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		write_length_remaining = urb->actual_length -
20628ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			serial->curr_rx_urb_offset;
206372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		D1("data to push to tty");
20648ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		while (write_length_remaining) {
20655839b414f9f9d2d6a471988763b61dbf85eb2dbaDenis Joseph Barrow			if (test_bit(TTY_THROTTLED, &tty->flags)) {
20665839b414f9f9d2d6a471988763b61dbf85eb2dbaDenis Joseph Barrow				tty_kref_put(tty);
20678ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				return -1;
20685839b414f9f9d2d6a471988763b61dbf85eb2dbaDenis Joseph Barrow			}
20698ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			curr_write_len =  tty_insert_flip_string
20708ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				(tty, urb->transfer_buffer +
20718ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				 serial->curr_rx_urb_offset,
20728ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				 write_length_remaining);
20738ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			serial->curr_rx_urb_offset += curr_write_len;
20748ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			write_length_remaining -= curr_write_len;
20758ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow			tty_flip_buffer_push(tty);
207672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
207772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
20788ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	if (write_length_remaining == 0) {
20798ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->curr_rx_urb_offset = 0;
20808ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0;
208172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
2082e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	tty_kref_put(tty);
20838ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	return write_length_remaining;
208472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
208572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
20868ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow
208772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Base driver functions */
208872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
208972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_log_port(struct hso_device *hso_dev)
209072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
209172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	char *port_type;
209272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	char port_dev[20];
209372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
209472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	switch (hso_dev->port_spec & HSO_PORT_MASK) {
209572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_CONTROL:
209672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Control";
209772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
209872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_APP:
209972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Application";
210072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
210172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_GPS:
210272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "GPS";
210372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
210472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_GPS_CONTROL:
210572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "GPS control";
210672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
210772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_APP2:
210872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Application2";
210972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
211072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_PCSC:
211172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "PCSC";
211272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
211372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_DIAG:
211472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Diagnostic";
211572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
211672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_DIAG2:
211772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Diagnostic2";
211872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
211972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_MODEM:
212072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Modem";
212172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
212272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_PORT_NETWORK:
212372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Network";
212472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
212572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	default:
212672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type = "Unknown";
212772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
212872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
212972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
213072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		sprintf(port_dev, "%s", dev2net(hso_dev)->net->name);
213172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} else
213272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		sprintf(port_dev, "/dev/%s%d", tty_filename,
213372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			dev2ser(hso_dev)->minor);
213472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
213572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n",
213672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_type, port_dev);
213772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
213872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
213972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_start_net_device(struct hso_device *hso_dev)
214072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
214172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i, result = 0;
214272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *hso_net = dev2net(hso_dev);
214372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
214472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_net)
214572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
214672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
214772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* send URBs for all read buffers */
214872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
214972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
215072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* Prep a receive URB */
215172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i],
215272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  hso_dev->usb,
215372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  usb_rcvbulkpipe(hso_dev->usb,
215472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						  hso_net->in_endp->
215572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						  bEndpointAddress & 0x7F),
215672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  hso_net->mux_bulk_rx_buf_pool[i],
215772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  MUX_BULK_RX_BUF_SIZE, read_bulk_callback,
215872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				  hso_net);
215972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
216072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* Put it out there so the device can send us stuff */
216172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i],
216272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					GFP_NOIO);
216372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (result)
216472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			dev_warn(&hso_dev->usb->dev,
216572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				"%s failed mux_bulk_rx_urb[%d] %d\n", __func__,
216672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				i, result);
216772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
216872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
216972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return result;
217072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
217172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
217272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_stop_net_device(struct hso_device *hso_dev)
217372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
217472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
217572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *hso_net = dev2net(hso_dev);
217672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
217772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_net)
217872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
217972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
218072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
218172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (hso_net->mux_bulk_rx_urb_pool[i])
218272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			usb_kill_urb(hso_net->mux_bulk_rx_urb_pool[i]);
218372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
218472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
218572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_net->mux_bulk_tx_urb)
218672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_kill_urb(hso_net->mux_bulk_tx_urb);
218772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
218872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
218972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
219072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
219172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags)
219272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
219372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i, result = 0;
219472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = dev2ser(hso_dev);
219572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
219672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial)
219772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
219872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
219972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* If it is not the MUX port fill in and submit a bulk urb (already
220072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 * allocated in hso_serial_start) */
220172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!(serial->parent->port_spec & HSO_INTF_MUX)) {
220272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		for (i = 0; i < serial->num_rx_urbs; i++) {
220372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			usb_fill_bulk_urb(serial->rx_urb[i],
220472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  serial->parent->usb,
220572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  usb_rcvbulkpipe(serial->parent->usb,
220672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman							  serial->in_endp->
220772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman							  bEndpointAddress &
220872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman							  0x7F),
220972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  serial->rx_data[i],
221072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  serial->rx_data_length,
221172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  hso_std_serial_read_bulk_callback,
221272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					  serial);
221372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			result = usb_submit_urb(serial->rx_urb[i], flags);
221472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (result) {
221572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				dev_warn(&serial->parent->usb->dev,
221672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					 "Failed to submit urb - res %d\n",
221772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					 result);
221872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				break;
221972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
222072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
222172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	} else {
222272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		mutex_lock(&serial->shared_int->shared_int_lock);
222372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!serial->shared_int->use_count) {
222472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			result =
222572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    hso_mux_submit_intr_urb(serial->shared_int,
222672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						    hso_dev->usb, flags);
222772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
222872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->shared_int->use_count++;
222972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		mutex_unlock(&serial->shared_int->shared_int_lock);
223072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
2231542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (serial->tiocmget)
2232542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		tiocmget_submit_urb(serial,
2233542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow				    serial->tiocmget,
2234542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow				    serial->parent->usb);
223572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return result;
223672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
223772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
223872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_stop_serial_device(struct hso_device *hso_dev)
223972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
224072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
224172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = dev2ser(hso_dev);
2242542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_tiocmget  *tiocmget;
224372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
224472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial)
224572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
224672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
224772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < serial->num_rx_urbs; i++) {
22488ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		if (serial->rx_urb[i]) {
224972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				usb_kill_urb(serial->rx_urb[i]);
22508ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow				serial->rx_urb_filled[i] = 0;
22518ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow		}
225272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
22538ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	serial->curr_rx_urb_idx = 0;
22548ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	serial->curr_rx_urb_offset = 0;
225572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
225672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->tx_urb)
225772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_kill_urb(serial->tx_urb);
225872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
225972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->shared_int) {
226072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		mutex_lock(&serial->shared_int->shared_int_lock);
226172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (serial->shared_int->use_count &&
226272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		    (--serial->shared_int->use_count == 0)) {
226372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			struct urb *urb;
226472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
226572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			urb = serial->shared_int->shared_intr_urb;
226672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (urb)
226772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				usb_kill_urb(urb);
226872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
226972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		mutex_unlock(&serial->shared_int->shared_int_lock);
227072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
2271542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	tiocmget = serial->tiocmget;
2272542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (tiocmget) {
2273542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		wake_up_interruptible(&tiocmget->waitq);
2274542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		usb_kill_urb(tiocmget->urb);
2275542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
227672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
227772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
227872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
227972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
228072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_serial_common_free(struct hso_serial *serial)
228172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
228272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
228372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
228472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->parent->dev)
228572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		device_remove_file(serial->parent->dev, &dev_attr_hsotype);
228672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
228772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_unregister_device(tty_drv, serial->minor);
228872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
228972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < serial->num_rx_urbs; i++) {
229072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* unlink and free RX URB */
229172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_free_urb(serial->rx_urb[i]);
229272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* free the RX buffer */
229372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		kfree(serial->rx_data[i]);
229472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
229572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
229672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* unlink and free TX URB */
229772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_free_urb(serial->tx_urb);
229872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(serial->tx_data);
229972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
230072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
230172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
230272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				    int rx_size, int tx_size)
230372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
230472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct device *dev;
230572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int minor;
230672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
230772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
230872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	minor = get_free_serial_index();
230972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (minor < 0)
231072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
231172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
231272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* register our minor number */
231372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->parent->dev = tty_register_device(tty_drv, minor,
231472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					&serial->parent->interface->dev);
231572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	dev = serial->parent->dev;
23161aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman	dev_set_drvdata(dev, serial->parent);
231772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	i = device_create_file(dev, &dev_attr_hsotype);
231872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
231972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* fill in specific data for later use */
232072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->minor = minor;
232172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->magic = HSO_SERIAL_MAGIC;
232272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_init(&serial->serial_lock);
232372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->num_rx_urbs = num_urbs;
232472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
232572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* RX, allocate urb and initialize */
232672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
232772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* prepare our RX buffer */
232872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->rx_data_length = rx_size;
232972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < serial->num_rx_urbs; i++) {
233072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
233172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!serial->rx_urb[i]) {
233272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			dev_err(dev, "Could not allocate urb?\n");
233372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			goto exit;
233472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
233572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->rx_urb[i]->transfer_buffer = NULL;
233672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->rx_urb[i]->transfer_buffer_length = 0;
233772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial->rx_data[i] = kzalloc(serial->rx_data_length,
233872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					     GFP_KERNEL);
233972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!serial->rx_data[i]) {
234072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			dev_err(dev, "%s - Out of memory\n", __func__);
234172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			goto exit;
234272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
234372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
234472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
234572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* TX, allocate urb and initialize */
234672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
234772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial->tx_urb) {
234872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(dev, "Could not allocate urb?\n");
234972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
235072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
235172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_urb->transfer_buffer = NULL;
235272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_urb->transfer_buffer_length = 0;
235372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* prepare our TX buffer */
235472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_data_count = 0;
235572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_buffer_count = 0;
235672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_data_length = tx_size;
235772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_data = kzalloc(serial->tx_data_length, GFP_KERNEL);
235872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial->tx_data) {
23598a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon		dev_err(dev, "%s - Out of memory\n", __func__);
236072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
236172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
236272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL);
236372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial->tx_buffer) {
23648a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon		dev_err(dev, "%s - Out of memory\n", __func__);
236572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
236672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
236772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
236872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
236972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanexit:
237072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_serial_common_free(serial);
237172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return -1;
237272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
237372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
237472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Creates a general hso device */
237572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct hso_device *hso_create_device(struct usb_interface *intf,
237672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					    int port_spec)
237772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
237872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev;
237972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
238072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev = kzalloc(sizeof(*hso_dev), GFP_ATOMIC);
238172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_dev)
238272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return NULL;
238372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
238472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev->port_spec = port_spec;
238572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev->usb = interface_to_usbdev(intf);
238672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev->interface = intf;
238772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kref_init(&hso_dev->ref);
2388ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	mutex_init(&hso_dev->mutex);
2389ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller
239072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	INIT_WORK(&hso_dev->async_get_intf, async_get_intf);
239172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
239268a351c501ad22077a969df157cd13367cb43a40Jan Dumon	INIT_WORK(&hso_dev->reset_device, reset_device);
239372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
239472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return hso_dev;
239572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
239672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
239772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Removes a network device in the network device table */
239872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int remove_net_device(struct hso_device *hso_dev)
239972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
240072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
240172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
240272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
240372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (network_table[i] == hso_dev) {
240472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			network_table[i] = NULL;
240572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			break;
240672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
240772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
240872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (i == HSO_MAX_NET_DEVICES)
240972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -1;
241072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
241172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
241272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
241372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Frees our network device */
241472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_free_net_device(struct hso_device *hso_dev)
241572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
241672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
241772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *hso_net = dev2net(hso_dev);
241872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
241972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_net)
242072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
242172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
24223b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon	remove_net_device(hso_net->parent);
24233b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon
24245e2cd0825a43824827b233d95bc47c0c970e5befGreg Kroah-Hartman	if (hso_net->net)
24253b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon		unregister_netdev(hso_net->net);
24263b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon
242772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* start freeing */
242872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
242972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]);
243072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		kfree(hso_net->mux_bulk_rx_buf_pool[i]);
24313b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon		hso_net->mux_bulk_rx_buf_pool[i] = NULL;
243272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
243372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_free_urb(hso_net->mux_bulk_tx_urb);
243472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(hso_net->mux_bulk_tx_buf);
24353b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon	hso_net->mux_bulk_tx_buf = NULL;
2436ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller
24375e2cd0825a43824827b233d95bc47c0c970e5befGreg Kroah-Hartman	if (hso_net->net)
24385e2cd0825a43824827b233d95bc47c0c970e5befGreg Kroah-Hartman		free_netdev(hso_net->net);
24395e2cd0825a43824827b233d95bc47c0c970e5befGreg Kroah-Hartman
2440e44578ea7c4f87462e05e0d0a2dd43b6beecd5f6Paulius Zaleckas	kfree(hso_dev);
244172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
244272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
2443c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemmingerstatic const struct net_device_ops hso_netdev_ops = {
2444c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemminger	.ndo_open	= hso_net_open,
2445c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemminger	.ndo_stop	= hso_net_close,
2446c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemminger	.ndo_start_xmit = hso_net_start_xmit,
2447c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemminger	.ndo_tx_timeout = hso_net_tx_timeout,
2448c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemminger};
2449c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemminger
245072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* initialize the network interface */
245172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_net_init(struct net_device *net)
245272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
245372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *hso_net = netdev_priv(net);
245472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
245572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	D1("sizeof hso_net is %d", (int)sizeof(*hso_net));
245672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
245772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* fill in the other fields */
2458c266cb4ef2ef1f1e3f46d81022939feebe8fa54dStephen Hemminger	net->netdev_ops = &hso_netdev_ops;
245972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net->watchdog_timeo = HSO_NET_TX_TIMEOUT;
246072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
246172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net->type = ARPHRD_NONE;
246272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net->mtu = DEFAULT_MTU - 14;
246372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net->tx_queue_len = 10;
246472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	SET_ETHTOOL_OPS(net, &ops);
246572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
246672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* and initialize the semaphore */
246772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_init(&hso_net->net_lock);
246872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
246972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
247072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Adds a network device in the network device table */
247172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int add_net_device(struct hso_device *hso_dev)
247272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
247372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
247472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
247572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
247672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (network_table[i] == NULL) {
247772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			network_table[i] = hso_dev;
247872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			break;
247972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
248072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
248172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (i == HSO_MAX_NET_DEVICES)
248272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -1;
248372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
248472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
248572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
248619d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Bergstatic int hso_rfkill_set_block(void *data, bool blocked)
248772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
248872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev = data;
248919d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg	int enabled = !blocked;
249072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int rv;
249172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
2492ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	mutex_lock(&hso_dev->mutex);
249372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_dev->usb_gone)
249472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		rv = 0;
249572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	else
249672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		rv = usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0),
249772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				       enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0,
249872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				       USB_CTRL_SET_TIMEOUT);
2499ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller	mutex_unlock(&hso_dev->mutex);
250072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return rv;
250172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
250272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
250319d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Bergstatic const struct rfkill_ops hso_rfkill_ops = {
250419d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg	.set_block = hso_rfkill_set_block,
250519d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg};
250619d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg
250772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Creates and sets up everything for rfkill */
250872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_create_rfkill(struct hso_device *hso_dev,
250972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			     struct usb_interface *interface)
251072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
251172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *hso_net = dev2net(hso_dev);
2512939a9516416ad8ccec27aa05bd19236c550c0c03Jonathan McDowell	struct device *dev = &hso_net->net->dev;
251372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	char *rfkn;
251472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
251572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	rfkn = kzalloc(20, GFP_KERNEL);
251619d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg	if (!rfkn)
2517939a9516416ad8ccec27aa05bd19236c550c0c03Jonathan McDowell		dev_err(dev, "%s - Out of memory\n", __func__);
251819d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg
251972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	snprintf(rfkn, 20, "hso-%d",
252072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		 interface->altsetting->desc.bInterfaceNumber);
252119d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg
252219d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg	hso_net->rfkill = rfkill_alloc(rfkn,
252319d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg				       &interface_to_usbdev(interface)->dev,
252419d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg				       RFKILL_TYPE_WWAN,
252519d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg				       &hso_rfkill_ops, hso_dev);
252619d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg	if (!hso_net->rfkill) {
252719d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg		dev_err(dev, "%s - Out of memory\n", __func__);
252819d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg		kfree(rfkn);
252919d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg		return;
253019d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg	}
253172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (rfkill_register(hso_net->rfkill) < 0) {
253219d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg		rfkill_destroy(hso_net->rfkill);
253372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		kfree(rfkn);
2534939a9516416ad8ccec27aa05bd19236c550c0c03Jonathan McDowell		hso_net->rfkill = NULL;
2535939a9516416ad8ccec27aa05bd19236c550c0c03Jonathan McDowell		dev_err(dev, "%s - Failed to register rfkill\n", __func__);
253672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
253772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
253872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
253972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
2540384912ed194e43c03ad1cdaa09b0b1e488c34d46Marcel Holtmannstatic struct device_type hso_type = {
2541384912ed194e43c03ad1cdaa09b0b1e488c34d46Marcel Holtmann	.name	= "wwan",
2542384912ed194e43c03ad1cdaa09b0b1e488c34d46Marcel Holtmann};
2543384912ed194e43c03ad1cdaa09b0b1e488c34d46Marcel Holtmann
254472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Creates our network device */
25450de8ca597d7b449e9e7ce7af138944acf06c8f05Jan Dumonstatic struct hso_device *hso_create_net_device(struct usb_interface *interface,
25460de8ca597d7b449e9e7ce7af138944acf06c8f05Jan Dumon						int port_spec)
254772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
254872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int result, i;
254972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct net_device *net;
255072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *hso_net;
255172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev;
255272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
25530de8ca597d7b449e9e7ce7af138944acf06c8f05Jan Dumon	hso_dev = hso_create_device(interface, port_spec);
255472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_dev)
255572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return NULL;
255672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
255772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* allocate our network device, then we can put in our private data */
255872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* call hso_net_init to do the basic initialization */
255972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	net = alloc_netdev(sizeof(struct hso_net), "hso%d", hso_net_init);
256072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!net) {
256172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Unable to create ethernet device\n");
256272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
256372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
256472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
256572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_net = netdev_priv(net);
256672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
256772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev->port_data.dev_net = hso_net;
256872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_net->net = net;
256972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_net->parent = hso_dev;
257072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
257172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_net->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
257272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				      USB_DIR_IN);
257372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_net->in_endp) {
257472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Can't find BULK IN endpoint\n");
257572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
257672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
257772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_net->out_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
257872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				       USB_DIR_OUT);
257972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_net->out_endp) {
258072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Can't find BULK OUT endpoint\n");
258172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
258272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
258372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	SET_NETDEV_DEV(net, &interface->dev);
2584384912ed194e43c03ad1cdaa09b0b1e488c34d46Marcel Holtmann	SET_NETDEV_DEVTYPE(net, &hso_type);
258572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
258672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* registering our net device */
258772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = register_netdev(net);
258872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result) {
258972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Failed to register device\n");
259072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
259172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
259272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
259372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* start allocating */
259472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
259572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		hso_net->mux_bulk_rx_urb_pool[i] = usb_alloc_urb(0, GFP_KERNEL);
259672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!hso_net->mux_bulk_rx_urb_pool[i]) {
259772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			dev_err(&interface->dev, "Could not allocate rx urb\n");
259872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			goto exit;
259972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
260072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		hso_net->mux_bulk_rx_buf_pool[i] = kzalloc(MUX_BULK_RX_BUF_SIZE,
260172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman							   GFP_KERNEL);
260272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!hso_net->mux_bulk_rx_buf_pool[i]) {
260372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			dev_err(&interface->dev, "Could not allocate rx buf\n");
260472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			goto exit;
260572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
260672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
260772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_net->mux_bulk_tx_urb = usb_alloc_urb(0, GFP_KERNEL);
260872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_net->mux_bulk_tx_urb) {
260972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Could not allocate tx urb\n");
261072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
261172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
261272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL);
261372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_net->mux_bulk_tx_buf) {
261472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Could not allocate tx buf\n");
261572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
261672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
261772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
261872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	add_net_device(hso_dev);
261972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
262072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_log_port(hso_dev);
262172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
262272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_create_rfkill(hso_dev, interface);
262372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
262472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return hso_dev;
262572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanexit:
262672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_free_net_device(hso_dev);
262772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return NULL;
262872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
262972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
2630542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrowstatic void hso_free_tiomget(struct hso_serial *serial)
2631542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow{
26325b89db0e84bef81f6aa324f8f22a9258ff873de3Jesper Juhl	struct hso_tiocmget *tiocmget;
26335b89db0e84bef81f6aa324f8f22a9258ff873de3Jesper Juhl	if (!serial)
26345b89db0e84bef81f6aa324f8f22a9258ff873de3Jesper Juhl		return;
26355b89db0e84bef81f6aa324f8f22a9258ff873de3Jesper Juhl	tiocmget = serial->tiocmget;
2636542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	if (tiocmget) {
26375b89db0e84bef81f6aa324f8f22a9258ff873de3Jesper Juhl		usb_free_urb(tiocmget->urb);
26385b89db0e84bef81f6aa324f8f22a9258ff873de3Jesper Juhl		tiocmget->urb = NULL;
2639542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		serial->tiocmget = NULL;
26403b7d2b319db0ba1f6208ca58b297fb419301f85aJan Dumon		kfree(tiocmget);
2641542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
2642542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow}
2643542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow
264472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Frees an AT channel ( goes for both mux and non-mux ) */
264572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_free_serial_device(struct hso_device *hso_dev)
264672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
264772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial = dev2ser(hso_dev);
264872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
264972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial)
265072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return;
265172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	set_serial_by_index(serial->minor, NULL);
265272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
265372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_serial_common_free(serial);
265472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
265572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial->shared_int) {
265672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		mutex_lock(&serial->shared_int->shared_int_lock);
265772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (--serial->shared_int->ref_count == 0)
265872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_free_shared_int(serial->shared_int);
265972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		else
266072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			mutex_unlock(&serial->shared_int->shared_int_lock);
266172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
2662542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	hso_free_tiomget(serial);
266372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(serial);
2664e44578ea7c4f87462e05e0d0a2dd43b6beecd5f6Paulius Zaleckas	kfree(hso_dev);
266572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
266672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
266772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Creates a bulk AT channel */
266872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct hso_device *hso_create_bulk_serial_device(
266972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			struct usb_interface *interface, int port)
267072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
267172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev;
267272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial;
267372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int num_urbs;
2674542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	struct hso_tiocmget *tiocmget;
267572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
267672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev = hso_create_device(interface, port);
267772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_dev)
267872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return NULL;
267972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
268072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial = kzalloc(sizeof(*serial), GFP_KERNEL);
268172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial)
268272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
268372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
268472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->parent = hso_dev;
268572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev->port_data.dev_serial = serial;
268672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
268758eb17f155704062d76729d1fb7e23d3559ca86aDenis Joseph Barrow	if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
268872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		num_urbs = 2;
2689542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget),
2690542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow					   GFP_KERNEL);
2691542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		/* it isn't going to break our heart if serial->tiocmget
2692542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		 *  allocation fails don't bother checking this.
2693542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		 */
2694542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		if (serial->tiocmget) {
2695542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			tiocmget = serial->tiocmget;
2696542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL);
2697542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			if (tiocmget->urb) {
2698542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow				mutex_init(&tiocmget->mutex);
2699542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow				init_waitqueue_head(&tiocmget->waitq);
2700542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow				tiocmget->endp = hso_get_ep(
2701542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow					interface,
2702542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow					USB_ENDPOINT_XFER_INT,
2703542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow					USB_DIR_IN);
2704542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow			} else
2705542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow				hso_free_tiomget(serial);
2706542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow		}
2707542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	}
270872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	else
270972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		num_urbs = 1;
271072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
271172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_serial_common_create(serial, num_urbs, BULK_URB_RX_SIZE,
271272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				     BULK_URB_TX_SIZE))
271372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
271472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
271572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
271672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				     USB_DIR_IN);
271772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial->in_endp) {
271872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Failed to find BULK IN ep\n");
2719e57b641dfafc10ce23d26cf271fd2638589fdb3fAdrian Bunk		goto exit2;
272072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
272172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
272272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!
272372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	    (serial->out_endp =
272472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	     hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT))) {
272572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Failed to find BULK IN ep\n");
2726e57b641dfafc10ce23d26cf271fd2638589fdb3fAdrian Bunk		goto exit2;
272772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
272872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
272972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->write_data = hso_std_serial_write_data;
273072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
273172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* and record this serial */
273272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	set_serial_by_index(serial->minor, serial);
273372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
273472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* setup the proc dirs and files if needed */
273572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_log_port(hso_dev);
273672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
273772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done, return it */
273872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return hso_dev;
2739e57b641dfafc10ce23d26cf271fd2638589fdb3fAdrian Bunk
2740e57b641dfafc10ce23d26cf271fd2638589fdb3fAdrian Bunkexit2:
2741e57b641dfafc10ce23d26cf271fd2638589fdb3fAdrian Bunk	hso_serial_common_free(serial);
274272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanexit:
2743542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	hso_free_tiomget(serial);
274472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(serial);
2745e44578ea7c4f87462e05e0d0a2dd43b6beecd5f6Paulius Zaleckas	kfree(hso_dev);
274672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return NULL;
274772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
274872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
274972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Creates a multiplexed AT channel */
275072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic
275172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstruct hso_device *hso_create_mux_serial_device(struct usb_interface *interface,
275272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						int port,
275372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						struct hso_shared_int *mux)
275472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
275572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev;
275672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *serial;
275772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int port_spec;
275872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
275972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	port_spec = HSO_INTF_MUX;
276072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	port_spec &= ~HSO_PORT_MASK;
276172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
276272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	port_spec |= hso_mux_to_port(port);
276372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NO_PORT)
276472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return NULL;
276572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
276672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev = hso_create_device(interface, port_spec);
276772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!hso_dev)
276872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return NULL;
276972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
277072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial = kzalloc(sizeof(*serial), GFP_KERNEL);
277172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!serial)
277272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
277372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
277472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev->port_data.dev_serial = serial;
277572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->parent = hso_dev;
277672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
277772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_serial_common_create
277872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	    (serial, 1, CTRL_URB_RX_SIZE, CTRL_URB_TX_SIZE))
277972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
278072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
278172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->tx_data_length--;
278272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->write_data = hso_mux_serial_write_data;
278372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
278472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->shared_int = mux;
278572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	mutex_lock(&serial->shared_int->shared_int_lock);
278672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	serial->shared_int->ref_count++;
278772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	mutex_unlock(&serial->shared_int->shared_int_lock);
278872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
278972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* and record this serial */
279072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	set_serial_by_index(serial->minor, serial);
279172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
279272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* setup the proc dirs and files if needed */
279372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_log_port(hso_dev);
279472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
279572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done, return it */
279672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return hso_dev;
279772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
279872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanexit:
279972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (serial) {
280072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		tty_unregister_device(tty_drv, serial->minor);
280172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		kfree(serial);
280272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
280372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_dev)
2804e44578ea7c4f87462e05e0d0a2dd43b6beecd5f6Paulius Zaleckas		kfree(hso_dev);
280572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return NULL;
280672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
280772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
280872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
280972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_free_shared_int(struct hso_shared_int *mux)
281072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
281172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_free_urb(mux->shared_intr_urb);
281272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(mux->shared_intr_buf);
281372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	mutex_unlock(&mux->shared_int_lock);
281472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(mux);
281572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
281672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
281772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic
281872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstruct hso_shared_int *hso_create_shared_int(struct usb_interface *interface)
281972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
282072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_shared_int *mux = kzalloc(sizeof(*mux), GFP_KERNEL);
282172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
282272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!mux)
282372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return NULL;
282472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
282572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	mux->intr_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_INT,
282672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				    USB_DIR_IN);
282772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!mux->intr_endp) {
282872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Can't find INT IN endpoint\n");
282972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
283072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
283172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
283272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	mux->shared_intr_urb = usb_alloc_urb(0, GFP_KERNEL);
283372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!mux->shared_intr_urb) {
28348a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon		dev_err(&interface->dev, "Could not allocate intr urb?\n");
283572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
283672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
2837d9ced80d1084758772d350ac66b1ad0eeefc7f95Jan Dumon	mux->shared_intr_buf =
2838d9ced80d1084758772d350ac66b1ad0eeefc7f95Jan Dumon		kzalloc(le16_to_cpu(mux->intr_endp->wMaxPacketSize),
2839d9ced80d1084758772d350ac66b1ad0eeefc7f95Jan Dumon			GFP_KERNEL);
284072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!mux->shared_intr_buf) {
28418a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon		dev_err(&interface->dev, "Could not allocate intr buf?\n");
284272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
284372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
284472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
284572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	mutex_init(&mux->shared_int_lock);
284672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
284772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return mux;
284872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
284972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanexit:
285072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(mux->shared_intr_buf);
285172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_free_urb(mux->shared_intr_urb);
285272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	kfree(mux);
285372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return NULL;
285472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
285572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
285672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Gets the port spec for a certain interface */
285772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_get_config_data(struct usb_interface *interface)
285872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
285972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_device *usbdev = interface_to_usbdev(interface);
286072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u8 config_data[17];
286172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	u32 if_num = interface->altsetting->desc.bInterfaceNumber;
286272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	s32 result;
286372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
286472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
286572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    0x86, 0xC0, 0, 0, config_data, 17,
286672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    USB_CTRL_SET_TIMEOUT) != 0x11) {
286772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -EIO;
286872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
286972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
287072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	switch (config_data[if_num]) {
287172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x0:
287272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0;
287372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
287472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x1:
287572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_DIAG;
287672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
287772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x2:
287872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_GPS;
287972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
288072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x3:
288172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_GPS_CONTROL;
288272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
288372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x4:
288472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_APP;
288572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
288672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x5:
288772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_APP2;
288872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
288972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x6:
289072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_CONTROL;
289172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
289272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x7:
289372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_NETWORK;
289472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
289572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x8:
289672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_MODEM;
289772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
289872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0x9:
289972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_MSD;
290072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
290172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0xa:
290272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_PCSC;
290372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
290472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case 0xb:
290572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = HSO_PORT_VOICE;
290672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
290772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	default:
290872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result = 0;
290972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
291072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
291172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result)
291272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result |= HSO_INTF_BULK;
291372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
291472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (config_data[16] & 0x1)
291572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		result |= HSO_INFO_CRC_BUG;
291672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
291772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return result;
291872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
291972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
292072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called once for each interface upon device insertion */
292172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_probe(struct usb_interface *interface,
292272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		     const struct usb_device_id *id)
292372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
292472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int mux, i, if_num, port_spec;
292572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	unsigned char port_mask;
292672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev = NULL;
292772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_shared_int *shared_int;
292872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *tmp_dev = NULL;
292972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
293072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if_num = interface->altsetting->desc.bInterfaceNumber;
293172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
293272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Get the interface/port specification from either driver_info or from
293372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 * the device itself */
293472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (id->driver_info)
293572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_spec = ((u32 *)(id->driver_info))[if_num];
293672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	else
293772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		port_spec = hso_get_config_data(interface);
293872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
293972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
294072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		dev_err(&interface->dev, "Not our interface\n");
294172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENODEV;
294272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
294372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Check if we need to switch to alt interfaces prior to port
294472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	 * configuration */
294572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (interface->num_altsetting > 1)
294672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		usb_set_interface(interface_to_usbdev(interface), if_num, 1);
294772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	interface->needs_remote_wakeup = 1;
294872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
294972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Allocate new hso device(s) */
295072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	switch (port_spec & HSO_INTF_MASK) {
295172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_INTF_MUX:
295272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
295372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			/* Create the network device */
295472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (!disable_net) {
29550de8ca597d7b449e9e7ce7af138944acf06c8f05Jan Dumon				hso_dev = hso_create_net_device(interface,
29560de8ca597d7b449e9e7ce7af138944acf06c8f05Jan Dumon								port_spec);
295772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				if (!hso_dev)
295872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					goto exit;
295972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				tmp_dev = hso_dev;
296072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
296172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
296272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
296372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (hso_get_mux_ports(interface, &port_mask))
296472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			/* TODO: de-allocate everything */
296572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			goto exit;
296672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
296772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		shared_int = hso_create_shared_int(interface);
296872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!shared_int)
296972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			goto exit;
297072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
297172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		for (i = 1, mux = 0; i < 0x100; i = i << 1, mux++) {
297272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (port_mask & i) {
297372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				hso_dev = hso_create_mux_serial_device(
297472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						interface, i, shared_int);
297572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				if (!hso_dev)
297672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					goto exit;
297772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
297872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
297972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
298072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (tmp_dev)
298172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_dev = tmp_dev;
298272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
298372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
298472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	case HSO_INTF_BULK:
298572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* It's a regular bulk interface */
29868e65c0ece6f2aa732f9b755331869c67aeb1c7f6Filip Aben		if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
29878e65c0ece6f2aa732f9b755331869c67aeb1c7f6Filip Aben			if (!disable_net)
29888e65c0ece6f2aa732f9b755331869c67aeb1c7f6Filip Aben				hso_dev =
29898e65c0ece6f2aa732f9b755331869c67aeb1c7f6Filip Aben				    hso_create_net_device(interface, port_spec);
29908e65c0ece6f2aa732f9b755331869c67aeb1c7f6Filip Aben		} else {
299172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_dev =
299272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			    hso_create_bulk_serial_device(interface, port_spec);
29938e65c0ece6f2aa732f9b755331869c67aeb1c7f6Filip Aben		}
299472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!hso_dev)
299572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			goto exit;
299672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		break;
299772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	default:
299872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		goto exit;
299972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
300072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
300172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* save our data pointer in this device */
300272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_set_intfdata(interface, hso_dev);
300372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
300472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done */
300572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
300672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanexit:
300772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_free_interface(interface);
300872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return -ENODEV;
300972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
301072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
301172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* device removed, cleaning up */
301272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_disconnect(struct usb_interface *interface)
301372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
301472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_free_interface(interface);
301572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
301672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* remove reference of our private data */
301772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_set_intfdata(interface, NULL);
301872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
301972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
302072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void async_get_intf(struct work_struct *data)
302172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
302272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev =
302372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	    container_of(data, struct hso_device, async_get_intf);
302472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_autopm_get_interface(hso_dev->interface);
302572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
302672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
302772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void async_put_intf(struct work_struct *data)
302872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
302972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev =
303072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	    container_of(data, struct hso_device, async_put_intf);
303172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_autopm_put_interface(hso_dev->interface);
303272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
303372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
303472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_get_activity(struct hso_device *hso_dev)
303572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
303672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_dev->usb->state == USB_STATE_SUSPENDED) {
303772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (!hso_dev->is_active) {
303872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_dev->is_active = 1;
303972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			schedule_work(&hso_dev->async_get_intf);
304072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
304172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
304272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
304372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_dev->usb->state != USB_STATE_CONFIGURED)
304472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -EAGAIN;
304572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
304672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_mark_last_busy(hso_dev->usb);
304772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
304872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
304972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
305072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
305172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_put_activity(struct hso_device *hso_dev)
305272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
305372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (hso_dev->usb->state != USB_STATE_SUSPENDED) {
305472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (hso_dev->is_active) {
305572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_dev->is_active = 0;
305672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			schedule_work(&hso_dev->async_put_intf);
305772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			return -EAGAIN;
305872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
305972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
306072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_dev->is_active = 0;
306172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
306272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
306372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
306472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called by kernel when we need to suspend device */
306572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_suspend(struct usb_interface *iface, pm_message_t message)
306672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
306772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i, result;
306872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
306972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Stop all serial ports */
307072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
307172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (serial_table[i] && (serial_table[i]->interface == iface)) {
307272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			result = hso_stop_serial_device(serial_table[i]);
307372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (result)
307472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				goto out;
307572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
307672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
307772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
307872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Stop all network ports */
307972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
308072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (network_table[i] &&
308172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		    (network_table[i]->interface == iface)) {
308272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			result = hso_stop_net_device(network_table[i]);
308372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (result)
308472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				goto out;
308572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
308672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
308772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
308872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanout:
308972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
309072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
309172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
309272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* called by kernel when we need to resume device */
309372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_resume(struct usb_interface *iface)
309472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
309572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i, result = 0;
309672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_net *hso_net;
309772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
309872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Start all serial ports */
309972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
310072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (serial_table[i] && (serial_table[i]->interface == iface)) {
310172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			if (dev2ser(serial_table[i])->open_count) {
310272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				result =
310372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				    hso_start_serial_device(serial_table[i], GFP_NOIO);
310472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				hso_kick_transmit(dev2ser(serial_table[i]));
310572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				if (result)
310672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman					goto out;
310772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
310872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
310972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
311072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
311172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Start all network ports */
311272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
311372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (network_table[i] &&
311472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		    (network_table[i]->interface == iface)) {
311572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_net = dev2net(network_table[i]);
311689930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow			if (hso_net->flags & IFF_UP) {
311789930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow				/* First transmit any lingering data,
311889930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow				   then restart the device. */
311989930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow				if (hso_net->skb_tx_buf) {
312089930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow					dev_dbg(&iface->dev,
312189930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow						"Transmitting"
312289930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow						" lingering data\n");
312389930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow					hso_net_start_xmit(hso_net->skb_tx_buf,
312489930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow							   hso_net->net);
312589930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow					hso_net->skb_tx_buf = NULL;
312689930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow				}
312789930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow				result = hso_start_net_device(network_table[i]);
312889930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow				if (result)
312989930b7b5e3e9bfe9c6ec5e19920451c8f5d9088Denis Joseph Barrow					goto out;
313072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			}
313172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
313272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
313372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
313472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanout:
313572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return result;
313672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
313772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
313868a351c501ad22077a969df157cd13367cb43a40Jan Dumonstatic void reset_device(struct work_struct *data)
313968a351c501ad22077a969df157cd13367cb43a40Jan Dumon{
314068a351c501ad22077a969df157cd13367cb43a40Jan Dumon	struct hso_device *hso_dev =
314168a351c501ad22077a969df157cd13367cb43a40Jan Dumon	    container_of(data, struct hso_device, reset_device);
314268a351c501ad22077a969df157cd13367cb43a40Jan Dumon	struct usb_device *usb = hso_dev->usb;
314368a351c501ad22077a969df157cd13367cb43a40Jan Dumon	int result;
314468a351c501ad22077a969df157cd13367cb43a40Jan Dumon
314568a351c501ad22077a969df157cd13367cb43a40Jan Dumon	if (hso_dev->usb_gone) {
314668a351c501ad22077a969df157cd13367cb43a40Jan Dumon		D1("No reset during disconnect\n");
314768a351c501ad22077a969df157cd13367cb43a40Jan Dumon	} else {
314868a351c501ad22077a969df157cd13367cb43a40Jan Dumon		result = usb_lock_device_for_reset(usb, hso_dev->interface);
314968a351c501ad22077a969df157cd13367cb43a40Jan Dumon		if (result < 0)
315068a351c501ad22077a969df157cd13367cb43a40Jan Dumon			D1("unable to lock device for reset: %d\n", result);
315168a351c501ad22077a969df157cd13367cb43a40Jan Dumon		else {
315268a351c501ad22077a969df157cd13367cb43a40Jan Dumon			usb_reset_device(usb);
315368a351c501ad22077a969df157cd13367cb43a40Jan Dumon			usb_unlock_device(usb);
315468a351c501ad22077a969df157cd13367cb43a40Jan Dumon		}
315568a351c501ad22077a969df157cd13367cb43a40Jan Dumon	}
315668a351c501ad22077a969df157cd13367cb43a40Jan Dumon}
315768a351c501ad22077a969df157cd13367cb43a40Jan Dumon
315872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_serial_ref_free(struct kref *ref)
315972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
316072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_device *hso_dev = container_of(ref, struct hso_device, ref);
316172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
316272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	hso_free_serial_device(hso_dev);
316372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
316472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
316572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void hso_free_interface(struct usb_interface *interface)
316672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
316772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct hso_serial *hso_dev;
3168e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox	struct tty_struct *tty;
316972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
317072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
317172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
31728e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		if (serial_table[i] &&
31738e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		    (serial_table[i]->interface == interface)) {
317472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_dev = dev2ser(serial_table[i]);
3175e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox			spin_lock_irq(&hso_dev->serial_lock);
3176e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox			tty = tty_kref_get(hso_dev->tty);
3177e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox			spin_unlock_irq(&hso_dev->serial_lock);
3178e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox			if (tty)
3179e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox				tty_hangup(tty);
3180ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller			mutex_lock(&hso_dev->parent->mutex);
3181e136e3036bf27569dbfeae245cc09c7167cdc749Alan Cox			tty_kref_put(tty);
318272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_dev->parent->usb_gone = 1;
3183ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller			mutex_unlock(&hso_dev->parent->mutex);
3184ab153d84d9609b4e6f53632a6f14b882e866cb47David S. Miller			kref_put(&serial_table[i]->ref, hso_serial_ref_free);
318572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
318672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
318772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
318872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
31898e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		if (network_table[i] &&
31908e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		    (network_table[i]->interface == interface)) {
319172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			struct rfkill *rfk = dev2net(network_table[i])->rfkill;
319272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			/* hso_stop_net_device doesn't stop the net queue since
319372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 * traffic needs to start it again when suspended */
319472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			netif_stop_queue(dev2net(network_table[i])->net);
319572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_stop_net_device(network_table[i]);
319672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			cancel_work_sync(&network_table[i]->async_put_intf);
319772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			cancel_work_sync(&network_table[i]->async_get_intf);
319819d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg			if (rfk) {
319972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				rfkill_unregister(rfk);
320019d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg				rfkill_destroy(rfk);
320119d337dff95cbf76edd3ad95c0cee2732c3e1ec5Johannes Berg			}
320272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			hso_free_net_device(network_table[i]);
320372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
320472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
320572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
320672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
320772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Helper functions */
320872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
320972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Get the endpoint ! */
321072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
321172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman						  int type, int dir)
321272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
321372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
321472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_host_interface *iface = intf->cur_altsetting;
321572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_endpoint_descriptor *endp;
321672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
321772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
321872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		endp = &iface->endpoint[i].desc;
321972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) &&
3220f201a8a4511a4c9953189924df3c880207194b41Julia Lawall		    (usb_endpoint_type(endp) == type))
322172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			return endp;
322272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
322372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
322472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return NULL;
322572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
322672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
322772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Get the byte that describes which ports are enabled */
322872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports)
322972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
323072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
323172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	struct usb_host_interface *iface = intf->cur_altsetting;
323272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
323372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (iface->extralen == 3) {
323472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		*ports = iface->extra[2];
323572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return 0;
323672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
323772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
323872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
323972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		if (iface->endpoint[i].extralen == 3) {
324072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			*ports = iface->endpoint[i].extra[2];
324172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			return 0;
324272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		}
324372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
324472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
324572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return -1;
324672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
324772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
324872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* interrupt urb needs to be submitted, used for serial read of muxed port */
324972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int,
325072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				   struct usb_device *usb, gfp_t gfp)
325172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
325272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int result;
325372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
325472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_fill_int_urb(shared_int->shared_intr_urb, usb,
325572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 usb_rcvintpipe(usb,
325672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman				shared_int->intr_endp->bEndpointAddress & 0x7F),
325772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 shared_int->shared_intr_buf,
3258d9ced80d1084758772d350ac66b1ad0eeefc7f95Jan Dumon			 1,
325972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 intr_callback, shared_int,
326072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			 shared_int->intr_endp->bInterval);
326172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
326272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = usb_submit_urb(shared_int->shared_intr_urb, gfp);
326372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result)
32648a5c9c4932ad1fbe9daa501e89a7357a2804e3faJan Dumon		dev_warn(&usb->dev, "%s failed mux_intr_urb %d\n", __func__,
326572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			result);
326672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
326772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return result;
326872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
326972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
327072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* operations setup of the serial interface */
32716c59f56978c3dffd447176d218bc5852842c9ce9Greg Kroah-Hartmanstatic const struct tty_operations hso_serial_ops = {
327272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.open = hso_serial_open,
327372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.close = hso_serial_close,
327472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.write = hso_serial_write,
327572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.write_room = hso_serial_write_room,
3276542f54823614915780c3459b0e6062f06c0c0f99Denis Joseph Barrow	.ioctl = hso_serial_ioctl,
327772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.set_termios = hso_serial_set_termios,
327872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.chars_in_buffer = hso_serial_chars_in_buffer,
327972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.tiocmget = hso_serial_tiocmget,
328072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.tiocmset = hso_serial_tiocmset,
32810bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	.get_icount = hso_get_count,
32828ef5ba63b94b04b182ac4a6009dbbf1406beb3c5Denis Joseph Barrow	.unthrottle = hso_unthrottle
328372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
328472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
328572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic struct usb_driver hso_driver = {
328672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.name = driver_name,
328772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.probe = hso_probe,
328872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.disconnect = hso_disconnect,
328972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.id_table = hso_ids,
329072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.suspend = hso_suspend,
329172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.resume = hso_resume,
32929c8f92aed16dbd1924910f3305f5992a4f29fe2aDenis Joseph Barrow	.reset_resume = hso_resume,
329372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	.supports_autosuspend = 1,
329472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman};
329572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
329672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic int __init hso_init(void)
329772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
329872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int i;
329972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	int result;
330072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
330172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* put it in the log */
330272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	printk(KERN_INFO "hso: %s\n", version);
330372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
330472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* Initialise the serial table semaphore and table */
330572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	spin_lock_init(&serial_table_lock);
330672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++)
330772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		serial_table[i] = NULL;
330872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
330972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* allocate our driver using the proper amount of supported minors */
331072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv = alloc_tty_driver(HSO_SERIAL_TTY_MINORS);
331172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (!tty_drv)
331272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return -ENOMEM;
331372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
331472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* fill in all needed values */
331572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->magic = TTY_DRIVER_MAGIC;
331672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->owner = THIS_MODULE;
331772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->driver_name = driver_name;
331872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->name = tty_filename;
331972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
332072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* if major number is provided as parameter, use that one */
332172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (tty_major)
332272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		tty_drv->major = tty_major;
332372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
332472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->minor_start = 0;
332572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->num = HSO_SERIAL_TTY_MINORS;
332672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
332772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->subtype = SERIAL_TYPE_NORMAL;
332872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
332972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_drv->init_termios = tty_std_termios;
3330ac9720c37e8795317e8be3adad63cb0d5522a640Alan Cox	hso_init_termios(&tty_drv->init_termios);
333172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_set_operations(tty_drv, &hso_serial_ops);
333272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
333372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* register the tty driver */
333472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = tty_register_driver(tty_drv);
333572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result) {
333672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		printk(KERN_ERR "%s - tty_register_driver failed(%d)\n",
333772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			__func__, result);
333872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return result;
333972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
334072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
334172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* register this module as an usb driver */
334272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	result = usb_register(&hso_driver);
334372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	if (result) {
334472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		printk(KERN_ERR "Could not register hso driver? error: %d\n",
334572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman			result);
334672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		/* cleanup serial interface */
334772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		tty_unregister_driver(tty_drv);
334872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman		return result;
334972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	}
335072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
335172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* done */
335272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	return 0;
335372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
335472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
335572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanstatic void __exit hso_exit(void)
335672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman{
335772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	printk(KERN_INFO "hso: unloaded\n");
335872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
335972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	tty_unregister_driver(tty_drv);
336072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	/* deregister the usb driver */
336172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman	usb_deregister(&hso_driver);
336272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman}
336372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
336472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* Module definitions */
336572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanmodule_init(hso_init);
336672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanmodule_exit(hso_exit);
336772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
336872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-HartmanMODULE_AUTHOR(MOD_AUTHOR);
336972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-HartmanMODULE_DESCRIPTION(MOD_DESCRIPTION);
337072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-HartmanMODULE_LICENSE(MOD_LICENSE);
337172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
337272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* change the debug level (eg: insmod hso.ko debug=0x04) */
337372dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-HartmanMODULE_PARM_DESC(debug, "Level of debug [0x01 | 0x02 | 0x04 | 0x08 | 0x10]");
337472dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanmodule_param(debug, int, S_IRUGO | S_IWUSR);
337572dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
337672dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* set the major tty number (eg: insmod hso.ko tty_major=245) */
337772dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-HartmanMODULE_PARM_DESC(tty_major, "Set the major tty number");
337872dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanmodule_param(tty_major, int, S_IRUGO | S_IWUSR);
337972dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman
338072dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartman/* disable network interface (eg: insmod hso.ko disable_net=1) */
338172dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-HartmanMODULE_PARM_DESC(disable_net, "Disable the network interface");
338272dc1c096c7051a48ab1dbb12f71976656b55eb5Greg Kroah-Hartmanmodule_param(disable_net, int, S_IRUGO | S_IWUSR);
3383