xref: /openbmc/linux/drivers/net/usb/hso.c (revision 6fcd3b67)
104672fe6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
272dc1c09SGreg Kroah-Hartman /******************************************************************************
372dc1c09SGreg Kroah-Hartman  *
472dc1c09SGreg Kroah-Hartman  * Driver for Option High Speed Mobile Devices.
572dc1c09SGreg Kroah-Hartman  *
672dc1c09SGreg Kroah-Hartman  *  Copyright (C) 2008 Option International
711cd29b0SDenis Joseph Barrow  *                     Filip Aben <f.aben@option.com>
811cd29b0SDenis Joseph Barrow  *                     Denis Joseph Barrow <d.barow@option.com>
93b7d2b31SJan Dumon  *                     Jan Dumon <j.dumon@option.com>
1072dc1c09SGreg Kroah-Hartman  *  Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd)
1172dc1c09SGreg Kroah-Hartman  *  			<ajb@spheresystems.co.uk>
1272dc1c09SGreg Kroah-Hartman  *  Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
1372dc1c09SGreg Kroah-Hartman  *  Copyright (C) 2008 Novell, Inc.
1472dc1c09SGreg Kroah-Hartman  *
1572dc1c09SGreg Kroah-Hartman  *****************************************************************************/
1672dc1c09SGreg Kroah-Hartman 
1772dc1c09SGreg Kroah-Hartman /******************************************************************************
1872dc1c09SGreg Kroah-Hartman  *
1972dc1c09SGreg Kroah-Hartman  * Description of the device:
2072dc1c09SGreg Kroah-Hartman  *
2172dc1c09SGreg Kroah-Hartman  * Interface 0:	Contains the IP network interface on the bulk end points.
2272dc1c09SGreg Kroah-Hartman  *		The multiplexed serial ports are using the interrupt and
2372dc1c09SGreg Kroah-Hartman  *		control endpoints.
2472dc1c09SGreg Kroah-Hartman  *		Interrupt contains a bitmap telling which multiplexed
2572dc1c09SGreg Kroah-Hartman  *		serialport needs servicing.
2672dc1c09SGreg Kroah-Hartman  *
2772dc1c09SGreg Kroah-Hartman  * Interface 1:	Diagnostics port, uses bulk only, do not submit urbs until the
2872dc1c09SGreg Kroah-Hartman  *		port is opened, as this have a huge impact on the network port
2972dc1c09SGreg Kroah-Hartman  *		throughput.
3072dc1c09SGreg Kroah-Hartman  *
31542f5482SDenis Joseph Barrow  * Interface 2:	Standard modem interface - circuit switched interface, this
32542f5482SDenis Joseph Barrow  *		can be used to make a standard ppp connection however it
33542f5482SDenis Joseph Barrow  *              should not be used in conjunction with the IP network interface
34542f5482SDenis Joseph Barrow  *              enabled for USB performance reasons i.e. if using this set
35542f5482SDenis Joseph Barrow  *              ideally disable_net=1.
3672dc1c09SGreg Kroah-Hartman  *
3772dc1c09SGreg Kroah-Hartman  *****************************************************************************/
3872dc1c09SGreg Kroah-Hartman 
393981cce6SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
403981cce6SJoe Perches 
41174cd4b1SIngo Molnar #include <linux/sched/signal.h>
4272dc1c09SGreg Kroah-Hartman #include <linux/slab.h>
4372dc1c09SGreg Kroah-Hartman #include <linux/init.h>
4472dc1c09SGreg Kroah-Hartman #include <linux/delay.h>
4572dc1c09SGreg Kroah-Hartman #include <linux/netdevice.h>
4672dc1c09SGreg Kroah-Hartman #include <linux/module.h>
4772dc1c09SGreg Kroah-Hartman #include <linux/ethtool.h>
4872dc1c09SGreg Kroah-Hartman #include <linux/usb.h>
4972dc1c09SGreg Kroah-Hartman #include <linux/tty.h>
5072dc1c09SGreg Kroah-Hartman #include <linux/tty_driver.h>
5172dc1c09SGreg Kroah-Hartman #include <linux/tty_flip.h>
5272dc1c09SGreg Kroah-Hartman #include <linux/kmod.h>
5372dc1c09SGreg Kroah-Hartman #include <linux/rfkill.h>
5472dc1c09SGreg Kroah-Hartman #include <linux/ip.h>
5572dc1c09SGreg Kroah-Hartman #include <linux/uaccess.h>
5672dc1c09SGreg Kroah-Hartman #include <linux/usb/cdc.h>
5772dc1c09SGreg Kroah-Hartman #include <net/arp.h>
5872dc1c09SGreg Kroah-Hartman #include <asm/byteorder.h>
59542f5482SDenis Joseph Barrow #include <linux/serial_core.h>
60542f5482SDenis Joseph Barrow #include <linux/serial.h>
6172dc1c09SGreg Kroah-Hartman 
6272dc1c09SGreg Kroah-Hartman 
6372dc1c09SGreg Kroah-Hartman #define MOD_AUTHOR			"Option Wireless"
6472dc1c09SGreg Kroah-Hartman #define MOD_DESCRIPTION			"USB High Speed Option driver"
6572dc1c09SGreg Kroah-Hartman 
6672dc1c09SGreg Kroah-Hartman #define HSO_MAX_NET_DEVICES		10
6772dc1c09SGreg Kroah-Hartman #define HSO__MAX_MTU			2048
6872dc1c09SGreg Kroah-Hartman #define DEFAULT_MTU			1500
6972dc1c09SGreg Kroah-Hartman #define DEFAULT_MRU			1500
7072dc1c09SGreg Kroah-Hartman 
7172dc1c09SGreg Kroah-Hartman #define CTRL_URB_RX_SIZE		1024
7272dc1c09SGreg Kroah-Hartman #define CTRL_URB_TX_SIZE		64
7372dc1c09SGreg Kroah-Hartman 
7472dc1c09SGreg Kroah-Hartman #define BULK_URB_RX_SIZE		4096
7572dc1c09SGreg Kroah-Hartman #define BULK_URB_TX_SIZE		8192
7672dc1c09SGreg Kroah-Hartman 
7772dc1c09SGreg Kroah-Hartman #define MUX_BULK_RX_BUF_SIZE		HSO__MAX_MTU
7872dc1c09SGreg Kroah-Hartman #define MUX_BULK_TX_BUF_SIZE		HSO__MAX_MTU
7972dc1c09SGreg Kroah-Hartman #define MUX_BULK_RX_BUF_COUNT		4
8072dc1c09SGreg Kroah-Hartman #define USB_TYPE_OPTION_VENDOR		0x20
8172dc1c09SGreg Kroah-Hartman 
8272dc1c09SGreg Kroah-Hartman /* These definitions are used with the struct hso_net flags element */
8372dc1c09SGreg Kroah-Hartman /* - use *_bit operations on it. (bit indices not values.) */
8472dc1c09SGreg Kroah-Hartman #define HSO_NET_RUNNING			0
8572dc1c09SGreg Kroah-Hartman 
8672dc1c09SGreg Kroah-Hartman #define	HSO_NET_TX_TIMEOUT		(HZ*10)
8772dc1c09SGreg Kroah-Hartman 
8872dc1c09SGreg Kroah-Hartman #define HSO_SERIAL_MAGIC		0x48534f31
8972dc1c09SGreg Kroah-Hartman 
9072dc1c09SGreg Kroah-Hartman /* Number of ttys to handle */
9172dc1c09SGreg Kroah-Hartman #define HSO_SERIAL_TTY_MINORS		256
9272dc1c09SGreg Kroah-Hartman 
9372dc1c09SGreg Kroah-Hartman #define MAX_RX_URBS			2
9472dc1c09SGreg Kroah-Hartman 
9572dc1c09SGreg Kroah-Hartman /*****************************************************************************/
9672dc1c09SGreg Kroah-Hartman /* Debugging functions                                                       */
9772dc1c09SGreg Kroah-Hartman /*****************************************************************************/
9895a69117SJoe Perches #define hso_dbg(lvl, fmt, ...)						\
9972dc1c09SGreg Kroah-Hartman do {									\
10095a69117SJoe Perches 	if ((lvl) & debug)						\
10195a69117SJoe Perches 		pr_info("[%d:%s] " fmt,					\
10295a69117SJoe Perches 			__LINE__, __func__, ##__VA_ARGS__);		\
10372dc1c09SGreg Kroah-Hartman } while (0)
10472dc1c09SGreg Kroah-Hartman 
10572dc1c09SGreg Kroah-Hartman /*****************************************************************************/
10672dc1c09SGreg Kroah-Hartman /* Enumerators                                                               */
10772dc1c09SGreg Kroah-Hartman /*****************************************************************************/
10872dc1c09SGreg Kroah-Hartman enum pkt_parse_state {
10972dc1c09SGreg Kroah-Hartman 	WAIT_IP,
11072dc1c09SGreg Kroah-Hartman 	WAIT_DATA,
11172dc1c09SGreg Kroah-Hartman 	WAIT_SYNC
11272dc1c09SGreg Kroah-Hartman };
11372dc1c09SGreg Kroah-Hartman 
11472dc1c09SGreg Kroah-Hartman /*****************************************************************************/
11572dc1c09SGreg Kroah-Hartman /* Structs                                                                   */
11672dc1c09SGreg Kroah-Hartman /*****************************************************************************/
11772dc1c09SGreg Kroah-Hartman 
11872dc1c09SGreg Kroah-Hartman struct hso_shared_int {
11972dc1c09SGreg Kroah-Hartman 	struct usb_endpoint_descriptor *intr_endp;
12072dc1c09SGreg Kroah-Hartman 	void *shared_intr_buf;
12172dc1c09SGreg Kroah-Hartman 	struct urb *shared_intr_urb;
12272dc1c09SGreg Kroah-Hartman 	struct usb_device *usb;
12372dc1c09SGreg Kroah-Hartman 	int use_count;
12472dc1c09SGreg Kroah-Hartman 	int ref_count;
12572dc1c09SGreg Kroah-Hartman 	struct mutex shared_int_lock;
12672dc1c09SGreg Kroah-Hartman };
12772dc1c09SGreg Kroah-Hartman 
12872dc1c09SGreg Kroah-Hartman struct hso_net {
12972dc1c09SGreg Kroah-Hartman 	struct hso_device *parent;
13072dc1c09SGreg Kroah-Hartman 	struct net_device *net;
13172dc1c09SGreg Kroah-Hartman 	struct rfkill *rfkill;
13238121067SOlivier Sobrie 	char name[24];
13372dc1c09SGreg Kroah-Hartman 
13472dc1c09SGreg Kroah-Hartman 	struct usb_endpoint_descriptor *in_endp;
13572dc1c09SGreg Kroah-Hartman 	struct usb_endpoint_descriptor *out_endp;
13672dc1c09SGreg Kroah-Hartman 
13772dc1c09SGreg Kroah-Hartman 	struct urb *mux_bulk_rx_urb_pool[MUX_BULK_RX_BUF_COUNT];
13872dc1c09SGreg Kroah-Hartman 	struct urb *mux_bulk_tx_urb;
13972dc1c09SGreg Kroah-Hartman 	void *mux_bulk_rx_buf_pool[MUX_BULK_RX_BUF_COUNT];
14072dc1c09SGreg Kroah-Hartman 	void *mux_bulk_tx_buf;
14172dc1c09SGreg Kroah-Hartman 
14272dc1c09SGreg Kroah-Hartman 	struct sk_buff *skb_rx_buf;
14372dc1c09SGreg Kroah-Hartman 	struct sk_buff *skb_tx_buf;
14472dc1c09SGreg Kroah-Hartman 
14572dc1c09SGreg Kroah-Hartman 	enum pkt_parse_state rx_parse_state;
14672dc1c09SGreg Kroah-Hartman 	spinlock_t net_lock;
14772dc1c09SGreg Kroah-Hartman 
14872dc1c09SGreg Kroah-Hartman 	unsigned short rx_buf_size;
14972dc1c09SGreg Kroah-Hartman 	unsigned short rx_buf_missing;
15072dc1c09SGreg Kroah-Hartman 	struct iphdr rx_ip_hdr;
15172dc1c09SGreg Kroah-Hartman 
15272dc1c09SGreg Kroah-Hartman 	unsigned long flags;
15372dc1c09SGreg Kroah-Hartman };
15472dc1c09SGreg Kroah-Hartman 
1558ef5ba63SDenis Joseph Barrow enum rx_ctrl_state{
1568ef5ba63SDenis Joseph Barrow 	RX_IDLE,
1578ef5ba63SDenis Joseph Barrow 	RX_SENT,
1588ef5ba63SDenis Joseph Barrow 	RX_PENDING
1598ef5ba63SDenis Joseph Barrow };
1608ef5ba63SDenis Joseph Barrow 
161542f5482SDenis Joseph Barrow #define BM_REQUEST_TYPE (0xa1)
162542f5482SDenis Joseph Barrow #define B_NOTIFICATION  (0x20)
163542f5482SDenis Joseph Barrow #define W_VALUE         (0x0)
164542f5482SDenis Joseph Barrow #define W_LENGTH        (0x2)
165542f5482SDenis Joseph Barrow 
166542f5482SDenis Joseph Barrow #define B_OVERRUN       (0x1<<6)
167542f5482SDenis Joseph Barrow #define B_PARITY        (0x1<<5)
168542f5482SDenis Joseph Barrow #define B_FRAMING       (0x1<<4)
169542f5482SDenis Joseph Barrow #define B_RING_SIGNAL   (0x1<<3)
170542f5482SDenis Joseph Barrow #define B_BREAK         (0x1<<2)
171542f5482SDenis Joseph Barrow #define B_TX_CARRIER    (0x1<<1)
172542f5482SDenis Joseph Barrow #define B_RX_CARRIER    (0x1<<0)
173542f5482SDenis Joseph Barrow 
174542f5482SDenis Joseph Barrow struct hso_serial_state_notification {
175542f5482SDenis Joseph Barrow 	u8 bmRequestType;
176542f5482SDenis Joseph Barrow 	u8 bNotification;
177542f5482SDenis Joseph Barrow 	u16 wValue;
178542f5482SDenis Joseph Barrow 	u16 wIndex;
179542f5482SDenis Joseph Barrow 	u16 wLength;
180542f5482SDenis Joseph Barrow 	u16 UART_state_bitmap;
181ba2d3587SEric Dumazet } __packed;
182542f5482SDenis Joseph Barrow 
183542f5482SDenis Joseph Barrow struct hso_tiocmget {
184542f5482SDenis Joseph Barrow 	struct mutex mutex;
185542f5482SDenis Joseph Barrow 	wait_queue_head_t waitq;
186542f5482SDenis Joseph Barrow 	int    intr_completed;
187542f5482SDenis Joseph Barrow 	struct usb_endpoint_descriptor *endp;
188542f5482SDenis Joseph Barrow 	struct urb *urb;
189af0de130SOliver Neukum 	struct hso_serial_state_notification *serial_state_notification;
190542f5482SDenis Joseph Barrow 	u16    prev_UART_state_bitmap;
191542f5482SDenis Joseph Barrow 	struct uart_icount icount;
192542f5482SDenis Joseph Barrow };
193542f5482SDenis Joseph Barrow 
194542f5482SDenis Joseph Barrow 
19572dc1c09SGreg Kroah-Hartman struct hso_serial {
19672dc1c09SGreg Kroah-Hartman 	struct hso_device *parent;
19772dc1c09SGreg Kroah-Hartman 	int magic;
19872dc1c09SGreg Kroah-Hartman 	u8 minor;
19972dc1c09SGreg Kroah-Hartman 
20072dc1c09SGreg Kroah-Hartman 	struct hso_shared_int *shared_int;
20172dc1c09SGreg Kroah-Hartman 
20272dc1c09SGreg Kroah-Hartman 	/* rx/tx urb could be either a bulk urb or a control urb depending
20372dc1c09SGreg Kroah-Hartman 	   on which serial port it is used on. */
20472dc1c09SGreg Kroah-Hartman 	struct urb *rx_urb[MAX_RX_URBS];
20572dc1c09SGreg Kroah-Hartman 	u8 num_rx_urbs;
20672dc1c09SGreg Kroah-Hartman 	u8 *rx_data[MAX_RX_URBS];
20772dc1c09SGreg Kroah-Hartman 	u16 rx_data_length;	/* should contain allocated length */
20872dc1c09SGreg Kroah-Hartman 
20972dc1c09SGreg Kroah-Hartman 	struct urb *tx_urb;
21072dc1c09SGreg Kroah-Hartman 	u8 *tx_data;
21172dc1c09SGreg Kroah-Hartman 	u8 *tx_buffer;
21272dc1c09SGreg Kroah-Hartman 	u16 tx_data_length;	/* should contain allocated length */
21372dc1c09SGreg Kroah-Hartman 	u16 tx_data_count;
21472dc1c09SGreg Kroah-Hartman 	u16 tx_buffer_count;
21572dc1c09SGreg Kroah-Hartman 	struct usb_ctrlrequest ctrl_req_tx;
21672dc1c09SGreg Kroah-Hartman 	struct usb_ctrlrequest ctrl_req_rx;
21772dc1c09SGreg Kroah-Hartman 
21872dc1c09SGreg Kroah-Hartman 	struct usb_endpoint_descriptor *in_endp;
21972dc1c09SGreg Kroah-Hartman 	struct usb_endpoint_descriptor *out_endp;
22072dc1c09SGreg Kroah-Hartman 
2218ef5ba63SDenis Joseph Barrow 	enum rx_ctrl_state rx_state;
22272dc1c09SGreg Kroah-Hartman 	u8 rts_state;
22372dc1c09SGreg Kroah-Hartman 	u8 dtr_state;
22472dc1c09SGreg Kroah-Hartman 	unsigned tx_urb_used:1;
22572dc1c09SGreg Kroah-Hartman 
2265ce76e77SJiri Slaby 	struct tty_port port;
22772dc1c09SGreg Kroah-Hartman 	/* from usb_serial_port */
22872dc1c09SGreg Kroah-Hartman 	spinlock_t serial_lock;
22972dc1c09SGreg Kroah-Hartman 
23072dc1c09SGreg Kroah-Hartman 	int (*write_data) (struct hso_serial *serial);
231542f5482SDenis Joseph Barrow 	struct hso_tiocmget  *tiocmget;
2328ef5ba63SDenis Joseph Barrow 	/* Hacks required to get flow control
2338ef5ba63SDenis Joseph Barrow 	 * working on the serial receive buffers
2348ef5ba63SDenis Joseph Barrow 	 * so as not to drop characters on the floor.
2358ef5ba63SDenis Joseph Barrow 	 */
2368ef5ba63SDenis Joseph Barrow 	int  curr_rx_urb_idx;
2378ef5ba63SDenis Joseph Barrow 	u8   rx_urb_filled[MAX_RX_URBS];
2388ef5ba63SDenis Joseph Barrow 	struct tasklet_struct unthrottle_tasklet;
23972dc1c09SGreg Kroah-Hartman };
24072dc1c09SGreg Kroah-Hartman 
24172dc1c09SGreg Kroah-Hartman struct hso_device {
24272dc1c09SGreg Kroah-Hartman 	union {
24372dc1c09SGreg Kroah-Hartman 		struct hso_serial *dev_serial;
24472dc1c09SGreg Kroah-Hartman 		struct hso_net *dev_net;
24572dc1c09SGreg Kroah-Hartman 	} port_data;
24672dc1c09SGreg Kroah-Hartman 
24772dc1c09SGreg Kroah-Hartman 	u32 port_spec;
24872dc1c09SGreg Kroah-Hartman 
24972dc1c09SGreg Kroah-Hartman 	u8 is_active;
25072dc1c09SGreg Kroah-Hartman 	u8 usb_gone;
25172dc1c09SGreg Kroah-Hartman 	struct work_struct async_get_intf;
25272dc1c09SGreg Kroah-Hartman 	struct work_struct async_put_intf;
25372dc1c09SGreg Kroah-Hartman 
25472dc1c09SGreg Kroah-Hartman 	struct usb_device *usb;
25572dc1c09SGreg Kroah-Hartman 	struct usb_interface *interface;
25672dc1c09SGreg Kroah-Hartman 
25772dc1c09SGreg Kroah-Hartman 	struct device *dev;
25872dc1c09SGreg Kroah-Hartman 	struct kref ref;
259ab153d84SDavid S. Miller 	struct mutex mutex;
26072dc1c09SGreg Kroah-Hartman };
26172dc1c09SGreg Kroah-Hartman 
26272dc1c09SGreg Kroah-Hartman /* Type of interface */
26372dc1c09SGreg Kroah-Hartman #define HSO_INTF_MASK		0xFF00
26472dc1c09SGreg Kroah-Hartman #define	HSO_INTF_MUX		0x0100
26572dc1c09SGreg Kroah-Hartman #define	HSO_INTF_BULK   	0x0200
26672dc1c09SGreg Kroah-Hartman 
26772dc1c09SGreg Kroah-Hartman /* Type of port */
26872dc1c09SGreg Kroah-Hartman #define HSO_PORT_MASK		0xFF
26972dc1c09SGreg Kroah-Hartman #define HSO_PORT_NO_PORT	0x0
27072dc1c09SGreg Kroah-Hartman #define	HSO_PORT_CONTROL	0x1
27172dc1c09SGreg Kroah-Hartman #define	HSO_PORT_APP		0x2
27272dc1c09SGreg Kroah-Hartman #define	HSO_PORT_GPS		0x3
27372dc1c09SGreg Kroah-Hartman #define	HSO_PORT_PCSC		0x4
27472dc1c09SGreg Kroah-Hartman #define	HSO_PORT_APP2		0x5
27572dc1c09SGreg Kroah-Hartman #define HSO_PORT_GPS_CONTROL	0x6
27672dc1c09SGreg Kroah-Hartman #define HSO_PORT_MSD		0x7
27772dc1c09SGreg Kroah-Hartman #define HSO_PORT_VOICE		0x8
27872dc1c09SGreg Kroah-Hartman #define HSO_PORT_DIAG2		0x9
27972dc1c09SGreg Kroah-Hartman #define	HSO_PORT_DIAG		0x10
28072dc1c09SGreg Kroah-Hartman #define	HSO_PORT_MODEM		0x11
28172dc1c09SGreg Kroah-Hartman #define	HSO_PORT_NETWORK	0x12
28272dc1c09SGreg Kroah-Hartman 
28372dc1c09SGreg Kroah-Hartman /* Additional device info */
28472dc1c09SGreg Kroah-Hartman #define HSO_INFO_MASK		0xFF000000
28572dc1c09SGreg Kroah-Hartman #define HSO_INFO_CRC_BUG	0x01000000
28672dc1c09SGreg Kroah-Hartman 
28772dc1c09SGreg Kroah-Hartman /*****************************************************************************/
28872dc1c09SGreg Kroah-Hartman /* Prototypes                                                                */
28972dc1c09SGreg Kroah-Hartman /*****************************************************************************/
29072dc1c09SGreg Kroah-Hartman /* Serial driver functions */
29120b9d177SAlan Cox static int hso_serial_tiocmset(struct tty_struct *tty,
29272dc1c09SGreg Kroah-Hartman 			       unsigned int set, unsigned int clear);
29372dc1c09SGreg Kroah-Hartman static void ctrl_callback(struct urb *urb);
2948ef5ba63SDenis Joseph Barrow static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial);
29572dc1c09SGreg Kroah-Hartman static void hso_kick_transmit(struct hso_serial *serial);
29672dc1c09SGreg Kroah-Hartman /* Helper functions */
29772dc1c09SGreg Kroah-Hartman static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int,
29872dc1c09SGreg Kroah-Hartman 				   struct usb_device *usb, gfp_t gfp);
29968a351c5SJan Dumon static void handle_usb_error(int status, const char *function,
30068a351c5SJan Dumon 			     struct hso_device *hso_dev);
30172dc1c09SGreg Kroah-Hartman static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
30272dc1c09SGreg Kroah-Hartman 						  int type, int dir);
30372dc1c09SGreg Kroah-Hartman static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports);
30472dc1c09SGreg Kroah-Hartman static void hso_free_interface(struct usb_interface *intf);
30572dc1c09SGreg Kroah-Hartman static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags);
30672dc1c09SGreg Kroah-Hartman static int hso_stop_serial_device(struct hso_device *hso_dev);
30772dc1c09SGreg Kroah-Hartman static int hso_start_net_device(struct hso_device *hso_dev);
30872dc1c09SGreg Kroah-Hartman static void hso_free_shared_int(struct hso_shared_int *shared_int);
30972dc1c09SGreg Kroah-Hartman static int hso_stop_net_device(struct hso_device *hso_dev);
31072dc1c09SGreg Kroah-Hartman static void hso_serial_ref_free(struct kref *ref);
3118ef5ba63SDenis Joseph Barrow static void hso_std_serial_read_bulk_callback(struct urb *urb);
3128ef5ba63SDenis Joseph Barrow static int hso_mux_serial_read(struct hso_serial *serial);
31372dc1c09SGreg Kroah-Hartman static void async_get_intf(struct work_struct *data);
31472dc1c09SGreg Kroah-Hartman static void async_put_intf(struct work_struct *data);
31572dc1c09SGreg Kroah-Hartman static int hso_put_activity(struct hso_device *hso_dev);
31672dc1c09SGreg Kroah-Hartman static int hso_get_activity(struct hso_device *hso_dev);
317542f5482SDenis Joseph Barrow static void tiocmget_intr_callback(struct urb *urb);
31872dc1c09SGreg Kroah-Hartman /*****************************************************************************/
31972dc1c09SGreg Kroah-Hartman /* Helping functions                                                         */
32072dc1c09SGreg Kroah-Hartman /*****************************************************************************/
32172dc1c09SGreg Kroah-Hartman 
32272dc1c09SGreg Kroah-Hartman /* #define DEBUG */
32372dc1c09SGreg Kroah-Hartman 
dev2net(struct hso_device * hso_dev)3240235f641SGreg Kroah-Hartman static inline struct hso_net *dev2net(struct hso_device *hso_dev)
3250235f641SGreg Kroah-Hartman {
3260235f641SGreg Kroah-Hartman 	return hso_dev->port_data.dev_net;
3270235f641SGreg Kroah-Hartman }
3280235f641SGreg Kroah-Hartman 
dev2ser(struct hso_device * hso_dev)3290235f641SGreg Kroah-Hartman static inline struct hso_serial *dev2ser(struct hso_device *hso_dev)
3300235f641SGreg Kroah-Hartman {
3310235f641SGreg Kroah-Hartman 	return hso_dev->port_data.dev_serial;
3320235f641SGreg Kroah-Hartman }
33372dc1c09SGreg Kroah-Hartman 
33472dc1c09SGreg Kroah-Hartman /* Debugging functions */
33572dc1c09SGreg Kroah-Hartman #ifdef DEBUG
dbg_dump(int line_count,const char * func_name,unsigned char * buf,unsigned int len)33672dc1c09SGreg Kroah-Hartman static void dbg_dump(int line_count, const char *func_name, unsigned char *buf,
33772dc1c09SGreg Kroah-Hartman 		     unsigned int len)
33872dc1c09SGreg Kroah-Hartman {
3390235f641SGreg Kroah-Hartman 	static char name[255];
34072dc1c09SGreg Kroah-Hartman 
3410235f641SGreg Kroah-Hartman 	sprintf(name, "hso[%d:%s]", line_count, func_name);
3420235f641SGreg Kroah-Hartman 	print_hex_dump_bytes(name, DUMP_PREFIX_NONE, buf, len);
34372dc1c09SGreg Kroah-Hartman }
34472dc1c09SGreg Kroah-Hartman 
34572dc1c09SGreg Kroah-Hartman #define DUMP(buf_, len_)	\
3469ce673d5SAntti Kaijanmäki 	dbg_dump(__LINE__, __func__, (unsigned char *)buf_, len_)
34772dc1c09SGreg Kroah-Hartman 
34872dc1c09SGreg Kroah-Hartman #define DUMP1(buf_, len_)			\
34972dc1c09SGreg Kroah-Hartman 	do {					\
35072dc1c09SGreg Kroah-Hartman 		if (0x01 & debug)		\
35172dc1c09SGreg Kroah-Hartman 			DUMP(buf_, len_);	\
35272dc1c09SGreg Kroah-Hartman 	} while (0)
35372dc1c09SGreg Kroah-Hartman #else
35472dc1c09SGreg Kroah-Hartman #define DUMP(buf_, len_)
35572dc1c09SGreg Kroah-Hartman #define DUMP1(buf_, len_)
35672dc1c09SGreg Kroah-Hartman #endif
35772dc1c09SGreg Kroah-Hartman 
35872dc1c09SGreg Kroah-Hartman /* module parameters */
35972dc1c09SGreg Kroah-Hartman static int debug;
36072dc1c09SGreg Kroah-Hartman static int tty_major;
36172dc1c09SGreg Kroah-Hartman static int disable_net;
36272dc1c09SGreg Kroah-Hartman 
36372dc1c09SGreg Kroah-Hartman /* driver info */
36472dc1c09SGreg Kroah-Hartman static const char driver_name[] = "hso";
36572dc1c09SGreg Kroah-Hartman static const char tty_filename[] = "ttyHS";
366242647bcSFilip Aben static const char *version = __FILE__ ": " MOD_AUTHOR;
36772dc1c09SGreg Kroah-Hartman /* the usb driver itself (registered in hso_init) */
36872dc1c09SGreg Kroah-Hartman static struct usb_driver hso_driver;
36972dc1c09SGreg Kroah-Hartman /* serial structures */
37072dc1c09SGreg Kroah-Hartman static struct tty_driver *tty_drv;
37172dc1c09SGreg Kroah-Hartman static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS];
37272dc1c09SGreg Kroah-Hartman static struct hso_device *network_table[HSO_MAX_NET_DEVICES];
373447d871aSZheng Yongjun static DEFINE_SPINLOCK(serial_table_lock);
37472dc1c09SGreg Kroah-Hartman 
37572dc1c09SGreg Kroah-Hartman static const s32 default_port_spec[] = {
37672dc1c09SGreg Kroah-Hartman 	HSO_INTF_MUX | HSO_PORT_NETWORK,
37772dc1c09SGreg Kroah-Hartman 	HSO_INTF_BULK | HSO_PORT_DIAG,
37872dc1c09SGreg Kroah-Hartman 	HSO_INTF_BULK | HSO_PORT_MODEM,
37972dc1c09SGreg Kroah-Hartman 	0
38072dc1c09SGreg Kroah-Hartman };
38172dc1c09SGreg Kroah-Hartman 
38272dc1c09SGreg Kroah-Hartman static const s32 icon321_port_spec[] = {
38372dc1c09SGreg Kroah-Hartman 	HSO_INTF_MUX | HSO_PORT_NETWORK,
38472dc1c09SGreg Kroah-Hartman 	HSO_INTF_BULK | HSO_PORT_DIAG2,
38572dc1c09SGreg Kroah-Hartman 	HSO_INTF_BULK | HSO_PORT_MODEM,
38672dc1c09SGreg Kroah-Hartman 	HSO_INTF_BULK | HSO_PORT_DIAG,
38772dc1c09SGreg Kroah-Hartman 	0
38872dc1c09SGreg Kroah-Hartman };
38972dc1c09SGreg Kroah-Hartman 
39072dc1c09SGreg Kroah-Hartman #define default_port_device(vendor, product)	\
39172dc1c09SGreg Kroah-Hartman 	USB_DEVICE(vendor, product),	\
39272dc1c09SGreg Kroah-Hartman 		.driver_info = (kernel_ulong_t)default_port_spec
39372dc1c09SGreg Kroah-Hartman 
39472dc1c09SGreg Kroah-Hartman #define icon321_port_device(vendor, product)	\
39572dc1c09SGreg Kroah-Hartman 	USB_DEVICE(vendor, product),	\
39672dc1c09SGreg Kroah-Hartman 		.driver_info = (kernel_ulong_t)icon321_port_spec
39772dc1c09SGreg Kroah-Hartman 
39872dc1c09SGreg Kroah-Hartman /* list of devices we support */
39972dc1c09SGreg Kroah-Hartman static const struct usb_device_id hso_ids[] = {
40072dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6711)},
40172dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6731)},
40272dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6751)},
40372dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6771)},
40472dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6791)},
40572dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6811)},
40672dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6911)},
40772dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6951)},
40872dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x6971)},
40972dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7011)},
41072dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7031)},
41172dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7051)},
41272dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7071)},
41372dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7111)},
41472dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7211)},
41572dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7251)},
41672dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7271)},
41772dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0x7311)},
41872dc1c09SGreg Kroah-Hartman 	{default_port_device(0x0af0, 0xc031)},	/* Icon-Edge */
41972dc1c09SGreg Kroah-Hartman 	{icon321_port_device(0x0af0, 0xd013)},	/* Module HSxPA */
42072dc1c09SGreg Kroah-Hartman 	{icon321_port_device(0x0af0, 0xd031)},	/* Icon-321 */
42195eacee8SDenis Joseph Barrow 	{icon321_port_device(0x0af0, 0xd033)},	/* Icon-322 */
42272dc1c09SGreg Kroah-Hartman 	{USB_DEVICE(0x0af0, 0x7301)},		/* GE40x */
42372dc1c09SGreg Kroah-Hartman 	{USB_DEVICE(0x0af0, 0x7361)},		/* GE40x */
42467dd8246SFilip Aben 	{USB_DEVICE(0x0af0, 0x7381)},		/* GE40x */
42572dc1c09SGreg Kroah-Hartman 	{USB_DEVICE(0x0af0, 0x7401)},		/* GI 0401 */
42672dc1c09SGreg Kroah-Hartman 	{USB_DEVICE(0x0af0, 0x7501)},		/* GTM 382 */
42772dc1c09SGreg Kroah-Hartman 	{USB_DEVICE(0x0af0, 0x7601)},		/* GE40x */
428bab04c3aSDenis Joseph Barrow 	{USB_DEVICE(0x0af0, 0x7701)},
429ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0x7706)},
430bab04c3aSDenis Joseph Barrow 	{USB_DEVICE(0x0af0, 0x7801)},
431bab04c3aSDenis Joseph Barrow 	{USB_DEVICE(0x0af0, 0x7901)},
432ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0x7A01)},
433ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0x7A05)},
4349961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0x8200)},
4359961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0x8201)},
436ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0x8300)},
437ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0x8302)},
438ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0x8304)},
439ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0x8400)},
440dd7496f2SFilip Aben 	{USB_DEVICE(0x0af0, 0x8600)},
441dd7496f2SFilip Aben 	{USB_DEVICE(0x0af0, 0x8800)},
442dd7496f2SFilip Aben 	{USB_DEVICE(0x0af0, 0x8900)},
4435c7bf2f4SFilip Aben 	{USB_DEVICE(0x0af0, 0x9000)},
44461ab9efdSRicardo Ribalda 	{USB_DEVICE(0x0af0, 0x9200)},		/* Option GTM671WFS */
4459961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0xd035)},
44667dd8246SFilip Aben 	{USB_DEVICE(0x0af0, 0xd055)},
4479961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0xd155)},
4489961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0xd255)},
4499961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0xd057)},
4509961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0xd157)},
4519961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0xd257)},
4529961d842SJan Dumon 	{USB_DEVICE(0x0af0, 0xd357)},
453ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0xd058)},
454ec157937SJan Dumon 	{USB_DEVICE(0x0af0, 0xc100)},
45572dc1c09SGreg Kroah-Hartman 	{}
45672dc1c09SGreg Kroah-Hartman };
45772dc1c09SGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, hso_ids);
45872dc1c09SGreg Kroah-Hartman 
45972dc1c09SGreg Kroah-Hartman /* Sysfs attribute */
hsotype_show(struct device * dev,struct device_attribute * attr,char * buf)4607567d603SYueHaibing static ssize_t hsotype_show(struct device *dev,
4617567d603SYueHaibing 			    struct device_attribute *attr, char *buf)
46272dc1c09SGreg Kroah-Hartman {
4631aec5bdfSGreg Kroah-Hartman 	struct hso_device *hso_dev = dev_get_drvdata(dev);
46472dc1c09SGreg Kroah-Hartman 	char *port_name;
46572dc1c09SGreg Kroah-Hartman 
46672dc1c09SGreg Kroah-Hartman 	if (!hso_dev)
46772dc1c09SGreg Kroah-Hartman 		return 0;
46872dc1c09SGreg Kroah-Hartman 
46972dc1c09SGreg Kroah-Hartman 	switch (hso_dev->port_spec & HSO_PORT_MASK) {
47072dc1c09SGreg Kroah-Hartman 	case HSO_PORT_CONTROL:
47172dc1c09SGreg Kroah-Hartman 		port_name = "Control";
47272dc1c09SGreg Kroah-Hartman 		break;
47372dc1c09SGreg Kroah-Hartman 	case HSO_PORT_APP:
47472dc1c09SGreg Kroah-Hartman 		port_name = "Application";
47572dc1c09SGreg Kroah-Hartman 		break;
47672dc1c09SGreg Kroah-Hartman 	case HSO_PORT_APP2:
47772dc1c09SGreg Kroah-Hartman 		port_name = "Application2";
47872dc1c09SGreg Kroah-Hartman 		break;
47972dc1c09SGreg Kroah-Hartman 	case HSO_PORT_GPS:
48072dc1c09SGreg Kroah-Hartman 		port_name = "GPS";
48172dc1c09SGreg Kroah-Hartman 		break;
48272dc1c09SGreg Kroah-Hartman 	case HSO_PORT_GPS_CONTROL:
48372dc1c09SGreg Kroah-Hartman 		port_name = "GPS Control";
48472dc1c09SGreg Kroah-Hartman 		break;
48572dc1c09SGreg Kroah-Hartman 	case HSO_PORT_PCSC:
48672dc1c09SGreg Kroah-Hartman 		port_name = "PCSC";
48772dc1c09SGreg Kroah-Hartman 		break;
48872dc1c09SGreg Kroah-Hartman 	case HSO_PORT_DIAG:
48972dc1c09SGreg Kroah-Hartman 		port_name = "Diagnostic";
49072dc1c09SGreg Kroah-Hartman 		break;
49172dc1c09SGreg Kroah-Hartman 	case HSO_PORT_DIAG2:
49272dc1c09SGreg Kroah-Hartman 		port_name = "Diagnostic2";
49372dc1c09SGreg Kroah-Hartman 		break;
49472dc1c09SGreg Kroah-Hartman 	case HSO_PORT_MODEM:
49572dc1c09SGreg Kroah-Hartman 		port_name = "Modem";
49672dc1c09SGreg Kroah-Hartman 		break;
49772dc1c09SGreg Kroah-Hartman 	case HSO_PORT_NETWORK:
49872dc1c09SGreg Kroah-Hartman 		port_name = "Network";
49972dc1c09SGreg Kroah-Hartman 		break;
50072dc1c09SGreg Kroah-Hartman 	default:
50172dc1c09SGreg Kroah-Hartman 		port_name = "Unknown";
50272dc1c09SGreg Kroah-Hartman 		break;
50372dc1c09SGreg Kroah-Hartman 	}
50472dc1c09SGreg Kroah-Hartman 
50572dc1c09SGreg Kroah-Hartman 	return sprintf(buf, "%s\n", port_name);
50672dc1c09SGreg Kroah-Hartman }
5077567d603SYueHaibing static DEVICE_ATTR_RO(hsotype);
50872dc1c09SGreg Kroah-Hartman 
5094134069fSTakashi Iwai static struct attribute *hso_serial_dev_attrs[] = {
5104134069fSTakashi Iwai 	&dev_attr_hsotype.attr,
5114134069fSTakashi Iwai 	NULL
5124134069fSTakashi Iwai };
5134134069fSTakashi Iwai 
5144134069fSTakashi Iwai ATTRIBUTE_GROUPS(hso_serial_dev);
5154134069fSTakashi Iwai 
hso_urb_to_index(struct hso_serial * serial,struct urb * urb)5168ef5ba63SDenis Joseph Barrow static int hso_urb_to_index(struct hso_serial *serial, struct urb *urb)
5178ef5ba63SDenis Joseph Barrow {
5188ef5ba63SDenis Joseph Barrow 	int idx;
5198ef5ba63SDenis Joseph Barrow 
5208ef5ba63SDenis Joseph Barrow 	for (idx = 0; idx < serial->num_rx_urbs; idx++)
5218ef5ba63SDenis Joseph Barrow 		if (serial->rx_urb[idx] == urb)
5228ef5ba63SDenis Joseph Barrow 			return idx;
5238ef5ba63SDenis Joseph Barrow 	dev_err(serial->parent->dev, "hso_urb_to_index failed\n");
5248ef5ba63SDenis Joseph Barrow 	return -1;
5258ef5ba63SDenis Joseph Barrow }
5268ef5ba63SDenis Joseph Barrow 
52772dc1c09SGreg Kroah-Hartman /* converts mux value to a port spec value */
hso_mux_to_port(int mux)52872dc1c09SGreg Kroah-Hartman static u32 hso_mux_to_port(int mux)
52972dc1c09SGreg Kroah-Hartman {
53072dc1c09SGreg Kroah-Hartman 	u32 result;
53172dc1c09SGreg Kroah-Hartman 
53272dc1c09SGreg Kroah-Hartman 	switch (mux) {
53372dc1c09SGreg Kroah-Hartman 	case 0x1:
53472dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_CONTROL;
53572dc1c09SGreg Kroah-Hartman 		break;
53672dc1c09SGreg Kroah-Hartman 	case 0x2:
53772dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_APP;
53872dc1c09SGreg Kroah-Hartman 		break;
53972dc1c09SGreg Kroah-Hartman 	case 0x4:
54072dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_PCSC;
54172dc1c09SGreg Kroah-Hartman 		break;
54272dc1c09SGreg Kroah-Hartman 	case 0x8:
54372dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_GPS;
54472dc1c09SGreg Kroah-Hartman 		break;
54572dc1c09SGreg Kroah-Hartman 	case 0x10:
54672dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_APP2;
54772dc1c09SGreg Kroah-Hartman 		break;
54872dc1c09SGreg Kroah-Hartman 	default:
54972dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_NO_PORT;
55072dc1c09SGreg Kroah-Hartman 	}
55172dc1c09SGreg Kroah-Hartman 	return result;
55272dc1c09SGreg Kroah-Hartman }
55372dc1c09SGreg Kroah-Hartman 
55472dc1c09SGreg Kroah-Hartman /* converts port spec value to a mux value */
hso_port_to_mux(int port)55572dc1c09SGreg Kroah-Hartman static u32 hso_port_to_mux(int port)
55672dc1c09SGreg Kroah-Hartman {
55772dc1c09SGreg Kroah-Hartman 	u32 result;
55872dc1c09SGreg Kroah-Hartman 
55972dc1c09SGreg Kroah-Hartman 	switch (port & HSO_PORT_MASK) {
56072dc1c09SGreg Kroah-Hartman 	case HSO_PORT_CONTROL:
56172dc1c09SGreg Kroah-Hartman 		result = 0x0;
56272dc1c09SGreg Kroah-Hartman 		break;
56372dc1c09SGreg Kroah-Hartman 	case HSO_PORT_APP:
56472dc1c09SGreg Kroah-Hartman 		result = 0x1;
56572dc1c09SGreg Kroah-Hartman 		break;
56672dc1c09SGreg Kroah-Hartman 	case HSO_PORT_PCSC:
56772dc1c09SGreg Kroah-Hartman 		result = 0x2;
56872dc1c09SGreg Kroah-Hartman 		break;
56972dc1c09SGreg Kroah-Hartman 	case HSO_PORT_GPS:
57072dc1c09SGreg Kroah-Hartman 		result = 0x3;
57172dc1c09SGreg Kroah-Hartman 		break;
57272dc1c09SGreg Kroah-Hartman 	case HSO_PORT_APP2:
57372dc1c09SGreg Kroah-Hartman 		result = 0x4;
57472dc1c09SGreg Kroah-Hartman 		break;
57572dc1c09SGreg Kroah-Hartman 	default:
57672dc1c09SGreg Kroah-Hartman 		result = 0x0;
57772dc1c09SGreg Kroah-Hartman 	}
57872dc1c09SGreg Kroah-Hartman 	return result;
57972dc1c09SGreg Kroah-Hartman }
58072dc1c09SGreg Kroah-Hartman 
get_serial_by_shared_int_and_type(struct hso_shared_int * shared_int,int mux)58172dc1c09SGreg Kroah-Hartman static struct hso_serial *get_serial_by_shared_int_and_type(
58272dc1c09SGreg Kroah-Hartman 					struct hso_shared_int *shared_int,
58372dc1c09SGreg Kroah-Hartman 					int mux)
58472dc1c09SGreg Kroah-Hartman {
58572dc1c09SGreg Kroah-Hartman 	int i, port;
58672dc1c09SGreg Kroah-Hartman 
58772dc1c09SGreg Kroah-Hartman 	port = hso_mux_to_port(mux);
58872dc1c09SGreg Kroah-Hartman 
58972dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
5908e95a202SJoe Perches 		if (serial_table[i] &&
5918e95a202SJoe Perches 		    (dev2ser(serial_table[i])->shared_int == shared_int) &&
5928e95a202SJoe Perches 		    ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) {
59372dc1c09SGreg Kroah-Hartman 			return dev2ser(serial_table[i]);
59472dc1c09SGreg Kroah-Hartman 		}
59572dc1c09SGreg Kroah-Hartman 	}
59672dc1c09SGreg Kroah-Hartman 
59772dc1c09SGreg Kroah-Hartman 	return NULL;
59872dc1c09SGreg Kroah-Hartman }
59972dc1c09SGreg Kroah-Hartman 
get_serial_by_index(unsigned index)60072dc1c09SGreg Kroah-Hartman static struct hso_serial *get_serial_by_index(unsigned index)
60172dc1c09SGreg Kroah-Hartman {
6020235f641SGreg Kroah-Hartman 	struct hso_serial *serial = NULL;
60372dc1c09SGreg Kroah-Hartman 	unsigned long flags;
60472dc1c09SGreg Kroah-Hartman 
60572dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial_table_lock, flags);
6060235f641SGreg Kroah-Hartman 	if (serial_table[index])
60772dc1c09SGreg Kroah-Hartman 		serial = dev2ser(serial_table[index]);
60872dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial_table_lock, flags);
60972dc1c09SGreg Kroah-Hartman 
61072dc1c09SGreg Kroah-Hartman 	return serial;
61172dc1c09SGreg Kroah-Hartman }
61272dc1c09SGreg Kroah-Hartman 
obtain_minor(struct hso_serial * serial)6138a12f883SAnirudh Rayabharam static int obtain_minor(struct hso_serial *serial)
61472dc1c09SGreg Kroah-Hartman {
61572dc1c09SGreg Kroah-Hartman 	int index;
61672dc1c09SGreg Kroah-Hartman 	unsigned long flags;
61772dc1c09SGreg Kroah-Hartman 
61872dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial_table_lock, flags);
61972dc1c09SGreg Kroah-Hartman 	for (index = 0; index < HSO_SERIAL_TTY_MINORS; index++) {
62072dc1c09SGreg Kroah-Hartman 		if (serial_table[index] == NULL) {
6218a12f883SAnirudh Rayabharam 			serial_table[index] = serial->parent;
6228a12f883SAnirudh Rayabharam 			serial->minor = index;
62372dc1c09SGreg Kroah-Hartman 			spin_unlock_irqrestore(&serial_table_lock, flags);
6248a12f883SAnirudh Rayabharam 			return 0;
62572dc1c09SGreg Kroah-Hartman 		}
62672dc1c09SGreg Kroah-Hartman 	}
62772dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial_table_lock, flags);
62872dc1c09SGreg Kroah-Hartman 
6293981cce6SJoe Perches 	pr_err("%s: no free serial devices in table\n", __func__);
63072dc1c09SGreg Kroah-Hartman 	return -1;
63172dc1c09SGreg Kroah-Hartman }
63272dc1c09SGreg Kroah-Hartman 
release_minor(struct hso_serial * serial)6338a12f883SAnirudh Rayabharam static void release_minor(struct hso_serial *serial)
63472dc1c09SGreg Kroah-Hartman {
63572dc1c09SGreg Kroah-Hartman 	unsigned long flags;
6360235f641SGreg Kroah-Hartman 
63772dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial_table_lock, flags);
6388a12f883SAnirudh Rayabharam 	serial_table[serial->minor] = NULL;
63972dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial_table_lock, flags);
64072dc1c09SGreg Kroah-Hartman }
64172dc1c09SGreg Kroah-Hartman 
handle_usb_error(int status,const char * function,struct hso_device * hso_dev)64268a351c5SJan Dumon static void handle_usb_error(int status, const char *function,
64368a351c5SJan Dumon 			     struct hso_device *hso_dev)
64472dc1c09SGreg Kroah-Hartman {
64572dc1c09SGreg Kroah-Hartman 	char *explanation;
64672dc1c09SGreg Kroah-Hartman 
64772dc1c09SGreg Kroah-Hartman 	switch (status) {
64872dc1c09SGreg Kroah-Hartman 	case -ENODEV:
64972dc1c09SGreg Kroah-Hartman 		explanation = "no device";
65072dc1c09SGreg Kroah-Hartman 		break;
65172dc1c09SGreg Kroah-Hartman 	case -ENOENT:
65272dc1c09SGreg Kroah-Hartman 		explanation = "endpoint not enabled";
65372dc1c09SGreg Kroah-Hartman 		break;
65472dc1c09SGreg Kroah-Hartman 	case -EPIPE:
65572dc1c09SGreg Kroah-Hartman 		explanation = "endpoint stalled";
65672dc1c09SGreg Kroah-Hartman 		break;
65772dc1c09SGreg Kroah-Hartman 	case -ENOSPC:
65872dc1c09SGreg Kroah-Hartman 		explanation = "not enough bandwidth";
65972dc1c09SGreg Kroah-Hartman 		break;
66072dc1c09SGreg Kroah-Hartman 	case -ESHUTDOWN:
66172dc1c09SGreg Kroah-Hartman 		explanation = "device disabled";
66272dc1c09SGreg Kroah-Hartman 		break;
66372dc1c09SGreg Kroah-Hartman 	case -EHOSTUNREACH:
66472dc1c09SGreg Kroah-Hartman 		explanation = "device suspended";
66572dc1c09SGreg Kroah-Hartman 		break;
66672dc1c09SGreg Kroah-Hartman 	case -EINVAL:
66772dc1c09SGreg Kroah-Hartman 	case -EAGAIN:
66872dc1c09SGreg Kroah-Hartman 	case -EFBIG:
66972dc1c09SGreg Kroah-Hartman 	case -EMSGSIZE:
67072dc1c09SGreg Kroah-Hartman 		explanation = "internal error";
67172dc1c09SGreg Kroah-Hartman 		break;
67268a351c5SJan Dumon 	case -EILSEQ:
67368a351c5SJan Dumon 	case -EPROTO:
67468a351c5SJan Dumon 	case -ETIME:
67568a351c5SJan Dumon 	case -ETIMEDOUT:
67668a351c5SJan Dumon 		explanation = "protocol error";
67768a351c5SJan Dumon 		if (hso_dev)
67826c1f1f5SOlivier Sobrie 			usb_queue_reset_device(hso_dev->interface);
67968a351c5SJan Dumon 		break;
68072dc1c09SGreg Kroah-Hartman 	default:
68172dc1c09SGreg Kroah-Hartman 		explanation = "unknown status";
68272dc1c09SGreg Kroah-Hartman 		break;
68372dc1c09SGreg Kroah-Hartman 	}
68468a351c5SJan Dumon 
68568a351c5SJan Dumon 	/* log a meaningful explanation of an USB status */
68695a69117SJoe Perches 	hso_dbg(0x1, "%s: received USB status - %s (%d)\n",
68795a69117SJoe Perches 		function, explanation, status);
68872dc1c09SGreg Kroah-Hartman }
68972dc1c09SGreg Kroah-Hartman 
69072dc1c09SGreg Kroah-Hartman /* Network interface functions */
69172dc1c09SGreg Kroah-Hartman 
69272dc1c09SGreg Kroah-Hartman /* called when net interface is brought up by ifconfig */
hso_net_open(struct net_device * net)69372dc1c09SGreg Kroah-Hartman static int hso_net_open(struct net_device *net)
69472dc1c09SGreg Kroah-Hartman {
69572dc1c09SGreg Kroah-Hartman 	struct hso_net *odev = netdev_priv(net);
69672dc1c09SGreg Kroah-Hartman 	unsigned long flags = 0;
69772dc1c09SGreg Kroah-Hartman 
69872dc1c09SGreg Kroah-Hartman 	if (!odev) {
69972dc1c09SGreg Kroah-Hartman 		dev_err(&net->dev, "No net device !\n");
70072dc1c09SGreg Kroah-Hartman 		return -ENODEV;
70172dc1c09SGreg Kroah-Hartman 	}
70272dc1c09SGreg Kroah-Hartman 
70372dc1c09SGreg Kroah-Hartman 	odev->skb_tx_buf = NULL;
70472dc1c09SGreg Kroah-Hartman 
70572dc1c09SGreg Kroah-Hartman 	/* setup environment */
70672dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&odev->net_lock, flags);
70772dc1c09SGreg Kroah-Hartman 	odev->rx_parse_state = WAIT_IP;
70872dc1c09SGreg Kroah-Hartman 	odev->rx_buf_size = 0;
70972dc1c09SGreg Kroah-Hartman 	odev->rx_buf_missing = sizeof(struct iphdr);
71072dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&odev->net_lock, flags);
71172dc1c09SGreg Kroah-Hartman 
71272dc1c09SGreg Kroah-Hartman 	/* We are up and running. */
71372dc1c09SGreg Kroah-Hartman 	set_bit(HSO_NET_RUNNING, &odev->flags);
714889bd9b6SOliver Neukum 	hso_start_net_device(odev->parent);
71572dc1c09SGreg Kroah-Hartman 
71672dc1c09SGreg Kroah-Hartman 	/* Tell the kernel we are ready to start receiving from it */
71772dc1c09SGreg Kroah-Hartman 	netif_start_queue(net);
71872dc1c09SGreg Kroah-Hartman 
71972dc1c09SGreg Kroah-Hartman 	return 0;
72072dc1c09SGreg Kroah-Hartman }
72172dc1c09SGreg Kroah-Hartman 
72272dc1c09SGreg Kroah-Hartman /* called when interface is brought down by ifconfig */
hso_net_close(struct net_device * net)72372dc1c09SGreg Kroah-Hartman static int hso_net_close(struct net_device *net)
72472dc1c09SGreg Kroah-Hartman {
72572dc1c09SGreg Kroah-Hartman 	struct hso_net *odev = netdev_priv(net);
72672dc1c09SGreg Kroah-Hartman 
72772dc1c09SGreg Kroah-Hartman 	/* we don't need the queue anymore */
72872dc1c09SGreg Kroah-Hartman 	netif_stop_queue(net);
72972dc1c09SGreg Kroah-Hartman 	/* no longer running */
73072dc1c09SGreg Kroah-Hartman 	clear_bit(HSO_NET_RUNNING, &odev->flags);
73172dc1c09SGreg Kroah-Hartman 
73272dc1c09SGreg Kroah-Hartman 	hso_stop_net_device(odev->parent);
73372dc1c09SGreg Kroah-Hartman 
73472dc1c09SGreg Kroah-Hartman 	/* done */
73572dc1c09SGreg Kroah-Hartman 	return 0;
73672dc1c09SGreg Kroah-Hartman }
73772dc1c09SGreg Kroah-Hartman 
73872dc1c09SGreg Kroah-Hartman /* USB tells is xmit done, we should start the netqueue again */
write_bulk_callback(struct urb * urb)73972dc1c09SGreg Kroah-Hartman static void write_bulk_callback(struct urb *urb)
74072dc1c09SGreg Kroah-Hartman {
74172dc1c09SGreg Kroah-Hartman 	struct hso_net *odev = urb->context;
74272dc1c09SGreg Kroah-Hartman 	int status = urb->status;
74372dc1c09SGreg Kroah-Hartman 
74472dc1c09SGreg Kroah-Hartman 	/* Sanity check */
74572dc1c09SGreg Kroah-Hartman 	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
74672dc1c09SGreg Kroah-Hartman 		dev_err(&urb->dev->dev, "%s: device not running\n", __func__);
74772dc1c09SGreg Kroah-Hartman 		return;
74872dc1c09SGreg Kroah-Hartman 	}
74972dc1c09SGreg Kroah-Hartman 
75072dc1c09SGreg Kroah-Hartman 	/* Do we still have a valid kernel network device? */
75172dc1c09SGreg Kroah-Hartman 	if (!netif_device_present(odev->net)) {
75272dc1c09SGreg Kroah-Hartman 		dev_err(&urb->dev->dev, "%s: net device not present\n",
75372dc1c09SGreg Kroah-Hartman 			__func__);
75472dc1c09SGreg Kroah-Hartman 		return;
75572dc1c09SGreg Kroah-Hartman 	}
75672dc1c09SGreg Kroah-Hartman 
75772dc1c09SGreg Kroah-Hartman 	/* log status, but don't act on it, we don't need to resubmit anything
75872dc1c09SGreg Kroah-Hartman 	 * anyhow */
75972dc1c09SGreg Kroah-Hartman 	if (status)
76068a351c5SJan Dumon 		handle_usb_error(status, __func__, odev->parent);
76172dc1c09SGreg Kroah-Hartman 
76272dc1c09SGreg Kroah-Hartman 	hso_put_activity(odev->parent);
76372dc1c09SGreg Kroah-Hartman 
76472dc1c09SGreg Kroah-Hartman 	/* Tell the network interface we are ready for another frame */
76572dc1c09SGreg Kroah-Hartman 	netif_wake_queue(odev->net);
76672dc1c09SGreg Kroah-Hartman }
76772dc1c09SGreg Kroah-Hartman 
76872dc1c09SGreg Kroah-Hartman /* called by kernel when we need to transmit a packet */
hso_net_start_xmit(struct sk_buff * skb,struct net_device * net)76925a79c41SStephen Hemminger static netdev_tx_t hso_net_start_xmit(struct sk_buff *skb,
77025a79c41SStephen Hemminger 					    struct net_device *net)
77172dc1c09SGreg Kroah-Hartman {
77272dc1c09SGreg Kroah-Hartman 	struct hso_net *odev = netdev_priv(net);
77372dc1c09SGreg Kroah-Hartman 	int result;
77472dc1c09SGreg Kroah-Hartman 
77572dc1c09SGreg Kroah-Hartman 	/* Tell the kernel, "No more frames 'til we are done with this one." */
77672dc1c09SGreg Kroah-Hartman 	netif_stop_queue(net);
77772dc1c09SGreg Kroah-Hartman 	if (hso_get_activity(odev->parent) == -EAGAIN) {
77872dc1c09SGreg Kroah-Hartman 		odev->skb_tx_buf = skb;
7796ed10654SPatrick McHardy 		return NETDEV_TX_OK;
78072dc1c09SGreg Kroah-Hartman 	}
78172dc1c09SGreg Kroah-Hartman 
78272dc1c09SGreg Kroah-Hartman 	/* log if asked */
78372dc1c09SGreg Kroah-Hartman 	DUMP1(skb->data, skb->len);
78472dc1c09SGreg Kroah-Hartman 	/* Copy it from kernel memory to OUR memory */
78572dc1c09SGreg Kroah-Hartman 	memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len);
78695a69117SJoe Perches 	hso_dbg(0x1, "len: %d/%d\n", skb->len, MUX_BULK_TX_BUF_SIZE);
78772dc1c09SGreg Kroah-Hartman 
78872dc1c09SGreg Kroah-Hartman 	/* Fill in the URB for shipping it out. */
78972dc1c09SGreg Kroah-Hartman 	usb_fill_bulk_urb(odev->mux_bulk_tx_urb,
79072dc1c09SGreg Kroah-Hartman 			  odev->parent->usb,
79172dc1c09SGreg Kroah-Hartman 			  usb_sndbulkpipe(odev->parent->usb,
79272dc1c09SGreg Kroah-Hartman 					  odev->out_endp->
79372dc1c09SGreg Kroah-Hartman 					  bEndpointAddress & 0x7F),
79472dc1c09SGreg Kroah-Hartman 			  odev->mux_bulk_tx_buf, skb->len, write_bulk_callback,
79572dc1c09SGreg Kroah-Hartman 			  odev);
79672dc1c09SGreg Kroah-Hartman 
79772dc1c09SGreg Kroah-Hartman 	/* Deal with the Zero Length packet problem, I hope */
79872dc1c09SGreg Kroah-Hartman 	odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET;
79972dc1c09SGreg Kroah-Hartman 
80072dc1c09SGreg Kroah-Hartman 	/* Send the URB on its merry way. */
80172dc1c09SGreg Kroah-Hartman 	result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC);
80272dc1c09SGreg Kroah-Hartman 	if (result) {
80372dc1c09SGreg Kroah-Hartman 		dev_warn(&odev->parent->interface->dev,
8048a5c9c49SJan Dumon 			"failed mux_bulk_tx_urb %d\n", result);
80572dc1c09SGreg Kroah-Hartman 		net->stats.tx_errors++;
80672dc1c09SGreg Kroah-Hartman 		netif_start_queue(net);
80772dc1c09SGreg Kroah-Hartman 	} else {
80872dc1c09SGreg Kroah-Hartman 		net->stats.tx_packets++;
80972dc1c09SGreg Kroah-Hartman 		net->stats.tx_bytes += skb->len;
81072dc1c09SGreg Kroah-Hartman 	}
81172dc1c09SGreg Kroah-Hartman 	dev_kfree_skb(skb);
81272dc1c09SGreg Kroah-Hartman 	/* we're done */
8135b2c4b97SPatrick McHardy 	return NETDEV_TX_OK;
81472dc1c09SGreg Kroah-Hartman }
81572dc1c09SGreg Kroah-Hartman 
8160fc0b732SStephen Hemminger static const struct ethtool_ops ops = {
81772dc1c09SGreg Kroah-Hartman 	.get_link = ethtool_op_get_link
81872dc1c09SGreg Kroah-Hartman };
81972dc1c09SGreg Kroah-Hartman 
82072dc1c09SGreg Kroah-Hartman /* called when a packet did not ack after watchdogtimeout */
hso_net_tx_timeout(struct net_device * net,unsigned int txqueue)8210290bd29SMichael S. Tsirkin static void hso_net_tx_timeout(struct net_device *net, unsigned int txqueue)
82272dc1c09SGreg Kroah-Hartman {
82372dc1c09SGreg Kroah-Hartman 	struct hso_net *odev = netdev_priv(net);
82472dc1c09SGreg Kroah-Hartman 
82572dc1c09SGreg Kroah-Hartman 	if (!odev)
82672dc1c09SGreg Kroah-Hartman 		return;
82772dc1c09SGreg Kroah-Hartman 
82872dc1c09SGreg Kroah-Hartman 	/* Tell syslog we are hosed. */
82972dc1c09SGreg Kroah-Hartman 	dev_warn(&net->dev, "Tx timed out.\n");
83072dc1c09SGreg Kroah-Hartman 
83172dc1c09SGreg Kroah-Hartman 	/* Tear the waiting frame off the list */
832abaf00ffSOliver Neukum 	if (odev->mux_bulk_tx_urb)
83372dc1c09SGreg Kroah-Hartman 		usb_unlink_urb(odev->mux_bulk_tx_urb);
83472dc1c09SGreg Kroah-Hartman 
83572dc1c09SGreg Kroah-Hartman 	/* Update statistics */
83672dc1c09SGreg Kroah-Hartman 	net->stats.tx_errors++;
83772dc1c09SGreg Kroah-Hartman }
83872dc1c09SGreg Kroah-Hartman 
83972dc1c09SGreg Kroah-Hartman /* make a real packet from the received USB buffer */
packetizeRx(struct hso_net * odev,unsigned char * ip_pkt,unsigned int count,unsigned char is_eop)84072dc1c09SGreg Kroah-Hartman static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,
84172dc1c09SGreg Kroah-Hartman 			unsigned int count, unsigned char is_eop)
84272dc1c09SGreg Kroah-Hartman {
84372dc1c09SGreg Kroah-Hartman 	unsigned short temp_bytes;
84472dc1c09SGreg Kroah-Hartman 	unsigned short buffer_offset = 0;
84572dc1c09SGreg Kroah-Hartman 	unsigned short frame_len;
84672dc1c09SGreg Kroah-Hartman 
84772dc1c09SGreg Kroah-Hartman 	/* log if needed */
84895a69117SJoe Perches 	hso_dbg(0x1, "Rx %d bytes\n", count);
84972dc1c09SGreg Kroah-Hartman 	DUMP(ip_pkt, min(128, (int)count));
85072dc1c09SGreg Kroah-Hartman 
85172dc1c09SGreg Kroah-Hartman 	while (count) {
85272dc1c09SGreg Kroah-Hartman 		switch (odev->rx_parse_state) {
85372dc1c09SGreg Kroah-Hartman 		case WAIT_IP:
85472dc1c09SGreg Kroah-Hartman 			/* waiting for IP header. */
85572dc1c09SGreg Kroah-Hartman 			/* wanted bytes - size of ip header */
85672dc1c09SGreg Kroah-Hartman 			temp_bytes =
85772dc1c09SGreg Kroah-Hartman 			    (count <
85872dc1c09SGreg Kroah-Hartman 			     odev->rx_buf_missing) ? count : odev->
85972dc1c09SGreg Kroah-Hartman 			    rx_buf_missing;
86072dc1c09SGreg Kroah-Hartman 
86172dc1c09SGreg Kroah-Hartman 			memcpy(((unsigned char *)(&odev->rx_ip_hdr)) +
86272dc1c09SGreg Kroah-Hartman 			       odev->rx_buf_size, ip_pkt + buffer_offset,
86372dc1c09SGreg Kroah-Hartman 			       temp_bytes);
86472dc1c09SGreg Kroah-Hartman 
86572dc1c09SGreg Kroah-Hartman 			odev->rx_buf_size += temp_bytes;
86672dc1c09SGreg Kroah-Hartman 			buffer_offset += temp_bytes;
86772dc1c09SGreg Kroah-Hartman 			odev->rx_buf_missing -= temp_bytes;
86872dc1c09SGreg Kroah-Hartman 			count -= temp_bytes;
86972dc1c09SGreg Kroah-Hartman 
87072dc1c09SGreg Kroah-Hartman 			if (!odev->rx_buf_missing) {
87172dc1c09SGreg Kroah-Hartman 				/* header is complete allocate an sk_buffer and
87272dc1c09SGreg Kroah-Hartman 				 * continue to WAIT_DATA */
87372dc1c09SGreg Kroah-Hartman 				frame_len = ntohs(odev->rx_ip_hdr.tot_len);
87472dc1c09SGreg Kroah-Hartman 
87572dc1c09SGreg Kroah-Hartman 				if ((frame_len > DEFAULT_MRU) ||
87672dc1c09SGreg Kroah-Hartman 				    (frame_len < sizeof(struct iphdr))) {
87772dc1c09SGreg Kroah-Hartman 					dev_err(&odev->net->dev,
87872dc1c09SGreg Kroah-Hartman 						"Invalid frame (%d) length\n",
87972dc1c09SGreg Kroah-Hartman 						frame_len);
88072dc1c09SGreg Kroah-Hartman 					odev->rx_parse_state = WAIT_SYNC;
88172dc1c09SGreg Kroah-Hartman 					continue;
88272dc1c09SGreg Kroah-Hartman 				}
88372dc1c09SGreg Kroah-Hartman 				/* Allocate an sk_buff */
884d65a68a8SPaulius Zaleckas 				odev->skb_rx_buf = netdev_alloc_skb(odev->net,
885d65a68a8SPaulius Zaleckas 								    frame_len);
88672dc1c09SGreg Kroah-Hartman 				if (!odev->skb_rx_buf) {
88772dc1c09SGreg Kroah-Hartman 					/* We got no receive buffer. */
88895a69117SJoe Perches 					hso_dbg(0x1, "could not allocate memory\n");
88972dc1c09SGreg Kroah-Hartman 					odev->rx_parse_state = WAIT_SYNC;
89004166055SAleksander Morgado 					continue;
89172dc1c09SGreg Kroah-Hartman 				}
89272dc1c09SGreg Kroah-Hartman 
89372dc1c09SGreg Kroah-Hartman 				/* Copy what we got so far. make room for iphdr
89472dc1c09SGreg Kroah-Hartman 				 * after tail. */
895b952f4dfSyuan linyu 				skb_put_data(odev->skb_rx_buf,
89659ae1d12SJohannes Berg 					     (char *)&(odev->rx_ip_hdr),
89772dc1c09SGreg Kroah-Hartman 					     sizeof(struct iphdr));
89872dc1c09SGreg Kroah-Hartman 
89972dc1c09SGreg Kroah-Hartman 				/* ETH_HLEN */
90072dc1c09SGreg Kroah-Hartman 				odev->rx_buf_size = sizeof(struct iphdr);
90172dc1c09SGreg Kroah-Hartman 
90272dc1c09SGreg Kroah-Hartman 				/* Filip actually use .tot_len */
90372dc1c09SGreg Kroah-Hartman 				odev->rx_buf_missing =
90472dc1c09SGreg Kroah-Hartman 				    frame_len - sizeof(struct iphdr);
90572dc1c09SGreg Kroah-Hartman 				odev->rx_parse_state = WAIT_DATA;
90672dc1c09SGreg Kroah-Hartman 			}
90772dc1c09SGreg Kroah-Hartman 			break;
90872dc1c09SGreg Kroah-Hartman 
90972dc1c09SGreg Kroah-Hartman 		case WAIT_DATA:
91072dc1c09SGreg Kroah-Hartman 			temp_bytes = (count < odev->rx_buf_missing)
91172dc1c09SGreg Kroah-Hartman 					? count : odev->rx_buf_missing;
91272dc1c09SGreg Kroah-Hartman 
91372dc1c09SGreg Kroah-Hartman 			/* Copy the rest of the bytes that are left in the
91472dc1c09SGreg Kroah-Hartman 			 * buffer into the waiting sk_buf. */
91572dc1c09SGreg Kroah-Hartman 			/* Make room for temp_bytes after tail. */
916b952f4dfSyuan linyu 			skb_put_data(odev->skb_rx_buf,
91759ae1d12SJohannes Berg 				     ip_pkt + buffer_offset,
91859ae1d12SJohannes Berg 				     temp_bytes);
91972dc1c09SGreg Kroah-Hartman 
92072dc1c09SGreg Kroah-Hartman 			odev->rx_buf_missing -= temp_bytes;
92172dc1c09SGreg Kroah-Hartman 			count -= temp_bytes;
92272dc1c09SGreg Kroah-Hartman 			buffer_offset += temp_bytes;
92372dc1c09SGreg Kroah-Hartman 			odev->rx_buf_size += temp_bytes;
92472dc1c09SGreg Kroah-Hartman 			if (!odev->rx_buf_missing) {
92572dc1c09SGreg Kroah-Hartman 				/* Packet is complete. Inject into stack. */
92672dc1c09SGreg Kroah-Hartman 				/* We have IP packet here */
92709640e63SHarvey Harrison 				odev->skb_rx_buf->protocol = cpu_to_be16(ETH_P_IP);
92872dc1c09SGreg Kroah-Hartman 				skb_reset_mac_header(odev->skb_rx_buf);
92972dc1c09SGreg Kroah-Hartman 
93072dc1c09SGreg Kroah-Hartman 				/* Ship it off to the kernel */
93172dc1c09SGreg Kroah-Hartman 				netif_rx(odev->skb_rx_buf);
93272dc1c09SGreg Kroah-Hartman 				/* No longer our buffer. */
93372dc1c09SGreg Kroah-Hartman 				odev->skb_rx_buf = NULL;
93472dc1c09SGreg Kroah-Hartman 
93572dc1c09SGreg Kroah-Hartman 				/* update out statistics */
93672dc1c09SGreg Kroah-Hartman 				odev->net->stats.rx_packets++;
93772dc1c09SGreg Kroah-Hartman 
93872dc1c09SGreg Kroah-Hartman 				odev->net->stats.rx_bytes += odev->rx_buf_size;
93972dc1c09SGreg Kroah-Hartman 
94072dc1c09SGreg Kroah-Hartman 				odev->rx_buf_size = 0;
94172dc1c09SGreg Kroah-Hartman 				odev->rx_buf_missing = sizeof(struct iphdr);
94272dc1c09SGreg Kroah-Hartman 				odev->rx_parse_state = WAIT_IP;
94372dc1c09SGreg Kroah-Hartman 			}
94472dc1c09SGreg Kroah-Hartman 			break;
94572dc1c09SGreg Kroah-Hartman 
94672dc1c09SGreg Kroah-Hartman 		case WAIT_SYNC:
94795a69117SJoe Perches 			hso_dbg(0x1, " W_S\n");
94872dc1c09SGreg Kroah-Hartman 			count = 0;
94972dc1c09SGreg Kroah-Hartman 			break;
95072dc1c09SGreg Kroah-Hartman 		default:
95195a69117SJoe Perches 			hso_dbg(0x1, "\n");
95272dc1c09SGreg Kroah-Hartman 			count--;
95372dc1c09SGreg Kroah-Hartman 			break;
95472dc1c09SGreg Kroah-Hartman 		}
95572dc1c09SGreg Kroah-Hartman 	}
95672dc1c09SGreg Kroah-Hartman 
95772dc1c09SGreg Kroah-Hartman 	/* Recovery mechanism for WAIT_SYNC state. */
95872dc1c09SGreg Kroah-Hartman 	if (is_eop) {
95972dc1c09SGreg Kroah-Hartman 		if (odev->rx_parse_state == WAIT_SYNC) {
96072dc1c09SGreg Kroah-Hartman 			odev->rx_parse_state = WAIT_IP;
96172dc1c09SGreg Kroah-Hartman 			odev->rx_buf_size = 0;
96272dc1c09SGreg Kroah-Hartman 			odev->rx_buf_missing = sizeof(struct iphdr);
96372dc1c09SGreg Kroah-Hartman 		}
96472dc1c09SGreg Kroah-Hartman 	}
96572dc1c09SGreg Kroah-Hartman }
96672dc1c09SGreg Kroah-Hartman 
fix_crc_bug(struct urb * urb,__le16 max_packet_size)9675591c75dSJoe Perches static void fix_crc_bug(struct urb *urb, __le16 max_packet_size)
9685591c75dSJoe Perches {
9695591c75dSJoe Perches 	static const u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
9705591c75dSJoe Perches 	u32 rest = urb->actual_length % le16_to_cpu(max_packet_size);
9715591c75dSJoe Perches 
9725591c75dSJoe Perches 	if (((rest == 5) || (rest == 6)) &&
9735591c75dSJoe Perches 	    !memcmp(((u8 *)urb->transfer_buffer) + urb->actual_length - 4,
9745591c75dSJoe Perches 		    crc_check, 4)) {
9755591c75dSJoe Perches 		urb->actual_length -= 4;
9765591c75dSJoe Perches 	}
9775591c75dSJoe Perches }
9785591c75dSJoe Perches 
97972dc1c09SGreg Kroah-Hartman /* Moving data from usb to kernel (in interrupt state) */
read_bulk_callback(struct urb * urb)98072dc1c09SGreg Kroah-Hartman static void read_bulk_callback(struct urb *urb)
98172dc1c09SGreg Kroah-Hartman {
98272dc1c09SGreg Kroah-Hartman 	struct hso_net *odev = urb->context;
98372dc1c09SGreg Kroah-Hartman 	struct net_device *net;
98472dc1c09SGreg Kroah-Hartman 	int result;
98512c4de4bSSebastian Andrzej Siewior 	unsigned long flags;
98672dc1c09SGreg Kroah-Hartman 	int status = urb->status;
98772dc1c09SGreg Kroah-Hartman 
98872dc1c09SGreg Kroah-Hartman 	/* is al ok?  (Filip: Who's Al ?) */
98972dc1c09SGreg Kroah-Hartman 	if (status) {
99068a351c5SJan Dumon 		handle_usb_error(status, __func__, odev->parent);
99172dc1c09SGreg Kroah-Hartman 		return;
99272dc1c09SGreg Kroah-Hartman 	}
99372dc1c09SGreg Kroah-Hartman 
99472dc1c09SGreg Kroah-Hartman 	/* Sanity check */
99572dc1c09SGreg Kroah-Hartman 	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
99695a69117SJoe Perches 		hso_dbg(0x1, "BULK IN callback but driver is not active!\n");
99772dc1c09SGreg Kroah-Hartman 		return;
99872dc1c09SGreg Kroah-Hartman 	}
99972dc1c09SGreg Kroah-Hartman 	usb_mark_last_busy(urb->dev);
100072dc1c09SGreg Kroah-Hartman 
100172dc1c09SGreg Kroah-Hartman 	net = odev->net;
100272dc1c09SGreg Kroah-Hartman 
100372dc1c09SGreg Kroah-Hartman 	if (!netif_device_present(net)) {
100472dc1c09SGreg Kroah-Hartman 		/* Somebody killed our network interface... */
100572dc1c09SGreg Kroah-Hartman 		return;
100672dc1c09SGreg Kroah-Hartman 	}
100772dc1c09SGreg Kroah-Hartman 
10085591c75dSJoe Perches 	if (odev->parent->port_spec & HSO_INFO_CRC_BUG)
10095591c75dSJoe Perches 		fix_crc_bug(urb, odev->in_endp->wMaxPacketSize);
101072dc1c09SGreg Kroah-Hartman 
101172dc1c09SGreg Kroah-Hartman 	/* do we even have a packet? */
101272dc1c09SGreg Kroah-Hartman 	if (urb->actual_length) {
101372dc1c09SGreg Kroah-Hartman 		/* Handle the IP stream, add header and push it onto network
101472dc1c09SGreg Kroah-Hartman 		 * stack if the packet is complete. */
101512c4de4bSSebastian Andrzej Siewior 		spin_lock_irqsave(&odev->net_lock, flags);
101672dc1c09SGreg Kroah-Hartman 		packetizeRx(odev, urb->transfer_buffer, urb->actual_length,
101772dc1c09SGreg Kroah-Hartman 			    (urb->transfer_buffer_length >
101872dc1c09SGreg Kroah-Hartman 			     urb->actual_length) ? 1 : 0);
101912c4de4bSSebastian Andrzej Siewior 		spin_unlock_irqrestore(&odev->net_lock, flags);
102072dc1c09SGreg Kroah-Hartman 	}
102172dc1c09SGreg Kroah-Hartman 
102272dc1c09SGreg Kroah-Hartman 	/* We are done with this URB, resubmit it. Prep the USB to wait for
102372dc1c09SGreg Kroah-Hartman 	 * another frame. Reuse same as received. */
102472dc1c09SGreg Kroah-Hartman 	usb_fill_bulk_urb(urb,
102572dc1c09SGreg Kroah-Hartman 			  odev->parent->usb,
102672dc1c09SGreg Kroah-Hartman 			  usb_rcvbulkpipe(odev->parent->usb,
102772dc1c09SGreg Kroah-Hartman 					  odev->in_endp->
102872dc1c09SGreg Kroah-Hartman 					  bEndpointAddress & 0x7F),
102972dc1c09SGreg Kroah-Hartman 			  urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE,
103072dc1c09SGreg Kroah-Hartman 			  read_bulk_callback, odev);
103172dc1c09SGreg Kroah-Hartman 
103272dc1c09SGreg Kroah-Hartman 	/* Give this to the USB subsystem so it can tell us when more data
103372dc1c09SGreg Kroah-Hartman 	 * arrives. */
103472dc1c09SGreg Kroah-Hartman 	result = usb_submit_urb(urb, GFP_ATOMIC);
103572dc1c09SGreg Kroah-Hartman 	if (result)
103672dc1c09SGreg Kroah-Hartman 		dev_warn(&odev->parent->interface->dev,
10378a5c9c49SJan Dumon 			 "%s failed submit mux_bulk_rx_urb %d\n", __func__,
103872dc1c09SGreg Kroah-Hartman 			 result);
103972dc1c09SGreg Kroah-Hartman }
104072dc1c09SGreg Kroah-Hartman 
104172dc1c09SGreg Kroah-Hartman /* Serial driver functions */
104272dc1c09SGreg Kroah-Hartman 
hso_init_termios(struct ktermios * termios)1043ac9720c3SAlan Cox static void hso_init_termios(struct ktermios *termios)
104472dc1c09SGreg Kroah-Hartman {
104572dc1c09SGreg Kroah-Hartman 	/*
104672dc1c09SGreg Kroah-Hartman 	 * The default requirements for this device are:
104772dc1c09SGreg Kroah-Hartman 	 */
104872dc1c09SGreg Kroah-Hartman 	termios->c_iflag &=
104972dc1c09SGreg Kroah-Hartman 		~(IGNBRK	/* disable ignore break */
105072dc1c09SGreg Kroah-Hartman 		| BRKINT	/* disable break causes interrupt */
105172dc1c09SGreg Kroah-Hartman 		| PARMRK	/* disable mark parity errors */
105272dc1c09SGreg Kroah-Hartman 		| ISTRIP	/* disable clear high bit of input characters */
105372dc1c09SGreg Kroah-Hartman 		| INLCR		/* disable translate NL to CR */
105472dc1c09SGreg Kroah-Hartman 		| IGNCR		/* disable ignore CR */
105572dc1c09SGreg Kroah-Hartman 		| ICRNL		/* disable translate CR to NL */
105672dc1c09SGreg Kroah-Hartman 		| IXON);	/* disable enable XON/XOFF flow control */
105772dc1c09SGreg Kroah-Hartman 
105872dc1c09SGreg Kroah-Hartman 	/* disable postprocess output characters */
105972dc1c09SGreg Kroah-Hartman 	termios->c_oflag &= ~OPOST;
106072dc1c09SGreg Kroah-Hartman 
106172dc1c09SGreg Kroah-Hartman 	termios->c_lflag &=
106272dc1c09SGreg Kroah-Hartman 		~(ECHO		/* disable echo input characters */
106372dc1c09SGreg Kroah-Hartman 		| ECHONL	/* disable echo new line */
106472dc1c09SGreg Kroah-Hartman 		| ICANON	/* disable erase, kill, werase, and rprnt
106572dc1c09SGreg Kroah-Hartman 				   special characters */
106672dc1c09SGreg Kroah-Hartman 		| ISIG		/* disable interrupt, quit, and suspend special
106772dc1c09SGreg Kroah-Hartman 				   characters */
106872dc1c09SGreg Kroah-Hartman 		| IEXTEN);	/* disable non-POSIX special characters */
106972dc1c09SGreg Kroah-Hartman 
107072dc1c09SGreg Kroah-Hartman 	termios->c_cflag &=
107172dc1c09SGreg Kroah-Hartman 		~(CSIZE		/* no size */
107272dc1c09SGreg Kroah-Hartman 		| PARENB	/* disable parity bit */
107372dc1c09SGreg Kroah-Hartman 		| CBAUD		/* clear current baud rate */
107472dc1c09SGreg Kroah-Hartman 		| CBAUDEX);	/* clear current buad rate */
107572dc1c09SGreg Kroah-Hartman 
107672dc1c09SGreg Kroah-Hartman 	termios->c_cflag |= CS8;	/* character size 8 bits */
107772dc1c09SGreg Kroah-Hartman 
107872dc1c09SGreg Kroah-Hartman 	/* baud rate 115200 */
1079ac9720c3SAlan Cox 	tty_termios_encode_baud_rate(termios, 115200, 115200);
1080ac9720c3SAlan Cox }
1081ac9720c3SAlan Cox 
_hso_serial_set_termios(struct tty_struct * tty)1082b06a1ffeSPavel Skripkin static void _hso_serial_set_termios(struct tty_struct *tty)
1083ac9720c3SAlan Cox {
108430409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
1085ac9720c3SAlan Cox 
1086ac9720c3SAlan Cox 	if (!serial) {
10873981cce6SJoe Perches 		pr_err("%s: no tty structures", __func__);
1088ac9720c3SAlan Cox 		return;
1089ac9720c3SAlan Cox 	}
1090ac9720c3SAlan Cox 
109195a69117SJoe Perches 	hso_dbg(0x8, "port %d\n", serial->minor);
109272dc1c09SGreg Kroah-Hartman 
109372dc1c09SGreg Kroah-Hartman 	/*
1094ac9720c3SAlan Cox 	 *	Fix up unsupported bits
109572dc1c09SGreg Kroah-Hartman 	 */
1096adc8d746SAlan Cox 	tty->termios.c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */
1097ac9720c3SAlan Cox 
1098adc8d746SAlan Cox 	tty->termios.c_cflag &=
1099ac9720c3SAlan Cox 		~(CSIZE		/* no size */
1100ac9720c3SAlan Cox 		| PARENB	/* disable parity bit */
1101ac9720c3SAlan Cox 		| CBAUD		/* clear current baud rate */
1102ac9720c3SAlan Cox 		| CBAUDEX);	/* clear current buad rate */
1103ac9720c3SAlan Cox 
1104adc8d746SAlan Cox 	tty->termios.c_cflag |= CS8;	/* character size 8 bits */
1105ac9720c3SAlan Cox 
1106ac9720c3SAlan Cox 	/* baud rate 115200 */
1107ac9720c3SAlan Cox 	tty_encode_baud_rate(tty, 115200, 115200);
110872dc1c09SGreg Kroah-Hartman }
110972dc1c09SGreg Kroah-Hartman 
hso_resubmit_rx_bulk_urb(struct hso_serial * serial,struct urb * urb)11108ef5ba63SDenis Joseph Barrow static void hso_resubmit_rx_bulk_urb(struct hso_serial *serial, struct urb *urb)
11118ef5ba63SDenis Joseph Barrow {
11128ef5ba63SDenis Joseph Barrow 	int result;
11138ef5ba63SDenis Joseph Barrow 	/* We are done with this URB, resubmit it. Prep the USB to wait for
11148ef5ba63SDenis Joseph Barrow 	 * another frame */
11158ef5ba63SDenis Joseph Barrow 	usb_fill_bulk_urb(urb, serial->parent->usb,
11168ef5ba63SDenis Joseph Barrow 			  usb_rcvbulkpipe(serial->parent->usb,
11178ef5ba63SDenis Joseph Barrow 					  serial->in_endp->
11188ef5ba63SDenis Joseph Barrow 					  bEndpointAddress & 0x7F),
11198ef5ba63SDenis Joseph Barrow 			  urb->transfer_buffer, serial->rx_data_length,
11208ef5ba63SDenis Joseph Barrow 			  hso_std_serial_read_bulk_callback, serial);
11218ef5ba63SDenis Joseph Barrow 	/* Give this to the USB subsystem so it can tell us when more data
11228ef5ba63SDenis Joseph Barrow 	 * arrives. */
11238ef5ba63SDenis Joseph Barrow 	result = usb_submit_urb(urb, GFP_ATOMIC);
11248ef5ba63SDenis Joseph Barrow 	if (result) {
11258ef5ba63SDenis Joseph Barrow 		dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d\n",
11268ef5ba63SDenis Joseph Barrow 			__func__, result);
11278ef5ba63SDenis Joseph Barrow 	}
11288ef5ba63SDenis Joseph Barrow }
11298ef5ba63SDenis Joseph Barrow 
11308ef5ba63SDenis Joseph Barrow 
11318ef5ba63SDenis Joseph Barrow 
11328ef5ba63SDenis Joseph Barrow 
put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial * serial)11338ef5ba63SDenis Joseph Barrow static void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial)
11348ef5ba63SDenis Joseph Barrow {
11358ef5ba63SDenis Joseph Barrow 	int count;
11368ef5ba63SDenis Joseph Barrow 	struct urb *curr_urb;
11378ef5ba63SDenis Joseph Barrow 
11388ef5ba63SDenis Joseph Barrow 	while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) {
11398ef5ba63SDenis Joseph Barrow 		curr_urb = serial->rx_urb[serial->curr_rx_urb_idx];
11408ef5ba63SDenis Joseph Barrow 		count = put_rxbuf_data(curr_urb, serial);
11418ef5ba63SDenis Joseph Barrow 		if (count == -1)
11428ef5ba63SDenis Joseph Barrow 			return;
11438ef5ba63SDenis Joseph Barrow 		if (count == 0) {
11448ef5ba63SDenis Joseph Barrow 			serial->curr_rx_urb_idx++;
11458ef5ba63SDenis Joseph Barrow 			if (serial->curr_rx_urb_idx >= serial->num_rx_urbs)
11468ef5ba63SDenis Joseph Barrow 				serial->curr_rx_urb_idx = 0;
11478ef5ba63SDenis Joseph Barrow 			hso_resubmit_rx_bulk_urb(serial, curr_urb);
11488ef5ba63SDenis Joseph Barrow 		}
11498ef5ba63SDenis Joseph Barrow 	}
11508ef5ba63SDenis Joseph Barrow }
11518ef5ba63SDenis Joseph Barrow 
put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial * serial)11528ef5ba63SDenis Joseph Barrow static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial)
11538ef5ba63SDenis Joseph Barrow {
11548ef5ba63SDenis Joseph Barrow 	int count = 0;
11558ef5ba63SDenis Joseph Barrow 	struct urb *urb;
11568ef5ba63SDenis Joseph Barrow 
11578ef5ba63SDenis Joseph Barrow 	urb = serial->rx_urb[0];
11585ce76e77SJiri Slaby 	if (serial->port.count > 0) {
11598ef5ba63SDenis Joseph Barrow 		count = put_rxbuf_data(urb, serial);
11608ef5ba63SDenis Joseph Barrow 		if (count == -1)
11618ef5ba63SDenis Joseph Barrow 			return;
11628ef5ba63SDenis Joseph Barrow 	}
11638ef5ba63SDenis Joseph Barrow 	/* Re issue a read as long as we receive data. */
11648ef5ba63SDenis Joseph Barrow 
11658ef5ba63SDenis Joseph Barrow 	if (count == 0 && ((urb->actual_length != 0) ||
11668ef5ba63SDenis Joseph Barrow 			   (serial->rx_state == RX_PENDING))) {
11678ef5ba63SDenis Joseph Barrow 		serial->rx_state = RX_SENT;
11688ef5ba63SDenis Joseph Barrow 		hso_mux_serial_read(serial);
11698ef5ba63SDenis Joseph Barrow 	} else
11708ef5ba63SDenis Joseph Barrow 		serial->rx_state = RX_IDLE;
11718ef5ba63SDenis Joseph Barrow }
11728ef5ba63SDenis Joseph Barrow 
11738ef5ba63SDenis Joseph Barrow 
11748ef5ba63SDenis Joseph Barrow /* read callback for Diag and CS port */
hso_std_serial_read_bulk_callback(struct urb * urb)11758ef5ba63SDenis Joseph Barrow static void hso_std_serial_read_bulk_callback(struct urb *urb)
11768ef5ba63SDenis Joseph Barrow {
11778ef5ba63SDenis Joseph Barrow 	struct hso_serial *serial = urb->context;
11788ef5ba63SDenis Joseph Barrow 	int status = urb->status;
117912c4de4bSSebastian Andrzej Siewior 	unsigned long flags;
11808ef5ba63SDenis Joseph Barrow 
118195a69117SJoe Perches 	hso_dbg(0x8, "--- Got serial_read_bulk callback %02x ---\n", status);
11824ccd0bb9SDan Carpenter 
11838ef5ba63SDenis Joseph Barrow 	/* sanity check */
11848ef5ba63SDenis Joseph Barrow 	if (!serial) {
118595a69117SJoe Perches 		hso_dbg(0x1, "serial == NULL\n");
11868ef5ba63SDenis Joseph Barrow 		return;
11874ccd0bb9SDan Carpenter 	}
11884ccd0bb9SDan Carpenter 	if (status) {
118968a351c5SJan Dumon 		handle_usb_error(status, __func__, serial->parent);
11908ef5ba63SDenis Joseph Barrow 		return;
11918ef5ba63SDenis Joseph Barrow 	}
11928ef5ba63SDenis Joseph Barrow 
119395a69117SJoe Perches 	hso_dbg(0x1, "Actual length = %d\n", urb->actual_length);
11948ef5ba63SDenis Joseph Barrow 	DUMP1(urb->transfer_buffer, urb->actual_length);
11958ef5ba63SDenis Joseph Barrow 
11968ef5ba63SDenis Joseph Barrow 	/* Anyone listening? */
11975ce76e77SJiri Slaby 	if (serial->port.count == 0)
11988ef5ba63SDenis Joseph Barrow 		return;
11998ef5ba63SDenis Joseph Barrow 
12005591c75dSJoe Perches 	if (serial->parent->port_spec & HSO_INFO_CRC_BUG)
12015591c75dSJoe Perches 		fix_crc_bug(urb, serial->in_endp->wMaxPacketSize);
12028ef5ba63SDenis Joseph Barrow 	/* Valid data, handle RX data */
120312c4de4bSSebastian Andrzej Siewior 	spin_lock_irqsave(&serial->serial_lock, flags);
12048ef5ba63SDenis Joseph Barrow 	serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 1;
12058ef5ba63SDenis Joseph Barrow 	put_rxbuf_data_and_resubmit_bulk_urb(serial);
120612c4de4bSSebastian Andrzej Siewior 	spin_unlock_irqrestore(&serial->serial_lock, flags);
12078ef5ba63SDenis Joseph Barrow }
12088ef5ba63SDenis Joseph Barrow 
12098ef5ba63SDenis Joseph Barrow /*
12108ef5ba63SDenis Joseph Barrow  * This needs to be a tasklet otherwise we will
12118ef5ba63SDenis Joseph Barrow  * end up recursively calling this function.
12128ef5ba63SDenis Joseph Barrow  */
hso_unthrottle_tasklet(struct tasklet_struct * t)1213fb1eb9b3SEmil Renner Berthing static void hso_unthrottle_tasklet(struct tasklet_struct *t)
12148ef5ba63SDenis Joseph Barrow {
1215fb1eb9b3SEmil Renner Berthing 	struct hso_serial *serial = from_tasklet(serial, t,
1216fb1eb9b3SEmil Renner Berthing 						 unthrottle_tasklet);
12178ef5ba63SDenis Joseph Barrow 	unsigned long flags;
12188ef5ba63SDenis Joseph Barrow 
12198ef5ba63SDenis Joseph Barrow 	spin_lock_irqsave(&serial->serial_lock, flags);
12208ef5ba63SDenis Joseph Barrow 	if ((serial->parent->port_spec & HSO_INTF_MUX))
12218ef5ba63SDenis Joseph Barrow 		put_rxbuf_data_and_resubmit_ctrl_urb(serial);
12228ef5ba63SDenis Joseph Barrow 	else
12238ef5ba63SDenis Joseph Barrow 		put_rxbuf_data_and_resubmit_bulk_urb(serial);
12248ef5ba63SDenis Joseph Barrow 	spin_unlock_irqrestore(&serial->serial_lock, flags);
12258ef5ba63SDenis Joseph Barrow }
12268ef5ba63SDenis Joseph Barrow 
hso_unthrottle(struct tty_struct * tty)12278ef5ba63SDenis Joseph Barrow static	void hso_unthrottle(struct tty_struct *tty)
12288ef5ba63SDenis Joseph Barrow {
122930409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
12308ef5ba63SDenis Joseph Barrow 
12318ef5ba63SDenis Joseph Barrow 	tasklet_hi_schedule(&serial->unthrottle_tasklet);
12328ef5ba63SDenis Joseph Barrow }
12338ef5ba63SDenis Joseph Barrow 
123472dc1c09SGreg Kroah-Hartman /* open the requested serial port */
hso_serial_open(struct tty_struct * tty,struct file * filp)123572dc1c09SGreg Kroah-Hartman static int hso_serial_open(struct tty_struct *tty, struct file *filp)
123672dc1c09SGreg Kroah-Hartman {
123772dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial = get_serial_by_index(tty->index);
1238ab153d84SDavid S. Miller 	int result;
123972dc1c09SGreg Kroah-Hartman 
124072dc1c09SGreg Kroah-Hartman 	/* sanity check */
124172dc1c09SGreg Kroah-Hartman 	if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) {
1242e136e303SAlan Cox 		WARN_ON(1);
124372dc1c09SGreg Kroah-Hartman 		tty->driver_data = NULL;
124495a69117SJoe Perches 		hso_dbg(0x1, "Failed to open port\n");
124572dc1c09SGreg Kroah-Hartman 		return -ENODEV;
124672dc1c09SGreg Kroah-Hartman 	}
124772dc1c09SGreg Kroah-Hartman 
1248ab153d84SDavid S. Miller 	mutex_lock(&serial->parent->mutex);
1249ab153d84SDavid S. Miller 	result = usb_autopm_get_interface(serial->parent->interface);
1250ab153d84SDavid S. Miller 	if (result < 0)
125172dc1c09SGreg Kroah-Hartman 		goto err_out;
125272dc1c09SGreg Kroah-Hartman 
125395a69117SJoe Perches 	hso_dbg(0x1, "Opening %d\n", serial->minor);
125472dc1c09SGreg Kroah-Hartman 
125572dc1c09SGreg Kroah-Hartman 	/* setup */
125672dc1c09SGreg Kroah-Hartman 	tty->driver_data = serial;
12579f8c0b08SJiri Slaby 	tty_port_tty_set(&serial->port, tty);
125872dc1c09SGreg Kroah-Hartman 
12592f9889a2SDavid S. Miller 	/* check for port already opened, if not set the termios */
12605ce76e77SJiri Slaby 	serial->port.count++;
12615ce76e77SJiri Slaby 	if (serial->port.count == 1) {
12628ef5ba63SDenis Joseph Barrow 		serial->rx_state = RX_IDLE;
126372dc1c09SGreg Kroah-Hartman 		/* Force default termio settings */
1264b06a1ffeSPavel Skripkin 		_hso_serial_set_termios(tty);
1265fb1eb9b3SEmil Renner Berthing 		tasklet_setup(&serial->unthrottle_tasklet,
1266fb1eb9b3SEmil Renner Berthing 			      hso_unthrottle_tasklet);
1267ab153d84SDavid S. Miller 		result = hso_start_serial_device(serial->parent, GFP_KERNEL);
1268ab153d84SDavid S. Miller 		if (result) {
126972dc1c09SGreg Kroah-Hartman 			hso_stop_serial_device(serial->parent);
12705ce76e77SJiri Slaby 			serial->port.count--;
127129bd3bc1SOlivier Sobrie 		} else {
127229bd3bc1SOlivier Sobrie 			kref_get(&serial->parent->ref);
127372dc1c09SGreg Kroah-Hartman 		}
127472dc1c09SGreg Kroah-Hartman 	} else {
127595a69117SJoe Perches 		hso_dbg(0x1, "Port was already open\n");
127672dc1c09SGreg Kroah-Hartman 	}
127772dc1c09SGreg Kroah-Hartman 
127872dc1c09SGreg Kroah-Hartman 	usb_autopm_put_interface(serial->parent->interface);
127972dc1c09SGreg Kroah-Hartman 
128072dc1c09SGreg Kroah-Hartman 	/* done */
1281ab153d84SDavid S. Miller 	if (result)
128220b9d177SAlan Cox 		hso_serial_tiocmset(tty, TIOCM_RTS | TIOCM_DTR, 0);
128372dc1c09SGreg Kroah-Hartman err_out:
1284ab153d84SDavid S. Miller 	mutex_unlock(&serial->parent->mutex);
1285ab153d84SDavid S. Miller 	return result;
128672dc1c09SGreg Kroah-Hartman }
128772dc1c09SGreg Kroah-Hartman 
128872dc1c09SGreg Kroah-Hartman /* close the requested serial port */
hso_serial_close(struct tty_struct * tty,struct file * filp)128972dc1c09SGreg Kroah-Hartman static void hso_serial_close(struct tty_struct *tty, struct file *filp)
129072dc1c09SGreg Kroah-Hartman {
129172dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial = tty->driver_data;
129272dc1c09SGreg Kroah-Hartman 	u8 usb_gone;
129372dc1c09SGreg Kroah-Hartman 
129495a69117SJoe Perches 	hso_dbg(0x1, "Closing serial port\n");
129572dc1c09SGreg Kroah-Hartman 
1296e136e303SAlan Cox 	/* Open failed, no close cleanup required */
1297e136e303SAlan Cox 	if (serial == NULL)
1298e136e303SAlan Cox 		return;
1299e136e303SAlan Cox 
1300ab153d84SDavid S. Miller 	mutex_lock(&serial->parent->mutex);
130172dc1c09SGreg Kroah-Hartman 	usb_gone = serial->parent->usb_gone;
130272dc1c09SGreg Kroah-Hartman 
130372dc1c09SGreg Kroah-Hartman 	if (!usb_gone)
130472dc1c09SGreg Kroah-Hartman 		usb_autopm_get_interface(serial->parent->interface);
130572dc1c09SGreg Kroah-Hartman 
130672dc1c09SGreg Kroah-Hartman 	/* reset the rts and dtr */
130772dc1c09SGreg Kroah-Hartman 	/* do the actual close */
13085ce76e77SJiri Slaby 	serial->port.count--;
1309dcfcb256SAntti Kaijanmäki 
13105ce76e77SJiri Slaby 	if (serial->port.count <= 0) {
13115ce76e77SJiri Slaby 		serial->port.count = 0;
13129f8c0b08SJiri Slaby 		tty_port_tty_set(&serial->port, NULL);
131372dc1c09SGreg Kroah-Hartman 		if (!usb_gone)
131472dc1c09SGreg Kroah-Hartman 			hso_stop_serial_device(serial->parent);
13158ef5ba63SDenis Joseph Barrow 		tasklet_kill(&serial->unthrottle_tasklet);
131672dc1c09SGreg Kroah-Hartman 	}
13178ef5ba63SDenis Joseph Barrow 
131872dc1c09SGreg Kroah-Hartman 	if (!usb_gone)
131972dc1c09SGreg Kroah-Hartman 		usb_autopm_put_interface(serial->parent->interface);
1320ab153d84SDavid S. Miller 
1321ab153d84SDavid S. Miller 	mutex_unlock(&serial->parent->mutex);
132272dc1c09SGreg Kroah-Hartman }
132372dc1c09SGreg Kroah-Hartman 
132472dc1c09SGreg Kroah-Hartman /* close the requested serial port */
hso_serial_write(struct tty_struct * tty,const u8 * buf,size_t count)132595713967SJiri Slaby (SUSE) static ssize_t hso_serial_write(struct tty_struct *tty, const u8 *buf,
132695713967SJiri Slaby (SUSE) 				size_t count)
132772dc1c09SGreg Kroah-Hartman {
132830409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
132972dc1c09SGreg Kroah-Hartman 	unsigned long flags;
133072dc1c09SGreg Kroah-Hartman 
133172dc1c09SGreg Kroah-Hartman 	/* sanity check */
133272dc1c09SGreg Kroah-Hartman 	if (serial == NULL) {
13333981cce6SJoe Perches 		pr_err("%s: serial is NULL\n", __func__);
133472dc1c09SGreg Kroah-Hartman 		return -ENODEV;
133572dc1c09SGreg Kroah-Hartman 	}
133672dc1c09SGreg Kroah-Hartman 
133772dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial->serial_lock, flags);
133872dc1c09SGreg Kroah-Hartman 
1339*6fcd3b67SJiri Slaby (SUSE) 	count = min_t(size_t, serial->tx_data_length - serial->tx_buffer_count,
1340*6fcd3b67SJiri Slaby (SUSE) 		      count);
1341*6fcd3b67SJiri Slaby (SUSE) 	memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, count);
1342*6fcd3b67SJiri Slaby (SUSE) 	serial->tx_buffer_count += count;
134372dc1c09SGreg Kroah-Hartman 
134472dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial->serial_lock, flags);
134572dc1c09SGreg Kroah-Hartman 
134672dc1c09SGreg Kroah-Hartman 	hso_kick_transmit(serial);
134772dc1c09SGreg Kroah-Hartman 	/* done */
1348*6fcd3b67SJiri Slaby (SUSE) 	return count;
134972dc1c09SGreg Kroah-Hartman }
135072dc1c09SGreg Kroah-Hartman 
135172dc1c09SGreg Kroah-Hartman /* how much room is there for writing */
hso_serial_write_room(struct tty_struct * tty)135203b3b1a2SJiri Slaby static unsigned int hso_serial_write_room(struct tty_struct *tty)
135372dc1c09SGreg Kroah-Hartman {
135430409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
135503b3b1a2SJiri Slaby 	unsigned int room;
135672dc1c09SGreg Kroah-Hartman 	unsigned long flags;
135772dc1c09SGreg Kroah-Hartman 
135872dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial->serial_lock, flags);
135972dc1c09SGreg Kroah-Hartman 	room = serial->tx_data_length - serial->tx_buffer_count;
136072dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial->serial_lock, flags);
136172dc1c09SGreg Kroah-Hartman 
136272dc1c09SGreg Kroah-Hartman 	/* return free room */
136372dc1c09SGreg Kroah-Hartman 	return room;
136472dc1c09SGreg Kroah-Hartman }
136572dc1c09SGreg Kroah-Hartman 
hso_serial_cleanup(struct tty_struct * tty)136629bd3bc1SOlivier Sobrie static void hso_serial_cleanup(struct tty_struct *tty)
136729bd3bc1SOlivier Sobrie {
136829bd3bc1SOlivier Sobrie 	struct hso_serial *serial = tty->driver_data;
136929bd3bc1SOlivier Sobrie 
137029bd3bc1SOlivier Sobrie 	if (!serial)
137129bd3bc1SOlivier Sobrie 		return;
137229bd3bc1SOlivier Sobrie 
137329bd3bc1SOlivier Sobrie 	kref_put(&serial->parent->ref, hso_serial_ref_free);
137429bd3bc1SOlivier Sobrie }
137529bd3bc1SOlivier Sobrie 
137672dc1c09SGreg Kroah-Hartman /* setup the term */
hso_serial_set_termios(struct tty_struct * tty,const struct ktermios * old)1377a8c11c15SIlpo Järvinen static void hso_serial_set_termios(struct tty_struct *tty,
1378a8c11c15SIlpo Järvinen 				   const struct ktermios *old)
137972dc1c09SGreg Kroah-Hartman {
138030409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
138172dc1c09SGreg Kroah-Hartman 	unsigned long flags;
138272dc1c09SGreg Kroah-Hartman 
138372dc1c09SGreg Kroah-Hartman 	if (old)
1384e0484010SGeert Uytterhoeven 		hso_dbg(0x16, "Termios called with: cflags new[%u] - old[%u]\n",
1385e0484010SGeert Uytterhoeven 			(unsigned int)tty->termios.c_cflag,
1386e0484010SGeert Uytterhoeven 			(unsigned int)old->c_cflag);
138772dc1c09SGreg Kroah-Hartman 
138872dc1c09SGreg Kroah-Hartman 	/* the actual setup */
138972dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial->serial_lock, flags);
13905ce76e77SJiri Slaby 	if (serial->port.count)
1391b06a1ffeSPavel Skripkin 		_hso_serial_set_termios(tty);
139272dc1c09SGreg Kroah-Hartman 	else
1393adc8d746SAlan Cox 		tty->termios = *old;
139472dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial->serial_lock, flags);
139572dc1c09SGreg Kroah-Hartman 
139672dc1c09SGreg Kroah-Hartman 	/* done */
139772dc1c09SGreg Kroah-Hartman }
139872dc1c09SGreg Kroah-Hartman 
139972dc1c09SGreg Kroah-Hartman /* how many characters in the buffer */
hso_serial_chars_in_buffer(struct tty_struct * tty)1400fff4ef17SJiri Slaby static unsigned int hso_serial_chars_in_buffer(struct tty_struct *tty)
140172dc1c09SGreg Kroah-Hartman {
140230409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
140372dc1c09SGreg Kroah-Hartman 	unsigned long flags;
1404fff4ef17SJiri Slaby 	unsigned int chars;
140572dc1c09SGreg Kroah-Hartman 
140672dc1c09SGreg Kroah-Hartman 	/* sanity check */
140772dc1c09SGreg Kroah-Hartman 	if (serial == NULL)
140872dc1c09SGreg Kroah-Hartman 		return 0;
140972dc1c09SGreg Kroah-Hartman 
141072dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial->serial_lock, flags);
141172dc1c09SGreg Kroah-Hartman 	chars = serial->tx_buffer_count;
141272dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial->serial_lock, flags);
141372dc1c09SGreg Kroah-Hartman 
141472dc1c09SGreg Kroah-Hartman 	return chars;
141572dc1c09SGreg Kroah-Hartman }
tiocmget_submit_urb(struct hso_serial * serial,struct hso_tiocmget * tiocmget,struct usb_device * usb)14160227abc9SHannes Eder static int tiocmget_submit_urb(struct hso_serial *serial,
1417542f5482SDenis Joseph Barrow 			       struct hso_tiocmget *tiocmget,
1418542f5482SDenis Joseph Barrow 			       struct usb_device *usb)
1419542f5482SDenis Joseph Barrow {
1420542f5482SDenis Joseph Barrow 	int result;
1421542f5482SDenis Joseph Barrow 
1422542f5482SDenis Joseph Barrow 	if (serial->parent->usb_gone)
1423542f5482SDenis Joseph Barrow 		return -ENODEV;
1424542f5482SDenis Joseph Barrow 	usb_fill_int_urb(tiocmget->urb, usb,
1425542f5482SDenis Joseph Barrow 			 usb_rcvintpipe(usb,
1426542f5482SDenis Joseph Barrow 					tiocmget->endp->
1427542f5482SDenis Joseph Barrow 					bEndpointAddress & 0x7F),
1428af0de130SOliver Neukum 			 tiocmget->serial_state_notification,
1429542f5482SDenis Joseph Barrow 			 sizeof(struct hso_serial_state_notification),
1430542f5482SDenis Joseph Barrow 			 tiocmget_intr_callback, serial,
1431542f5482SDenis Joseph Barrow 			 tiocmget->endp->bInterval);
1432542f5482SDenis Joseph Barrow 	result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC);
1433542f5482SDenis Joseph Barrow 	if (result) {
1434542f5482SDenis Joseph Barrow 		dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__,
1435542f5482SDenis Joseph Barrow 			 result);
1436542f5482SDenis Joseph Barrow 	}
1437542f5482SDenis Joseph Barrow 	return result;
1438542f5482SDenis Joseph Barrow 
1439542f5482SDenis Joseph Barrow }
1440542f5482SDenis Joseph Barrow 
tiocmget_intr_callback(struct urb * urb)1441542f5482SDenis Joseph Barrow static void tiocmget_intr_callback(struct urb *urb)
1442542f5482SDenis Joseph Barrow {
1443542f5482SDenis Joseph Barrow 	struct hso_serial *serial = urb->context;
1444542f5482SDenis Joseph Barrow 	struct hso_tiocmget *tiocmget;
1445542f5482SDenis Joseph Barrow 	int status = urb->status;
1446542f5482SDenis Joseph Barrow 	u16 UART_state_bitmap, prev_UART_state_bitmap;
1447542f5482SDenis Joseph Barrow 	struct uart_icount *icount;
1448542f5482SDenis Joseph Barrow 	struct hso_serial_state_notification *serial_state_notification;
1449542f5482SDenis Joseph Barrow 	struct usb_device *usb;
145064bea46eSAleksander Morgado 	struct usb_interface *interface;
1451e5e97ee9SDan Williams 	int if_num;
1452542f5482SDenis Joseph Barrow 
1453542f5482SDenis Joseph Barrow 	/* Sanity checks */
1454542f5482SDenis Joseph Barrow 	if (!serial)
1455542f5482SDenis Joseph Barrow 		return;
1456542f5482SDenis Joseph Barrow 	if (status) {
145768a351c5SJan Dumon 		handle_usb_error(status, __func__, serial->parent);
1458542f5482SDenis Joseph Barrow 		return;
1459542f5482SDenis Joseph Barrow 	}
1460e5e97ee9SDan Williams 
1461e5e97ee9SDan Williams 	/* tiocmget is only supported on HSO_PORT_MODEM */
1462542f5482SDenis Joseph Barrow 	tiocmget = serial->tiocmget;
1463542f5482SDenis Joseph Barrow 	if (!tiocmget)
1464542f5482SDenis Joseph Barrow 		return;
1465e5e97ee9SDan Williams 	BUG_ON((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM);
1466e5e97ee9SDan Williams 
1467542f5482SDenis Joseph Barrow 	usb = serial->parent->usb;
146864bea46eSAleksander Morgado 	interface = serial->parent->interface;
146964bea46eSAleksander Morgado 
147064bea46eSAleksander Morgado 	if_num = interface->cur_altsetting->desc.bInterfaceNumber;
1471e5e97ee9SDan Williams 
1472e5e97ee9SDan Williams 	/* wIndex should be the USB interface number of the port to which the
1473e5e97ee9SDan Williams 	 * notification applies, which should always be the Modem port.
1474e5e97ee9SDan Williams 	 */
1475af0de130SOliver Neukum 	serial_state_notification = tiocmget->serial_state_notification;
1476542f5482SDenis Joseph Barrow 	if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE ||
1477542f5482SDenis Joseph Barrow 	    serial_state_notification->bNotification != B_NOTIFICATION ||
1478542f5482SDenis Joseph Barrow 	    le16_to_cpu(serial_state_notification->wValue) != W_VALUE ||
1479e5e97ee9SDan Williams 	    le16_to_cpu(serial_state_notification->wIndex) != if_num ||
1480542f5482SDenis Joseph Barrow 	    le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) {
1481542f5482SDenis Joseph Barrow 		dev_warn(&usb->dev,
1482542f5482SDenis Joseph Barrow 			 "hso received invalid serial state notification\n");
1483542f5482SDenis Joseph Barrow 		DUMP(serial_state_notification,
14849ce673d5SAntti Kaijanmäki 		     sizeof(struct hso_serial_state_notification));
1485542f5482SDenis Joseph Barrow 	} else {
148612c4de4bSSebastian Andrzej Siewior 		unsigned long flags;
1487542f5482SDenis Joseph Barrow 
1488542f5482SDenis Joseph Barrow 		UART_state_bitmap = le16_to_cpu(serial_state_notification->
1489542f5482SDenis Joseph Barrow 						UART_state_bitmap);
1490542f5482SDenis Joseph Barrow 		prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap;
1491542f5482SDenis Joseph Barrow 		icount = &tiocmget->icount;
149212c4de4bSSebastian Andrzej Siewior 		spin_lock_irqsave(&serial->serial_lock, flags);
1493542f5482SDenis Joseph Barrow 		if ((UART_state_bitmap & B_OVERRUN) !=
1494542f5482SDenis Joseph Barrow 		   (prev_UART_state_bitmap & B_OVERRUN))
1495542f5482SDenis Joseph Barrow 			icount->parity++;
1496542f5482SDenis Joseph Barrow 		if ((UART_state_bitmap & B_PARITY) !=
1497542f5482SDenis Joseph Barrow 		   (prev_UART_state_bitmap & B_PARITY))
1498542f5482SDenis Joseph Barrow 			icount->parity++;
1499542f5482SDenis Joseph Barrow 		if ((UART_state_bitmap & B_FRAMING) !=
1500542f5482SDenis Joseph Barrow 		   (prev_UART_state_bitmap & B_FRAMING))
1501542f5482SDenis Joseph Barrow 			icount->frame++;
1502542f5482SDenis Joseph Barrow 		if ((UART_state_bitmap & B_RING_SIGNAL) &&
1503542f5482SDenis Joseph Barrow 		   !(prev_UART_state_bitmap & B_RING_SIGNAL))
1504542f5482SDenis Joseph Barrow 			icount->rng++;
1505542f5482SDenis Joseph Barrow 		if ((UART_state_bitmap & B_BREAK) !=
1506542f5482SDenis Joseph Barrow 		   (prev_UART_state_bitmap & B_BREAK))
1507542f5482SDenis Joseph Barrow 			icount->brk++;
1508542f5482SDenis Joseph Barrow 		if ((UART_state_bitmap & B_TX_CARRIER) !=
1509542f5482SDenis Joseph Barrow 		   (prev_UART_state_bitmap & B_TX_CARRIER))
1510542f5482SDenis Joseph Barrow 			icount->dsr++;
1511542f5482SDenis Joseph Barrow 		if ((UART_state_bitmap & B_RX_CARRIER) !=
1512542f5482SDenis Joseph Barrow 		   (prev_UART_state_bitmap & B_RX_CARRIER))
1513542f5482SDenis Joseph Barrow 			icount->dcd++;
1514542f5482SDenis Joseph Barrow 		tiocmget->prev_UART_state_bitmap = UART_state_bitmap;
151512c4de4bSSebastian Andrzej Siewior 		spin_unlock_irqrestore(&serial->serial_lock, flags);
1516542f5482SDenis Joseph Barrow 		tiocmget->intr_completed = 1;
1517542f5482SDenis Joseph Barrow 		wake_up_interruptible(&tiocmget->waitq);
1518542f5482SDenis Joseph Barrow 	}
1519542f5482SDenis Joseph Barrow 	memset(serial_state_notification, 0,
1520542f5482SDenis Joseph Barrow 	       sizeof(struct hso_serial_state_notification));
1521542f5482SDenis Joseph Barrow 	tiocmget_submit_urb(serial,
1522542f5482SDenis Joseph Barrow 			    tiocmget,
1523542f5482SDenis Joseph Barrow 			    serial->parent->usb);
1524542f5482SDenis Joseph Barrow }
1525542f5482SDenis Joseph Barrow 
1526542f5482SDenis Joseph Barrow /*
1527542f5482SDenis Joseph Barrow  * next few functions largely stolen from drivers/serial/serial_core.c
1528542f5482SDenis Joseph Barrow  */
1529542f5482SDenis Joseph Barrow /* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
1530542f5482SDenis Joseph Barrow  * - mask passed in arg for lines of interest
1531542f5482SDenis Joseph Barrow  *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
1532542f5482SDenis Joseph Barrow  * Caller should use TIOCGICOUNT to see which one it was
1533542f5482SDenis Joseph Barrow  */
1534542f5482SDenis Joseph Barrow static int
hso_wait_modem_status(struct hso_serial * serial,unsigned long arg)1535542f5482SDenis Joseph Barrow hso_wait_modem_status(struct hso_serial *serial, unsigned long arg)
1536542f5482SDenis Joseph Barrow {
1537542f5482SDenis Joseph Barrow 	DECLARE_WAITQUEUE(wait, current);
1538542f5482SDenis Joseph Barrow 	struct uart_icount cprev, cnow;
1539542f5482SDenis Joseph Barrow 	struct hso_tiocmget  *tiocmget;
1540542f5482SDenis Joseph Barrow 	int ret;
1541542f5482SDenis Joseph Barrow 
1542542f5482SDenis Joseph Barrow 	tiocmget = serial->tiocmget;
1543542f5482SDenis Joseph Barrow 	if (!tiocmget)
1544542f5482SDenis Joseph Barrow 		return -ENOENT;
1545542f5482SDenis Joseph Barrow 	/*
1546542f5482SDenis Joseph Barrow 	 * note the counters on entry
1547542f5482SDenis Joseph Barrow 	 */
1548542f5482SDenis Joseph Barrow 	spin_lock_irq(&serial->serial_lock);
1549542f5482SDenis Joseph Barrow 	memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount));
1550542f5482SDenis Joseph Barrow 	spin_unlock_irq(&serial->serial_lock);
1551542f5482SDenis Joseph Barrow 	add_wait_queue(&tiocmget->waitq, &wait);
1552542f5482SDenis Joseph Barrow 	for (;;) {
1553542f5482SDenis Joseph Barrow 		spin_lock_irq(&serial->serial_lock);
1554542f5482SDenis Joseph Barrow 		memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
1555542f5482SDenis Joseph Barrow 		spin_unlock_irq(&serial->serial_lock);
1556542f5482SDenis Joseph Barrow 		set_current_state(TASK_INTERRUPTIBLE);
1557542f5482SDenis Joseph Barrow 		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
1558542f5482SDenis Joseph Barrow 		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
1559542f5482SDenis Joseph Barrow 		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd))) {
1560542f5482SDenis Joseph Barrow 			ret = 0;
1561542f5482SDenis Joseph Barrow 			break;
1562542f5482SDenis Joseph Barrow 		}
1563542f5482SDenis Joseph Barrow 		schedule();
1564542f5482SDenis Joseph Barrow 		/* see if a signal did it */
1565542f5482SDenis Joseph Barrow 		if (signal_pending(current)) {
1566542f5482SDenis Joseph Barrow 			ret = -ERESTARTSYS;
1567542f5482SDenis Joseph Barrow 			break;
1568542f5482SDenis Joseph Barrow 		}
1569542f5482SDenis Joseph Barrow 		cprev = cnow;
1570542f5482SDenis Joseph Barrow 	}
157150462ce0SFabian Frederick 	__set_current_state(TASK_RUNNING);
1572542f5482SDenis Joseph Barrow 	remove_wait_queue(&tiocmget->waitq, &wait);
1573542f5482SDenis Joseph Barrow 
1574542f5482SDenis Joseph Barrow 	return ret;
1575542f5482SDenis Joseph Barrow }
1576542f5482SDenis Joseph Barrow 
1577542f5482SDenis Joseph Barrow /*
1578542f5482SDenis Joseph Barrow  * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
1579542f5482SDenis Joseph Barrow  * Return: write counters to the user passed counter struct
1580542f5482SDenis Joseph Barrow  * NB: both 1->0 and 0->1 transitions are counted except for
1581542f5482SDenis Joseph Barrow  *     RI where only 0->1 is counted.
1582542f5482SDenis Joseph Barrow  */
hso_get_count(struct tty_struct * tty,struct serial_icounter_struct * icount)15830bca1b91SAlan Cox static int hso_get_count(struct tty_struct *tty,
15840bca1b91SAlan Cox 		  struct serial_icounter_struct *icount)
1585542f5482SDenis Joseph Barrow {
1586542f5482SDenis Joseph Barrow 	struct uart_icount cnow;
158730409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
1588542f5482SDenis Joseph Barrow 	struct hso_tiocmget  *tiocmget = serial->tiocmget;
1589542f5482SDenis Joseph Barrow 
159022ad7499SDan Carpenter 	memset(icount, 0, sizeof(struct serial_icounter_struct));
15917011e660SDan Rosenberg 
1592542f5482SDenis Joseph Barrow 	if (!tiocmget)
1593542f5482SDenis Joseph Barrow 		 return -ENOENT;
1594542f5482SDenis Joseph Barrow 	spin_lock_irq(&serial->serial_lock);
1595542f5482SDenis Joseph Barrow 	memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
1596542f5482SDenis Joseph Barrow 	spin_unlock_irq(&serial->serial_lock);
1597542f5482SDenis Joseph Barrow 
15980bca1b91SAlan Cox 	icount->cts         = cnow.cts;
15990bca1b91SAlan Cox 	icount->dsr         = cnow.dsr;
16000bca1b91SAlan Cox 	icount->rng         = cnow.rng;
16010bca1b91SAlan Cox 	icount->dcd         = cnow.dcd;
16020bca1b91SAlan Cox 	icount->rx          = cnow.rx;
16030bca1b91SAlan Cox 	icount->tx          = cnow.tx;
16040bca1b91SAlan Cox 	icount->frame       = cnow.frame;
16050bca1b91SAlan Cox 	icount->overrun     = cnow.overrun;
16060bca1b91SAlan Cox 	icount->parity      = cnow.parity;
16070bca1b91SAlan Cox 	icount->brk         = cnow.brk;
16080bca1b91SAlan Cox 	icount->buf_overrun = cnow.buf_overrun;
1609542f5482SDenis Joseph Barrow 
16100bca1b91SAlan Cox 	return 0;
1611542f5482SDenis Joseph Barrow }
1612542f5482SDenis Joseph Barrow 
161372dc1c09SGreg Kroah-Hartman 
hso_serial_tiocmget(struct tty_struct * tty)161460b33c13SAlan Cox static int hso_serial_tiocmget(struct tty_struct *tty)
161572dc1c09SGreg Kroah-Hartman {
1616542f5482SDenis Joseph Barrow 	int retval;
161730409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
1618542f5482SDenis Joseph Barrow 	struct hso_tiocmget  *tiocmget;
1619542f5482SDenis Joseph Barrow 	u16 UART_state_bitmap;
162072dc1c09SGreg Kroah-Hartman 
162172dc1c09SGreg Kroah-Hartman 	/* sanity check */
162272dc1c09SGreg Kroah-Hartman 	if (!serial) {
162395a69117SJoe Perches 		hso_dbg(0x1, "no tty structures\n");
162472dc1c09SGreg Kroah-Hartman 		return -EINVAL;
162572dc1c09SGreg Kroah-Hartman 	}
1626542f5482SDenis Joseph Barrow 	spin_lock_irq(&serial->serial_lock);
1627542f5482SDenis Joseph Barrow 	retval = ((serial->rts_state) ? TIOCM_RTS : 0) |
1628cd90ee17SDavid S. Miller 	    ((serial->dtr_state) ? TIOCM_DTR : 0);
1629542f5482SDenis Joseph Barrow 	tiocmget = serial->tiocmget;
1630542f5482SDenis Joseph Barrow 	if (tiocmget) {
1631cd90ee17SDavid S. Miller 
1632542f5482SDenis Joseph Barrow 		UART_state_bitmap = le16_to_cpu(
1633542f5482SDenis Joseph Barrow 			tiocmget->prev_UART_state_bitmap);
1634542f5482SDenis Joseph Barrow 		if (UART_state_bitmap & B_RING_SIGNAL)
1635542f5482SDenis Joseph Barrow 			retval |=  TIOCM_RNG;
1636542f5482SDenis Joseph Barrow 		if (UART_state_bitmap & B_RX_CARRIER)
1637542f5482SDenis Joseph Barrow 			retval |=  TIOCM_CD;
1638542f5482SDenis Joseph Barrow 		if (UART_state_bitmap & B_TX_CARRIER)
1639542f5482SDenis Joseph Barrow 			retval |=  TIOCM_DSR;
1640542f5482SDenis Joseph Barrow 	}
1641542f5482SDenis Joseph Barrow 	spin_unlock_irq(&serial->serial_lock);
1642542f5482SDenis Joseph Barrow 	return retval;
164372dc1c09SGreg Kroah-Hartman }
164472dc1c09SGreg Kroah-Hartman 
hso_serial_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)164520b9d177SAlan Cox static int hso_serial_tiocmset(struct tty_struct *tty,
164672dc1c09SGreg Kroah-Hartman 			       unsigned int set, unsigned int clear)
164772dc1c09SGreg Kroah-Hartman {
164872dc1c09SGreg Kroah-Hartman 	int val = 0;
164972dc1c09SGreg Kroah-Hartman 	unsigned long flags;
165072dc1c09SGreg Kroah-Hartman 	int if_num;
165130409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
165264bea46eSAleksander Morgado 	struct usb_interface *interface;
165372dc1c09SGreg Kroah-Hartman 
165472dc1c09SGreg Kroah-Hartman 	/* sanity check */
165572dc1c09SGreg Kroah-Hartman 	if (!serial) {
165695a69117SJoe Perches 		hso_dbg(0x1, "no tty structures\n");
165772dc1c09SGreg Kroah-Hartman 		return -EINVAL;
165872dc1c09SGreg Kroah-Hartman 	}
16590e0367e9SJan Dumon 
16600e0367e9SJan Dumon 	if ((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM)
16610e0367e9SJan Dumon 		return -EINVAL;
16620e0367e9SJan Dumon 
166364bea46eSAleksander Morgado 	interface = serial->parent->interface;
166464bea46eSAleksander Morgado 	if_num = interface->cur_altsetting->desc.bInterfaceNumber;
166572dc1c09SGreg Kroah-Hartman 
166672dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial->serial_lock, flags);
166772dc1c09SGreg Kroah-Hartman 	if (set & TIOCM_RTS)
166872dc1c09SGreg Kroah-Hartman 		serial->rts_state = 1;
166972dc1c09SGreg Kroah-Hartman 	if (set & TIOCM_DTR)
167072dc1c09SGreg Kroah-Hartman 		serial->dtr_state = 1;
167172dc1c09SGreg Kroah-Hartman 
167272dc1c09SGreg Kroah-Hartman 	if (clear & TIOCM_RTS)
167372dc1c09SGreg Kroah-Hartman 		serial->rts_state = 0;
167472dc1c09SGreg Kroah-Hartman 	if (clear & TIOCM_DTR)
167572dc1c09SGreg Kroah-Hartman 		serial->dtr_state = 0;
167672dc1c09SGreg Kroah-Hartman 
167772dc1c09SGreg Kroah-Hartman 	if (serial->dtr_state)
167872dc1c09SGreg Kroah-Hartman 		val |= 0x01;
167972dc1c09SGreg Kroah-Hartman 	if (serial->rts_state)
168072dc1c09SGreg Kroah-Hartman 		val |= 0x02;
168172dc1c09SGreg Kroah-Hartman 
168272dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial->serial_lock, flags);
168372dc1c09SGreg Kroah-Hartman 
168472dc1c09SGreg Kroah-Hartman 	return usb_control_msg(serial->parent->usb,
16851a6e9a9cSJohan Hovold 			       usb_sndctrlpipe(serial->parent->usb, 0), 0x22,
168672dc1c09SGreg Kroah-Hartman 			       0x21, val, if_num, NULL, 0,
168772dc1c09SGreg Kroah-Hartman 			       USB_CTRL_SET_TIMEOUT);
168872dc1c09SGreg Kroah-Hartman }
168972dc1c09SGreg Kroah-Hartman 
hso_serial_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)16906caa76b7SAlan Cox static int hso_serial_ioctl(struct tty_struct *tty,
1691542f5482SDenis Joseph Barrow 			    unsigned int cmd, unsigned long arg)
1692542f5482SDenis Joseph Barrow {
169330409420SJiri Slaby 	struct hso_serial *serial = tty->driver_data;
1694542f5482SDenis Joseph Barrow 	int ret = 0;
169595a69117SJoe Perches 	hso_dbg(0x8, "IOCTL cmd: %d, arg: %ld\n", cmd, arg);
1696542f5482SDenis Joseph Barrow 
1697542f5482SDenis Joseph Barrow 	if (!serial)
1698542f5482SDenis Joseph Barrow 		return -ENODEV;
1699542f5482SDenis Joseph Barrow 	switch (cmd) {
1700542f5482SDenis Joseph Barrow 	case TIOCMIWAIT:
1701542f5482SDenis Joseph Barrow 		ret = hso_wait_modem_status(serial, arg);
1702542f5482SDenis Joseph Barrow 		break;
1703542f5482SDenis Joseph Barrow 	default:
1704542f5482SDenis Joseph Barrow 		ret = -ENOIOCTLCMD;
1705542f5482SDenis Joseph Barrow 		break;
1706542f5482SDenis Joseph Barrow 	}
1707542f5482SDenis Joseph Barrow 	return ret;
1708542f5482SDenis Joseph Barrow }
1709542f5482SDenis Joseph Barrow 
1710542f5482SDenis Joseph Barrow 
171172dc1c09SGreg Kroah-Hartman /* starts a transmit */
hso_kick_transmit(struct hso_serial * serial)171272dc1c09SGreg Kroah-Hartman static void hso_kick_transmit(struct hso_serial *serial)
171372dc1c09SGreg Kroah-Hartman {
171472dc1c09SGreg Kroah-Hartman 	unsigned long flags;
171572dc1c09SGreg Kroah-Hartman 	int res;
171672dc1c09SGreg Kroah-Hartman 
171772dc1c09SGreg Kroah-Hartman 	spin_lock_irqsave(&serial->serial_lock, flags);
171872dc1c09SGreg Kroah-Hartman 	if (!serial->tx_buffer_count)
171972dc1c09SGreg Kroah-Hartman 		goto out;
172072dc1c09SGreg Kroah-Hartman 
172172dc1c09SGreg Kroah-Hartman 	if (serial->tx_urb_used)
172272dc1c09SGreg Kroah-Hartman 		goto out;
172372dc1c09SGreg Kroah-Hartman 
172472dc1c09SGreg Kroah-Hartman 	/* Wakeup USB interface if necessary */
172572dc1c09SGreg Kroah-Hartman 	if (hso_get_activity(serial->parent) == -EAGAIN)
172672dc1c09SGreg Kroah-Hartman 		goto out;
172772dc1c09SGreg Kroah-Hartman 
172872dc1c09SGreg Kroah-Hartman 	/* Switch pointers around to avoid memcpy */
1729ae85467cSGustavo A. R. Silva 	swap(serial->tx_buffer, serial->tx_data);
173072dc1c09SGreg Kroah-Hartman 	serial->tx_data_count = serial->tx_buffer_count;
173172dc1c09SGreg Kroah-Hartman 	serial->tx_buffer_count = 0;
173272dc1c09SGreg Kroah-Hartman 
1733ae85467cSGustavo A. R. Silva 	/* If serial->tx_data is set, it means we switched buffers */
1734ae85467cSGustavo A. R. Silva 	if (serial->tx_data && serial->write_data) {
173572dc1c09SGreg Kroah-Hartman 		res = serial->write_data(serial);
173672dc1c09SGreg Kroah-Hartman 		if (res >= 0)
173772dc1c09SGreg Kroah-Hartman 			serial->tx_urb_used = 1;
173872dc1c09SGreg Kroah-Hartman 	}
173972dc1c09SGreg Kroah-Hartman out:
174072dc1c09SGreg Kroah-Hartman 	spin_unlock_irqrestore(&serial->serial_lock, flags);
174172dc1c09SGreg Kroah-Hartman }
174272dc1c09SGreg Kroah-Hartman 
174372dc1c09SGreg Kroah-Hartman /* make a request (for reading and writing data to muxed serial port) */
mux_device_request(struct hso_serial * serial,u8 type,u16 port,struct urb * ctrl_urb,struct usb_ctrlrequest * ctrl_req,u8 * ctrl_urb_data,u32 size)174472dc1c09SGreg Kroah-Hartman static int mux_device_request(struct hso_serial *serial, u8 type, u16 port,
174572dc1c09SGreg Kroah-Hartman 			      struct urb *ctrl_urb,
174672dc1c09SGreg Kroah-Hartman 			      struct usb_ctrlrequest *ctrl_req,
174772dc1c09SGreg Kroah-Hartman 			      u8 *ctrl_urb_data, u32 size)
174872dc1c09SGreg Kroah-Hartman {
174972dc1c09SGreg Kroah-Hartman 	int result;
175072dc1c09SGreg Kroah-Hartman 	int pipe;
175172dc1c09SGreg Kroah-Hartman 
175272dc1c09SGreg Kroah-Hartman 	/* Sanity check */
175372dc1c09SGreg Kroah-Hartman 	if (!serial || !ctrl_urb || !ctrl_req) {
17543981cce6SJoe Perches 		pr_err("%s: Wrong arguments\n", __func__);
175572dc1c09SGreg Kroah-Hartman 		return -EINVAL;
175672dc1c09SGreg Kroah-Hartman 	}
175772dc1c09SGreg Kroah-Hartman 
175872dc1c09SGreg Kroah-Hartman 	/* initialize */
175972dc1c09SGreg Kroah-Hartman 	ctrl_req->wValue = 0;
1760b74f62c1SDenis Joseph Barrow 	ctrl_req->wIndex = cpu_to_le16(hso_port_to_mux(port));
1761b74f62c1SDenis Joseph Barrow 	ctrl_req->wLength = cpu_to_le16(size);
176272dc1c09SGreg Kroah-Hartman 
176372dc1c09SGreg Kroah-Hartman 	if (type == USB_CDC_GET_ENCAPSULATED_RESPONSE) {
176472dc1c09SGreg Kroah-Hartman 		/* Reading command */
176572dc1c09SGreg Kroah-Hartman 		ctrl_req->bRequestType = USB_DIR_IN |
176672dc1c09SGreg Kroah-Hartman 					 USB_TYPE_OPTION_VENDOR |
176772dc1c09SGreg Kroah-Hartman 					 USB_RECIP_INTERFACE;
176872dc1c09SGreg Kroah-Hartman 		ctrl_req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
176972dc1c09SGreg Kroah-Hartman 		pipe = usb_rcvctrlpipe(serial->parent->usb, 0);
177072dc1c09SGreg Kroah-Hartman 	} else {
177172dc1c09SGreg Kroah-Hartman 		/* Writing command */
177272dc1c09SGreg Kroah-Hartman 		ctrl_req->bRequestType = USB_DIR_OUT |
177372dc1c09SGreg Kroah-Hartman 					 USB_TYPE_OPTION_VENDOR |
177472dc1c09SGreg Kroah-Hartman 					 USB_RECIP_INTERFACE;
177572dc1c09SGreg Kroah-Hartman 		ctrl_req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
177672dc1c09SGreg Kroah-Hartman 		pipe = usb_sndctrlpipe(serial->parent->usb, 0);
177772dc1c09SGreg Kroah-Hartman 	}
177872dc1c09SGreg Kroah-Hartman 	/* syslog */
177995a69117SJoe Perches 	hso_dbg(0x2, "%s command (%02x) len: %d, port: %d\n",
178072dc1c09SGreg Kroah-Hartman 		type == USB_CDC_GET_ENCAPSULATED_RESPONSE ? "Read" : "Write",
178172dc1c09SGreg Kroah-Hartman 		ctrl_req->bRequestType, ctrl_req->wLength, port);
178272dc1c09SGreg Kroah-Hartman 
178372dc1c09SGreg Kroah-Hartman 	/* Load ctrl urb */
178472dc1c09SGreg Kroah-Hartman 	ctrl_urb->transfer_flags = 0;
178572dc1c09SGreg Kroah-Hartman 	usb_fill_control_urb(ctrl_urb,
178672dc1c09SGreg Kroah-Hartman 			     serial->parent->usb,
178772dc1c09SGreg Kroah-Hartman 			     pipe,
178872dc1c09SGreg Kroah-Hartman 			     (u8 *) ctrl_req,
178972dc1c09SGreg Kroah-Hartman 			     ctrl_urb_data, size, ctrl_callback, serial);
179072dc1c09SGreg Kroah-Hartman 	/* Send it on merry way */
179172dc1c09SGreg Kroah-Hartman 	result = usb_submit_urb(ctrl_urb, GFP_ATOMIC);
179272dc1c09SGreg Kroah-Hartman 	if (result) {
179372dc1c09SGreg Kroah-Hartman 		dev_err(&ctrl_urb->dev->dev,
17948a5c9c49SJan Dumon 			"%s failed submit ctrl_urb %d type %d\n", __func__,
179572dc1c09SGreg Kroah-Hartman 			result, type);
179672dc1c09SGreg Kroah-Hartman 		return result;
179772dc1c09SGreg Kroah-Hartman 	}
179872dc1c09SGreg Kroah-Hartman 
179972dc1c09SGreg Kroah-Hartman 	/* done */
180072dc1c09SGreg Kroah-Hartman 	return size;
180172dc1c09SGreg Kroah-Hartman }
180272dc1c09SGreg Kroah-Hartman 
180372dc1c09SGreg Kroah-Hartman /* called by intr_callback when read occurs */
hso_mux_serial_read(struct hso_serial * serial)180472dc1c09SGreg Kroah-Hartman static int hso_mux_serial_read(struct hso_serial *serial)
180572dc1c09SGreg Kroah-Hartman {
180672dc1c09SGreg Kroah-Hartman 	if (!serial)
180772dc1c09SGreg Kroah-Hartman 		return -EINVAL;
180872dc1c09SGreg Kroah-Hartman 
180972dc1c09SGreg Kroah-Hartman 	/* clean data */
181072dc1c09SGreg Kroah-Hartman 	memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE);
181172dc1c09SGreg Kroah-Hartman 	/* make the request */
181272dc1c09SGreg Kroah-Hartman 
181372dc1c09SGreg Kroah-Hartman 	if (serial->num_rx_urbs != 1) {
181472dc1c09SGreg Kroah-Hartman 		dev_err(&serial->parent->interface->dev,
181572dc1c09SGreg Kroah-Hartman 			"ERROR: mux'd reads with multiple buffers "
181672dc1c09SGreg Kroah-Hartman 			"not possible\n");
181772dc1c09SGreg Kroah-Hartman 		return 0;
181872dc1c09SGreg Kroah-Hartman 	}
181972dc1c09SGreg Kroah-Hartman 	return mux_device_request(serial,
182072dc1c09SGreg Kroah-Hartman 				  USB_CDC_GET_ENCAPSULATED_RESPONSE,
182172dc1c09SGreg Kroah-Hartman 				  serial->parent->port_spec & HSO_PORT_MASK,
182272dc1c09SGreg Kroah-Hartman 				  serial->rx_urb[0],
182372dc1c09SGreg Kroah-Hartman 				  &serial->ctrl_req_rx,
182472dc1c09SGreg Kroah-Hartman 				  serial->rx_data[0], serial->rx_data_length);
182572dc1c09SGreg Kroah-Hartman }
182672dc1c09SGreg Kroah-Hartman 
182772dc1c09SGreg Kroah-Hartman /* used for muxed serial port callback (muxed serial read) */
intr_callback(struct urb * urb)182872dc1c09SGreg Kroah-Hartman static void intr_callback(struct urb *urb)
182972dc1c09SGreg Kroah-Hartman {
183072dc1c09SGreg Kroah-Hartman 	struct hso_shared_int *shared_int = urb->context;
183172dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial;
183272dc1c09SGreg Kroah-Hartman 	unsigned char *port_req;
183372dc1c09SGreg Kroah-Hartman 	int status = urb->status;
183412c4de4bSSebastian Andrzej Siewior 	unsigned long flags;
183572dc1c09SGreg Kroah-Hartman 	int i;
183672dc1c09SGreg Kroah-Hartman 
183772dc1c09SGreg Kroah-Hartman 	usb_mark_last_busy(urb->dev);
183872dc1c09SGreg Kroah-Hartman 
183972dc1c09SGreg Kroah-Hartman 	/* sanity check */
184072dc1c09SGreg Kroah-Hartman 	if (!shared_int)
184172dc1c09SGreg Kroah-Hartman 		return;
184272dc1c09SGreg Kroah-Hartman 
184372dc1c09SGreg Kroah-Hartman 	/* status check */
184472dc1c09SGreg Kroah-Hartman 	if (status) {
184568a351c5SJan Dumon 		handle_usb_error(status, __func__, NULL);
184672dc1c09SGreg Kroah-Hartman 		return;
184772dc1c09SGreg Kroah-Hartman 	}
184895a69117SJoe Perches 	hso_dbg(0x8, "--- Got intr callback 0x%02X ---\n", status);
184972dc1c09SGreg Kroah-Hartman 
185072dc1c09SGreg Kroah-Hartman 	/* what request? */
185172dc1c09SGreg Kroah-Hartman 	port_req = urb->transfer_buffer;
185295a69117SJoe Perches 	hso_dbg(0x8, "port_req = 0x%.2X\n", *port_req);
185372dc1c09SGreg Kroah-Hartman 	/* loop over all muxed ports to find the one sending this */
185472dc1c09SGreg Kroah-Hartman 	for (i = 0; i < 8; i++) {
185572dc1c09SGreg Kroah-Hartman 		/* max 8 channels on MUX */
185672dc1c09SGreg Kroah-Hartman 		if (*port_req & (1 << i)) {
185772dc1c09SGreg Kroah-Hartman 			serial = get_serial_by_shared_int_and_type(shared_int,
185872dc1c09SGreg Kroah-Hartman 								   (1 << i));
185972dc1c09SGreg Kroah-Hartman 			if (serial != NULL) {
186095a69117SJoe Perches 				hso_dbg(0x1, "Pending read interrupt on port %d\n",
186195a69117SJoe Perches 					i);
186212c4de4bSSebastian Andrzej Siewior 				spin_lock_irqsave(&serial->serial_lock, flags);
1863f4763e96SJan Dumon 				if (serial->rx_state == RX_IDLE &&
18645ce76e77SJiri Slaby 					serial->port.count > 0) {
186572dc1c09SGreg Kroah-Hartman 					/* Setup and send a ctrl req read on
186672dc1c09SGreg Kroah-Hartman 					 * port i */
18678ef5ba63SDenis Joseph Barrow 					if (!serial->rx_urb_filled[0]) {
18688ef5ba63SDenis Joseph Barrow 						serial->rx_state = RX_SENT;
186972dc1c09SGreg Kroah-Hartman 						hso_mux_serial_read(serial);
18708ef5ba63SDenis Joseph Barrow 					} else
18718ef5ba63SDenis Joseph Barrow 						serial->rx_state = RX_PENDING;
187272dc1c09SGreg Kroah-Hartman 				} else {
187395a69117SJoe Perches 					hso_dbg(0x1, "Already a read pending on port %d or port not open\n",
187495a69117SJoe Perches 						i);
187572dc1c09SGreg Kroah-Hartman 				}
187612c4de4bSSebastian Andrzej Siewior 				spin_unlock_irqrestore(&serial->serial_lock,
187712c4de4bSSebastian Andrzej Siewior 						       flags);
187872dc1c09SGreg Kroah-Hartman 			}
187972dc1c09SGreg Kroah-Hartman 		}
188072dc1c09SGreg Kroah-Hartman 	}
188172dc1c09SGreg Kroah-Hartman 	/* Resubmit interrupt urb */
188272dc1c09SGreg Kroah-Hartman 	hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_ATOMIC);
188372dc1c09SGreg Kroah-Hartman }
188472dc1c09SGreg Kroah-Hartman 
188572dc1c09SGreg Kroah-Hartman /* called for writing to muxed serial port */
hso_mux_serial_write_data(struct hso_serial * serial)188672dc1c09SGreg Kroah-Hartman static int hso_mux_serial_write_data(struct hso_serial *serial)
188772dc1c09SGreg Kroah-Hartman {
188872dc1c09SGreg Kroah-Hartman 	if (NULL == serial)
188972dc1c09SGreg Kroah-Hartman 		return -EINVAL;
189072dc1c09SGreg Kroah-Hartman 
189172dc1c09SGreg Kroah-Hartman 	return mux_device_request(serial,
189272dc1c09SGreg Kroah-Hartman 				  USB_CDC_SEND_ENCAPSULATED_COMMAND,
189372dc1c09SGreg Kroah-Hartman 				  serial->parent->port_spec & HSO_PORT_MASK,
189472dc1c09SGreg Kroah-Hartman 				  serial->tx_urb,
189572dc1c09SGreg Kroah-Hartman 				  &serial->ctrl_req_tx,
189672dc1c09SGreg Kroah-Hartman 				  serial->tx_data, serial->tx_data_count);
189772dc1c09SGreg Kroah-Hartman }
189872dc1c09SGreg Kroah-Hartman 
189972dc1c09SGreg Kroah-Hartman /* write callback for Diag and CS port */
hso_std_serial_write_bulk_callback(struct urb * urb)190072dc1c09SGreg Kroah-Hartman static void hso_std_serial_write_bulk_callback(struct urb *urb)
190172dc1c09SGreg Kroah-Hartman {
190272dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial = urb->context;
190372dc1c09SGreg Kroah-Hartman 	int status = urb->status;
190412c4de4bSSebastian Andrzej Siewior 	unsigned long flags;
190572dc1c09SGreg Kroah-Hartman 
190672dc1c09SGreg Kroah-Hartman 	/* sanity check */
190772dc1c09SGreg Kroah-Hartman 	if (!serial) {
190895a69117SJoe Perches 		hso_dbg(0x1, "serial == NULL\n");
190972dc1c09SGreg Kroah-Hartman 		return;
191072dc1c09SGreg Kroah-Hartman 	}
191172dc1c09SGreg Kroah-Hartman 
191212c4de4bSSebastian Andrzej Siewior 	spin_lock_irqsave(&serial->serial_lock, flags);
191372dc1c09SGreg Kroah-Hartman 	serial->tx_urb_used = 0;
191412c4de4bSSebastian Andrzej Siewior 	spin_unlock_irqrestore(&serial->serial_lock, flags);
191572dc1c09SGreg Kroah-Hartman 	if (status) {
191668a351c5SJan Dumon 		handle_usb_error(status, __func__, serial->parent);
191772dc1c09SGreg Kroah-Hartman 		return;
191872dc1c09SGreg Kroah-Hartman 	}
191972dc1c09SGreg Kroah-Hartman 	hso_put_activity(serial->parent);
19206aad04f2SJiri Slaby 	tty_port_tty_wakeup(&serial->port);
192172dc1c09SGreg Kroah-Hartman 	hso_kick_transmit(serial);
192272dc1c09SGreg Kroah-Hartman 
192395a69117SJoe Perches 	hso_dbg(0x1, "\n");
192472dc1c09SGreg Kroah-Hartman }
192572dc1c09SGreg Kroah-Hartman 
192672dc1c09SGreg Kroah-Hartman /* called for writing diag or CS serial port */
hso_std_serial_write_data(struct hso_serial * serial)192772dc1c09SGreg Kroah-Hartman static int hso_std_serial_write_data(struct hso_serial *serial)
192872dc1c09SGreg Kroah-Hartman {
192972dc1c09SGreg Kroah-Hartman 	int count = serial->tx_data_count;
193072dc1c09SGreg Kroah-Hartman 	int result;
193172dc1c09SGreg Kroah-Hartman 
193272dc1c09SGreg Kroah-Hartman 	usb_fill_bulk_urb(serial->tx_urb,
193372dc1c09SGreg Kroah-Hartman 			  serial->parent->usb,
193472dc1c09SGreg Kroah-Hartman 			  usb_sndbulkpipe(serial->parent->usb,
193572dc1c09SGreg Kroah-Hartman 					  serial->out_endp->
193672dc1c09SGreg Kroah-Hartman 					  bEndpointAddress & 0x7F),
193772dc1c09SGreg Kroah-Hartman 			  serial->tx_data, serial->tx_data_count,
193872dc1c09SGreg Kroah-Hartman 			  hso_std_serial_write_bulk_callback, serial);
193972dc1c09SGreg Kroah-Hartman 
194072dc1c09SGreg Kroah-Hartman 	result = usb_submit_urb(serial->tx_urb, GFP_ATOMIC);
194172dc1c09SGreg Kroah-Hartman 	if (result) {
194272dc1c09SGreg Kroah-Hartman 		dev_warn(&serial->parent->usb->dev,
194372dc1c09SGreg Kroah-Hartman 			 "Failed to submit urb - res %d\n", result);
194472dc1c09SGreg Kroah-Hartman 		return result;
194572dc1c09SGreg Kroah-Hartman 	}
194672dc1c09SGreg Kroah-Hartman 
194772dc1c09SGreg Kroah-Hartman 	return count;
194872dc1c09SGreg Kroah-Hartman }
194972dc1c09SGreg Kroah-Hartman 
195072dc1c09SGreg Kroah-Hartman /* callback after read or write on muxed serial port */
ctrl_callback(struct urb * urb)195172dc1c09SGreg Kroah-Hartman static void ctrl_callback(struct urb *urb)
195272dc1c09SGreg Kroah-Hartman {
195372dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial = urb->context;
195472dc1c09SGreg Kroah-Hartman 	struct usb_ctrlrequest *req;
195572dc1c09SGreg Kroah-Hartman 	int status = urb->status;
195612c4de4bSSebastian Andrzej Siewior 	unsigned long flags;
195772dc1c09SGreg Kroah-Hartman 
195872dc1c09SGreg Kroah-Hartman 	/* sanity check */
195972dc1c09SGreg Kroah-Hartman 	if (!serial)
196072dc1c09SGreg Kroah-Hartman 		return;
196172dc1c09SGreg Kroah-Hartman 
196212c4de4bSSebastian Andrzej Siewior 	spin_lock_irqsave(&serial->serial_lock, flags);
196372dc1c09SGreg Kroah-Hartman 	serial->tx_urb_used = 0;
196412c4de4bSSebastian Andrzej Siewior 	spin_unlock_irqrestore(&serial->serial_lock, flags);
196572dc1c09SGreg Kroah-Hartman 	if (status) {
196668a351c5SJan Dumon 		handle_usb_error(status, __func__, serial->parent);
196772dc1c09SGreg Kroah-Hartman 		return;
196872dc1c09SGreg Kroah-Hartman 	}
196972dc1c09SGreg Kroah-Hartman 
197072dc1c09SGreg Kroah-Hartman 	/* what request? */
197172dc1c09SGreg Kroah-Hartman 	req = (struct usb_ctrlrequest *)(urb->setup_packet);
197295a69117SJoe Perches 	hso_dbg(0x8, "--- Got muxed ctrl callback 0x%02X ---\n", status);
197395a69117SJoe Perches 	hso_dbg(0x8, "Actual length of urb = %d\n", urb->actual_length);
197472dc1c09SGreg Kroah-Hartman 	DUMP1(urb->transfer_buffer, urb->actual_length);
197572dc1c09SGreg Kroah-Hartman 
197672dc1c09SGreg Kroah-Hartman 	if (req->bRequestType ==
197772dc1c09SGreg Kroah-Hartman 	    (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) {
197872dc1c09SGreg Kroah-Hartman 		/* response to a read command */
19798ef5ba63SDenis Joseph Barrow 		serial->rx_urb_filled[0] = 1;
198012c4de4bSSebastian Andrzej Siewior 		spin_lock_irqsave(&serial->serial_lock, flags);
19818ef5ba63SDenis Joseph Barrow 		put_rxbuf_data_and_resubmit_ctrl_urb(serial);
198212c4de4bSSebastian Andrzej Siewior 		spin_unlock_irqrestore(&serial->serial_lock, flags);
198372dc1c09SGreg Kroah-Hartman 	} else {
198472dc1c09SGreg Kroah-Hartman 		hso_put_activity(serial->parent);
19856aad04f2SJiri Slaby 		tty_port_tty_wakeup(&serial->port);
198672dc1c09SGreg Kroah-Hartman 		/* response to a write command */
198772dc1c09SGreg Kroah-Hartman 		hso_kick_transmit(serial);
198872dc1c09SGreg Kroah-Hartman 	}
198972dc1c09SGreg Kroah-Hartman }
199072dc1c09SGreg Kroah-Hartman 
199172dc1c09SGreg Kroah-Hartman /* handle RX data for serial port */
put_rxbuf_data(struct urb * urb,struct hso_serial * serial)19928ef5ba63SDenis Joseph Barrow static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
199372dc1c09SGreg Kroah-Hartman {
1994e136e303SAlan Cox 	struct tty_struct *tty;
19958f9818afSOlivier Sobrie 	int count;
1996e136e303SAlan Cox 
199772dc1c09SGreg Kroah-Hartman 	/* Sanity check */
199872dc1c09SGreg Kroah-Hartman 	if (urb == NULL || serial == NULL) {
199995a69117SJoe Perches 		hso_dbg(0x1, "serial = NULL\n");
20008ef5ba63SDenis Joseph Barrow 		return -2;
200172dc1c09SGreg Kroah-Hartman 	}
200272dc1c09SGreg Kroah-Hartman 
20039f8c0b08SJiri Slaby 	tty = tty_port_tty_get(&serial->port);
2004e136e303SAlan Cox 
200597ef38b8SPeter Hurley 	if (tty && tty_throttled(tty)) {
20065839b414SDenis Joseph Barrow 		tty_kref_put(tty);
20078ef5ba63SDenis Joseph Barrow 		return -1;
20085839b414SDenis Joseph Barrow 	}
20098f9818afSOlivier Sobrie 
20108f9818afSOlivier Sobrie 	/* Push data to tty */
201195a69117SJoe Perches 	hso_dbg(0x1, "data to push to tty\n");
20128f9818afSOlivier Sobrie 	count = tty_buffer_request_room(&serial->port, urb->actual_length);
20138f9818afSOlivier Sobrie 	if (count >= urb->actual_length) {
20148f9818afSOlivier Sobrie 		tty_insert_flip_string(&serial->port, urb->transfer_buffer,
20158f9818afSOlivier Sobrie 				       urb->actual_length);
20162e124b4aSJiri Slaby 		tty_flip_buffer_push(&serial->port);
20178f9818afSOlivier Sobrie 	} else {
20188f9818afSOlivier Sobrie 		dev_warn(&serial->parent->usb->dev,
20198f9818afSOlivier Sobrie 			 "dropping data, %d bytes lost\n", urb->actual_length);
202072dc1c09SGreg Kroah-Hartman 	}
20218f9818afSOlivier Sobrie 
20229f8c0b08SJiri Slaby 	tty_kref_put(tty);
20232e124b4aSJiri Slaby 
20248ef5ba63SDenis Joseph Barrow 	serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0;
20258f9818afSOlivier Sobrie 
20268f9818afSOlivier Sobrie 	return 0;
202772dc1c09SGreg Kroah-Hartman }
202872dc1c09SGreg Kroah-Hartman 
202972dc1c09SGreg Kroah-Hartman 
203072dc1c09SGreg Kroah-Hartman /* Base driver functions */
203172dc1c09SGreg Kroah-Hartman 
hso_log_port(struct hso_device * hso_dev)203272dc1c09SGreg Kroah-Hartman static void hso_log_port(struct hso_device *hso_dev)
203372dc1c09SGreg Kroah-Hartman {
203472dc1c09SGreg Kroah-Hartman 	char *port_type;
203572dc1c09SGreg Kroah-Hartman 	char port_dev[20];
203672dc1c09SGreg Kroah-Hartman 
203772dc1c09SGreg Kroah-Hartman 	switch (hso_dev->port_spec & HSO_PORT_MASK) {
203872dc1c09SGreg Kroah-Hartman 	case HSO_PORT_CONTROL:
203972dc1c09SGreg Kroah-Hartman 		port_type = "Control";
204072dc1c09SGreg Kroah-Hartman 		break;
204172dc1c09SGreg Kroah-Hartman 	case HSO_PORT_APP:
204272dc1c09SGreg Kroah-Hartman 		port_type = "Application";
204372dc1c09SGreg Kroah-Hartman 		break;
204472dc1c09SGreg Kroah-Hartman 	case HSO_PORT_GPS:
204572dc1c09SGreg Kroah-Hartman 		port_type = "GPS";
204672dc1c09SGreg Kroah-Hartman 		break;
204772dc1c09SGreg Kroah-Hartman 	case HSO_PORT_GPS_CONTROL:
204872dc1c09SGreg Kroah-Hartman 		port_type = "GPS control";
204972dc1c09SGreg Kroah-Hartman 		break;
205072dc1c09SGreg Kroah-Hartman 	case HSO_PORT_APP2:
205172dc1c09SGreg Kroah-Hartman 		port_type = "Application2";
205272dc1c09SGreg Kroah-Hartman 		break;
205372dc1c09SGreg Kroah-Hartman 	case HSO_PORT_PCSC:
205472dc1c09SGreg Kroah-Hartman 		port_type = "PCSC";
205572dc1c09SGreg Kroah-Hartman 		break;
205672dc1c09SGreg Kroah-Hartman 	case HSO_PORT_DIAG:
205772dc1c09SGreg Kroah-Hartman 		port_type = "Diagnostic";
205872dc1c09SGreg Kroah-Hartman 		break;
205972dc1c09SGreg Kroah-Hartman 	case HSO_PORT_DIAG2:
206072dc1c09SGreg Kroah-Hartman 		port_type = "Diagnostic2";
206172dc1c09SGreg Kroah-Hartman 		break;
206272dc1c09SGreg Kroah-Hartman 	case HSO_PORT_MODEM:
206372dc1c09SGreg Kroah-Hartman 		port_type = "Modem";
206472dc1c09SGreg Kroah-Hartman 		break;
206572dc1c09SGreg Kroah-Hartman 	case HSO_PORT_NETWORK:
206672dc1c09SGreg Kroah-Hartman 		port_type = "Network";
206772dc1c09SGreg Kroah-Hartman 		break;
206872dc1c09SGreg Kroah-Hartman 	default:
206972dc1c09SGreg Kroah-Hartman 		port_type = "Unknown";
207072dc1c09SGreg Kroah-Hartman 		break;
207172dc1c09SGreg Kroah-Hartman 	}
207272dc1c09SGreg Kroah-Hartman 	if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
207372dc1c09SGreg Kroah-Hartman 		sprintf(port_dev, "%s", dev2net(hso_dev)->net->name);
207472dc1c09SGreg Kroah-Hartman 	} else
207572dc1c09SGreg Kroah-Hartman 		sprintf(port_dev, "/dev/%s%d", tty_filename,
207672dc1c09SGreg Kroah-Hartman 			dev2ser(hso_dev)->minor);
207772dc1c09SGreg Kroah-Hartman 
207872dc1c09SGreg Kroah-Hartman 	dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n",
207972dc1c09SGreg Kroah-Hartman 		port_type, port_dev);
208072dc1c09SGreg Kroah-Hartman }
208172dc1c09SGreg Kroah-Hartman 
hso_start_net_device(struct hso_device * hso_dev)208272dc1c09SGreg Kroah-Hartman static int hso_start_net_device(struct hso_device *hso_dev)
208372dc1c09SGreg Kroah-Hartman {
208472dc1c09SGreg Kroah-Hartman 	int i, result = 0;
208572dc1c09SGreg Kroah-Hartman 	struct hso_net *hso_net = dev2net(hso_dev);
208672dc1c09SGreg Kroah-Hartman 
208772dc1c09SGreg Kroah-Hartman 	if (!hso_net)
208872dc1c09SGreg Kroah-Hartman 		return -ENODEV;
208972dc1c09SGreg Kroah-Hartman 
209072dc1c09SGreg Kroah-Hartman 	/* send URBs for all read buffers */
209172dc1c09SGreg Kroah-Hartman 	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
209272dc1c09SGreg Kroah-Hartman 
209372dc1c09SGreg Kroah-Hartman 		/* Prep a receive URB */
209472dc1c09SGreg Kroah-Hartman 		usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i],
209572dc1c09SGreg Kroah-Hartman 				  hso_dev->usb,
209672dc1c09SGreg Kroah-Hartman 				  usb_rcvbulkpipe(hso_dev->usb,
209772dc1c09SGreg Kroah-Hartman 						  hso_net->in_endp->
209872dc1c09SGreg Kroah-Hartman 						  bEndpointAddress & 0x7F),
209972dc1c09SGreg Kroah-Hartman 				  hso_net->mux_bulk_rx_buf_pool[i],
210072dc1c09SGreg Kroah-Hartman 				  MUX_BULK_RX_BUF_SIZE, read_bulk_callback,
210172dc1c09SGreg Kroah-Hartman 				  hso_net);
210272dc1c09SGreg Kroah-Hartman 
210372dc1c09SGreg Kroah-Hartman 		/* Put it out there so the device can send us stuff */
210472dc1c09SGreg Kroah-Hartman 		result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i],
210572dc1c09SGreg Kroah-Hartman 					GFP_NOIO);
210672dc1c09SGreg Kroah-Hartman 		if (result)
210772dc1c09SGreg Kroah-Hartman 			dev_warn(&hso_dev->usb->dev,
210872dc1c09SGreg Kroah-Hartman 				"%s failed mux_bulk_rx_urb[%d] %d\n", __func__,
210972dc1c09SGreg Kroah-Hartman 				i, result);
211072dc1c09SGreg Kroah-Hartman 	}
211172dc1c09SGreg Kroah-Hartman 
211272dc1c09SGreg Kroah-Hartman 	return result;
211372dc1c09SGreg Kroah-Hartman }
211472dc1c09SGreg Kroah-Hartman 
hso_stop_net_device(struct hso_device * hso_dev)211572dc1c09SGreg Kroah-Hartman static int hso_stop_net_device(struct hso_device *hso_dev)
211672dc1c09SGreg Kroah-Hartman {
211772dc1c09SGreg Kroah-Hartman 	int i;
211872dc1c09SGreg Kroah-Hartman 	struct hso_net *hso_net = dev2net(hso_dev);
211972dc1c09SGreg Kroah-Hartman 
212072dc1c09SGreg Kroah-Hartman 	if (!hso_net)
212172dc1c09SGreg Kroah-Hartman 		return -ENODEV;
212272dc1c09SGreg Kroah-Hartman 
212372dc1c09SGreg Kroah-Hartman 	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
212472dc1c09SGreg Kroah-Hartman 		if (hso_net->mux_bulk_rx_urb_pool[i])
212572dc1c09SGreg Kroah-Hartman 			usb_kill_urb(hso_net->mux_bulk_rx_urb_pool[i]);
212672dc1c09SGreg Kroah-Hartman 
212772dc1c09SGreg Kroah-Hartman 	}
212872dc1c09SGreg Kroah-Hartman 	if (hso_net->mux_bulk_tx_urb)
212972dc1c09SGreg Kroah-Hartman 		usb_kill_urb(hso_net->mux_bulk_tx_urb);
213072dc1c09SGreg Kroah-Hartman 
213172dc1c09SGreg Kroah-Hartman 	return 0;
213272dc1c09SGreg Kroah-Hartman }
213372dc1c09SGreg Kroah-Hartman 
hso_start_serial_device(struct hso_device * hso_dev,gfp_t flags)213472dc1c09SGreg Kroah-Hartman static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags)
213572dc1c09SGreg Kroah-Hartman {
213672dc1c09SGreg Kroah-Hartman 	int i, result = 0;
213772dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial = dev2ser(hso_dev);
213872dc1c09SGreg Kroah-Hartman 
213972dc1c09SGreg Kroah-Hartman 	if (!serial)
214072dc1c09SGreg Kroah-Hartman 		return -ENODEV;
214172dc1c09SGreg Kroah-Hartman 
214272dc1c09SGreg Kroah-Hartman 	/* If it is not the MUX port fill in and submit a bulk urb (already
214372dc1c09SGreg Kroah-Hartman 	 * allocated in hso_serial_start) */
214472dc1c09SGreg Kroah-Hartman 	if (!(serial->parent->port_spec & HSO_INTF_MUX)) {
214572dc1c09SGreg Kroah-Hartman 		for (i = 0; i < serial->num_rx_urbs; i++) {
214672dc1c09SGreg Kroah-Hartman 			usb_fill_bulk_urb(serial->rx_urb[i],
214772dc1c09SGreg Kroah-Hartman 					  serial->parent->usb,
214872dc1c09SGreg Kroah-Hartman 					  usb_rcvbulkpipe(serial->parent->usb,
214972dc1c09SGreg Kroah-Hartman 							  serial->in_endp->
215072dc1c09SGreg Kroah-Hartman 							  bEndpointAddress &
215172dc1c09SGreg Kroah-Hartman 							  0x7F),
215272dc1c09SGreg Kroah-Hartman 					  serial->rx_data[i],
215372dc1c09SGreg Kroah-Hartman 					  serial->rx_data_length,
215472dc1c09SGreg Kroah-Hartman 					  hso_std_serial_read_bulk_callback,
215572dc1c09SGreg Kroah-Hartman 					  serial);
215672dc1c09SGreg Kroah-Hartman 			result = usb_submit_urb(serial->rx_urb[i], flags);
215772dc1c09SGreg Kroah-Hartman 			if (result) {
215872dc1c09SGreg Kroah-Hartman 				dev_warn(&serial->parent->usb->dev,
215972dc1c09SGreg Kroah-Hartman 					 "Failed to submit urb - res %d\n",
216072dc1c09SGreg Kroah-Hartman 					 result);
216172dc1c09SGreg Kroah-Hartman 				break;
216272dc1c09SGreg Kroah-Hartman 			}
216372dc1c09SGreg Kroah-Hartman 		}
216472dc1c09SGreg Kroah-Hartman 	} else {
216572dc1c09SGreg Kroah-Hartman 		mutex_lock(&serial->shared_int->shared_int_lock);
216672dc1c09SGreg Kroah-Hartman 		if (!serial->shared_int->use_count) {
216772dc1c09SGreg Kroah-Hartman 			result =
216872dc1c09SGreg Kroah-Hartman 			    hso_mux_submit_intr_urb(serial->shared_int,
216972dc1c09SGreg Kroah-Hartman 						    hso_dev->usb, flags);
217072dc1c09SGreg Kroah-Hartman 		}
217172dc1c09SGreg Kroah-Hartman 		serial->shared_int->use_count++;
217272dc1c09SGreg Kroah-Hartman 		mutex_unlock(&serial->shared_int->shared_int_lock);
217372dc1c09SGreg Kroah-Hartman 	}
2174542f5482SDenis Joseph Barrow 	if (serial->tiocmget)
2175542f5482SDenis Joseph Barrow 		tiocmget_submit_urb(serial,
2176542f5482SDenis Joseph Barrow 				    serial->tiocmget,
2177542f5482SDenis Joseph Barrow 				    serial->parent->usb);
217872dc1c09SGreg Kroah-Hartman 	return result;
217972dc1c09SGreg Kroah-Hartman }
218072dc1c09SGreg Kroah-Hartman 
hso_stop_serial_device(struct hso_device * hso_dev)218172dc1c09SGreg Kroah-Hartman static int hso_stop_serial_device(struct hso_device *hso_dev)
218272dc1c09SGreg Kroah-Hartman {
218372dc1c09SGreg Kroah-Hartman 	int i;
218472dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial = dev2ser(hso_dev);
2185542f5482SDenis Joseph Barrow 	struct hso_tiocmget  *tiocmget;
218672dc1c09SGreg Kroah-Hartman 
218772dc1c09SGreg Kroah-Hartman 	if (!serial)
218872dc1c09SGreg Kroah-Hartman 		return -ENODEV;
218972dc1c09SGreg Kroah-Hartman 
219072dc1c09SGreg Kroah-Hartman 	for (i = 0; i < serial->num_rx_urbs; i++) {
21918ef5ba63SDenis Joseph Barrow 		if (serial->rx_urb[i]) {
219272dc1c09SGreg Kroah-Hartman 			usb_kill_urb(serial->rx_urb[i]);
21938ef5ba63SDenis Joseph Barrow 			serial->rx_urb_filled[i] = 0;
219472dc1c09SGreg Kroah-Hartman 		}
21958ef5ba63SDenis Joseph Barrow 	}
21968ef5ba63SDenis Joseph Barrow 	serial->curr_rx_urb_idx = 0;
219772dc1c09SGreg Kroah-Hartman 
219872dc1c09SGreg Kroah-Hartman 	if (serial->tx_urb)
219972dc1c09SGreg Kroah-Hartman 		usb_kill_urb(serial->tx_urb);
220072dc1c09SGreg Kroah-Hartman 
220172dc1c09SGreg Kroah-Hartman 	if (serial->shared_int) {
220272dc1c09SGreg Kroah-Hartman 		mutex_lock(&serial->shared_int->shared_int_lock);
220372dc1c09SGreg Kroah-Hartman 		if (serial->shared_int->use_count &&
220472dc1c09SGreg Kroah-Hartman 		    (--serial->shared_int->use_count == 0)) {
220572dc1c09SGreg Kroah-Hartman 			struct urb *urb;
220672dc1c09SGreg Kroah-Hartman 
220772dc1c09SGreg Kroah-Hartman 			urb = serial->shared_int->shared_intr_urb;
220872dc1c09SGreg Kroah-Hartman 			if (urb)
220972dc1c09SGreg Kroah-Hartman 				usb_kill_urb(urb);
221072dc1c09SGreg Kroah-Hartman 		}
221172dc1c09SGreg Kroah-Hartman 		mutex_unlock(&serial->shared_int->shared_int_lock);
221272dc1c09SGreg Kroah-Hartman 	}
2213542f5482SDenis Joseph Barrow 	tiocmget = serial->tiocmget;
2214542f5482SDenis Joseph Barrow 	if (tiocmget) {
2215542f5482SDenis Joseph Barrow 		wake_up_interruptible(&tiocmget->waitq);
2216542f5482SDenis Joseph Barrow 		usb_kill_urb(tiocmget->urb);
2217542f5482SDenis Joseph Barrow 	}
221872dc1c09SGreg Kroah-Hartman 
221972dc1c09SGreg Kroah-Hartman 	return 0;
222072dc1c09SGreg Kroah-Hartman }
222172dc1c09SGreg Kroah-Hartman 
hso_serial_tty_unregister(struct hso_serial * serial)222269b377b3SOlivier Sobrie static void hso_serial_tty_unregister(struct hso_serial *serial)
222372dc1c09SGreg Kroah-Hartman {
222472dc1c09SGreg Kroah-Hartman 	tty_unregister_device(tty_drv, serial->minor);
22258a12f883SAnirudh Rayabharam 	release_minor(serial);
222669b377b3SOlivier Sobrie }
222769b377b3SOlivier Sobrie 
hso_serial_common_free(struct hso_serial * serial)222869b377b3SOlivier Sobrie static void hso_serial_common_free(struct hso_serial *serial)
222969b377b3SOlivier Sobrie {
223069b377b3SOlivier Sobrie 	int i;
223172dc1c09SGreg Kroah-Hartman 
223272dc1c09SGreg Kroah-Hartman 	for (i = 0; i < serial->num_rx_urbs; i++) {
223372dc1c09SGreg Kroah-Hartman 		/* unlink and free RX URB */
223472dc1c09SGreg Kroah-Hartman 		usb_free_urb(serial->rx_urb[i]);
223572dc1c09SGreg Kroah-Hartman 		/* free the RX buffer */
223672dc1c09SGreg Kroah-Hartman 		kfree(serial->rx_data[i]);
223772dc1c09SGreg Kroah-Hartman 	}
223872dc1c09SGreg Kroah-Hartman 
223972dc1c09SGreg Kroah-Hartman 	/* unlink and free TX URB */
224072dc1c09SGreg Kroah-Hartman 	usb_free_urb(serial->tx_urb);
2241295fc56fSOlivier Sobrie 	kfree(serial->tx_buffer);
224272dc1c09SGreg Kroah-Hartman 	kfree(serial->tx_data);
2243191c5f10SJiri Slaby 	tty_port_destroy(&serial->port);
224472dc1c09SGreg Kroah-Hartman }
224572dc1c09SGreg Kroah-Hartman 
hso_serial_common_create(struct hso_serial * serial,int num_urbs,int rx_size,int tx_size)224672dc1c09SGreg Kroah-Hartman static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
224772dc1c09SGreg Kroah-Hartman 				    int rx_size, int tx_size)
224872dc1c09SGreg Kroah-Hartman {
224972dc1c09SGreg Kroah-Hartman 	int i;
225072dc1c09SGreg Kroah-Hartman 
2251191c5f10SJiri Slaby 	tty_port_init(&serial->port);
2252191c5f10SJiri Slaby 
22538a12f883SAnirudh Rayabharam 	if (obtain_minor(serial))
2254e911e99aSRustam Kovhaev 		goto exit2;
225572dc1c09SGreg Kroah-Hartman 
225672dc1c09SGreg Kroah-Hartman 	/* register our minor number */
22574134069fSTakashi Iwai 	serial->parent->dev = tty_port_register_device_attr(&serial->port,
22588a12f883SAnirudh Rayabharam 			tty_drv, serial->minor, &serial->parent->interface->dev,
22594134069fSTakashi Iwai 			serial->parent, hso_serial_dev_groups);
22608a12f883SAnirudh Rayabharam 	if (IS_ERR(serial->parent->dev)) {
22618a12f883SAnirudh Rayabharam 		release_minor(serial);
2262e911e99aSRustam Kovhaev 		goto exit2;
22638a12f883SAnirudh Rayabharam 	}
226472dc1c09SGreg Kroah-Hartman 
226572dc1c09SGreg Kroah-Hartman 	serial->magic = HSO_SERIAL_MAGIC;
226672dc1c09SGreg Kroah-Hartman 	spin_lock_init(&serial->serial_lock);
226772dc1c09SGreg Kroah-Hartman 	serial->num_rx_urbs = num_urbs;
226872dc1c09SGreg Kroah-Hartman 
226972dc1c09SGreg Kroah-Hartman 	/* RX, allocate urb and initialize */
227072dc1c09SGreg Kroah-Hartman 
227172dc1c09SGreg Kroah-Hartman 	/* prepare our RX buffer */
227272dc1c09SGreg Kroah-Hartman 	serial->rx_data_length = rx_size;
227372dc1c09SGreg Kroah-Hartman 	for (i = 0; i < serial->num_rx_urbs; i++) {
227472dc1c09SGreg Kroah-Hartman 		serial->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
227512800ea9SWolfram Sang 		if (!serial->rx_urb[i])
227672dc1c09SGreg Kroah-Hartman 			goto exit;
227772dc1c09SGreg Kroah-Hartman 		serial->rx_urb[i]->transfer_buffer = NULL;
227872dc1c09SGreg Kroah-Hartman 		serial->rx_urb[i]->transfer_buffer_length = 0;
227972dc1c09SGreg Kroah-Hartman 		serial->rx_data[i] = kzalloc(serial->rx_data_length,
228072dc1c09SGreg Kroah-Hartman 					     GFP_KERNEL);
228138673c82SJoe Perches 		if (!serial->rx_data[i])
228272dc1c09SGreg Kroah-Hartman 			goto exit;
228372dc1c09SGreg Kroah-Hartman 	}
228472dc1c09SGreg Kroah-Hartman 
228572dc1c09SGreg Kroah-Hartman 	/* TX, allocate urb and initialize */
228672dc1c09SGreg Kroah-Hartman 	serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
228712800ea9SWolfram Sang 	if (!serial->tx_urb)
228872dc1c09SGreg Kroah-Hartman 		goto exit;
228972dc1c09SGreg Kroah-Hartman 	serial->tx_urb->transfer_buffer = NULL;
229072dc1c09SGreg Kroah-Hartman 	serial->tx_urb->transfer_buffer_length = 0;
229172dc1c09SGreg Kroah-Hartman 	/* prepare our TX buffer */
229272dc1c09SGreg Kroah-Hartman 	serial->tx_data_count = 0;
229372dc1c09SGreg Kroah-Hartman 	serial->tx_buffer_count = 0;
229472dc1c09SGreg Kroah-Hartman 	serial->tx_data_length = tx_size;
229572dc1c09SGreg Kroah-Hartman 	serial->tx_data = kzalloc(serial->tx_data_length, GFP_KERNEL);
229638673c82SJoe Perches 	if (!serial->tx_data)
229772dc1c09SGreg Kroah-Hartman 		goto exit;
229838673c82SJoe Perches 
229972dc1c09SGreg Kroah-Hartman 	serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL);
230038673c82SJoe Perches 	if (!serial->tx_buffer)
230172dc1c09SGreg Kroah-Hartman 		goto exit;
230272dc1c09SGreg Kroah-Hartman 
230372dc1c09SGreg Kroah-Hartman 	return 0;
230472dc1c09SGreg Kroah-Hartman exit:
230569b377b3SOlivier Sobrie 	hso_serial_tty_unregister(serial);
2306e911e99aSRustam Kovhaev exit2:
230772dc1c09SGreg Kroah-Hartman 	hso_serial_common_free(serial);
230872dc1c09SGreg Kroah-Hartman 	return -1;
230972dc1c09SGreg Kroah-Hartman }
231072dc1c09SGreg Kroah-Hartman 
231172dc1c09SGreg Kroah-Hartman /* Creates a general hso device */
hso_create_device(struct usb_interface * intf,int port_spec)231272dc1c09SGreg Kroah-Hartman static struct hso_device *hso_create_device(struct usb_interface *intf,
231372dc1c09SGreg Kroah-Hartman 					    int port_spec)
231472dc1c09SGreg Kroah-Hartman {
231572dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev;
231672dc1c09SGreg Kroah-Hartman 
231725ce79dbSChristophe JAILLET 	hso_dev = kzalloc(sizeof(*hso_dev), GFP_KERNEL);
231872dc1c09SGreg Kroah-Hartman 	if (!hso_dev)
231972dc1c09SGreg Kroah-Hartman 		return NULL;
232072dc1c09SGreg Kroah-Hartman 
232172dc1c09SGreg Kroah-Hartman 	hso_dev->port_spec = port_spec;
232272dc1c09SGreg Kroah-Hartman 	hso_dev->usb = interface_to_usbdev(intf);
232372dc1c09SGreg Kroah-Hartman 	hso_dev->interface = intf;
232472dc1c09SGreg Kroah-Hartman 	kref_init(&hso_dev->ref);
2325ab153d84SDavid S. Miller 	mutex_init(&hso_dev->mutex);
2326ab153d84SDavid S. Miller 
232772dc1c09SGreg Kroah-Hartman 	INIT_WORK(&hso_dev->async_get_intf, async_get_intf);
232872dc1c09SGreg Kroah-Hartman 	INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
232972dc1c09SGreg Kroah-Hartman 
233072dc1c09SGreg Kroah-Hartman 	return hso_dev;
233172dc1c09SGreg Kroah-Hartman }
233272dc1c09SGreg Kroah-Hartman 
233372dc1c09SGreg Kroah-Hartman /* Removes a network device in the network device table */
remove_net_device(struct hso_device * hso_dev)233472dc1c09SGreg Kroah-Hartman static int remove_net_device(struct hso_device *hso_dev)
233572dc1c09SGreg Kroah-Hartman {
233672dc1c09SGreg Kroah-Hartman 	int i;
233772dc1c09SGreg Kroah-Hartman 
233872dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
233972dc1c09SGreg Kroah-Hartman 		if (network_table[i] == hso_dev) {
234072dc1c09SGreg Kroah-Hartman 			network_table[i] = NULL;
234172dc1c09SGreg Kroah-Hartman 			break;
234272dc1c09SGreg Kroah-Hartman 		}
234372dc1c09SGreg Kroah-Hartman 	}
234472dc1c09SGreg Kroah-Hartman 	if (i == HSO_MAX_NET_DEVICES)
234572dc1c09SGreg Kroah-Hartman 		return -1;
234672dc1c09SGreg Kroah-Hartman 	return 0;
234772dc1c09SGreg Kroah-Hartman }
234872dc1c09SGreg Kroah-Hartman 
234972dc1c09SGreg Kroah-Hartman /* Frees our network device */
hso_free_net_device(struct hso_device * hso_dev)2350dcb713d5SDongliang Mu static void hso_free_net_device(struct hso_device *hso_dev)
235172dc1c09SGreg Kroah-Hartman {
235272dc1c09SGreg Kroah-Hartman 	int i;
235372dc1c09SGreg Kroah-Hartman 	struct hso_net *hso_net = dev2net(hso_dev);
235472dc1c09SGreg Kroah-Hartman 
235572dc1c09SGreg Kroah-Hartman 	if (!hso_net)
235672dc1c09SGreg Kroah-Hartman 		return;
235772dc1c09SGreg Kroah-Hartman 
235872dc1c09SGreg Kroah-Hartman 	remove_net_device(hso_net->parent);
235972dc1c09SGreg Kroah-Hartman 
23605e2cd082SGreg KH 	if (hso_net->net)
236172dc1c09SGreg Kroah-Hartman 		unregister_netdev(hso_net->net);
2362ab153d84SDavid S. Miller 
23633b7d2b31SJan Dumon 	/* start freeing */
23643b7d2b31SJan Dumon 	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
23653b7d2b31SJan Dumon 		usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]);
23663b7d2b31SJan Dumon 		kfree(hso_net->mux_bulk_rx_buf_pool[i]);
23673b7d2b31SJan Dumon 		hso_net->mux_bulk_rx_buf_pool[i] = NULL;
23683b7d2b31SJan Dumon 	}
23693b7d2b31SJan Dumon 	usb_free_urb(hso_net->mux_bulk_tx_urb);
23703b7d2b31SJan Dumon 	kfree(hso_net->mux_bulk_tx_buf);
23713b7d2b31SJan Dumon 	hso_net->mux_bulk_tx_buf = NULL;
23723b7d2b31SJan Dumon 
2373dcb713d5SDongliang Mu 	if (hso_net->net)
23745e2cd082SGreg KH 		free_netdev(hso_net->net);
23755e2cd082SGreg KH 
2376e44578eaSPaulius Zaleckas 	kfree(hso_dev);
237772dc1c09SGreg Kroah-Hartman }
237872dc1c09SGreg Kroah-Hartman 
2379c266cb4eSStephen Hemminger static const struct net_device_ops hso_netdev_ops = {
2380c266cb4eSStephen Hemminger 	.ndo_open	= hso_net_open,
2381c266cb4eSStephen Hemminger 	.ndo_stop	= hso_net_close,
2382c266cb4eSStephen Hemminger 	.ndo_start_xmit = hso_net_start_xmit,
2383c266cb4eSStephen Hemminger 	.ndo_tx_timeout = hso_net_tx_timeout,
2384c266cb4eSStephen Hemminger };
2385c266cb4eSStephen Hemminger 
238672dc1c09SGreg Kroah-Hartman /* initialize the network interface */
hso_net_init(struct net_device * net)238772dc1c09SGreg Kroah-Hartman static void hso_net_init(struct net_device *net)
238872dc1c09SGreg Kroah-Hartman {
238972dc1c09SGreg Kroah-Hartman 	struct hso_net *hso_net = netdev_priv(net);
239072dc1c09SGreg Kroah-Hartman 
239195a69117SJoe Perches 	hso_dbg(0x1, "sizeof hso_net is %zu\n", sizeof(*hso_net));
239272dc1c09SGreg Kroah-Hartman 
239372dc1c09SGreg Kroah-Hartman 	/* fill in the other fields */
2394c266cb4eSStephen Hemminger 	net->netdev_ops = &hso_netdev_ops;
239572dc1c09SGreg Kroah-Hartman 	net->watchdog_timeo = HSO_NET_TX_TIMEOUT;
239672dc1c09SGreg Kroah-Hartman 	net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
239772dc1c09SGreg Kroah-Hartman 	net->type = ARPHRD_NONE;
239872dc1c09SGreg Kroah-Hartman 	net->mtu = DEFAULT_MTU - 14;
239972dc1c09SGreg Kroah-Hartman 	net->tx_queue_len = 10;
24007ad24ea4SWilfried Klaebe 	net->ethtool_ops = &ops;
240172dc1c09SGreg Kroah-Hartman 
240272dc1c09SGreg Kroah-Hartman 	/* and initialize the semaphore */
240372dc1c09SGreg Kroah-Hartman 	spin_lock_init(&hso_net->net_lock);
240472dc1c09SGreg Kroah-Hartman }
240572dc1c09SGreg Kroah-Hartman 
240672dc1c09SGreg Kroah-Hartman /* Adds a network device in the network device table */
add_net_device(struct hso_device * hso_dev)240772dc1c09SGreg Kroah-Hartman static int add_net_device(struct hso_device *hso_dev)
240872dc1c09SGreg Kroah-Hartman {
240972dc1c09SGreg Kroah-Hartman 	int i;
241072dc1c09SGreg Kroah-Hartman 
241172dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
241272dc1c09SGreg Kroah-Hartman 		if (network_table[i] == NULL) {
241372dc1c09SGreg Kroah-Hartman 			network_table[i] = hso_dev;
241472dc1c09SGreg Kroah-Hartman 			break;
241572dc1c09SGreg Kroah-Hartman 		}
241672dc1c09SGreg Kroah-Hartman 	}
241772dc1c09SGreg Kroah-Hartman 	if (i == HSO_MAX_NET_DEVICES)
241872dc1c09SGreg Kroah-Hartman 		return -1;
241972dc1c09SGreg Kroah-Hartman 	return 0;
242072dc1c09SGreg Kroah-Hartman }
242172dc1c09SGreg Kroah-Hartman 
hso_rfkill_set_block(void * data,bool blocked)242219d337dfSJohannes Berg static int hso_rfkill_set_block(void *data, bool blocked)
242372dc1c09SGreg Kroah-Hartman {
242472dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev = data;
242519d337dfSJohannes Berg 	int enabled = !blocked;
242672dc1c09SGreg Kroah-Hartman 	int rv;
242772dc1c09SGreg Kroah-Hartman 
2428ab153d84SDavid S. Miller 	mutex_lock(&hso_dev->mutex);
242972dc1c09SGreg Kroah-Hartman 	if (hso_dev->usb_gone)
243072dc1c09SGreg Kroah-Hartman 		rv = 0;
243172dc1c09SGreg Kroah-Hartman 	else
24321a6e9a9cSJohan Hovold 		rv = usb_control_msg(hso_dev->usb, usb_sndctrlpipe(hso_dev->usb, 0),
243372dc1c09SGreg Kroah-Hartman 				       enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0,
243472dc1c09SGreg Kroah-Hartman 				       USB_CTRL_SET_TIMEOUT);
2435ab153d84SDavid S. Miller 	mutex_unlock(&hso_dev->mutex);
243672dc1c09SGreg Kroah-Hartman 	return rv;
243772dc1c09SGreg Kroah-Hartman }
243872dc1c09SGreg Kroah-Hartman 
243919d337dfSJohannes Berg static const struct rfkill_ops hso_rfkill_ops = {
244019d337dfSJohannes Berg 	.set_block = hso_rfkill_set_block,
244119d337dfSJohannes Berg };
244219d337dfSJohannes Berg 
244372dc1c09SGreg Kroah-Hartman /* Creates and sets up everything for rfkill */
hso_create_rfkill(struct hso_device * hso_dev,struct usb_interface * interface)244472dc1c09SGreg Kroah-Hartman static void hso_create_rfkill(struct hso_device *hso_dev,
244572dc1c09SGreg Kroah-Hartman 			     struct usb_interface *interface)
244672dc1c09SGreg Kroah-Hartman {
244772dc1c09SGreg Kroah-Hartman 	struct hso_net *hso_net = dev2net(hso_dev);
2448939a9516SJonathan McDowell 	struct device *dev = &hso_net->net->dev;
244938121067SOlivier Sobrie 	static u32 rfkill_counter;
245072dc1c09SGreg Kroah-Hartman 
24512e6d01ffSOlivier Sobrie 	snprintf(hso_net->name, sizeof(hso_net->name), "hso-%d",
245238121067SOlivier Sobrie 		 rfkill_counter++);
245319d337dfSJohannes Berg 
24542e6d01ffSOlivier Sobrie 	hso_net->rfkill = rfkill_alloc(hso_net->name,
245519d337dfSJohannes Berg 				       &interface_to_usbdev(interface)->dev,
245619d337dfSJohannes Berg 				       RFKILL_TYPE_WWAN,
245719d337dfSJohannes Berg 				       &hso_rfkill_ops, hso_dev);
245811c5f6d2SOliver Neukum 	if (!hso_net->rfkill)
245919d337dfSJohannes Berg 		return;
246011c5f6d2SOliver Neukum 
246119d337dfSJohannes Berg 	if (rfkill_register(hso_net->rfkill) < 0) {
246219d337dfSJohannes Berg 		rfkill_destroy(hso_net->rfkill);
2463939a9516SJonathan McDowell 		hso_net->rfkill = NULL;
2464939a9516SJonathan McDowell 		dev_err(dev, "%s - Failed to register rfkill\n", __func__);
246572dc1c09SGreg Kroah-Hartman 		return;
246672dc1c09SGreg Kroah-Hartman 	}
246772dc1c09SGreg Kroah-Hartman }
246872dc1c09SGreg Kroah-Hartman 
2469384912edSMarcel Holtmann static struct device_type hso_type = {
2470384912edSMarcel Holtmann 	.name	= "wwan",
2471384912edSMarcel Holtmann };
2472384912edSMarcel Holtmann 
247372dc1c09SGreg Kroah-Hartman /* Creates our network device */
hso_create_net_device(struct usb_interface * interface,int port_spec)24740de8ca59SJan Dumon static struct hso_device *hso_create_net_device(struct usb_interface *interface,
24750de8ca59SJan Dumon 						int port_spec)
247672dc1c09SGreg Kroah-Hartman {
247772dc1c09SGreg Kroah-Hartman 	int result, i;
247872dc1c09SGreg Kroah-Hartman 	struct net_device *net;
247972dc1c09SGreg Kroah-Hartman 	struct hso_net *hso_net;
248072dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev;
248172dc1c09SGreg Kroah-Hartman 
24820de8ca59SJan Dumon 	hso_dev = hso_create_device(interface, port_spec);
248372dc1c09SGreg Kroah-Hartman 	if (!hso_dev)
248472dc1c09SGreg Kroah-Hartman 		return NULL;
248572dc1c09SGreg Kroah-Hartman 
248672dc1c09SGreg Kroah-Hartman 	/* allocate our network device, then we can put in our private data */
248772dc1c09SGreg Kroah-Hartman 	/* call hso_net_init to do the basic initialization */
2488c835a677STom Gundersen 	net = alloc_netdev(sizeof(struct hso_net), "hso%d", NET_NAME_UNKNOWN,
2489c835a677STom Gundersen 			   hso_net_init);
249072dc1c09SGreg Kroah-Hartman 	if (!net) {
249172dc1c09SGreg Kroah-Hartman 		dev_err(&interface->dev, "Unable to create ethernet device\n");
2492788e67f1SDongliang Mu 		goto err_hso_dev;
249372dc1c09SGreg Kroah-Hartman 	}
249472dc1c09SGreg Kroah-Hartman 
249572dc1c09SGreg Kroah-Hartman 	hso_net = netdev_priv(net);
249672dc1c09SGreg Kroah-Hartman 
249772dc1c09SGreg Kroah-Hartman 	hso_dev->port_data.dev_net = hso_net;
249872dc1c09SGreg Kroah-Hartman 	hso_net->net = net;
249972dc1c09SGreg Kroah-Hartman 	hso_net->parent = hso_dev;
250072dc1c09SGreg Kroah-Hartman 
250172dc1c09SGreg Kroah-Hartman 	hso_net->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
250272dc1c09SGreg Kroah-Hartman 				      USB_DIR_IN);
250372dc1c09SGreg Kroah-Hartman 	if (!hso_net->in_endp) {
250472dc1c09SGreg Kroah-Hartman 		dev_err(&interface->dev, "Can't find BULK IN endpoint\n");
2505788e67f1SDongliang Mu 		goto err_net;
250672dc1c09SGreg Kroah-Hartman 	}
250772dc1c09SGreg Kroah-Hartman 	hso_net->out_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
250872dc1c09SGreg Kroah-Hartman 				       USB_DIR_OUT);
250972dc1c09SGreg Kroah-Hartman 	if (!hso_net->out_endp) {
251072dc1c09SGreg Kroah-Hartman 		dev_err(&interface->dev, "Can't find BULK OUT endpoint\n");
2511788e67f1SDongliang Mu 		goto err_net;
251272dc1c09SGreg Kroah-Hartman 	}
251372dc1c09SGreg Kroah-Hartman 	SET_NETDEV_DEV(net, &interface->dev);
2514384912edSMarcel Holtmann 	SET_NETDEV_DEVTYPE(net, &hso_type);
251572dc1c09SGreg Kroah-Hartman 
251672dc1c09SGreg Kroah-Hartman 	/* start allocating */
251772dc1c09SGreg Kroah-Hartman 	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
251872dc1c09SGreg Kroah-Hartman 		hso_net->mux_bulk_rx_urb_pool[i] = usb_alloc_urb(0, GFP_KERNEL);
251912800ea9SWolfram Sang 		if (!hso_net->mux_bulk_rx_urb_pool[i])
2520788e67f1SDongliang Mu 			goto err_mux_bulk_rx;
252172dc1c09SGreg Kroah-Hartman 		hso_net->mux_bulk_rx_buf_pool[i] = kzalloc(MUX_BULK_RX_BUF_SIZE,
252272dc1c09SGreg Kroah-Hartman 							   GFP_KERNEL);
252338673c82SJoe Perches 		if (!hso_net->mux_bulk_rx_buf_pool[i])
2524788e67f1SDongliang Mu 			goto err_mux_bulk_rx;
252572dc1c09SGreg Kroah-Hartman 	}
252672dc1c09SGreg Kroah-Hartman 	hso_net->mux_bulk_tx_urb = usb_alloc_urb(0, GFP_KERNEL);
252712800ea9SWolfram Sang 	if (!hso_net->mux_bulk_tx_urb)
2528788e67f1SDongliang Mu 		goto err_mux_bulk_rx;
252972dc1c09SGreg Kroah-Hartman 	hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL);
253038673c82SJoe Perches 	if (!hso_net->mux_bulk_tx_buf)
2531788e67f1SDongliang Mu 		goto err_free_tx_urb;
253272dc1c09SGreg Kroah-Hartman 
2533ecdc28deSZiyang Xuan 	result = add_net_device(hso_dev);
2534ecdc28deSZiyang Xuan 	if (result) {
2535ecdc28deSZiyang Xuan 		dev_err(&interface->dev, "Failed to add net device\n");
2536ecdc28deSZiyang Xuan 		goto err_free_tx_buf;
2537ecdc28deSZiyang Xuan 	}
253872dc1c09SGreg Kroah-Hartman 
25394c761dafSAndreas Kemnade 	/* registering our net device */
25404c761dafSAndreas Kemnade 	result = register_netdev(net);
25414c761dafSAndreas Kemnade 	if (result) {
25424c761dafSAndreas Kemnade 		dev_err(&interface->dev, "Failed to register device\n");
2543ecdc28deSZiyang Xuan 		goto err_rmv_ndev;
25444c761dafSAndreas Kemnade 	}
25454c761dafSAndreas Kemnade 
254672dc1c09SGreg Kroah-Hartman 	hso_log_port(hso_dev);
254772dc1c09SGreg Kroah-Hartman 
254872dc1c09SGreg Kroah-Hartman 	hso_create_rfkill(hso_dev, interface);
254972dc1c09SGreg Kroah-Hartman 
255072dc1c09SGreg Kroah-Hartman 	return hso_dev;
2551788e67f1SDongliang Mu 
2552ecdc28deSZiyang Xuan err_rmv_ndev:
2553788e67f1SDongliang Mu 	remove_net_device(hso_dev);
2554ecdc28deSZiyang Xuan err_free_tx_buf:
2555788e67f1SDongliang Mu 	kfree(hso_net->mux_bulk_tx_buf);
2556788e67f1SDongliang Mu err_free_tx_urb:
2557788e67f1SDongliang Mu 	usb_free_urb(hso_net->mux_bulk_tx_urb);
2558788e67f1SDongliang Mu err_mux_bulk_rx:
2559788e67f1SDongliang Mu 	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
2560788e67f1SDongliang Mu 		usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]);
2561788e67f1SDongliang Mu 		kfree(hso_net->mux_bulk_rx_buf_pool[i]);
2562788e67f1SDongliang Mu 	}
2563788e67f1SDongliang Mu err_net:
2564788e67f1SDongliang Mu 	free_netdev(net);
2565788e67f1SDongliang Mu err_hso_dev:
2566788e67f1SDongliang Mu 	kfree(hso_dev);
256772dc1c09SGreg Kroah-Hartman 	return NULL;
256872dc1c09SGreg Kroah-Hartman }
256972dc1c09SGreg Kroah-Hartman 
hso_free_tiomget(struct hso_serial * serial)2570542f5482SDenis Joseph Barrow static void hso_free_tiomget(struct hso_serial *serial)
2571542f5482SDenis Joseph Barrow {
25725b89db0eSJesper Juhl 	struct hso_tiocmget *tiocmget;
25735b89db0eSJesper Juhl 	if (!serial)
25745b89db0eSJesper Juhl 		return;
25755b89db0eSJesper Juhl 	tiocmget = serial->tiocmget;
2576542f5482SDenis Joseph Barrow 	if (tiocmget) {
2577542f5482SDenis Joseph Barrow 		usb_free_urb(tiocmget->urb);
2578542f5482SDenis Joseph Barrow 		tiocmget->urb = NULL;
2579542f5482SDenis Joseph Barrow 		serial->tiocmget = NULL;
2580af0de130SOliver Neukum 		kfree(tiocmget->serial_state_notification);
2581af0de130SOliver Neukum 		tiocmget->serial_state_notification = NULL;
25823b7d2b31SJan Dumon 		kfree(tiocmget);
2583542f5482SDenis Joseph Barrow 	}
2584542f5482SDenis Joseph Barrow }
2585542f5482SDenis Joseph Barrow 
258672dc1c09SGreg Kroah-Hartman /* Frees an AT channel ( goes for both mux and non-mux ) */
hso_free_serial_device(struct hso_device * hso_dev)258772dc1c09SGreg Kroah-Hartman static void hso_free_serial_device(struct hso_device *hso_dev)
258872dc1c09SGreg Kroah-Hartman {
258972dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial = dev2ser(hso_dev);
259072dc1c09SGreg Kroah-Hartman 
259172dc1c09SGreg Kroah-Hartman 	if (!serial)
259272dc1c09SGreg Kroah-Hartman 		return;
259372dc1c09SGreg Kroah-Hartman 
259472dc1c09SGreg Kroah-Hartman 	hso_serial_common_free(serial);
259572dc1c09SGreg Kroah-Hartman 
259672dc1c09SGreg Kroah-Hartman 	if (serial->shared_int) {
259772dc1c09SGreg Kroah-Hartman 		mutex_lock(&serial->shared_int->shared_int_lock);
259872dc1c09SGreg Kroah-Hartman 		if (--serial->shared_int->ref_count == 0)
259972dc1c09SGreg Kroah-Hartman 			hso_free_shared_int(serial->shared_int);
260072dc1c09SGreg Kroah-Hartman 		else
260172dc1c09SGreg Kroah-Hartman 			mutex_unlock(&serial->shared_int->shared_int_lock);
260272dc1c09SGreg Kroah-Hartman 	}
2603542f5482SDenis Joseph Barrow 	hso_free_tiomget(serial);
260472dc1c09SGreg Kroah-Hartman 	kfree(serial);
2605e44578eaSPaulius Zaleckas 	kfree(hso_dev);
260672dc1c09SGreg Kroah-Hartman }
260772dc1c09SGreg Kroah-Hartman 
260872dc1c09SGreg Kroah-Hartman /* Creates a bulk AT channel */
hso_create_bulk_serial_device(struct usb_interface * interface,int port)260972dc1c09SGreg Kroah-Hartman static struct hso_device *hso_create_bulk_serial_device(
261072dc1c09SGreg Kroah-Hartman 			struct usb_interface *interface, int port)
261172dc1c09SGreg Kroah-Hartman {
261272dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev;
261372dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial;
261472dc1c09SGreg Kroah-Hartman 	int num_urbs;
2615542f5482SDenis Joseph Barrow 	struct hso_tiocmget *tiocmget;
261672dc1c09SGreg Kroah-Hartman 
261772dc1c09SGreg Kroah-Hartman 	hso_dev = hso_create_device(interface, port);
261872dc1c09SGreg Kroah-Hartman 	if (!hso_dev)
261972dc1c09SGreg Kroah-Hartman 		return NULL;
262072dc1c09SGreg Kroah-Hartman 
262172dc1c09SGreg Kroah-Hartman 	serial = kzalloc(sizeof(*serial), GFP_KERNEL);
262272dc1c09SGreg Kroah-Hartman 	if (!serial)
262372dc1c09SGreg Kroah-Hartman 		goto exit;
262472dc1c09SGreg Kroah-Hartman 
262572dc1c09SGreg Kroah-Hartman 	serial->parent = hso_dev;
262672dc1c09SGreg Kroah-Hartman 	hso_dev->port_data.dev_serial = serial;
262772dc1c09SGreg Kroah-Hartman 
262858eb17f1SDenis Joseph Barrow 	if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
262972dc1c09SGreg Kroah-Hartman 		num_urbs = 2;
2630542f5482SDenis Joseph Barrow 		serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget),
2631542f5482SDenis Joseph Barrow 					   GFP_KERNEL);
263231db0dbdSDan Carpenter 		if (!serial->tiocmget)
263331db0dbdSDan Carpenter 			goto exit;
2634af0de130SOliver Neukum 		serial->tiocmget->serial_state_notification
2635af0de130SOliver Neukum 			= kzalloc(sizeof(struct hso_serial_state_notification),
2636af0de130SOliver Neukum 					   GFP_KERNEL);
263731db0dbdSDan Carpenter 		if (!serial->tiocmget->serial_state_notification)
263831db0dbdSDan Carpenter 			goto exit;
2639542f5482SDenis Joseph Barrow 		tiocmget = serial->tiocmget;
26408353da9fSJohan Hovold 		tiocmget->endp = hso_get_ep(interface,
26418353da9fSJohan Hovold 					    USB_ENDPOINT_XFER_INT,
26428353da9fSJohan Hovold 					    USB_DIR_IN);
26438353da9fSJohan Hovold 		if (!tiocmget->endp) {
26448353da9fSJohan Hovold 			dev_err(&interface->dev, "Failed to find INT IN ep\n");
26458353da9fSJohan Hovold 			goto exit;
26468353da9fSJohan Hovold 		}
26478353da9fSJohan Hovold 
2648542f5482SDenis Joseph Barrow 		tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL);
26494d52ebc7SJohan Hovold 		if (!tiocmget->urb)
26504d52ebc7SJohan Hovold 			goto exit;
26514d52ebc7SJohan Hovold 
2652542f5482SDenis Joseph Barrow 		mutex_init(&tiocmget->mutex);
2653542f5482SDenis Joseph Barrow 		init_waitqueue_head(&tiocmget->waitq);
26544d52ebc7SJohan Hovold 	} else {
265572dc1c09SGreg Kroah-Hartman 		num_urbs = 1;
26564d52ebc7SJohan Hovold 	}
265772dc1c09SGreg Kroah-Hartman 
265872dc1c09SGreg Kroah-Hartman 	if (hso_serial_common_create(serial, num_urbs, BULK_URB_RX_SIZE,
265972dc1c09SGreg Kroah-Hartman 				     BULK_URB_TX_SIZE))
266072dc1c09SGreg Kroah-Hartman 		goto exit;
266172dc1c09SGreg Kroah-Hartman 
266272dc1c09SGreg Kroah-Hartman 	serial->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
266372dc1c09SGreg Kroah-Hartman 				     USB_DIR_IN);
266472dc1c09SGreg Kroah-Hartman 	if (!serial->in_endp) {
266572dc1c09SGreg Kroah-Hartman 		dev_err(&interface->dev, "Failed to find BULK IN ep\n");
2666e57b641dSAdrian Bunk 		goto exit2;
266772dc1c09SGreg Kroah-Hartman 	}
266872dc1c09SGreg Kroah-Hartman 
266972dc1c09SGreg Kroah-Hartman 	if (!
267072dc1c09SGreg Kroah-Hartman 	    (serial->out_endp =
267172dc1c09SGreg Kroah-Hartman 	     hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT))) {
26721a10186eSOliver Neukum 		dev_err(&interface->dev, "Failed to find BULK OUT ep\n");
2673e57b641dSAdrian Bunk 		goto exit2;
267472dc1c09SGreg Kroah-Hartman 	}
267572dc1c09SGreg Kroah-Hartman 
267672dc1c09SGreg Kroah-Hartman 	serial->write_data = hso_std_serial_write_data;
267772dc1c09SGreg Kroah-Hartman 
267872dc1c09SGreg Kroah-Hartman 	/* setup the proc dirs and files if needed */
267972dc1c09SGreg Kroah-Hartman 	hso_log_port(hso_dev);
268072dc1c09SGreg Kroah-Hartman 
268172dc1c09SGreg Kroah-Hartman 	/* done, return it */
268272dc1c09SGreg Kroah-Hartman 	return hso_dev;
2683e57b641dSAdrian Bunk 
2684e57b641dSAdrian Bunk exit2:
268569b377b3SOlivier Sobrie 	hso_serial_tty_unregister(serial);
268672dc1c09SGreg Kroah-Hartman 	hso_serial_common_free(serial);
2687e57b641dSAdrian Bunk exit:
2688542f5482SDenis Joseph Barrow 	hso_free_tiomget(serial);
268972dc1c09SGreg Kroah-Hartman 	kfree(serial);
2690e44578eaSPaulius Zaleckas 	kfree(hso_dev);
269172dc1c09SGreg Kroah-Hartman 	return NULL;
269272dc1c09SGreg Kroah-Hartman }
269372dc1c09SGreg Kroah-Hartman 
269472dc1c09SGreg Kroah-Hartman /* Creates a multiplexed AT channel */
269572dc1c09SGreg Kroah-Hartman static
hso_create_mux_serial_device(struct usb_interface * interface,int port,struct hso_shared_int * mux)269672dc1c09SGreg Kroah-Hartman struct hso_device *hso_create_mux_serial_device(struct usb_interface *interface,
269772dc1c09SGreg Kroah-Hartman 						int port,
269872dc1c09SGreg Kroah-Hartman 						struct hso_shared_int *mux)
269972dc1c09SGreg Kroah-Hartman {
270072dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev;
270172dc1c09SGreg Kroah-Hartman 	struct hso_serial *serial;
270272dc1c09SGreg Kroah-Hartman 	int port_spec;
270372dc1c09SGreg Kroah-Hartman 
270472dc1c09SGreg Kroah-Hartman 	port_spec = HSO_INTF_MUX;
270572dc1c09SGreg Kroah-Hartman 	port_spec &= ~HSO_PORT_MASK;
270672dc1c09SGreg Kroah-Hartman 
270772dc1c09SGreg Kroah-Hartman 	port_spec |= hso_mux_to_port(port);
270872dc1c09SGreg Kroah-Hartman 	if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NO_PORT)
270972dc1c09SGreg Kroah-Hartman 		return NULL;
271072dc1c09SGreg Kroah-Hartman 
271172dc1c09SGreg Kroah-Hartman 	hso_dev = hso_create_device(interface, port_spec);
271272dc1c09SGreg Kroah-Hartman 	if (!hso_dev)
271372dc1c09SGreg Kroah-Hartman 		return NULL;
271472dc1c09SGreg Kroah-Hartman 
271572dc1c09SGreg Kroah-Hartman 	serial = kzalloc(sizeof(*serial), GFP_KERNEL);
271672dc1c09SGreg Kroah-Hartman 	if (!serial)
2717e8f69b16SJohan Hovold 		goto err_free_dev;
271872dc1c09SGreg Kroah-Hartman 
271972dc1c09SGreg Kroah-Hartman 	hso_dev->port_data.dev_serial = serial;
272072dc1c09SGreg Kroah-Hartman 	serial->parent = hso_dev;
272172dc1c09SGreg Kroah-Hartman 
272272dc1c09SGreg Kroah-Hartman 	if (hso_serial_common_create
272372dc1c09SGreg Kroah-Hartman 	    (serial, 1, CTRL_URB_RX_SIZE, CTRL_URB_TX_SIZE))
2724e8f69b16SJohan Hovold 		goto err_free_serial;
272572dc1c09SGreg Kroah-Hartman 
272672dc1c09SGreg Kroah-Hartman 	serial->tx_data_length--;
272772dc1c09SGreg Kroah-Hartman 	serial->write_data = hso_mux_serial_write_data;
272872dc1c09SGreg Kroah-Hartman 
272972dc1c09SGreg Kroah-Hartman 	serial->shared_int = mux;
273072dc1c09SGreg Kroah-Hartman 	mutex_lock(&serial->shared_int->shared_int_lock);
273172dc1c09SGreg Kroah-Hartman 	serial->shared_int->ref_count++;
273272dc1c09SGreg Kroah-Hartman 	mutex_unlock(&serial->shared_int->shared_int_lock);
273372dc1c09SGreg Kroah-Hartman 
273472dc1c09SGreg Kroah-Hartman 	/* setup the proc dirs and files if needed */
273572dc1c09SGreg Kroah-Hartman 	hso_log_port(hso_dev);
273672dc1c09SGreg Kroah-Hartman 
273772dc1c09SGreg Kroah-Hartman 	/* done, return it */
273872dc1c09SGreg Kroah-Hartman 	return hso_dev;
273972dc1c09SGreg Kroah-Hartman 
2740e8f69b16SJohan Hovold err_free_serial:
274172dc1c09SGreg Kroah-Hartman 	kfree(serial);
2742e8f69b16SJohan Hovold err_free_dev:
2743e44578eaSPaulius Zaleckas 	kfree(hso_dev);
274472dc1c09SGreg Kroah-Hartman 	return NULL;
274572dc1c09SGreg Kroah-Hartman 
274672dc1c09SGreg Kroah-Hartman }
274772dc1c09SGreg Kroah-Hartman 
hso_free_shared_int(struct hso_shared_int * mux)274872dc1c09SGreg Kroah-Hartman static void hso_free_shared_int(struct hso_shared_int *mux)
274972dc1c09SGreg Kroah-Hartman {
275072dc1c09SGreg Kroah-Hartman 	usb_free_urb(mux->shared_intr_urb);
275172dc1c09SGreg Kroah-Hartman 	kfree(mux->shared_intr_buf);
275272dc1c09SGreg Kroah-Hartman 	mutex_unlock(&mux->shared_int_lock);
275372dc1c09SGreg Kroah-Hartman 	kfree(mux);
275472dc1c09SGreg Kroah-Hartman }
275572dc1c09SGreg Kroah-Hartman 
275672dc1c09SGreg Kroah-Hartman static
hso_create_shared_int(struct usb_interface * interface)275772dc1c09SGreg Kroah-Hartman struct hso_shared_int *hso_create_shared_int(struct usb_interface *interface)
275872dc1c09SGreg Kroah-Hartman {
275972dc1c09SGreg Kroah-Hartman 	struct hso_shared_int *mux = kzalloc(sizeof(*mux), GFP_KERNEL);
276072dc1c09SGreg Kroah-Hartman 
276172dc1c09SGreg Kroah-Hartman 	if (!mux)
276272dc1c09SGreg Kroah-Hartman 		return NULL;
276372dc1c09SGreg Kroah-Hartman 
276472dc1c09SGreg Kroah-Hartman 	mux->intr_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_INT,
276572dc1c09SGreg Kroah-Hartman 				    USB_DIR_IN);
276672dc1c09SGreg Kroah-Hartman 	if (!mux->intr_endp) {
276772dc1c09SGreg Kroah-Hartman 		dev_err(&interface->dev, "Can't find INT IN endpoint\n");
276872dc1c09SGreg Kroah-Hartman 		goto exit;
276972dc1c09SGreg Kroah-Hartman 	}
277072dc1c09SGreg Kroah-Hartman 
277172dc1c09SGreg Kroah-Hartman 	mux->shared_intr_urb = usb_alloc_urb(0, GFP_KERNEL);
277212800ea9SWolfram Sang 	if (!mux->shared_intr_urb)
277372dc1c09SGreg Kroah-Hartman 		goto exit;
2774d9ced80dSJan Dumon 	mux->shared_intr_buf =
2775d9ced80dSJan Dumon 		kzalloc(le16_to_cpu(mux->intr_endp->wMaxPacketSize),
277672dc1c09SGreg Kroah-Hartman 			GFP_KERNEL);
277738673c82SJoe Perches 	if (!mux->shared_intr_buf)
277872dc1c09SGreg Kroah-Hartman 		goto exit;
277972dc1c09SGreg Kroah-Hartman 
278072dc1c09SGreg Kroah-Hartman 	mutex_init(&mux->shared_int_lock);
278172dc1c09SGreg Kroah-Hartman 
278272dc1c09SGreg Kroah-Hartman 	return mux;
278372dc1c09SGreg Kroah-Hartman 
278472dc1c09SGreg Kroah-Hartman exit:
278572dc1c09SGreg Kroah-Hartman 	kfree(mux->shared_intr_buf);
278672dc1c09SGreg Kroah-Hartman 	usb_free_urb(mux->shared_intr_urb);
278772dc1c09SGreg Kroah-Hartman 	kfree(mux);
278872dc1c09SGreg Kroah-Hartman 	return NULL;
278972dc1c09SGreg Kroah-Hartman }
279072dc1c09SGreg Kroah-Hartman 
279172dc1c09SGreg Kroah-Hartman /* Gets the port spec for a certain interface */
hso_get_config_data(struct usb_interface * interface)279272dc1c09SGreg Kroah-Hartman static int hso_get_config_data(struct usb_interface *interface)
279372dc1c09SGreg Kroah-Hartman {
279472dc1c09SGreg Kroah-Hartman 	struct usb_device *usbdev = interface_to_usbdev(interface);
2795e75dc677SDaniel Gimpelevich 	u8 *config_data = kmalloc(17, GFP_KERNEL);
279664bea46eSAleksander Morgado 	u32 if_num = interface->cur_altsetting->desc.bInterfaceNumber;
279772dc1c09SGreg Kroah-Hartman 	s32 result;
279872dc1c09SGreg Kroah-Hartman 
2799e75dc677SDaniel Gimpelevich 	if (!config_data)
2800e75dc677SDaniel Gimpelevich 		return -ENOMEM;
280172dc1c09SGreg Kroah-Hartman 	if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
280272dc1c09SGreg Kroah-Hartman 			    0x86, 0xC0, 0, 0, config_data, 17,
280372dc1c09SGreg Kroah-Hartman 			    USB_CTRL_SET_TIMEOUT) != 0x11) {
2804e75dc677SDaniel Gimpelevich 		kfree(config_data);
280572dc1c09SGreg Kroah-Hartman 		return -EIO;
280672dc1c09SGreg Kroah-Hartman 	}
280772dc1c09SGreg Kroah-Hartman 
28085146f95dSHui Peng 	/* check if we have a valid interface */
28095146f95dSHui Peng 	if (if_num > 16) {
28105146f95dSHui Peng 		kfree(config_data);
28115146f95dSHui Peng 		return -EINVAL;
28125146f95dSHui Peng 	}
28135146f95dSHui Peng 
281472dc1c09SGreg Kroah-Hartman 	switch (config_data[if_num]) {
281572dc1c09SGreg Kroah-Hartman 	case 0x0:
281672dc1c09SGreg Kroah-Hartman 		result = 0;
281772dc1c09SGreg Kroah-Hartman 		break;
281872dc1c09SGreg Kroah-Hartman 	case 0x1:
281972dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_DIAG;
282072dc1c09SGreg Kroah-Hartman 		break;
282172dc1c09SGreg Kroah-Hartman 	case 0x2:
282272dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_GPS;
282372dc1c09SGreg Kroah-Hartman 		break;
282472dc1c09SGreg Kroah-Hartman 	case 0x3:
282572dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_GPS_CONTROL;
282672dc1c09SGreg Kroah-Hartman 		break;
282772dc1c09SGreg Kroah-Hartman 	case 0x4:
282872dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_APP;
282972dc1c09SGreg Kroah-Hartman 		break;
283072dc1c09SGreg Kroah-Hartman 	case 0x5:
283172dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_APP2;
283272dc1c09SGreg Kroah-Hartman 		break;
283372dc1c09SGreg Kroah-Hartman 	case 0x6:
283472dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_CONTROL;
283572dc1c09SGreg Kroah-Hartman 		break;
283672dc1c09SGreg Kroah-Hartman 	case 0x7:
283772dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_NETWORK;
283872dc1c09SGreg Kroah-Hartman 		break;
283972dc1c09SGreg Kroah-Hartman 	case 0x8:
284072dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_MODEM;
284172dc1c09SGreg Kroah-Hartman 		break;
284272dc1c09SGreg Kroah-Hartman 	case 0x9:
284372dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_MSD;
284472dc1c09SGreg Kroah-Hartman 		break;
284572dc1c09SGreg Kroah-Hartman 	case 0xa:
284672dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_PCSC;
284772dc1c09SGreg Kroah-Hartman 		break;
284872dc1c09SGreg Kroah-Hartman 	case 0xb:
284972dc1c09SGreg Kroah-Hartman 		result = HSO_PORT_VOICE;
285072dc1c09SGreg Kroah-Hartman 		break;
285172dc1c09SGreg Kroah-Hartman 	default:
285272dc1c09SGreg Kroah-Hartman 		result = 0;
285372dc1c09SGreg Kroah-Hartman 	}
285472dc1c09SGreg Kroah-Hartman 
285572dc1c09SGreg Kroah-Hartman 	if (result)
285672dc1c09SGreg Kroah-Hartman 		result |= HSO_INTF_BULK;
285772dc1c09SGreg Kroah-Hartman 
285872dc1c09SGreg Kroah-Hartman 	if (config_data[16] & 0x1)
285972dc1c09SGreg Kroah-Hartman 		result |= HSO_INFO_CRC_BUG;
286072dc1c09SGreg Kroah-Hartman 
2861e75dc677SDaniel Gimpelevich 	kfree(config_data);
286272dc1c09SGreg Kroah-Hartman 	return result;
286372dc1c09SGreg Kroah-Hartman }
286472dc1c09SGreg Kroah-Hartman 
286572dc1c09SGreg Kroah-Hartman /* called once for each interface upon device insertion */
hso_probe(struct usb_interface * interface,const struct usb_device_id * id)286672dc1c09SGreg Kroah-Hartman static int hso_probe(struct usb_interface *interface,
286772dc1c09SGreg Kroah-Hartman 		     const struct usb_device_id *id)
286872dc1c09SGreg Kroah-Hartman {
286972dc1c09SGreg Kroah-Hartman 	int mux, i, if_num, port_spec;
287072dc1c09SGreg Kroah-Hartman 	unsigned char port_mask;
287172dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev = NULL;
287272dc1c09SGreg Kroah-Hartman 	struct hso_shared_int *shared_int;
287372dc1c09SGreg Kroah-Hartman 	struct hso_device *tmp_dev = NULL;
287472dc1c09SGreg Kroah-Hartman 
287535e57e1bSDaniel Gimpelevich 	if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
287635e57e1bSDaniel Gimpelevich 		dev_err(&interface->dev, "Not our interface\n");
287735e57e1bSDaniel Gimpelevich 		return -ENODEV;
287835e57e1bSDaniel Gimpelevich 	}
287935e57e1bSDaniel Gimpelevich 
288064bea46eSAleksander Morgado 	if_num = interface->cur_altsetting->desc.bInterfaceNumber;
288172dc1c09SGreg Kroah-Hartman 
288272dc1c09SGreg Kroah-Hartman 	/* Get the interface/port specification from either driver_info or from
288372dc1c09SGreg Kroah-Hartman 	 * the device itself */
28845146f95dSHui Peng 	if (id->driver_info) {
28855146f95dSHui Peng 		/* if_num is controlled by the device, driver_info is a 0 terminated
28865146f95dSHui Peng 		 * array. Make sure, the access is in bounds! */
28875146f95dSHui Peng 		for (i = 0; i <= if_num; ++i)
28885146f95dSHui Peng 			if (((u32 *)(id->driver_info))[i] == 0)
28895146f95dSHui Peng 				goto exit;
289072dc1c09SGreg Kroah-Hartman 		port_spec = ((u32 *)(id->driver_info))[if_num];
28915146f95dSHui Peng 	} else {
289272dc1c09SGreg Kroah-Hartman 		port_spec = hso_get_config_data(interface);
28935146f95dSHui Peng 		if (port_spec < 0)
28945146f95dSHui Peng 			goto exit;
28955146f95dSHui Peng 	}
289672dc1c09SGreg Kroah-Hartman 
289772dc1c09SGreg Kroah-Hartman 	/* Check if we need to switch to alt interfaces prior to port
289872dc1c09SGreg Kroah-Hartman 	 * configuration */
289972dc1c09SGreg Kroah-Hartman 	if (interface->num_altsetting > 1)
290072dc1c09SGreg Kroah-Hartman 		usb_set_interface(interface_to_usbdev(interface), if_num, 1);
290172dc1c09SGreg Kroah-Hartman 	interface->needs_remote_wakeup = 1;
290272dc1c09SGreg Kroah-Hartman 
290372dc1c09SGreg Kroah-Hartman 	/* Allocate new hso device(s) */
290472dc1c09SGreg Kroah-Hartman 	switch (port_spec & HSO_INTF_MASK) {
290572dc1c09SGreg Kroah-Hartman 	case HSO_INTF_MUX:
290672dc1c09SGreg Kroah-Hartman 		if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
290772dc1c09SGreg Kroah-Hartman 			/* Create the network device */
290872dc1c09SGreg Kroah-Hartman 			if (!disable_net) {
29090de8ca59SJan Dumon 				hso_dev = hso_create_net_device(interface,
29100de8ca59SJan Dumon 								port_spec);
291172dc1c09SGreg Kroah-Hartman 				if (!hso_dev)
291272dc1c09SGreg Kroah-Hartman 					goto exit;
291372dc1c09SGreg Kroah-Hartman 				tmp_dev = hso_dev;
291472dc1c09SGreg Kroah-Hartman 			}
291572dc1c09SGreg Kroah-Hartman 		}
291672dc1c09SGreg Kroah-Hartman 
291772dc1c09SGreg Kroah-Hartman 		if (hso_get_mux_ports(interface, &port_mask))
291872dc1c09SGreg Kroah-Hartman 			/* TODO: de-allocate everything */
291972dc1c09SGreg Kroah-Hartman 			goto exit;
292072dc1c09SGreg Kroah-Hartman 
292172dc1c09SGreg Kroah-Hartman 		shared_int = hso_create_shared_int(interface);
292272dc1c09SGreg Kroah-Hartman 		if (!shared_int)
292372dc1c09SGreg Kroah-Hartman 			goto exit;
292472dc1c09SGreg Kroah-Hartman 
292572dc1c09SGreg Kroah-Hartman 		for (i = 1, mux = 0; i < 0x100; i = i << 1, mux++) {
292672dc1c09SGreg Kroah-Hartman 			if (port_mask & i) {
292772dc1c09SGreg Kroah-Hartman 				hso_dev = hso_create_mux_serial_device(
292872dc1c09SGreg Kroah-Hartman 						interface, i, shared_int);
292972dc1c09SGreg Kroah-Hartman 				if (!hso_dev)
293072dc1c09SGreg Kroah-Hartman 					goto exit;
293172dc1c09SGreg Kroah-Hartman 			}
293272dc1c09SGreg Kroah-Hartman 		}
293372dc1c09SGreg Kroah-Hartman 
293472dc1c09SGreg Kroah-Hartman 		if (tmp_dev)
293572dc1c09SGreg Kroah-Hartman 			hso_dev = tmp_dev;
293672dc1c09SGreg Kroah-Hartman 		break;
293772dc1c09SGreg Kroah-Hartman 
293872dc1c09SGreg Kroah-Hartman 	case HSO_INTF_BULK:
293972dc1c09SGreg Kroah-Hartman 		/* It's a regular bulk interface */
29408e65c0ecSFilip Aben 		if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
29418e65c0ecSFilip Aben 			if (!disable_net)
29428e65c0ecSFilip Aben 				hso_dev =
29438e65c0ecSFilip Aben 				    hso_create_net_device(interface, port_spec);
29448e65c0ecSFilip Aben 		} else {
294572dc1c09SGreg Kroah-Hartman 			hso_dev =
294672dc1c09SGreg Kroah-Hartman 			    hso_create_bulk_serial_device(interface, port_spec);
29478e65c0ecSFilip Aben 		}
294872dc1c09SGreg Kroah-Hartman 		if (!hso_dev)
294972dc1c09SGreg Kroah-Hartman 			goto exit;
295072dc1c09SGreg Kroah-Hartman 		break;
295172dc1c09SGreg Kroah-Hartman 	default:
295272dc1c09SGreg Kroah-Hartman 		goto exit;
295372dc1c09SGreg Kroah-Hartman 	}
295472dc1c09SGreg Kroah-Hartman 
295572dc1c09SGreg Kroah-Hartman 	/* save our data pointer in this device */
295672dc1c09SGreg Kroah-Hartman 	usb_set_intfdata(interface, hso_dev);
295772dc1c09SGreg Kroah-Hartman 
295872dc1c09SGreg Kroah-Hartman 	/* done */
295972dc1c09SGreg Kroah-Hartman 	return 0;
296072dc1c09SGreg Kroah-Hartman exit:
296172dc1c09SGreg Kroah-Hartman 	hso_free_interface(interface);
296272dc1c09SGreg Kroah-Hartman 	return -ENODEV;
296372dc1c09SGreg Kroah-Hartman }
296472dc1c09SGreg Kroah-Hartman 
296572dc1c09SGreg Kroah-Hartman /* device removed, cleaning up */
hso_disconnect(struct usb_interface * interface)296672dc1c09SGreg Kroah-Hartman static void hso_disconnect(struct usb_interface *interface)
296772dc1c09SGreg Kroah-Hartman {
296872dc1c09SGreg Kroah-Hartman 	hso_free_interface(interface);
296972dc1c09SGreg Kroah-Hartman 
297072dc1c09SGreg Kroah-Hartman 	/* remove reference of our private data */
297172dc1c09SGreg Kroah-Hartman 	usb_set_intfdata(interface, NULL);
297272dc1c09SGreg Kroah-Hartman }
297372dc1c09SGreg Kroah-Hartman 
async_get_intf(struct work_struct * data)297472dc1c09SGreg Kroah-Hartman static void async_get_intf(struct work_struct *data)
297572dc1c09SGreg Kroah-Hartman {
297672dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev =
297772dc1c09SGreg Kroah-Hartman 	    container_of(data, struct hso_device, async_get_intf);
297872dc1c09SGreg Kroah-Hartman 	usb_autopm_get_interface(hso_dev->interface);
297972dc1c09SGreg Kroah-Hartman }
298072dc1c09SGreg Kroah-Hartman 
async_put_intf(struct work_struct * data)298172dc1c09SGreg Kroah-Hartman static void async_put_intf(struct work_struct *data)
298272dc1c09SGreg Kroah-Hartman {
298372dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev =
298472dc1c09SGreg Kroah-Hartman 	    container_of(data, struct hso_device, async_put_intf);
298572dc1c09SGreg Kroah-Hartman 	usb_autopm_put_interface(hso_dev->interface);
298672dc1c09SGreg Kroah-Hartman }
298772dc1c09SGreg Kroah-Hartman 
hso_get_activity(struct hso_device * hso_dev)298872dc1c09SGreg Kroah-Hartman static int hso_get_activity(struct hso_device *hso_dev)
298972dc1c09SGreg Kroah-Hartman {
299072dc1c09SGreg Kroah-Hartman 	if (hso_dev->usb->state == USB_STATE_SUSPENDED) {
299172dc1c09SGreg Kroah-Hartman 		if (!hso_dev->is_active) {
299272dc1c09SGreg Kroah-Hartman 			hso_dev->is_active = 1;
299372dc1c09SGreg Kroah-Hartman 			schedule_work(&hso_dev->async_get_intf);
299472dc1c09SGreg Kroah-Hartman 		}
299572dc1c09SGreg Kroah-Hartman 	}
299672dc1c09SGreg Kroah-Hartman 
299772dc1c09SGreg Kroah-Hartman 	if (hso_dev->usb->state != USB_STATE_CONFIGURED)
299872dc1c09SGreg Kroah-Hartman 		return -EAGAIN;
299972dc1c09SGreg Kroah-Hartman 
300072dc1c09SGreg Kroah-Hartman 	usb_mark_last_busy(hso_dev->usb);
300172dc1c09SGreg Kroah-Hartman 
300272dc1c09SGreg Kroah-Hartman 	return 0;
300372dc1c09SGreg Kroah-Hartman }
300472dc1c09SGreg Kroah-Hartman 
hso_put_activity(struct hso_device * hso_dev)300572dc1c09SGreg Kroah-Hartman static int hso_put_activity(struct hso_device *hso_dev)
300672dc1c09SGreg Kroah-Hartman {
300772dc1c09SGreg Kroah-Hartman 	if (hso_dev->usb->state != USB_STATE_SUSPENDED) {
300872dc1c09SGreg Kroah-Hartman 		if (hso_dev->is_active) {
300972dc1c09SGreg Kroah-Hartman 			hso_dev->is_active = 0;
301072dc1c09SGreg Kroah-Hartman 			schedule_work(&hso_dev->async_put_intf);
301172dc1c09SGreg Kroah-Hartman 			return -EAGAIN;
301272dc1c09SGreg Kroah-Hartman 		}
301372dc1c09SGreg Kroah-Hartman 	}
301472dc1c09SGreg Kroah-Hartman 	hso_dev->is_active = 0;
301572dc1c09SGreg Kroah-Hartman 	return 0;
301672dc1c09SGreg Kroah-Hartman }
301772dc1c09SGreg Kroah-Hartman 
301872dc1c09SGreg Kroah-Hartman /* called by kernel when we need to suspend device */
hso_suspend(struct usb_interface * iface,pm_message_t message)301972dc1c09SGreg Kroah-Hartman static int hso_suspend(struct usb_interface *iface, pm_message_t message)
302072dc1c09SGreg Kroah-Hartman {
302172dc1c09SGreg Kroah-Hartman 	int i, result;
302272dc1c09SGreg Kroah-Hartman 
302372dc1c09SGreg Kroah-Hartman 	/* Stop all serial ports */
302472dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
302572dc1c09SGreg Kroah-Hartman 		if (serial_table[i] && (serial_table[i]->interface == iface)) {
302672dc1c09SGreg Kroah-Hartman 			result = hso_stop_serial_device(serial_table[i]);
302772dc1c09SGreg Kroah-Hartman 			if (result)
302872dc1c09SGreg Kroah-Hartman 				goto out;
302972dc1c09SGreg Kroah-Hartman 		}
303072dc1c09SGreg Kroah-Hartman 	}
303172dc1c09SGreg Kroah-Hartman 
303272dc1c09SGreg Kroah-Hartman 	/* Stop all network ports */
303372dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
303472dc1c09SGreg Kroah-Hartman 		if (network_table[i] &&
303572dc1c09SGreg Kroah-Hartman 		    (network_table[i]->interface == iface)) {
303672dc1c09SGreg Kroah-Hartman 			result = hso_stop_net_device(network_table[i]);
303772dc1c09SGreg Kroah-Hartman 			if (result)
303872dc1c09SGreg Kroah-Hartman 				goto out;
303972dc1c09SGreg Kroah-Hartman 		}
304072dc1c09SGreg Kroah-Hartman 	}
304172dc1c09SGreg Kroah-Hartman 
304272dc1c09SGreg Kroah-Hartman out:
304372dc1c09SGreg Kroah-Hartman 	return 0;
304472dc1c09SGreg Kroah-Hartman }
304572dc1c09SGreg Kroah-Hartman 
304672dc1c09SGreg Kroah-Hartman /* called by kernel when we need to resume device */
hso_resume(struct usb_interface * iface)304772dc1c09SGreg Kroah-Hartman static int hso_resume(struct usb_interface *iface)
304872dc1c09SGreg Kroah-Hartman {
304972dc1c09SGreg Kroah-Hartman 	int i, result = 0;
305072dc1c09SGreg Kroah-Hartman 	struct hso_net *hso_net;
305172dc1c09SGreg Kroah-Hartman 
305272dc1c09SGreg Kroah-Hartman 	/* Start all serial ports */
305372dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
305472dc1c09SGreg Kroah-Hartman 		if (serial_table[i] && (serial_table[i]->interface == iface)) {
30555ce76e77SJiri Slaby 			if (dev2ser(serial_table[i])->port.count) {
305672dc1c09SGreg Kroah-Hartman 				result =
305772dc1c09SGreg Kroah-Hartman 				    hso_start_serial_device(serial_table[i], GFP_NOIO);
305872dc1c09SGreg Kroah-Hartman 				hso_kick_transmit(dev2ser(serial_table[i]));
305972dc1c09SGreg Kroah-Hartman 				if (result)
306072dc1c09SGreg Kroah-Hartman 					goto out;
306172dc1c09SGreg Kroah-Hartman 			}
306272dc1c09SGreg Kroah-Hartman 		}
306372dc1c09SGreg Kroah-Hartman 	}
306472dc1c09SGreg Kroah-Hartman 
306572dc1c09SGreg Kroah-Hartman 	/* Start all network ports */
306672dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
306772dc1c09SGreg Kroah-Hartman 		if (network_table[i] &&
306872dc1c09SGreg Kroah-Hartman 		    (network_table[i]->interface == iface)) {
306972dc1c09SGreg Kroah-Hartman 			hso_net = dev2net(network_table[i]);
307089930b7bSDenis Joseph Barrow 			if (hso_net->flags & IFF_UP) {
307189930b7bSDenis Joseph Barrow 				/* First transmit any lingering data,
307289930b7bSDenis Joseph Barrow 				   then restart the device. */
307372dc1c09SGreg Kroah-Hartman 				if (hso_net->skb_tx_buf) {
307472dc1c09SGreg Kroah-Hartman 					dev_dbg(&iface->dev,
307589930b7bSDenis Joseph Barrow 						"Transmitting"
307689930b7bSDenis Joseph Barrow 						" lingering data\n");
307772dc1c09SGreg Kroah-Hartman 					hso_net_start_xmit(hso_net->skb_tx_buf,
307872dc1c09SGreg Kroah-Hartman 							   hso_net->net);
3079c213f286SDenis Joseph Barrow 					hso_net->skb_tx_buf = NULL;
308072dc1c09SGreg Kroah-Hartman 				}
308172dc1c09SGreg Kroah-Hartman 				result = hso_start_net_device(network_table[i]);
308272dc1c09SGreg Kroah-Hartman 				if (result)
308372dc1c09SGreg Kroah-Hartman 					goto out;
308472dc1c09SGreg Kroah-Hartman 			}
308572dc1c09SGreg Kroah-Hartman 		}
308689930b7bSDenis Joseph Barrow 	}
308772dc1c09SGreg Kroah-Hartman 
308872dc1c09SGreg Kroah-Hartman out:
308972dc1c09SGreg Kroah-Hartman 	return result;
309072dc1c09SGreg Kroah-Hartman }
309172dc1c09SGreg Kroah-Hartman 
hso_serial_ref_free(struct kref * ref)309272dc1c09SGreg Kroah-Hartman static void hso_serial_ref_free(struct kref *ref)
309372dc1c09SGreg Kroah-Hartman {
309472dc1c09SGreg Kroah-Hartman 	struct hso_device *hso_dev = container_of(ref, struct hso_device, ref);
309572dc1c09SGreg Kroah-Hartman 
309672dc1c09SGreg Kroah-Hartman 	hso_free_serial_device(hso_dev);
309772dc1c09SGreg Kroah-Hartman }
309872dc1c09SGreg Kroah-Hartman 
hso_free_interface(struct usb_interface * interface)309972dc1c09SGreg Kroah-Hartman static void hso_free_interface(struct usb_interface *interface)
310072dc1c09SGreg Kroah-Hartman {
3101f6516b69SOlivier Sobrie 	struct hso_serial *serial;
310272dc1c09SGreg Kroah-Hartman 	int i;
310372dc1c09SGreg Kroah-Hartman 
310472dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
31058e95a202SJoe Perches 		if (serial_table[i] &&
31068e95a202SJoe Perches 		    (serial_table[i]->interface == interface)) {
3107f6516b69SOlivier Sobrie 			serial = dev2ser(serial_table[i]);
3108f6516b69SOlivier Sobrie 			tty_port_tty_hangup(&serial->port, false);
3109f6516b69SOlivier Sobrie 			mutex_lock(&serial->parent->mutex);
3110f6516b69SOlivier Sobrie 			serial->parent->usb_gone = 1;
3111f6516b69SOlivier Sobrie 			mutex_unlock(&serial->parent->mutex);
3112cc491970SOlivier Sobrie 			cancel_work_sync(&serial_table[i]->async_put_intf);
3113cc491970SOlivier Sobrie 			cancel_work_sync(&serial_table[i]->async_get_intf);
311469b377b3SOlivier Sobrie 			hso_serial_tty_unregister(serial);
31152ad5692dSJohan Hovold 			kref_put(&serial->parent->ref, hso_serial_ref_free);
311672dc1c09SGreg Kroah-Hartman 		}
311772dc1c09SGreg Kroah-Hartman 	}
311872dc1c09SGreg Kroah-Hartman 
311972dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
31208e95a202SJoe Perches 		if (network_table[i] &&
31218e95a202SJoe Perches 		    (network_table[i]->interface == interface)) {
312272dc1c09SGreg Kroah-Hartman 			struct rfkill *rfk = dev2net(network_table[i])->rfkill;
312372dc1c09SGreg Kroah-Hartman 			/* hso_stop_net_device doesn't stop the net queue since
312472dc1c09SGreg Kroah-Hartman 			 * traffic needs to start it again when suspended */
312572dc1c09SGreg Kroah-Hartman 			netif_stop_queue(dev2net(network_table[i])->net);
312672dc1c09SGreg Kroah-Hartman 			hso_stop_net_device(network_table[i]);
312772dc1c09SGreg Kroah-Hartman 			cancel_work_sync(&network_table[i]->async_put_intf);
312872dc1c09SGreg Kroah-Hartman 			cancel_work_sync(&network_table[i]->async_get_intf);
312919d337dfSJohannes Berg 			if (rfk) {
313072dc1c09SGreg Kroah-Hartman 				rfkill_unregister(rfk);
313119d337dfSJohannes Berg 				rfkill_destroy(rfk);
313219d337dfSJohannes Berg 			}
3133dcb713d5SDongliang Mu 			hso_free_net_device(network_table[i]);
313472dc1c09SGreg Kroah-Hartman 		}
313572dc1c09SGreg Kroah-Hartman 	}
313672dc1c09SGreg Kroah-Hartman }
313772dc1c09SGreg Kroah-Hartman 
313872dc1c09SGreg Kroah-Hartman /* Helper functions */
313972dc1c09SGreg Kroah-Hartman 
314072dc1c09SGreg Kroah-Hartman /* Get the endpoint ! */
hso_get_ep(struct usb_interface * intf,int type,int dir)314172dc1c09SGreg Kroah-Hartman static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
314272dc1c09SGreg Kroah-Hartman 						  int type, int dir)
314372dc1c09SGreg Kroah-Hartman {
314472dc1c09SGreg Kroah-Hartman 	int i;
314572dc1c09SGreg Kroah-Hartman 	struct usb_host_interface *iface = intf->cur_altsetting;
314672dc1c09SGreg Kroah-Hartman 	struct usb_endpoint_descriptor *endp;
314772dc1c09SGreg Kroah-Hartman 
314872dc1c09SGreg Kroah-Hartman 	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
314972dc1c09SGreg Kroah-Hartman 		endp = &iface->endpoint[i].desc;
315072dc1c09SGreg Kroah-Hartman 		if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) &&
3151f201a8a4SJulia Lawall 		    (usb_endpoint_type(endp) == type))
315272dc1c09SGreg Kroah-Hartman 			return endp;
315372dc1c09SGreg Kroah-Hartman 	}
315472dc1c09SGreg Kroah-Hartman 
315572dc1c09SGreg Kroah-Hartman 	return NULL;
315672dc1c09SGreg Kroah-Hartman }
315772dc1c09SGreg Kroah-Hartman 
315872dc1c09SGreg Kroah-Hartman /* Get the byte that describes which ports are enabled */
hso_get_mux_ports(struct usb_interface * intf,unsigned char * ports)315972dc1c09SGreg Kroah-Hartman static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports)
316072dc1c09SGreg Kroah-Hartman {
316172dc1c09SGreg Kroah-Hartman 	int i;
316272dc1c09SGreg Kroah-Hartman 	struct usb_host_interface *iface = intf->cur_altsetting;
316372dc1c09SGreg Kroah-Hartman 
316472dc1c09SGreg Kroah-Hartman 	if (iface->extralen == 3) {
316572dc1c09SGreg Kroah-Hartman 		*ports = iface->extra[2];
316672dc1c09SGreg Kroah-Hartman 		return 0;
316772dc1c09SGreg Kroah-Hartman 	}
316872dc1c09SGreg Kroah-Hartman 
316972dc1c09SGreg Kroah-Hartman 	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
317072dc1c09SGreg Kroah-Hartman 		if (iface->endpoint[i].extralen == 3) {
317172dc1c09SGreg Kroah-Hartman 			*ports = iface->endpoint[i].extra[2];
317272dc1c09SGreg Kroah-Hartman 			return 0;
317372dc1c09SGreg Kroah-Hartman 		}
317472dc1c09SGreg Kroah-Hartman 	}
317572dc1c09SGreg Kroah-Hartman 
317672dc1c09SGreg Kroah-Hartman 	return -1;
317772dc1c09SGreg Kroah-Hartman }
317872dc1c09SGreg Kroah-Hartman 
317972dc1c09SGreg Kroah-Hartman /* interrupt urb needs to be submitted, used for serial read of muxed port */
hso_mux_submit_intr_urb(struct hso_shared_int * shared_int,struct usb_device * usb,gfp_t gfp)318072dc1c09SGreg Kroah-Hartman static int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int,
318172dc1c09SGreg Kroah-Hartman 				   struct usb_device *usb, gfp_t gfp)
318272dc1c09SGreg Kroah-Hartman {
318372dc1c09SGreg Kroah-Hartman 	int result;
318472dc1c09SGreg Kroah-Hartman 
318572dc1c09SGreg Kroah-Hartman 	usb_fill_int_urb(shared_int->shared_intr_urb, usb,
318672dc1c09SGreg Kroah-Hartman 			 usb_rcvintpipe(usb,
318772dc1c09SGreg Kroah-Hartman 				shared_int->intr_endp->bEndpointAddress & 0x7F),
318872dc1c09SGreg Kroah-Hartman 			 shared_int->shared_intr_buf,
3189d9ced80dSJan Dumon 			 1,
319072dc1c09SGreg Kroah-Hartman 			 intr_callback, shared_int,
319172dc1c09SGreg Kroah-Hartman 			 shared_int->intr_endp->bInterval);
319272dc1c09SGreg Kroah-Hartman 
319372dc1c09SGreg Kroah-Hartman 	result = usb_submit_urb(shared_int->shared_intr_urb, gfp);
319472dc1c09SGreg Kroah-Hartman 	if (result)
31958a5c9c49SJan Dumon 		dev_warn(&usb->dev, "%s failed mux_intr_urb %d\n", __func__,
319672dc1c09SGreg Kroah-Hartman 			result);
319772dc1c09SGreg Kroah-Hartman 
319872dc1c09SGreg Kroah-Hartman 	return result;
319972dc1c09SGreg Kroah-Hartman }
320072dc1c09SGreg Kroah-Hartman 
320172dc1c09SGreg Kroah-Hartman /* operations setup of the serial interface */
32026c59f569SGreg Kroah-Hartman static const struct tty_operations hso_serial_ops = {
320372dc1c09SGreg Kroah-Hartman 	.open = hso_serial_open,
320472dc1c09SGreg Kroah-Hartman 	.close = hso_serial_close,
320572dc1c09SGreg Kroah-Hartman 	.write = hso_serial_write,
320672dc1c09SGreg Kroah-Hartman 	.write_room = hso_serial_write_room,
320729bd3bc1SOlivier Sobrie 	.cleanup = hso_serial_cleanup,
3208542f5482SDenis Joseph Barrow 	.ioctl = hso_serial_ioctl,
320972dc1c09SGreg Kroah-Hartman 	.set_termios = hso_serial_set_termios,
321072dc1c09SGreg Kroah-Hartman 	.chars_in_buffer = hso_serial_chars_in_buffer,
321172dc1c09SGreg Kroah-Hartman 	.tiocmget = hso_serial_tiocmget,
321272dc1c09SGreg Kroah-Hartman 	.tiocmset = hso_serial_tiocmset,
32130bca1b91SAlan Cox 	.get_icount = hso_get_count,
32148ef5ba63SDenis Joseph Barrow 	.unthrottle = hso_unthrottle
321572dc1c09SGreg Kroah-Hartman };
321672dc1c09SGreg Kroah-Hartman 
321772dc1c09SGreg Kroah-Hartman static struct usb_driver hso_driver = {
321872dc1c09SGreg Kroah-Hartman 	.name = driver_name,
321972dc1c09SGreg Kroah-Hartman 	.probe = hso_probe,
322072dc1c09SGreg Kroah-Hartman 	.disconnect = hso_disconnect,
322172dc1c09SGreg Kroah-Hartman 	.id_table = hso_ids,
322272dc1c09SGreg Kroah-Hartman 	.suspend = hso_suspend,
322372dc1c09SGreg Kroah-Hartman 	.resume = hso_resume,
32249c8f92aeSDenis Joseph Barrow 	.reset_resume = hso_resume,
322572dc1c09SGreg Kroah-Hartman 	.supports_autosuspend = 1,
3226e1f12eb6SSarah Sharp 	.disable_hub_initiated_lpm = 1,
322772dc1c09SGreg Kroah-Hartman };
322872dc1c09SGreg Kroah-Hartman 
hso_init(void)322972dc1c09SGreg Kroah-Hartman static int __init hso_init(void)
323072dc1c09SGreg Kroah-Hartman {
323172dc1c09SGreg Kroah-Hartman 	int i;
323272dc1c09SGreg Kroah-Hartman 	int result;
323372dc1c09SGreg Kroah-Hartman 
323472dc1c09SGreg Kroah-Hartman 	/* put it in the log */
32353981cce6SJoe Perches 	pr_info("%s\n", version);
323672dc1c09SGreg Kroah-Hartman 
323772dc1c09SGreg Kroah-Hartman 	/* Initialise the serial table semaphore and table */
323872dc1c09SGreg Kroah-Hartman 	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++)
323972dc1c09SGreg Kroah-Hartman 		serial_table[i] = NULL;
324072dc1c09SGreg Kroah-Hartman 
324172dc1c09SGreg Kroah-Hartman 	/* allocate our driver using the proper amount of supported minors */
324239b7b42bSJiri Slaby 	tty_drv = tty_alloc_driver(HSO_SERIAL_TTY_MINORS, TTY_DRIVER_REAL_RAW |
324339b7b42bSJiri Slaby 			TTY_DRIVER_DYNAMIC_DEV);
324439b7b42bSJiri Slaby 	if (IS_ERR(tty_drv))
324539b7b42bSJiri Slaby 		return PTR_ERR(tty_drv);
324672dc1c09SGreg Kroah-Hartman 
324772dc1c09SGreg Kroah-Hartman 	/* fill in all needed values */
324872dc1c09SGreg Kroah-Hartman 	tty_drv->driver_name = driver_name;
324972dc1c09SGreg Kroah-Hartman 	tty_drv->name = tty_filename;
325072dc1c09SGreg Kroah-Hartman 
325172dc1c09SGreg Kroah-Hartman 	/* if major number is provided as parameter, use that one */
325272dc1c09SGreg Kroah-Hartman 	if (tty_major)
325372dc1c09SGreg Kroah-Hartman 		tty_drv->major = tty_major;
325472dc1c09SGreg Kroah-Hartman 
325572dc1c09SGreg Kroah-Hartman 	tty_drv->minor_start = 0;
325672dc1c09SGreg Kroah-Hartman 	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
325772dc1c09SGreg Kroah-Hartman 	tty_drv->subtype = SERIAL_TYPE_NORMAL;
325872dc1c09SGreg Kroah-Hartman 	tty_drv->init_termios = tty_std_termios;
3259ac9720c3SAlan Cox 	hso_init_termios(&tty_drv->init_termios);
326072dc1c09SGreg Kroah-Hartman 	tty_set_operations(tty_drv, &hso_serial_ops);
326172dc1c09SGreg Kroah-Hartman 
326272dc1c09SGreg Kroah-Hartman 	/* register the tty driver */
326372dc1c09SGreg Kroah-Hartman 	result = tty_register_driver(tty_drv);
326472dc1c09SGreg Kroah-Hartman 	if (result) {
32653981cce6SJoe Perches 		pr_err("%s - tty_register_driver failed(%d)\n",
326672dc1c09SGreg Kroah-Hartman 		       __func__, result);
3267d230788fSJiri Slaby 		goto err_free_tty;
326872dc1c09SGreg Kroah-Hartman 	}
326972dc1c09SGreg Kroah-Hartman 
327072dc1c09SGreg Kroah-Hartman 	/* register this module as an usb driver */
327172dc1c09SGreg Kroah-Hartman 	result = usb_register(&hso_driver);
327272dc1c09SGreg Kroah-Hartman 	if (result) {
32733981cce6SJoe Perches 		pr_err("Could not register hso driver - error: %d\n", result);
3274d230788fSJiri Slaby 		goto err_unreg_tty;
327572dc1c09SGreg Kroah-Hartman 	}
327672dc1c09SGreg Kroah-Hartman 
327772dc1c09SGreg Kroah-Hartman 	/* done */
327872dc1c09SGreg Kroah-Hartman 	return 0;
3279d230788fSJiri Slaby err_unreg_tty:
3280d230788fSJiri Slaby 	tty_unregister_driver(tty_drv);
3281d230788fSJiri Slaby err_free_tty:
32829f90a4ddSJiri Slaby 	tty_driver_kref_put(tty_drv);
3283d230788fSJiri Slaby 	return result;
328472dc1c09SGreg Kroah-Hartman }
328572dc1c09SGreg Kroah-Hartman 
hso_exit(void)328672dc1c09SGreg Kroah-Hartman static void __exit hso_exit(void)
328772dc1c09SGreg Kroah-Hartman {
32883981cce6SJoe Perches 	pr_info("unloaded\n");
328972dc1c09SGreg Kroah-Hartman 
329072dc1c09SGreg Kroah-Hartman 	tty_unregister_driver(tty_drv);
329172dc1c09SGreg Kroah-Hartman 	/* deregister the usb driver */
329272dc1c09SGreg Kroah-Hartman 	usb_deregister(&hso_driver);
32939f90a4ddSJiri Slaby 	tty_driver_kref_put(tty_drv);
329472dc1c09SGreg Kroah-Hartman }
329572dc1c09SGreg Kroah-Hartman 
329672dc1c09SGreg Kroah-Hartman /* Module definitions */
329772dc1c09SGreg Kroah-Hartman module_init(hso_init);
329872dc1c09SGreg Kroah-Hartman module_exit(hso_exit);
329972dc1c09SGreg Kroah-Hartman 
330072dc1c09SGreg Kroah-Hartman MODULE_AUTHOR(MOD_AUTHOR);
330172dc1c09SGreg Kroah-Hartman MODULE_DESCRIPTION(MOD_DESCRIPTION);
3302461ee7f3SGreg Kroah-Hartman MODULE_LICENSE("GPL");
330372dc1c09SGreg Kroah-Hartman 
330472dc1c09SGreg Kroah-Hartman /* change the debug level (eg: insmod hso.ko debug=0x04) */
330595a69117SJoe Perches MODULE_PARM_DESC(debug, "debug level mask [0x01 | 0x02 | 0x04 | 0x08 | 0x10]");
3306d61e4038SJoe Perches module_param(debug, int, 0644);
330772dc1c09SGreg Kroah-Hartman 
330872dc1c09SGreg Kroah-Hartman /* set the major tty number (eg: insmod hso.ko tty_major=245) */
330972dc1c09SGreg Kroah-Hartman MODULE_PARM_DESC(tty_major, "Set the major tty number");
3310d61e4038SJoe Perches module_param(tty_major, int, 0644);
331172dc1c09SGreg Kroah-Hartman 
331272dc1c09SGreg Kroah-Hartman /* disable network interface (eg: insmod hso.ko disable_net=1) */
331372dc1c09SGreg Kroah-Hartman MODULE_PARM_DESC(disable_net, "Disable the network interface");
3314d61e4038SJoe Perches module_param(disable_net, int, 0644);
3315